diff options
Diffstat (limited to 'platform/android/java')
48 files changed, 1882 insertions, 524 deletions
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 7e9ce70d80..cc480d1c84 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -6,28 +6,21 @@ android:versionName="1.0" android:installLocation="auto" > - <!-- Adding custom text to the manifest is fine, but do it outside the custom USER and APPLICATION BEGIN/END comments, --> - <!-- as that gets rewritten. --> - <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" /> - <!-- glEsVersion is modified by the exporter, changing this value here has no effect. --> <uses-feature android:glEsVersion="0x00020000" android:required="true" /> -<!-- Custom user permissions XML added by add-ons. It's recommended to add them from the export preset, though. --> -<!--CHUNK_USER_PERMISSIONS_BEGIN--> -<!--CHUNK_USER_PERMISSIONS_END--> - - <!-- Any tag in this line after android:icon will be erased when doing custom builds. --> - <!-- If you want to add tags manually, do before it. --> - <!-- WARNING: This should stay on a single line until the parsing code is improved. See GH-32414. --> - <application android:label="@string/godot_project_name_string" android:allowBackup="false" tools:ignore="GoogleAppIndexingWarning" android:icon="@mipmap/icon" > + <application + android:label="@string/godot_project_name_string" + android:allowBackup="false" + tools:ignore="GoogleAppIndexingWarning" + android:icon="@mipmap/icon" > <!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. --> <!-- Do these changes in the export preset. Adding new ones is fine. --> @@ -37,6 +30,11 @@ android:name="xr_mode_metadata_name" android:value="xr_mode_metadata_value" /> + <!-- Metadata populated at export time and used by Godot to figure out which plugins must be enabled. --> + <meta-data + android:name="custom_template_plugins" + android:value="custom_template_plugins_value"/> + <activity android:name=".GodotApp" android:label="@string/godot_project_name_string" @@ -53,10 +51,6 @@ </intent-filter> </activity> -<!-- Custom application XML added by add-ons. --> -<!--CHUNK_APPLICATION_BEGIN--> -<!--CHUNK_APPLICATION_END--> - </application> </manifest> diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 2e4f2ffab0..9ae47d6174 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -1,7 +1,4 @@ // Gradle build config for Godot Engine's Android port. -// -// Do not remove/modify comments ending with BEGIN/END, they are used to inject -// addon-specific configuration. apply from: 'config.gradle' buildscript { @@ -10,13 +7,10 @@ buildscript { repositories { google() jcenter() -//CHUNK_BUILDSCRIPT_REPOSITORIES_BEGIN -//CHUNK_BUILDSCRIPT_REPOSITORIES_END } dependencies { classpath libraries.androidGradlePlugin -//CHUNK_BUILDSCRIPT_DEPENDENCIES_BEGIN -//CHUNK_BUILDSCRIPT_DEPENDENCIES_END + classpath libraries.kotlinGradlePlugin } } @@ -27,37 +21,51 @@ allprojects { mavenCentral() google() jcenter() -//CHUNK_ALLPROJECTS_REPOSITORIES_BEGIN -//CHUNK_ALLPROJECTS_REPOSITORIES_END } } dependencies { implementation libraries.supportCoreUtils + implementation libraries.kotlinStdLib + implementation libraries.v4Support if (rootProject.findProject(":lib")) { implementation project(":lib") + } else if (rootProject.findProject(":godot:lib")) { + implementation project(":godot:lib") } else { // Custom build mode. In this scenario this project is the only one around and the Godot // library is available through the pre-generated godot-lib.*.aar android archive files. debugImplementation fileTree(dir: 'libs/debug', include: ['*.jar', '*.aar']) releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar']) } -//CHUNK_DEPENDENCIES_BEGIN -//CHUNK_DEPENDENCIES_END + + // Godot prebuilt plugins + implementation fileTree(dir: 'libs/plugins', include: ["GodotPayment*.aar"]) + + // Godot user plugins dependencies + String pluginsDir = getGodotPluginsDirectory() + String[] pluginsBinaries = getGodotPluginsBinaries() + if (pluginsDir != null && !pluginsDir.isEmpty() && + pluginsBinaries != null && pluginsBinaries.size() > 0) { + implementation fileTree(dir: pluginsDir, include: pluginsBinaries) + } } android { compileSdkVersion versions.compileSdk buildToolsVersion versions.buildTools + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + defaultConfig { // Feel free to modify the application id to your own. applicationId getExportPackageName() minSdkVersion versions.minSdk targetSdkVersion versions.targetSdk -//CHUNK_ANDROID_DEFAULTCONFIG_BEGIN -//CHUNK_ANDROID_DEFAULTCONFIG_END } lintOptions { @@ -68,6 +76,9 @@ android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' + + // Should be uncommented for development purpose within Android Studio + // doNotStrip '**/*.so' } // Both signing and zip-aligning will be done at export time @@ -79,37 +90,13 @@ android { sourceSets { main { manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = [ - 'src' -//DIR_SRC_BEGIN -//DIR_SRC_END - ] - res.srcDirs = [ - 'res' -//DIR_RES_BEGIN -//DIR_RES_END - ] - aidl.srcDirs = [ - 'aidl' -//DIR_AIDL_BEGIN -//DIR_AIDL_END - ] - assets.srcDirs = [ - 'assets' -//DIR_ASSETS_BEGIN -//DIR_ASSETS_END - ] + java.srcDirs = ['src'] + res.srcDirs = ['res'] + aidl.srcDirs = ['aidl'] + assets.srcDirs = ['assets'] } - debug.jniLibs.srcDirs = [ - 'libs/debug' -//DIR_JNI_DEBUG_BEGIN -//DIR_JNI_DEBUG_END - ] - release.jniLibs.srcDirs = [ - 'libs/release' -//DIR_JNI_RELEASE_BEGIN -//DIR_JNI_RELEASE_END - ] + debug.jniLibs.srcDirs = ['libs/debug'] + release.jniLibs.srcDirs = ['libs/release'] } applicationVariants.all { variant -> @@ -118,6 +105,3 @@ android { } } } - -//CHUNK_GLOBAL_BEGIN -//CHUNK_GLOBAL_END diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 5550d3099d..aa98194a10 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -1,24 +1,66 @@ ext.versions = [ - androidGradlePlugin : '3.4.2', - compileSdk : 28, - minSdk : 18, - targetSdk : 28, - buildTools : '28.0.3', - supportCoreUtils : '28.0.0' + androidGradlePlugin: '3.5.3', + compileSdk : 29, + minSdk : 18, + targetSdk : 29, + buildTools : '29.0.3', + supportCoreUtils : '28.0.0', + kotlinVersion : '1.3.61', + v4Support : '28.0.0' ] ext.libraries = [ - androidGradlePlugin : "com.android.tools.build:gradle:$versions.androidGradlePlugin", - supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils" + androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin", + supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils", + kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion", + kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlinVersion", + v4Support : "com.android.support:support-v4:$versions.v4Support" ] ext.getExportPackageName = { -> - // Retrieve the app id from the project property set by the Godot build command. - String appId = project.hasProperty("export_package_name") ? project.property("export_package_name") : "" - // Check if the app id is valid, otherwise use the default. - if (appId == null || appId.isEmpty()) { - appId = "com.godot.game" - } - return appId + // Retrieve the app id from the project property set by the Godot build command. + String appId = project.hasProperty("export_package_name") ? project.property("export_package_name") : "" + // Check if the app id is valid, otherwise use the default. + if (appId == null || appId.isEmpty()) { + appId = "com.godot.game" + } + return appId +} + +/** + * Parse the project properties for the 'custom_template_plugins' property and return + * their binaries for inclusion in the build dependencies. + * + * The listed plugins must have their binaries in the project plugins directory. + */ +ext.getGodotPluginsBinaries = { -> + String[] binDeps = [] + + // Retrieve the list of enabled plugins. + if (project.hasProperty("custom_template_plugins")) { + String pluginsList = project.property("custom_template_plugins") + if (pluginsList != null && !pluginsList.trim().isEmpty()) { + for (String plugin : pluginsList.split(",")) { + binDeps += plugin.trim() + "*.aar" + } + } + } + + return binDeps +} + +/** + * Parse the project properties for the 'custom_template_plugins_dir' property and return + * its value. + * + * The returned value is the directory containing user plugins. + */ +ext.getGodotPluginsDirectory = { -> + // The plugins directory is provided by the 'custom_template_plugins_dir' property. + String pluginsDir = project.hasProperty("custom_template_plugins_dir") + ? project.property("custom_template_plugins_dir") + : "" + + return pluginsDir } diff --git a/platform/android/java/app/settings.gradle b/platform/android/java/app/settings.gradle new file mode 100644 index 0000000000..33b863c7bf --- /dev/null +++ b/platform/android/java/app/settings.gradle @@ -0,0 +1,2 @@ +// Empty settings.gradle file to denote this directory as being the root project +// of the Godot custom build. diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 2052017888..865b61956c 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -9,6 +9,7 @@ buildscript { } dependencies { classpath libraries.androidGradlePlugin + classpath libraries.kotlinGradlePlugin } } @@ -24,7 +25,7 @@ ext { sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : "" supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"] - supportedTargets = ['release':"release", 'debug':"release_debug"] + supportedTargets = ["release", "debug"] // Used by gradle to specify which architecture to build for by default when running `./gradlew build`. // This command is usually used by Android Studio. @@ -64,10 +65,10 @@ task copyReleaseBinaryToBin(type: Copy) { } /** - * Copy the Godot android library archive debug file into the app debug libs directory. + * Copy the Godot android library archive debug file into the app module debug libs directory. * Depends on the library build task to ensure the AAR file is generated prior to copying. */ -task copyDebugAAR(type: Copy) { +task copyDebugAARToAppModule(type: Copy) { dependsOn ':lib:assembleDebug' from('lib/build/outputs/aar') into('app/libs/debug') @@ -75,16 +76,45 @@ task copyDebugAAR(type: Copy) { } /** - * Copy the Godot android library archive release file into the app release libs directory. + * Copy the Godot android library archive debug file into the root bin directory. * Depends on the library build task to ensure the AAR file is generated prior to copying. */ -task copyReleaseAAR(type: Copy) { +task copyDebugAARToBin(type: Copy) { + dependsOn ':lib:assembleDebug' + from('lib/build/outputs/aar') + into(binDir) + include('godot-lib.debug.aar') +} + +/** + * Copy the Godot android library archive release file into the app module release libs directory. + * Depends on the library build task to ensure the AAR file is generated prior to copying. + */ +task copyReleaseAARToAppModule(type: Copy) { dependsOn ':lib:assembleRelease' from('lib/build/outputs/aar') into('app/libs/release') include('godot-lib.release.aar') } +task copyGodotPaymentPluginToAppModule(type: Copy) { + dependsOn ':plugins:godotpayment:assembleRelease' + from('plugins/godotpayment/build/outputs/aar') + into('app/libs/plugins') + include('GodotPayment.release.aar') +} + +/** + * Copy the Godot android library archive release file into the root bin directory. + * Depends on the library build task to ensure the AAR file is generated prior to copying. + */ +task copyReleaseAARToBin(type: Copy) { + dependsOn ':lib:assembleRelease' + from('lib/build/outputs/aar') + into(binDir) + include('godot-lib.release.aar') +} + /** * Generate Godot custom build template by zipping the source files from the app directory, as well * as the AAR files generated by 'copyDebugAAR' and 'copyReleaseAAR'. @@ -95,7 +125,7 @@ task zipCustomBuild(type: Zip) { doFirst { logger.lifecycle("Generating Godot custom build template") } - from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradle.properties','gradlew', 'gradlew.bat', 'gradle/**'])) + from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradle.properties', 'gradlew', 'gradlew.bat', 'gradle/**'])) include '**/*' archiveName 'android_source.zip' destinationDir(file(binDir)) @@ -106,23 +136,28 @@ task zipCustomBuild(type: Zip) { */ task generateGodotTemplates(type: GradleBuild) { // We exclude these gradle tasks so we can run the scons command manually. - for (String buildType : supportedTargets.keySet()) { + for (String buildType : supportedTargets) { startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType) } - tasks = [] + tasks = ["copyGodotPaymentPluginToAppModule"] // Only build the apks and aar files for which we have native shared libraries. - for (String target : supportedTargets.keySet()) { + for (String target : supportedTargets) { File targetLibs = new File("lib/libs/" + target) - if (targetLibs != null && targetLibs.isDirectory()) { - File[] targetLibsContents = targetLibs.listFiles() - if (targetLibsContents != null && targetLibsContents.length > 0) { - // Copy the generated aar library files to the custom build directory. - tasks += "copy" + target.capitalize() + "AAR" - // Copy the prebuilt binary templates to the bin directory. - tasks += "copy" + target.capitalize() + "BinaryToBin" - } + if (targetLibs != null + && targetLibs.isDirectory() + && targetLibs.listFiles() != null + && targetLibs.listFiles().length > 0) { + String capitalizedTarget = target.capitalize() + // Copy the generated aar library files to the custom build directory. + tasks += "copy" + capitalizedTarget + "AARToAppModule" + // Copy the generated aar library files to the bin directory. + tasks += "copy" + capitalizedTarget + "AARToBin" + // Copy the prebuilt binary templates to the bin directory. + tasks += "copy" + capitalizedTarget + "BinaryToBin" + } else { + logger.lifecycle("No native shared libs for target $target. Skipping build.") } } @@ -133,20 +168,28 @@ task generateGodotTemplates(type: GradleBuild) { * Clean the generated artifacts. */ task cleanGodotTemplates(type: Delete) { - // Delete the generated native libs - delete("lib/libs") + // Delete the generated native libs + delete("lib/libs") + + // Delete the library generated AAR files + delete("lib/build/outputs/aar") + + // Delete the godotpayment libs directory contents + delete("plugins/godotpayment/libs") - // Delete the library generated AAR files - delete("lib/build/outputs/aar") + // Delete the generated godotpayment aar + delete("plugins/godotpayment/build/outputs/aar") - // Delete the app libs directory contents - delete("app/libs") + // Delete the app libs directory contents + delete("app/libs") - // Delete the generated binary apks - delete("app/build/outputs/apk") + // Delete the generated binary apks + delete("app/build/outputs/apk") - // Delete the Godot templates in the Godot bin directory - delete("$binDir/android_debug.apk") - delete("$binDir/android_release.apk") - delete("$binDir/android_source.zip") + // Delete the Godot templates in the Godot bin directory + delete("$binDir/android_debug.apk") + delete("$binDir/android_release.apk") + delete("$binDir/android_source.zip") + delete("$binDir/godot-lib.debug.aar") + delete("$binDir/godot-lib.release.aar") } diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index bf50265715..f56b0f6a5e 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml index b133585f99..fa39bc0f1d 100644 --- a/platform/android/java/lib/AndroidManifest.xml +++ b/platform/android/java/lib/AndroidManifest.xml @@ -13,7 +13,7 @@ <instrumentation android:icon="@mipmap/icon" android:label="@string/godot_project_name_string" - android:name=".GodotInstrumentation" + android:name="org.godotengine.godot.GodotInstrumentation" android:targetPackage="org.godotengine.godot" /> </manifest> diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index eb97484b9c..19eee5a315 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -1,7 +1,10 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' dependencies { implementation libraries.supportCoreUtils + implementation libraries.kotlinStdLib + implementation libraries.v4Support } def pathToRootDir = "../../../../" @@ -9,7 +12,6 @@ def pathToRootDir = "../../../../" android { compileSdkVersion versions.compileSdk buildToolsVersion versions.buildTools - useLibrary 'org.apache.http.legacy' defaultConfig { minSdkVersion versions.minSdk @@ -24,6 +26,9 @@ android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' + + // Should be uncommented for development purpose within Android Studio + // doNotStrip '**/*.so' } sourceSets { @@ -54,7 +59,7 @@ android { // files is only setup for editing support. gradle.startParameter.excludedTaskNames += taskPrefix + "externalNativeBuild" + buildType - def releaseTarget = supportedTargets[buildType.toLowerCase()] + def releaseTarget = buildType.toLowerCase() if (releaseTarget == null || releaseTarget == "") { throw new GradleException("Invalid build type: " + buildType) } diff --git a/platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.png Binary files differdeleted file mode 100644 index f849d8e90d..0000000000 --- a/platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.png +++ /dev/null diff --git a/platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.png Binary files differdeleted file mode 100644 index 1dfb28b33a..0000000000 --- a/platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.png +++ /dev/null diff --git a/platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.png Binary files differdeleted file mode 100644 index 372b763ec5..0000000000 --- a/platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.png +++ /dev/null diff --git a/platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.png Binary files differdeleted file mode 100644 index 302a972049..0000000000 --- a/platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.png +++ /dev/null diff --git a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Constants.java b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Constants.java index 1dcc370d83..0700b78a28 100644 --- a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Constants.java +++ b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Constants.java @@ -233,4 +233,4 @@ public class Constants { */ public static final long ACTIVE_THREAD_WATCHDOG = 5*1000; -}
\ No newline at end of file +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 2f6a93fbb1..bf0d1c6273 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -55,14 +55,14 @@ import android.hardware.SensorManager; import android.os.Build; import android.os.Bundle; import android.os.Environment; -import android.os.Handler; -import android.os.Looper; import android.os.Messenger; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings.Secure; +import android.support.annotation.CallSuper; import android.support.annotation.Keep; -import android.support.annotation.Nullable; +import android.support.annotation.NonNull; +import android.support.v4.app.FragmentActivity; import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; @@ -87,22 +87,19 @@ import com.google.android.vending.expansion.downloader.IStub; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; -import java.lang.reflect.Method; import java.security.MessageDigest; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Locale; -import javax.microedition.khronos.opengles.GL10; import org.godotengine.godot.input.GodotEditText; -import org.godotengine.godot.payments.PaymentsManager; +import org.godotengine.godot.plugin.GodotPlugin; +import org.godotengine.godot.plugin.GodotPluginRegistry; import org.godotengine.godot.utils.GodotNetUtils; import org.godotengine.godot.utils.PermissionsUtil; import org.godotengine.godot.xr.XRMode; -public abstract class Godot extends Activity implements SensorEventListener, IDownloaderClient { +public abstract class Godot extends FragmentActivity implements SensorEventListener, IDownloaderClient { - static final int MAX_SINGLETONS = 64; private IStub mDownloaderClientStub; private TextView mStatusText; private TextView mProgressFraction; @@ -126,8 +123,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo private boolean activityResumed; private int mState; - // Used to dispatch events to the main thread. - private final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); + private GodotPluginRegistry pluginRegistry; static private Intent mCurrentIntent; @@ -154,87 +150,10 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo mPauseButton.setText(stringResourceID); } - static public class SingletonBase { - - protected void registerClass(String p_name, String[] p_methods) { - - GodotLib.singleton(p_name, this); - - Class clazz = getClass(); - Method[] methods = clazz.getDeclaredMethods(); - for (Method method : methods) { - boolean found = false; - - for (String s : p_methods) { - if (s.equals(method.getName())) { - found = true; - break; - } - } - if (!found) - continue; - - List<String> ptr = new ArrayList<String>(); - - Class[] paramTypes = method.getParameterTypes(); - for (Class c : paramTypes) { - ptr.add(c.getName()); - } - - String[] pt = new String[ptr.size()]; - ptr.toArray(pt); - - GodotLib.method(p_name, method.getName(), method.getReturnType().getName(), pt); - } - - Godot.singletons[Godot.singleton_count++] = this; - } - - /** - * Invoked once during the Godot Android initialization process after creation of the - * {@link GodotView} view. - * <p> - * This method should be overridden by descendants of this class that would like to add - * their view/layout to the Godot view hierarchy. - * - * @return the view to be included; null if no views should be included. - */ - @Nullable - protected View onMainCreateView(Activity activity) { - return null; - } - - protected void onMainActivityResult(int requestCode, int resultCode, Intent data) { - } - - protected void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - } - - protected void onMainPause() {} - protected void onMainResume() {} - protected void onMainDestroy() {} - protected boolean onMainBackPressed() { return false; } - - protected void onGLDrawFrame(GL10 gl) {} - protected void onGLSurfaceChanged(GL10 gl, int width, int height) {} // singletons will always miss first onGLSurfaceChanged call - //protected void onGLSurfaceCreated(GL10 gl, EGLConfig config) {} // singletons won't be ready until first GodotLib.step() - - public void registerMethods() {} - } - - /* - protected List<SingletonBase> singletons = new ArrayList<SingletonBase>(); - protected void instanceSingleton(SingletonBase s) { - - s.registerMethods(); - singletons.add(s); - } - */ - private String[] command_line; private boolean use_apk_expansion; - public GodotView mView; + public GodotRenderView mRenderView; private boolean godot_initialized = false; private SensorManager mSensorManager; @@ -246,35 +165,27 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo public static GodotIO io; public static GodotNetUtils netUtils; - static SingletonBase[] singletons = new SingletonBase[MAX_SINGLETONS]; - static int singleton_count = 0; - public interface ResultCallback { public void callback(int requestCode, int resultCode, Intent data); } public ResultCallback result_callback; - private PaymentsManager mPaymentsManager = null; - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) { - mPaymentsManager.processPurchaseResponse(resultCode, data); - } else if (result_callback != null) { + if (result_callback != null) { result_callback.callback(requestCode, resultCode, data); result_callback = null; - }; - - for (int i = 0; i < singleton_count; i++) { + } - singletons[i].onMainActivityResult(requestCode, resultCode, data); + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onMainActivityResult(requestCode, resultCode, data); } - }; + } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - for (int i = 0; i < singleton_count; i++) { - singletons[i].onMainRequestPermissionsResult(requestCode, permissions, grantResults); + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults); } for (int i = 0; i < permissions.length; i++) { @@ -283,63 +194,77 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo }; /** + * Invoked on the render thread when the Godot main loop has started. + */ + @CallSuper + protected void onGodotMainLoopStarted() { + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onGodotMainLoopStarted(); + } + } + + /** * Used by the native code (java_godot_lib_jni.cpp) to complete initialization of the GLSurfaceView view and renderer. */ @Keep private void onVideoInit() { - boolean use_gl3 = getGLESVersionCode() >= 0x00030000; - final FrameLayout layout = new FrameLayout(this); layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); setContentView(layout); // GodotEditText layout - GodotEditText edittext = new GodotEditText(this); - edittext.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + GodotEditText editText = new GodotEditText(this); + editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); // ...add to FrameLayout - layout.addView(edittext); + layout.addView(editText); + + GodotLib.setup(command_line); + + final String videoDriver = GodotLib.getGlobal("rendering/quality/driver/driver_name"); + if (videoDriver.equals("Vulkan")) { + mRenderView = new GodotVulkanRenderView(this); + } else { + mRenderView = new GodotGLRenderView(this, xrMode, use_32_bits, use_debug_opengl); + } - mView = new GodotView(this, xrMode, use_gl3, use_32_bits, use_debug_opengl); - layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - edittext.setView(mView); - io.setEdit(edittext); + View view = mRenderView.getView(); + layout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + editText.setView(mRenderView); + io.setEdit(editText); - final Godot godot = this; - mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Point fullSize = new Point(); - godot.getWindowManager().getDefaultDisplay().getSize(fullSize); + getWindowManager().getDefaultDisplay().getSize(fullSize); Rect gameSize = new Rect(); - godot.mView.getWindowVisibleDisplayFrame(gameSize); + mRenderView.getView().getWindowVisibleDisplayFrame(gameSize); final int keyboardHeight = fullSize.y - gameSize.bottom; GodotLib.setVirtualKeyboardHeight(keyboardHeight); } }); - final String[] current_command_line = command_line; - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { - GodotLib.setup(current_command_line); - setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))); - // The Godot Android plugins are setup on completion of GodotLib.setup - mainThreadHandler.post(new Runnable() { - @Override - public void run() { - // Include the non-null views returned in the Godot view hierarchy. - for (int i = 0; i < singleton_count; i++) { - View view = singletons[i].onMainCreateView(Godot.this); - if (view != null) { - layout.addView(view); - } - } - } - }); + // Must occur after GodotLib.setup has completed. + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onRegisterPluginWithGodotNative(); + } + + setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))); } }); + + // Include the returned non-null views in the Godot view hierarchy. + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + View pluginView = plugin.onMainCreateView(this); + if (pluginView != null) { + layout.addView(pluginView); + } + } } public void setKeepScreenOn(final boolean p_enabled) { @@ -466,7 +391,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo */ @Keep private Surface getSurface() { - return mView.getHolder().getSurface(); + return mRenderView.getView().getHolder().getSurface(); } /** @@ -519,8 +444,6 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo result_callback = null; - mPaymentsManager = PaymentsManager.createManager(this).initService(); - godot_initialized = true; } @@ -537,6 +460,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); mClipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); + pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this); //check for apk expansion API if (true) { @@ -676,9 +600,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo @Override protected void onDestroy() { - if (mPaymentsManager != null) mPaymentsManager.destroy(); - for (int i = 0; i < singleton_count; i++) { - singletons[i].onMainDestroy(); + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onMainDestroy(); } GodotLib.ondestroy(this); @@ -701,12 +624,12 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo } return; } - mView.onPause(); + mRenderView.onActivityPaused(); mSensorManager.unregisterListener(this); - for (int i = 0; i < singleton_count; i++) { - singletons[i].onMainPause(); + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onMainPause(); } } @@ -739,7 +662,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo return; } - mView.onResume(); + mRenderView.onActivityResumed(); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME); @@ -757,9 +680,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } - for (int i = 0; i < singleton_count; i++) { - - singletons[i].onMainResume(); + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onMainResume(); } } @@ -806,8 +728,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo final float z = adjustedValues[2]; final int typeOfSensor = event.sensor.getType(); - if (mView != null) { - mView.queueEvent(new Runnable() { + if (mRenderView != null) { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) { @@ -852,14 +774,14 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo public void onBackPressed() { boolean shouldQuit = true; - for (int i = 0; i < singleton_count; i++) { - if (singletons[i].onMainBackPressed()) { + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + if (plugin.onMainBackPressed()) { shouldQuit = false; } } - if (shouldQuit && mView != null) { - mView.queueEvent(new Runnable() { + if (shouldQuit && mRenderView != null) { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { GodotLib.back(); @@ -868,6 +790,17 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo } } + /** + * Queue a runnable to be run on the render thread. + * <p> + * This must be called after the render thread has started. + */ + public final void runOnRenderThread(@NonNull Runnable action) { + if (mRenderView != null) { + mRenderView.queueOnRenderThread(action); + } + } + private void forceQuit() { System.exit(0); } @@ -921,7 +854,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo if (evcount == 0) return true; - if (mView != null) { + if (mRenderView != null) { final int[] arr = new int[event.getPointerCount() * 3]; for (int i = 0; i < event.getPointerCount(); i++) { @@ -934,7 +867,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo //System.out.printf("gaction: %d\n",event.getAction()); final int action = event.getAction() & MotionEvent.ACTION_MASK; - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { switch (action) { @@ -985,15 +918,15 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0) ; if (cnt == 0) return super.onKeyMultiple(inKeyCode, repeatCount, event); - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { // This method will be called on the rendering thread: public void run() { for (int i = 0, n = cc.length; i < n; i++) { int keyCode; if ((keyCode = cc[i]) != 0) { // Simulate key down and up... - GodotLib.key(0, keyCode, true); - GodotLib.key(0, keyCode, false); + GodotLib.key(0, 0, keyCode, true); + GodotLib.key(0, 0, keyCode, false); } } } @@ -1001,10 +934,6 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo return true; } - public PaymentsManager getPaymentsManager() { - return mPaymentsManager; - } - public boolean requestPermission(String p_name) { return PermissionsUtil.requestPermission(p_name, this); } @@ -1111,6 +1040,6 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo progress.mOverallTotal)); } public void initInputDevices() { - mView.initInputDevices(); + mRenderView.initInputDevices(); } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index f938583082..9be93243b8 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -1,5 +1,5 @@ /*************************************************************************/ -/* GodotView.java */ +/* GodotGLRenderView.java */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -35,6 +35,7 @@ import android.opengl.GLSurfaceView; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.SurfaceView; import org.godotengine.godot.input.GodotGestureHandler; import org.godotengine.godot.input.GodotInputHandler; import org.godotengine.godot.utils.GLUtils; @@ -64,18 +65,15 @@ import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser; * that matches it exactly (with regards to red/green/blue/alpha channels * bit depths). Failure to do so would result in an EGL_BAD_MATCH error. */ -public class GodotView extends GLSurfaceView { - - private static String TAG = GodotView.class.getSimpleName(); +public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView { private final Godot activity; private final GodotInputHandler inputHandler; private final GestureDetector detector; private final GodotRenderer godotRenderer; - public GodotView(Godot activity, XRMode xrMode, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl) { + public GodotGLRenderView(Godot activity, XRMode xrMode, boolean p_use_32_bits, boolean p_use_debug_opengl) { super(activity); - GLUtils.use_gl3 = p_use_gl3; GLUtils.use_32 = p_use_32_bits; GLUtils.use_debug_opengl = p_use_debug_opengl; @@ -86,10 +84,36 @@ public class GodotView extends GLSurfaceView { init(xrMode, false, 16, 0); } + @Override + public SurfaceView getView() { + return this; + } + + @Override public void initInputDevices() { this.inputHandler.initInputDevices(); } + @Override + public void queueOnRenderThread(Runnable event) { + queueEvent(event); + } + + @Override + public void onActivityPaused() { + onPause(); + } + + @Override + public void onActivityResumed() { + onResume(); + } + + @Override + public void onBackPressed() { + activity.onBackPressed(); + } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { @@ -171,10 +195,6 @@ public class GodotView extends GLSurfaceView { setRenderer(godotRenderer); } - public void onBackPressed() { - activity.onBackPressed(); - } - @Override public void onResume() { super.onResume(); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java index 68ce40ba10..016a3a8d18 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -53,8 +53,6 @@ public class GodotIO { Godot activity; GodotEditText edit; - MediaPlayer mediaPlayer; - final int SCREEN_LANDSCAPE = 0; final int SCREEN_PORTRAIT = 1; final int SCREEN_REVERSE_LANDSCAPE = 2; @@ -530,44 +528,14 @@ public class GodotIO { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); } break; } - }; - - public void setEdit(GodotEditText _edit) { - edit = _edit; - } - - public void playVideo(String p_path) { - Uri filePath = Uri.parse(p_path); - mediaPlayer = new MediaPlayer(); - - try { - mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); - mediaPlayer.setDataSource(activity.getApplicationContext(), filePath); - mediaPlayer.prepare(); - mediaPlayer.start(); - } catch (IOException e) { - System.out.println("IOError while playing video"); - } } - public boolean isVideoPlaying() { - if (mediaPlayer != null) { - return mediaPlayer.isPlaying(); - } - return false; + public int getScreenOrientation() { + return activity.getRequestedOrientation(); } - public void pauseVideo() { - if (mediaPlayer != null) { - mediaPlayer.pause(); - } - } - - public void stopVideo() { - if (mediaPlayer != null) { - mediaPlayer.release(); - mediaPlayer = null; - } + public void setEdit(GodotEditText _edit) { + edit = _edit; } public static final int SYSTEM_DIR_DESKTOP = 0; diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index e0b46673ba..71fe822233 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -32,6 +32,7 @@ package org.godotengine.godot; import android.app.Activity; import android.hardware.SensorEvent; +import android.view.Surface; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; @@ -72,11 +73,11 @@ public class GodotLib { public static native void resize(int width, int height); /** - * Invoked on the GL thread when the underlying Android surface is created or recreated. + * Invoked on the render thread when the underlying Android surface is created or recreated. + * @param p_surface * @param p_32_bits - * @see android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig) */ - public static native void newcontext(boolean p_32_bits); + public static native void newcontext(Surface p_surface, boolean p_32_bits); /** * Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread. @@ -136,7 +137,7 @@ public class GodotLib { /** * Forward regular key events from the main thread to the GL thread. */ - public static native void key(int p_scancode, int p_unicode_char, boolean p_pressed); + public static native void key(int p_keycode, int p_scancode, int p_unicode_char, boolean p_pressed); /** * Forward game device's key events from the main thread to the GL thread. @@ -176,22 +177,6 @@ public class GodotLib { public static native void audio(); /** - * Used to setup a {@link org.godotengine.godot.Godot.SingletonBase} instance. - * @param p_name Name of the instance. - * @param p_object Reference to the singleton instance. - */ - public static native void singleton(String p_name, Object p_object); - - /** - * Used to complete registration of the {@link org.godotengine.godot.Godot.SingletonBase} instance's methods. - * @param p_sname Name of the instance - * @param p_name Name of the method to register - * @param p_ret Return type of the registered method - * @param p_params Method parameters types - */ - public static native void method(String p_sname, String p_name, String p_ret, String[] p_params); - - /** * Used to access Godot global properties. * @param p_key Property key * @return String value of the property diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java new file mode 100644 index 0000000000..170c433c9c --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* GodotRenderView.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +package org.godotengine.godot; + +import android.view.SurfaceView; + +public interface GodotRenderView { + + abstract public SurfaceView getView(); + + abstract public void initInputDevices(); + + abstract public void queueOnRenderThread(Runnable event); + + abstract public void onActivityPaused(); + abstract public void onActivityResumed(); + + abstract public void onBackPressed(); +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java index 26fa033f12..3e5bb4a4c9 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java @@ -30,9 +30,12 @@ package org.godotengine.godot; +import android.content.Context; import android.opengl.GLSurfaceView; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; +import org.godotengine.godot.plugin.GodotPlugin; +import org.godotengine.godot.plugin.GodotPluginRegistry; import org.godotengine.godot.utils.GLUtils; /** @@ -40,8 +43,13 @@ import org.godotengine.godot.utils.GLUtils; */ class GodotRenderer implements GLSurfaceView.Renderer { + private final GodotPluginRegistry pluginRegistry; private boolean activityJustResumed = false; + GodotRenderer() { + this.pluginRegistry = GodotPluginRegistry.getPluginRegistry(); + } + public void onDrawFrame(GL10 gl) { if (activityJustResumed) { GodotLib.onRendererResumed(); @@ -49,21 +57,23 @@ class GodotRenderer implements GLSurfaceView.Renderer { } GodotLib.step(); - for (int i = 0; i < Godot.singleton_count; i++) { - Godot.singletons[i].onGLDrawFrame(gl); + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onGLDrawFrame(gl); } } public void onSurfaceChanged(GL10 gl, int width, int height) { - GodotLib.resize(width, height); - for (int i = 0; i < Godot.singleton_count; i++) { - Godot.singletons[i].onGLSurfaceChanged(gl, width, height); + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onGLSurfaceChanged(gl, width, height); } } public void onSurfaceCreated(GL10 gl, EGLConfig config) { - GodotLib.newcontext(GLUtils.use_32); + GodotLib.newcontext(null, GLUtils.use_32); + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onGLSurfaceCreated(gl, config); + } } void onActivityResumed() { diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java new file mode 100644 index 0000000000..30197d5729 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -0,0 +1,142 @@ +/*************************************************************************/ +/* GodotVulkanRenderView.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +package org.godotengine.godot; + +import android.annotation.SuppressLint; +import android.view.GestureDetector; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceView; +import org.godotengine.godot.input.GodotGestureHandler; +import org.godotengine.godot.input.GodotInputHandler; +import org.godotengine.godot.vulkan.VkRenderer; +import org.godotengine.godot.vulkan.VkSurfaceView; + +public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { + + private final Godot mActivity; + private final GodotInputHandler mInputHandler; + private final GestureDetector mGestureDetector; + private final VkRenderer mRenderer; + + public GodotVulkanRenderView(Godot activity) { + super(activity); + + mActivity = activity; + mInputHandler = new GodotInputHandler(this); + mGestureDetector = new GestureDetector(mActivity, new GodotGestureHandler(this)); + mRenderer = new VkRenderer(); + + setFocusableInTouchMode(true); + startRenderer(mRenderer); + } + + @Override + public SurfaceView getView() { + return this; + } + + @Override + public void initInputDevices() { + mInputHandler.initInputDevices(); + } + + @Override + public void queueOnRenderThread(Runnable event) { + queueOnVkThread(event); + } + + @Override + public void onActivityPaused() { + onPause(); + } + + @Override + public void onActivityResumed() { + onResume(); + } + + @Override + public void onBackPressed() { + mActivity.onBackPressed(); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + mGestureDetector.onTouchEvent(event); + return mActivity.gotTouchEvent(event); + } + + @Override + public boolean onKeyUp(final int keyCode, KeyEvent event) { + return mInputHandler.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event); + } + + @Override + public boolean onKeyDown(final int keyCode, KeyEvent event) { + return mInputHandler.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + return mInputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); + } + + @Override + public void onResume() { + super.onResume(); + + queueOnVkThread(new Runnable() { + @Override + public void run() { + // Resume the renderer + mRenderer.onVkResume(); + GodotLib.focusin(); + } + }); + } + + @Override + public void onPause() { + super.onPause(); + + queueOnVkThread(new Runnable() { + @Override + public void run() { + GodotLib.focusout(); + // Pause the renderer + mRenderer.onVkPause(); + } + }); + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index e901b4b36d..92bb118e44 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -51,7 +51,7 @@ public class GodotEditText extends EditText { // =========================================================== // Fields // =========================================================== - private GodotView mView; + private GodotRenderView mRenderView; private GodotTextInputWrapper mInputWrapper; private EditHandler sHandler = new EditHandler(this); private String mOriginText; @@ -76,22 +76,22 @@ public class GodotEditText extends EditText { // =========================================================== public GodotEditText(final Context context) { super(context); - this.initView(); + initView(); } public GodotEditText(final Context context, final AttributeSet attrs) { super(context, attrs); - this.initView(); + initView(); } public GodotEditText(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); - this.initView(); + initView(); } protected void initView() { - this.setPadding(0, 0, 0, 0); - this.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + setPadding(0, 0, 0, 0); + setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); } private void handleMessage(final Message msg) { @@ -106,7 +106,7 @@ public class GodotEditText extends EditText { edit.mInputWrapper.setOriginText(text); edit.addTextChangedListener(edit.mInputWrapper); setMaxInputLength(edit, msg.arg1); - final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = (InputMethodManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(edit, 0); } } break; @@ -115,9 +115,9 @@ public class GodotEditText extends EditText { GodotEditText edit = (GodotEditText)msg.obj; edit.removeTextChangedListener(mInputWrapper); - final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = (InputMethodManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(edit.getWindowToken(), 0); - edit.mView.requestFocus(); + edit.mRenderView.getView().requestFocus(); } break; } } @@ -135,12 +135,12 @@ public class GodotEditText extends EditText { // =========================================================== // Getter & Setter // =========================================================== - public void setView(final GodotView view) { - this.mView = view; + public void setView(final GodotRenderView view) { + mRenderView = view; if (mInputWrapper == null) - mInputWrapper = new GodotTextInputWrapper(mView, this); - this.setOnEditorActionListener(mInputWrapper); - view.requestFocus(); + mInputWrapper = new GodotTextInputWrapper(mRenderView, this); + setOnEditorActionListener(mInputWrapper); + view.getView().requestFocus(); } // =========================================================== @@ -152,7 +152,7 @@ public class GodotEditText extends EditText { /* Let GlSurfaceView get focus if back key is input. */ if (keyCode == KeyEvent.KEYCODE_BACK) { - this.mView.requestFocus(); + mRenderView.getView().requestFocus(); } return true; @@ -162,7 +162,7 @@ public class GodotEditText extends EditText { // Methods // =========================================================== public void showKeyboard(String p_existing_text, int p_max_input_length) { - this.mOriginText = p_existing_text; + mOriginText = p_existing_text; final Message msg = new Message(); msg.what = HANDLER_OPEN_IME_KEYBOARD; diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java index 1a38a9c3d2..b1e0f66373 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java @@ -34,22 +34,22 @@ import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import org.godotengine.godot.GodotLib; -import org.godotengine.godot.GodotView; +import org.godotengine.godot.GodotRenderView; /** - * Handles gesture input related events for the {@link GodotView} view. + * Handles gesture input related events for the {@link GodotRenderView} view. * https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener */ public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener { - private final GodotView godotView; + private final GodotRenderView mRenderView; - public GodotGestureHandler(GodotView godotView) { - this.godotView = godotView; + public GodotGestureHandler(GodotRenderView godotView) { + mRenderView = godotView; } private void queueEvent(Runnable task) { - godotView.queueEvent(task); + mRenderView.queueOnRenderThread(task); } @Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index b2b88088e8..0e4fc65119 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -42,27 +42,27 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import org.godotengine.godot.GodotLib; -import org.godotengine.godot.GodotView; +import org.godotengine.godot.GodotRenderView; import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; /** - * Handles input related events for the {@link GodotView} view. + * Handles input related events for the {@link GodotRenderView} view. */ public class GodotInputHandler implements InputDeviceListener { - private final ArrayList<Joystick> joysticksDevices = new ArrayList<Joystick>(); + private final ArrayList<Joystick> mJoysticksDevices = new ArrayList<Joystick>(); - private final GodotView godotView; - private final InputManagerCompat inputManager; + private final GodotRenderView mRenderView; + private final InputManagerCompat mInputManager; - public GodotInputHandler(GodotView godotView) { - this.godotView = godotView; - this.inputManager = InputManagerCompat.Factory.getInputManager(godotView.getContext()); - this.inputManager.registerInputDeviceListener(this, null); + public GodotInputHandler(GodotRenderView godotView) { + mRenderView = godotView; + mInputManager = InputManagerCompat.Factory.getInputManager(mRenderView.getView().getContext()); + mInputManager.registerInputDeviceListener(this, null); } private void queueEvent(Runnable task) { - godotView.queueEvent(task); + mRenderView.queueOnRenderThread(task); } private boolean isKeyEvent_GameDevice(int source) { @@ -98,11 +98,12 @@ public class GodotInputHandler implements InputDeviceListener { }); } } else { + final int scanCode = event.getScanCode(); final int chr = event.getUnicodeChar(0); queueEvent(new Runnable() { @Override public void run() { - GodotLib.key(keyCode, chr, false); + GodotLib.key(keyCode, scanCode, chr, false); } }); }; @@ -112,7 +113,7 @@ public class GodotInputHandler implements InputDeviceListener { public boolean onKeyDown(final int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - godotView.onBackPressed(); + mRenderView.onBackPressed(); // press 'back' button should not terminate program //normal handle 'back' event in game logic return true; @@ -143,11 +144,12 @@ public class GodotInputHandler implements InputDeviceListener { }); } } else { + final int scanCode = event.getScanCode(); final int chr = event.getUnicodeChar(0); queueEvent(new Runnable() { @Override public void run() { - GodotLib.key(keyCode, chr, true); + GodotLib.key(keyCode, scanCode, chr, true); } }); }; @@ -162,7 +164,7 @@ public class GodotInputHandler implements InputDeviceListener { // Check if the device exists if (device_id > -1) { - Joystick joy = joysticksDevices.get(device_id); + Joystick joy = mJoysticksDevices.get(device_id); for (int i = 0; i < joy.axes.size(); i++) { InputDevice.MotionRange range = joy.axes.get(i); @@ -206,11 +208,11 @@ public class GodotInputHandler implements InputDeviceListener { public void initInputDevices() { /* initially add input devices*/ - int[] deviceIds = inputManager.getInputDeviceIds(); + int[] deviceIds = mInputManager.getInputDeviceIds(); for (int deviceId : deviceIds) { - InputDevice device = inputManager.getInputDevice(deviceId); + InputDevice device = mInputManager.getInputDevice(deviceId); if (DEBUG) { - Log.v("GodotView", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName())); + Log.v("GodotInputHandler", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName())); } onInputDeviceAdded(deviceId); } @@ -222,13 +224,13 @@ public class GodotInputHandler implements InputDeviceListener { // Check if the device has not been already added if (id < 0) { - InputDevice device = inputManager.getInputDevice(deviceId); + InputDevice device = mInputManager.getInputDevice(deviceId); //device can be null if deviceId is not found if (device != null) { int sources = device.getSources(); if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) { - id = joysticksDevices.size(); + id = mJoysticksDevices.size(); Joystick joy = new Joystick(); joy.device_id = deviceId; @@ -247,7 +249,7 @@ public class GodotInputHandler implements InputDeviceListener { } } - joysticksDevices.add(joy); + mJoysticksDevices.add(joy); final int device_id = id; final String name = joy.name; @@ -268,7 +270,7 @@ public class GodotInputHandler implements InputDeviceListener { // Check if the evice has not been already removed if (device_id > -1) { - joysticksDevices.remove(device_id); + mJoysticksDevices.remove(device_id); queueEvent(new Runnable() { @Override @@ -358,8 +360,8 @@ public class GodotInputHandler implements InputDeviceListener { } private int findJoystickDevice(int device_id) { - for (int i = 0; i < joysticksDevices.size(); i++) { - if (joysticksDevices.get(i).device_id == device_id) { + for (int i = 0; i < mJoysticksDevices.size(); i++) { + if (mJoysticksDevices.get(i).device_id == device_id) { return i; } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java index 3a154f1bf3..e12ff266bf 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java @@ -48,7 +48,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene // =========================================================== // Fields // =========================================================== - private final GodotView mView; + private final GodotRenderView mRenderView; private final GodotEditText mEdit; private String mOriginText; @@ -56,9 +56,9 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene // Constructors // =========================================================== - public GodotTextInputWrapper(final GodotView view, final GodotEditText edit) { - this.mView = view; - this.mEdit = edit; + public GodotTextInputWrapper(final GodotRenderView view, final GodotEditText edit) { + mRenderView = view; + mEdit = edit; } // =========================================================== @@ -66,13 +66,13 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene // =========================================================== private boolean isFullScreenEdit() { - final TextView textField = this.mEdit; + final TextView textField = mEdit; final InputMethodManager imm = (InputMethodManager)textField.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); return imm.isFullscreenMode(); } public void setOriginText(final String originText) { - this.mOriginText = originText; + mOriginText = originText; } // =========================================================== @@ -87,12 +87,12 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) { //Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after); - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { for (int i = 0; i < count; ++i) { - GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); - GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); + GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true); + GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false); } } }); @@ -106,12 +106,17 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene for (int i = start; i < start + count; ++i) { newChars[i - start] = pCharSequence.charAt(i); } - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { for (int i = 0; i < count; ++i) { - GodotLib.key(0, newChars[i], true); - GodotLib.key(0, newChars[i], false); + int key = newChars[i]; + if (key == '\n') { + // Return keys are handled through action events + continue; + } + GodotLib.key(0, 0, key, true); + GodotLib.key(0, 0, key, false); } } }); @@ -119,23 +124,28 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene @Override public boolean onEditorAction(final TextView pTextView, final int pActionID, final KeyEvent pKeyEvent) { - if (this.mEdit == pTextView && this.isFullScreenEdit()) { + if (mEdit == pTextView && isFullScreenEdit()) { final String characters = pKeyEvent.getCharacters(); - mView.queueEvent(new Runnable() { + mRenderView.queueOnRenderThread(new Runnable() { @Override public void run() { for (int i = 0; i < characters.length(); i++) { final int ch = characters.codePointAt(i); - GodotLib.key(0, ch, true); - GodotLib.key(0, ch, false); + GodotLib.key(0, 0, ch, true); + GodotLib.key(0, 0, ch, false); } } }); } - if (pActionID == EditorInfo.IME_ACTION_DONE) { - this.mView.requestFocus(); + if (pActionID == EditorInfo.IME_NULL) { + // Enter key has been pressed + GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true); + GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false); + + mRenderView.getView().requestFocus(); + return true; } return false; } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java new file mode 100644 index 0000000000..a051164d15 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -0,0 +1,358 @@ +/*************************************************************************/ +/* GodotPlugin.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +package org.godotengine.godot.plugin; + +import android.app.Activity; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.Log; +import android.view.Surface; +import android.view.View; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; +import org.godotengine.godot.BuildConfig; +import org.godotengine.godot.Godot; + +/** + * Base class for the Godot Android plugins. + * <p> + * A Godot Android plugin is a regular Android library packaged as an aar archive file with the following caveats: + * <p> + * - The library must have a dependency on the Godot Android library (godot-lib.aar). + * A stable version is available for each release. + * <p> + * - The library must include a <meta-data> tag in its manifest file setup as follow: + * <meta-data android:name="org.godotengine.plugin.v1.[PluginName]" android:value="[plugin.init.ClassFullName]" /> + * Where: + * - 'PluginName' is the name of the plugin. + * - 'plugin.init.ClassFullName' is the full name (package + class name) of the plugin class + * extending {@link GodotPlugin}. + * + * A plugin can also define and provide c/c++ gdnative libraries and nativescripts for the target + * app/game to leverage. + * The shared library for the gdnative library will be automatically bundled by the aar build + * system. + * Godot '*.gdnlib' and '*.gdns' resource files must however be manually defined in the project + * 'assets' directory. The recommended path for these resources in the 'assets' directory should be: + * 'godot/plugin/v1/[PluginName]/' + */ +public abstract class GodotPlugin { + + private static final String TAG = GodotPlugin.class.getSimpleName(); + + private final Godot godot; + private final ConcurrentHashMap<String, SignalInfo> registeredSignals = new ConcurrentHashMap<>(); + + public GodotPlugin(Godot godot) { + this.godot = godot; + } + + /** + * Provides access to the Godot engine. + */ + protected Godot getGodot() { + return godot; + } + + /** + * Register the plugin with Godot native code. + * + * This method is invoked on the render thread. + */ + public final void onRegisterPluginWithGodotNative() { + nativeRegisterSingleton(getPluginName()); + + Class clazz = getClass(); + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + boolean found = false; + + for (String s : getPluginMethods()) { + if (s.equals(method.getName())) { + found = true; + break; + } + } + if (!found) + continue; + + List<String> ptr = new ArrayList<String>(); + + Class[] paramTypes = method.getParameterTypes(); + for (Class c : paramTypes) { + ptr.add(c.getName()); + } + + String[] pt = new String[ptr.size()]; + ptr.toArray(pt); + + nativeRegisterMethod(getPluginName(), method.getName(), method.getReturnType().getName(), pt); + } + + // Register the signals for this plugin. + for (SignalInfo signalInfo : getPluginSignals()) { + String signalName = signalInfo.getName(); + nativeRegisterSignal(getPluginName(), signalName, signalInfo.getParamTypesNames()); + registeredSignals.put(signalName, signalInfo); + } + + // Get the list of gdnative libraries to register. + Set<String> gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths(); + if (!gdnativeLibrariesPaths.isEmpty()) { + nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0])); + } + } + + /** + * Invoked once during the Godot Android initialization process after creation of the + * {@link org.godotengine.godot.GodotView} view. + * <p> + * This method should be overridden by descendants of this class that would like to add + * their view/layout to the Godot view hierarchy. + * + * @return the view to be included; null if no views should be included. + */ + @Nullable + public View onMainCreateView(Activity activity) { + return null; + } + + /** + * @see Activity#onActivityResult(int, int, Intent) + */ + public void onMainActivityResult(int requestCode, int resultCode, Intent data) { + } + + /** + * @see Activity#onRequestPermissionsResult(int, String[], int[]) + */ + public void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + } + + /** + * @see Activity#onPause() + */ + public void onMainPause() {} + + /** + * @see Activity#onResume() + */ + public void onMainResume() {} + + /** + * @see Activity#onDestroy() + */ + public void onMainDestroy() {} + + /** + * @see Activity#onBackPressed() + */ + public boolean onMainBackPressed() { return false; } + + /** + * Invoked on the render thread when the Godot main loop has started. + */ + public void onGodotMainLoopStarted() {} + + /** + * Invoked once per frame on the GL thread after the frame is drawn. + */ + public void onGLDrawFrame(GL10 gl) {} + + /** + * Called on the GL thread after the surface is created and whenever the OpenGL ES surface size + * changes. + */ + public void onGLSurfaceChanged(GL10 gl, int width, int height) {} + + /** + * Called on the GL thread when the surface is created or recreated. + */ + public void onGLSurfaceCreated(GL10 gl, EGLConfig config) {} + + /** + * Invoked once per frame on the Vulkan thread after the frame is drawn. + */ + public void onVkDrawFrame() {} + + /** + * Called on the Vulkan thread after the surface is created and whenever the surface size + * changes. + */ + public void onVkSurfaceChanged(Surface surface, int width, int height) {} + + /** + * Called on the Vulkan thread when the surface is created or recreated. + */ + public void onVkSurfaceCreated(Surface surface) {} + + /** + * Returns the name of the plugin. + * <p> + * This value must match the one listed in the plugin '<meta-data>' manifest entry. + */ + @NonNull + public abstract String getPluginName(); + + /** + * Returns the list of methods to be exposed to Godot. + */ + @NonNull + public List<String> getPluginMethods() { + return Collections.emptyList(); + } + + /** + * Returns the list of signals to be exposed to Godot. + */ + @NonNull + public Set<SignalInfo> getPluginSignals() { + return Collections.emptySet(); + } + + /** + * Returns the paths for the plugin's gdnative libraries. + * + * The paths must be relative to the 'assets' directory and point to a '*.gdnlib' file. + */ + @NonNull + protected Set<String> getPluginGDNativeLibrariesPaths() { + return Collections.emptySet(); + } + + /** + * Runs the specified action on the UI thread. If the current thread is the UI + * thread, then the action is executed immediately. If the current thread is + * not the UI thread, the action is posted to the event queue of the UI thread. + * + * @param action the action to run on the UI thread + */ + protected void runOnUiThread(Runnable action) { + godot.runOnUiThread(action); + } + + /** + * Queue the specified action to be run on the render thread. + * + * @param action the action to run on the render thread + */ + protected void runOnRenderThread(Runnable action) { + godot.runOnRenderThread(action); + } + + /** + * Emit a registered Godot signal. + * @param signalName + * @param signalArgs + */ + protected void emitSignal(final String signalName, final Object... signalArgs) { + try { + // Check that the given signal is among the registered set. + SignalInfo signalInfo = registeredSignals.get(signalName); + if (signalInfo == null) { + throw new IllegalArgumentException( + "Signal " + signalName + " is not registered for this plugin."); + } + + // Validate the arguments count. + Class<?>[] signalParamTypes = signalInfo.getParamTypes(); + if (signalArgs.length != signalParamTypes.length) { + throw new IllegalArgumentException( + "Invalid arguments count. Should be " + signalParamTypes.length + " but is " + signalArgs.length); + } + + // Validate the argument's types. + for (int i = 0; i < signalParamTypes.length; i++) { + if (!signalParamTypes[i].isInstance(signalArgs[i])) { + throw new IllegalArgumentException( + "Invalid type for argument #" + i + ". Should be of type " + signalParamTypes[i].getName()); + } + } + + runOnRenderThread(new Runnable() { + @Override + public void run() { + nativeEmitSignal(getPluginName(), signalName, signalArgs); + } + }); + } catch (IllegalArgumentException exception) { + Log.w(TAG, exception.getMessage()); + if (BuildConfig.DEBUG) { + throw exception; + } + } + } + + /** + * Used to setup a {@link GodotPlugin} instance. + * @param p_name Name of the instance. + */ + private native void nativeRegisterSingleton(String p_name); + + /** + * Used to complete registration of the {@link GodotPlugin} instance's methods. + * @param p_sname Name of the instance + * @param p_name Name of the method to register + * @param p_ret Return type of the registered method + * @param p_params Method parameters types + */ + private native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params); + + /** + * Used to register gdnative libraries bundled by the plugin. + * @param gdnlibPaths Paths to the libraries relative to the 'assets' directory. + */ + private native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths); + + /** + * Used to complete registration of the {@link GodotPlugin} instance's methods. + * @param pluginName Name of the plugin + * @param signalName Name of the signal to register + * @param signalParamTypes Signal parameters types + */ + private native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes); + + /** + * Used to emit signal by {@link GodotPlugin} instance. + * @param pluginName Name of the plugin + * @param signalName Name of the signal to emit + * @param signalParams Signal parameters + */ + private native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams); +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java new file mode 100644 index 0000000000..e13a9c15d8 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java @@ -0,0 +1,199 @@ +/*************************************************************************/ +/* GodotPluginRegistry.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +package org.godotengine.godot.plugin; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.Log; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.godotengine.godot.Godot; + +/** + * Registry used to load and access the registered Godot Android plugins. + */ +public final class GodotPluginRegistry { + + private static final String TAG = GodotPluginRegistry.class.getSimpleName(); + + private static final String GODOT_PLUGIN_V1_NAME_PREFIX = "org.godotengine.plugin.v1."; + + /** + * Name for the metadata containing the list of Godot plugins to enable. + */ + private static final String GODOT_ENABLED_PLUGINS_LABEL = "custom_template_plugins"; + + private static GodotPluginRegistry instance; + private final ConcurrentHashMap<String, GodotPlugin> registry; + + private GodotPluginRegistry(Godot godot) { + registry = new ConcurrentHashMap<>(); + loadPlugins(godot); + } + + /** + * Retrieve the plugin tied to the given plugin name. + * @param pluginName Name of the plugin + * @return {@link GodotPlugin} handle if it exists, null otherwise. + */ + @Nullable + public GodotPlugin getPlugin(String pluginName) { + return registry.get(pluginName); + } + + /** + * Retrieve the full set of loaded plugins. + */ + public Collection<GodotPlugin> getAllPlugins() { + return registry.values(); + } + + /** + * Parse the manifest file and load all included Godot Android plugins. + * <p> + * A plugin manifest entry is a '<meta-data>' tag setup as described in the {@link GodotPlugin} + * documentation. + * + * @param godot Godot instance + * @return A singleton instance of {@link GodotPluginRegistry}. This ensures that only one instance + * of each Godot Android plugins is available at runtime. + */ + public static GodotPluginRegistry initializePluginRegistry(Godot godot) { + if (instance == null) { + instance = new GodotPluginRegistry(godot); + } + + return instance; + } + + /** + * Return the plugin registry if it's initialized. + * Throws a {@link IllegalStateException} exception if not. + * + * @throws IllegalStateException if {@link GodotPluginRegistry#initializePluginRegistry(Godot)} has not been called prior to calling this method. + */ + public static GodotPluginRegistry getPluginRegistry() throws IllegalStateException { + if (instance == null) { + throw new IllegalStateException("Plugin registry hasn't been initialized."); + } + + return instance; + } + + private void loadPlugins(Godot godot) { + try { + ApplicationInfo appInfo = godot + .getPackageManager() + .getApplicationInfo(godot.getPackageName(), PackageManager.GET_META_DATA); + Bundle metaData = appInfo.metaData; + if (metaData == null || metaData.isEmpty()) { + return; + } + + // When using the Godot editor for building and exporting the apk, this is used to check + // which plugins to enable since the custom build template may contain prebuilt plugins. + // When using a custom process to generate the apk, the metadata is not needed since + // it's assumed that the developer is aware of the dependencies included in the apk. + final Set<String> enabledPluginsSet; + if (metaData.containsKey(GODOT_ENABLED_PLUGINS_LABEL)) { + String enabledPlugins = metaData.getString(GODOT_ENABLED_PLUGINS_LABEL, ""); + String[] enabledPluginsList = enabledPlugins.split(","); + if (enabledPluginsList.length == 0) { + // No plugins to enable. Aborting early. + return; + } + + enabledPluginsSet = new HashSet<>(); + for (String enabledPlugin : enabledPluginsList) { + enabledPluginsSet.add(enabledPlugin.trim()); + } + } else { + enabledPluginsSet = null; + } + + int godotPluginV1NamePrefixLength = GODOT_PLUGIN_V1_NAME_PREFIX.length(); + for (String metaDataName : metaData.keySet()) { + // Parse the meta-data looking for entry with the Godot plugin name prefix. + if (metaDataName.startsWith(GODOT_PLUGIN_V1_NAME_PREFIX)) { + String pluginName = metaDataName.substring(godotPluginV1NamePrefixLength).trim(); + if (enabledPluginsSet != null && !enabledPluginsSet.contains(pluginName)) { + Log.w(TAG, "Plugin " + pluginName + " is listed in the dependencies but is not enabled."); + continue; + } + + // Retrieve the plugin class full name. + String pluginHandleClassFullName = metaData.getString(metaDataName); + if (!TextUtils.isEmpty(pluginHandleClassFullName)) { + try { + // Attempt to create the plugin init class via reflection. + @SuppressWarnings("unchecked") + Class<GodotPlugin> pluginClass = (Class<GodotPlugin>)Class + .forName(pluginHandleClassFullName); + Constructor<GodotPlugin> pluginConstructor = pluginClass + .getConstructor(Godot.class); + GodotPlugin pluginHandle = pluginConstructor.newInstance(godot); + + // Load the plugin initializer into the registry using the plugin name + // as key. + if (!pluginName.equals(pluginHandle.getPluginName())) { + Log.w(TAG, + "Meta-data plugin name does not match the value returned by the plugin handle: " + pluginName + " =/= " + pluginHandle.getPluginName()); + } + registry.put(pluginName, pluginHandle); + } catch (ClassNotFoundException e) { + Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); + } catch (IllegalAccessException e) { + Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); + } catch (InstantiationException e) { + Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); + } catch (NoSuchMethodException e) { + Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); + } catch (InvocationTargetException e) { + Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); + } + } else { + Log.w(TAG, "Invalid plugin loader class for " + pluginName); + } + } + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Unable load Godot Android plugins from the manifest file.", e); + } + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java new file mode 100644 index 0000000000..f907706889 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java @@ -0,0 +1,98 @@ +/*************************************************************************/ +/* SignalInfo.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +package org.godotengine.godot.plugin; + +import android.support.annotation.NonNull; +import android.text.TextUtils; +import java.util.Arrays; + +/** + * Store information about a {@link GodotPlugin}'s signal. + */ +public final class SignalInfo { + + private final String name; + private final Class<?>[] paramTypes; + private final String[] paramTypesNames; + + public SignalInfo(@NonNull String signalName, Class<?>... paramTypes) { + if (TextUtils.isEmpty(signalName)) { + throw new IllegalArgumentException("Invalid signal name: " + signalName); + } + + this.name = signalName; + this.paramTypes = paramTypes == null ? new Class<?>[ 0 ] : paramTypes; + this.paramTypesNames = new String[this.paramTypes.length]; + for (int i = 0; i < this.paramTypes.length; i++) { + this.paramTypesNames[i] = this.paramTypes[i].getName(); + } + } + + public String getName() { + return name; + } + + Class<?>[] getParamTypes() { + return paramTypes; + } + + String[] getParamTypesNames() { + return paramTypesNames; + } + + @Override + public String toString() { + return "SignalInfo{" + + + "name='" + name + '\'' + + ", paramsTypes=" + Arrays.toString(paramTypes) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SignalInfo)) { + return false; + } + + SignalInfo that = (SignalInfo)o; + + return name.equals(that.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java index bbf876ea1f..9d29551f89 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java @@ -44,7 +44,6 @@ public class GLUtils { public static final boolean DEBUG = false; - public static boolean use_gl3 = false; public static boolean use_32 = false; public static boolean use_debug_opengl = false; diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt new file mode 100644 index 0000000000..608ad48df9 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* VkRenderer.kt */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +@file:JvmName("VkRenderer") +package org.godotengine.godot.vulkan + +import android.view.Surface + +import org.godotengine.godot.Godot +import org.godotengine.godot.GodotLib +import org.godotengine.godot.plugin.GodotPlugin +import org.godotengine.godot.plugin.GodotPluginRegistry + +/** + * Responsible to setting up and driving the Vulkan rendering logic. + * + * <h3>Threading</h3> + * The renderer will be called on a separate thread, so that rendering + * performance is decoupled from the UI thread. Clients typically need to + * communicate with the renderer from the UI thread, because that's where + * input events are received. Clients can communicate using any of the + * standard Java techniques for cross-thread communication, or they can + * use the [VkSurfaceView.queueOnVkThread] convenience method. + * + * @see [VkSurfaceView.startRenderer] + */ +internal class VkRenderer { + + private val pluginRegistry: GodotPluginRegistry = GodotPluginRegistry.getPluginRegistry() + + /** + * Called when the surface is created and signals the beginning of rendering. + */ + fun onVkSurfaceCreated(surface: Surface) { + // TODO: properly implement surface re-creation: + // GodotLib.newcontext should be called here once it's done. + //GodotLib.newcontext(surface, false) + + for (plugin in pluginRegistry.getAllPlugins()) { + plugin.onVkSurfaceCreated(surface) + } + } + + /** + * Called after the surface is created and whenever its size changes. + */ + fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) { + GodotLib.resize(width, height) + + // TODO: properly implement surface re-creation: + // Update the native renderer instead of restarting the app. + // GodotLib.newcontext should not be called here once it's done. + GodotLib.newcontext(surface, false) + + for (plugin in pluginRegistry.getAllPlugins()) { + plugin.onVkSurfaceChanged(surface, width, height) + } + } + + /** + * Called to draw the current frame. + */ + fun onVkDrawFrame() { + GodotLib.step() + for (plugin in pluginRegistry.getAllPlugins()) { + plugin.onVkDrawFrame() + } + } + + /** + * Called when the rendering thread is resumed. + */ + fun onVkResume() { + GodotLib.onRendererResumed() + } + + /** + * Called when the rendering thread is paused. + */ + fun onVkPause() { + GodotLib.onRendererPaused() + } + + /** + * Called when the rendering thread is destroyed and used as signal to tear down the Vulkan logic. + */ + fun onVkDestroy() { + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt new file mode 100644 index 0000000000..6b0e12b21a --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt @@ -0,0 +1,136 @@ +/*************************************************************************/ +/* VkSurfaceView.kt */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +@file:JvmName("VkSurfaceView") +package org.godotengine.godot.vulkan + +import android.content.Context +import android.view.SurfaceHolder +import android.view.SurfaceView + +/** + * An implementation of SurfaceView that uses the dedicated surface for + * displaying Vulkan rendering. + * <p> + * A [VkSurfaceView] provides the following features: + * <p> + * <ul> + * <li>Manages a surface, which is a special piece of memory that can be + * composited into the Android view system. + * <li>Accepts a user-provided [VkRenderer] object that does the actual rendering. + * <li>Renders on a dedicated [VkThread] thread to decouple rendering performance from the + * UI thread. + * </ul> + */ +open internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback { + + companion object { + fun checkState(expression: Boolean, errorMessage: Any) { + check(expression) { errorMessage.toString() } + } + } + + /** + * Thread used to drive the vulkan logic. + */ + private val vkThread: VkThread by lazy { + VkThread(this, renderer) + } + + /** + * Performs the actual rendering. + */ + private lateinit var renderer: VkRenderer + + init { + isClickable = true + holder.addCallback(this) + } + + /** + * Set the [VkRenderer] associated with the view, and starts the thread that will drive the vulkan + * rendering. + * + * This method should be called once and only once in the life-cycle of [VkSurfaceView]. + */ + fun startRenderer(renderer: VkRenderer) { + checkState(!this::renderer.isInitialized, "startRenderer must only be invoked once") + this.renderer = renderer + vkThread.start() + } + + /** + * Queues a runnable to be run on the Vulkan rendering thread. + * + * Must not be called before a [VkRenderer] has been set. + */ + fun queueOnVkThread(runnable: Runnable) { + vkThread.queueEvent(runnable) + } + + /** + * Resumes the rendering thread. + * + * Must not be called before a [VkRenderer] has been set. + */ + open fun onResume() { + vkThread.onResume() + } + + /** + * Pauses the rendering thread. + * + * Must not be called before a [VkRenderer] has been set. + */ + open fun onPause() { + vkThread.onPause() + } + + /** + * Tear down the rendering thread. + * + * Must not be called before a [VkRenderer] has been set. + */ + fun onDestroy() { + vkThread.blockingExit() + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + vkThread.onSurfaceChanged(width, height) + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + vkThread.onSurfaceDestroyed() + } + + override fun surfaceCreated(holder: SurfaceHolder) { + vkThread.onSurfaceCreated() + } +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt new file mode 100644 index 0000000000..7557c8aa22 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt @@ -0,0 +1,230 @@ +/*************************************************************************/ +/* VkThread.kt */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +@file:JvmName("VkThread") +package org.godotengine.godot.vulkan + +import android.util.Log +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock + +/** + * Thread implementation for the [VkSurfaceView] onto which the vulkan logic is ran. + * + * The implementation is modeled after [android.opengl.GLSurfaceView]'s GLThread. + */ +internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vkRenderer: VkRenderer) : Thread(TAG) { + + companion object { + private val TAG = VkThread::class.java.simpleName + } + + /** + * Used to run events scheduled on the thread. + */ + private val eventQueue = ArrayList<Runnable>() + + /** + * Used to synchronize interaction with other threads (e.g: main thread). + */ + private val lock = ReentrantLock() + private val lockCondition = lock.newCondition() + + private var shouldExit = false + private var exited = false + private var rendererInitialized = false + private var rendererResumed = false + private var resumed = false + private var hasSurface = false + private var width = 0 + private var height = 0 + + /** + * Determine when drawing can occur on the thread. This usually occurs after the + * [android.view.Surface] is available, the app is in a resumed state. + */ + private val readyToDraw + get() = hasSurface && resumed + + private fun threadExiting() { + lock.withLock { + exited = true + lockCondition.signalAll() + } + } + + /** + * Queue an event on the [VkThread]. + */ + fun queueEvent(event: Runnable) { + lock.withLock { + eventQueue.add(event) + lockCondition.signalAll() + } + } + + /** + * Request the thread to exit and block until it's done. + */ + fun blockingExit() { + lock.withLock { + shouldExit = true + lockCondition.signalAll() + while (!exited) { + try { + Log.i(TAG, "Waiting on exit for $name") + lockCondition.await() + } catch (ex: InterruptedException) { + currentThread().interrupt() + } + } + } + } + + /** + * Invoked when the app resumes. + */ + fun onResume() { + lock.withLock { + resumed = true + lockCondition.signalAll() + } + } + + /** + * Invoked when the app pauses. + */ + fun onPause() { + lock.withLock { + resumed = false + lockCondition.signalAll() + } + } + + /** + * Invoked when the [android.view.Surface] has been created. + */ + fun onSurfaceCreated() { + // This is a no op because surface creation will always be followed by surfaceChanged() + // which provide all the needed information. + } + + /** + * Invoked following structural updates to [android.view.Surface]. + */ + fun onSurfaceChanged(width: Int, height: Int) { + lock.withLock { + hasSurface = true + this.width = width + this.height = height + lockCondition.signalAll() + } + } + + /** + * Invoked when the [android.view.Surface] is no longer available. + */ + fun onSurfaceDestroyed() { + lock.withLock { + hasSurface = false + lockCondition.signalAll() + } + } + + /** + * Thread loop modeled after [android.opengl.GLSurfaceView]'s GLThread. + */ + override fun run() { + try { + while (true) { + var event: Runnable? = null + lock.withLock { + while (true) { + // Code path for exiting the thread loop. + if (shouldExit) { + vkRenderer.onVkDestroy() + return + } + + // Check for events and execute them outside of the loop if found to avoid + // blocking the thread lifecycle by holding onto the lock. + if (eventQueue.isNotEmpty()) { + event = eventQueue.removeAt(0) + break; + } + + if (readyToDraw) { + if (!rendererResumed) { + rendererResumed = true + vkRenderer.onVkResume() + + if (!rendererInitialized) { + rendererInitialized = true + vkRenderer.onVkSurfaceCreated(vkSurfaceView.holder.surface) + } + + vkRenderer.onVkSurfaceChanged(vkSurfaceView.holder.surface, width, height) + } + + // Break out of the loop so drawing can occur without holding onto the lock. + break; + } else if (rendererResumed) { + // If we aren't ready to draw but are resumed, that means we either lost a surface + // or the app was paused. + rendererResumed = false + vkRenderer.onVkPause() + } + // We only reach this state if we are not ready to draw and have no queued events, so + // we wait. + // On state change, the thread will be awoken using the [lock] and [lockCondition], and + // we will resume execution. + lockCondition.await() + } + } + + // Run queued event. + if (event != null) { + event?.run() + continue + } + + // Draw only when there no more queued events. + vkRenderer.onVkDrawFrame() + } + } catch (ex: InterruptedException) { + Log.i(TAG, "InterruptedException", ex) + } catch (ex: IllegalStateException) { + Log.i(TAG, "IllegalStateException", ex) + } finally { + threadExiting() + } + } + +} diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java index ce4defd7a7..8409e37f8f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java @@ -45,6 +45,8 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser { private int[] mValue = new int[1]; + // FIXME: Add support for Vulkan. + /* This EGL config specification is used to specify 2.0 rendering. * We use a minimum size of 4 bits for red/green/blue, but will * perform actual matching in chooseConfig() below. @@ -59,15 +61,6 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser { EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE }; - private static int[] s_configAttribs3 = { - EGL10.EGL_RED_SIZE, 4, - EGL10.EGL_GREEN_SIZE, 4, - EGL10.EGL_BLUE_SIZE, 4, - // EGL10.EGL_DEPTH_SIZE, 16, - // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //apparently there is no EGL_OPENGL_ES3_BIT - EGL10.EGL_NONE - }; public RegularConfigChooser(int r, int g, int b, int a, int depth, int stencil) { mRedSize = r; @@ -83,7 +76,7 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser { /* Get the number of minimally matching EGL configurations */ int[] num_config = new int[1]; - egl.eglChooseConfig(display, GLUtils.use_gl3 ? s_configAttribs3 : s_configAttribs2, null, 0, num_config); + egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config); int numConfigs = num_config[0]; @@ -94,7 +87,7 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser { /* Allocate then read the array of minimally matching EGL configs */ EGLConfig[] configs = new EGLConfig[numConfigs]; - egl.eglChooseConfig(display, GLUtils.use_gl3 ? s_configAttribs3 : s_configAttribs2, configs, numConfigs, num_config); + egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config); if (GLUtils.DEBUG) { GLUtils.printConfigs(egl, display, configs); diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java index 22bd4ced87..31cf696195 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java +++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java @@ -51,25 +51,17 @@ public class RegularContextFactory implements GLSurfaceView.EGLContextFactory { private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { - String driver_name = GodotLib.getGlobal("rendering/quality/driver/driver_name"); - if (GLUtils.use_gl3 && !driver_name.equals("GLES3")) { - GLUtils.use_gl3 = false; - } - if (GLUtils.use_gl3) - Log.w(TAG, "creating OpenGL ES 3.0 context :"); - else - Log.w(TAG, "creating OpenGL ES 2.0 context :"); + // FIXME: Add support for Vulkan. + Log.w(TAG, "creating OpenGL ES 2.0 context :"); GLUtils.checkEglError(TAG, "Before eglCreateContext", egl); EGLContext context; if (GLUtils.use_debug_opengl) { int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; - int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; - context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, GLUtils.use_gl3 ? attrib_list3 : attrib_list2); + context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list2); } else { int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; - int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; - context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, GLUtils.use_gl3 ? attrib_list3 : attrib_list2); + context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list2); } GLUtils.checkEglError(TAG, "After eglCreateContext", egl); return context; diff --git a/platform/android/java/plugins/godotpayment/build.gradle b/platform/android/java/plugins/godotpayment/build.gradle new file mode 100644 index 0000000000..ffab86e26e --- /dev/null +++ b/platform/android/java/plugins/godotpayment/build.gradle @@ -0,0 +1,32 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion versions.compileSdk + buildToolsVersion versions.buildTools + useLibrary 'org.apache.http.legacy' + + defaultConfig { + minSdkVersion versions.minSdk + targetSdkVersion versions.targetSdk + } + + libraryVariants.all { variant -> + variant.outputs.all { output -> + output.outputFileName = "GodotPayment.${variant.name}.aar" + } + } + +} + +dependencies { + implementation libraries.supportCoreUtils + implementation libraries.v4Support + + if (rootProject.findProject(":lib")) { + compileOnly project(":lib") + } else if (rootProject.findProject(":godot:lib")) { + compileOnly project(":godot:lib") + } else { + compileOnly fileTree(dir: 'libs', include: ['godot-lib*.aar']) + } +} diff --git a/platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml b/platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..61afa03799 --- /dev/null +++ b/platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.godotengine.godot.plugin.payment"> + + <application> + + <meta-data + android:name="org.godotengine.plugin.v1.GodotPayment" + android:value="org.godotengine.godot.plugin.payment.GodotPayment" /> + + </application> +</manifest> diff --git a/platform/android/java/lib/aidl/com/android/vending/billing/IInAppBillingService.aidl b/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl index 0f2bcae338..0f2bcae338 100644 --- a/platform/android/java/lib/aidl/com/android/vending/billing/IInAppBillingService.aidl +++ b/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java index 95cc48f536..c15bc232ce 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.content.Context; import android.os.AsyncTask; diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java index 93265d509f..c7d0a5de65 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotPaymentV3.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java @@ -1,5 +1,5 @@ /*************************************************************************/ -/* GodotPaymentV3.java */ +/* GodotPayment.java */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,29 +28,53 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot; +package org.godotengine.godot.plugin.payment; -import android.app.Activity; +import android.content.Intent; +import android.support.annotation.NonNull; import android.util.Log; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.godotengine.godot.payments.PaymentsManager; +import org.godotengine.godot.Dictionary; +import org.godotengine.godot.Godot; +import org.godotengine.godot.GodotLib; +import org.godotengine.godot.plugin.GodotPlugin; import org.json.JSONException; import org.json.JSONObject; -public class GodotPaymentV3 extends Godot.SingletonBase { +public class GodotPayment extends GodotPlugin { - private Godot activity; private Integer purchaseCallbackId = 0; private String accessToken; private String purchaseValidationUrlPrefix; private String transactionId; - private PaymentsManager mPaymentManager; - private Dictionary mSkuDetails = new Dictionary(); + private final PaymentsManager mPaymentManager; + private final Dictionary mSkuDetails = new Dictionary(); + + public GodotPayment(Godot godot) { + super(godot); + mPaymentManager = new PaymentsManager(godot, this); + mPaymentManager.initService(); + } + + @Override + public void onMainActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) { + mPaymentManager.processPurchaseResponse(resultCode, data); + } + } + + @Override + public void onMainDestroy() { + super.onMainDestroy(); + if (mPaymentManager != null) { + mPaymentManager.destroy(); + } + } public void purchase(final String sku, final String transactionId) { - activity.runOnUiThread(new Runnable() { + runOnUiThread(new Runnable() { @Override public void run() { mPaymentManager.requestPurchase(sku, transactionId); @@ -58,21 +82,8 @@ public class GodotPaymentV3 extends Godot.SingletonBase { }); } - static public Godot.SingletonBase initialize(Activity p_activity) { - - return new GodotPaymentV3(p_activity); - } - - public GodotPaymentV3(Activity p_activity) { - - registerClass("GodotPayments", new String[] { "purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails", "isConnected" }); - activity = (Godot)p_activity; - mPaymentManager = activity.getPaymentsManager(); - mPaymentManager.setBaseSingleton(this); - } - public void consumeUnconsumedPurchases() { - activity.runOnUiThread(new Runnable() { + runOnUiThread(new Runnable() { @Override public void run() { mPaymentManager.consumeUnconsumedPurchases(); @@ -149,7 +160,7 @@ public class GodotPaymentV3 extends Godot.SingletonBase { // request purchased items are not consumed public void requestPurchased() { - activity.runOnUiThread(new Runnable() { + runOnUiThread(new Runnable() { @Override public void run() { mPaymentManager.requestPurchased(); @@ -227,4 +238,18 @@ public class GodotPaymentV3 extends Godot.SingletonBase { public void errorSkuDetail(String errorMessage) { GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[] { errorMessage }); } + + @NonNull + @Override + public String getPluginName() { + return "GodotPayment"; + } + + @NonNull + @Override + public List<String> getPluginMethods() { + return Arrays.asList("purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", + "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", + "setAutoConsume", "consume", "querySkuDetails", "isConnected"); + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java index 23d693cc8c..fe5685288b 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.app.Activity; import android.content.Intent; diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java index 84a7eda6e0..d5919e3d9d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java @@ -28,11 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.content.Context; import android.content.SharedPreferences; -import android.util.Log; public class PaymentsCache { diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java index 90b958266b..bded1f452f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.app.Activity; import android.content.ComponentName; @@ -43,7 +43,6 @@ import android.util.Log; import com.android.vending.billing.IInAppBillingService; import java.util.ArrayList; import java.util.Arrays; -import org.godotengine.godot.GodotPaymentV3; import org.json.JSONException; import org.json.JSONObject; @@ -53,20 +52,13 @@ public class PaymentsManager { public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001; private static boolean auto_consume = true; - private Activity activity; + private final Activity activity; + private final GodotPayment godotPayment; IInAppBillingService mService; - public void setActivity(Activity activity) { - this.activity = activity; - } - - public static PaymentsManager createManager(Activity activity) { - PaymentsManager manager = new PaymentsManager(activity); - return manager; - } - - private PaymentsManager(Activity activity) { + PaymentsManager(Activity activity, GodotPayment godotPayment) { this.activity = activity; + this.godotPayment = godotPayment; } public PaymentsManager initService() { @@ -90,9 +82,9 @@ public class PaymentsManager { public void onServiceDisconnected(ComponentName name) { mService = null; - // At this stage, godotPaymentV3 might not have been initialized yet. - if (godotPaymentV3 != null) { - godotPaymentV3.callbackDisconnected(); + // At this stage, godotPayment might not have been initialized yet. + if (godotPayment != null) { + godotPayment.callbackDisconnected(); } } @@ -100,9 +92,9 @@ public class PaymentsManager { public void onServiceConnected(ComponentName name, IBinder service) { mService = IInAppBillingService.Stub.asInterface(service); - // At this stage, godotPaymentV3 might not have been initialized yet. - if (godotPaymentV3 != null) { - godotPaymentV3.callbackConnected(); + // At this stage, godotPayment might not have been initialized yet. + if (godotPayment != null) { + godotPayment.callbackConnected(); } } }; @@ -111,17 +103,17 @@ public class PaymentsManager { new PurchaseTask(mService, activity) { @Override protected void error(String message) { - godotPaymentV3.callbackFail(message); + godotPayment.callbackFail(message); } @Override protected void canceled() { - godotPaymentV3.callbackCancel(); + godotPayment.callbackCancel(); } @Override protected void alreadyOwned() { - godotPaymentV3.callbackAlreadyOwned(sku); + godotPayment.callbackAlreadyOwned(sku); } } .purchase(sku, transactionId); @@ -135,19 +127,19 @@ public class PaymentsManager { new ReleaseAllConsumablesTask(mService, activity) { @Override protected void success(String sku, String receipt, String signature, String token) { - godotPaymentV3.callbackSuccessProductMassConsumed(receipt, signature, sku); + godotPayment.callbackSuccessProductMassConsumed(receipt, signature, sku); } @Override protected void error(String message) { Log.d("godot", "consumeUnconsumedPurchases :" + message); - godotPaymentV3.callbackFailConsume(message); + godotPayment.callbackFailConsume(message); } @Override protected void notRequired() { Log.d("godot", "callbackSuccessNoUnconsumedPurchases :"); - godotPaymentV3.callbackSuccessNoUnconsumedPurchases(); + godotPayment.callbackSuccessNoUnconsumedPurchases(); } } .consumeItAll(); @@ -168,7 +160,7 @@ public class PaymentsManager { final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); if (myPurchases == null || myPurchases.size() == 0) { - godotPaymentV3.callbackPurchased("", "", ""); + godotPayment.callbackPurchased("", "", ""); return; } @@ -186,7 +178,7 @@ public class PaymentsManager { pc.setConsumableFlag("block", sku, true); pc.setConsumableValue("token", sku, token); - godotPaymentV3.callbackPurchased(receipt, signature, sku); + godotPayment.callbackPurchased(receipt, signature, sku); } catch (JSONException e) { } } @@ -203,7 +195,7 @@ public class PaymentsManager { new HandlePurchaseTask(activity) { @Override protected void success(final String sku, final String signature, final String ticket) { - godotPaymentV3.callbackSuccess(ticket, signature, sku); + godotPayment.callbackSuccess(ticket, signature, sku); if (auto_consume) { new ConsumeTask(mService, activity) { @@ -213,7 +205,7 @@ public class PaymentsManager { @Override protected void error(String message) { - godotPaymentV3.callbackFail(message); + godotPayment.callbackFail(message); } } .consume(sku); @@ -222,12 +214,12 @@ public class PaymentsManager { @Override protected void error(String message) { - godotPaymentV3.callbackFail(message); + godotPayment.callbackFail(message); } @Override protected void canceled() { - godotPaymentV3.callbackCancel(); + godotPayment.callbackCancel(); } } .handlePurchaseRequest(resultCode, data); @@ -235,19 +227,19 @@ public class PaymentsManager { public void validatePurchase(String purchaseToken, final String sku) { - new ValidateTask(activity, godotPaymentV3) { + new ValidateTask(activity, godotPayment) { @Override protected void success() { new ConsumeTask(mService, activity) { @Override protected void success(String ticket) { - godotPaymentV3.callbackSuccess(ticket, null, sku); + godotPayment.callbackSuccess(ticket, null, sku); } @Override protected void error(String message) { - godotPaymentV3.callbackFail(message); + godotPayment.callbackFail(message); } } .consume(sku); @@ -255,12 +247,12 @@ public class PaymentsManager { @Override protected void error(String message) { - godotPaymentV3.callbackFail(message); + godotPayment.callbackFail(message); } @Override protected void canceled() { - godotPaymentV3.callbackCancel(); + godotPayment.callbackCancel(); } } .validatePurchase(sku); @@ -274,12 +266,12 @@ public class PaymentsManager { new ConsumeTask(mService, activity) { @Override protected void success(String ticket) { - godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku); + godotPayment.callbackSuccessProductMassConsumed(ticket, "", sku); } @Override protected void error(String message) { - godotPaymentV3.callbackFailConsume(message); + godotPayment.callbackFailConsume(message); } } .consume(sku); @@ -387,9 +379,9 @@ public class PaymentsManager { if (!skuDetails.containsKey("DETAILS_LIST")) { int response = getResponseCodeFromBundle(skuDetails); if (response != BILLING_RESPONSE_RESULT_OK) { - godotPaymentV3.errorSkuDetail(getResponseDesc(response)); + godotPayment.errorSkuDetail(getResponseDesc(response)); } else { - godotPaymentV3.errorSkuDetail("No error but no detail list."); + godotPayment.errorSkuDetail("No error but no detail list."); } return; } @@ -398,22 +390,16 @@ public class PaymentsManager { for (String thisResponse : responseList) { Log.d("godot", "response = " + thisResponse); - godotPaymentV3.addSkuDetail(thisResponse); + godotPayment.addSkuDetail(thisResponse); } } catch (RemoteException e) { e.printStackTrace(); - godotPaymentV3.errorSkuDetail("RemoteException error!"); + godotPayment.errorSkuDetail("RemoteException error!"); } } - godotPaymentV3.completeSkuDetail(); + godotPayment.completeSkuDetail(); } })) .start(); } - - private GodotPaymentV3 godotPaymentV3; - - public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) { - this.godotPaymentV3 = godotPaymentV3; - } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java index 09c9349124..eecd1d2151 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.app.Activity; import android.app.PendingIntent; diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java index a101780511..b7bd638feb 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.content.Context; import android.os.AsyncTask; diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java index dbb6b8a783..d42ded0c9b 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java @@ -28,22 +28,21 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.payments; +package org.godotengine.godot.plugin.payment; import android.app.Activity; import android.app.ProgressDialog; import android.os.AsyncTask; import java.lang.ref.WeakReference; -import org.godotengine.godot.GodotPaymentV3; -import org.godotengine.godot.utils.HttpRequester; -import org.godotengine.godot.utils.RequestParams; +import org.godotengine.godot.plugin.payment.utils.HttpRequester; +import org.godotengine.godot.plugin.payment.utils.RequestParams; import org.json.JSONException; import org.json.JSONObject; abstract public class ValidateTask { private Activity context; - private GodotPaymentV3 godotPaymentsV3; + private GodotPayment godotPayments; private ProgressDialog dialog; private String mSku; @@ -80,9 +79,9 @@ abstract public class ValidateTask { } } - public ValidateTask(Activity context, GodotPaymentV3 godotPaymentsV3) { + public ValidateTask(Activity context, GodotPayment godotPayments) { this.context = context; - this.godotPaymentsV3 = godotPaymentsV3; + this.godotPayments = godotPayments; } public void validatePurchase(final String sku) { @@ -96,7 +95,7 @@ abstract public class ValidateTask { private String doInBackground(String... params) { PaymentsCache pc = new PaymentsCache(context); - String url = godotPaymentsV3.getPurchaseValidationUrlPrefix(); + String url = godotPayments.getPurchaseValidationUrlPrefix(); RequestParams param = new RequestParams(); param.setUrl(url); param.put("ticket", pc.getConsumableValue("ticket", mSku)); diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java index c78e8c1c66..9571769cd3 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java @@ -28,7 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.utils; +package org.godotengine.godot.plugin.payment.utils; + import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java index 68f9a83597..dcb983201e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.utils; +package org.godotengine.godot.plugin.payment.utils; import android.content.Context; import android.content.SharedPreferences; @@ -61,6 +61,7 @@ import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; +import org.godotengine.godot.utils.Crypt; /** * diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java index 25fa10647d..4be8b37473 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java +++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java @@ -28,10 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -package org.godotengine.godot.utils; +package org.godotengine.godot.plugin.payment.utils; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.List; import org.apache.http.NameValuePair; diff --git a/platform/android/java/settings.gradle b/platform/android/java/settings.gradle index f6921c70aa..9536d3de6d 100644 --- a/platform/android/java/settings.gradle +++ b/platform/android/java/settings.gradle @@ -3,3 +3,4 @@ rootProject.name = "Godot" include ':app' include ':lib' +include ':plugins:godotpayment' |