Re-architecture of the Godot Android plugin.
This commit is contained in:
parent
ea2e976cdd
commit
c3660bb4dc
30 changed files with 2082 additions and 1077 deletions
|
@ -9,6 +9,10 @@ insert_final_newline = true
|
||||||
[*.{cpp,hpp,c,h,mm}]
|
[*.{cpp,hpp,c,h,mm}]
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[{*.gradle,AndroidManifest.xml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
[{*.{py,cs},SConstruct,SCsub}]
|
[{*.{py,cs},SConstruct,SCsub}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -19,7 +19,7 @@ local.properties
|
||||||
.gradletasknamecache
|
.gradletasknamecache
|
||||||
project.properties
|
project.properties
|
||||||
platform/android/java/libs/*
|
platform/android/java/libs/*
|
||||||
platform/android/java/assets
|
platform/android/java/app/libs/*
|
||||||
|
|
||||||
# General c++ generated files
|
# General c++ generated files
|
||||||
*.lib
|
*.lib
|
||||||
|
|
|
@ -560,13 +560,6 @@ Error ExportTemplateManager::install_android_template() {
|
||||||
|
|
||||||
// Make res://android dir (if it does not exist).
|
// Make res://android dir (if it does not exist).
|
||||||
da->make_dir("android");
|
da->make_dir("android");
|
||||||
{
|
|
||||||
// Add an empty .gdignore file to avoid scan.
|
|
||||||
FileAccessRef f = FileAccess::open("res://android/.gdignore", FileAccess::WRITE);
|
|
||||||
ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
|
|
||||||
f->store_line("");
|
|
||||||
f->close();
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
// Add version, to ensure building won't work if template and Godot version don't match.
|
// Add version, to ensure building won't work if template and Godot version don't match.
|
||||||
FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::WRITE);
|
FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::WRITE);
|
||||||
|
@ -575,9 +568,20 @@ Error ExportTemplateManager::install_android_template() {
|
||||||
f->close();
|
f->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Error err = da->make_dir_recursive("android/build");
|
// Create the android plugins directory.
|
||||||
|
Error err = da->make_dir_recursive("android/plugins");
|
||||||
ERR_FAIL_COND_V(err != OK, err);
|
ERR_FAIL_COND_V(err != OK, err);
|
||||||
|
|
||||||
|
err = da->make_dir_recursive("android/build");
|
||||||
|
ERR_FAIL_COND_V(err != OK, err);
|
||||||
|
{
|
||||||
|
// Add an empty .gdignore file to avoid scan.
|
||||||
|
FileAccessRef f = FileAccess::open("res://android/build/.gdignore", FileAccess::WRITE);
|
||||||
|
ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
|
||||||
|
f->store_line("");
|
||||||
|
f->close();
|
||||||
|
}
|
||||||
|
|
||||||
// Uncompress source template.
|
// Uncompress source template.
|
||||||
|
|
||||||
const String &templates_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
|
const String &templates_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
|
||||||
|
|
|
@ -18,6 +18,8 @@ android_files = [
|
||||||
'java_class_wrapper.cpp',
|
'java_class_wrapper.cpp',
|
||||||
'java_godot_wrapper.cpp',
|
'java_godot_wrapper.cpp',
|
||||||
'java_godot_io_wrapper.cpp',
|
'java_godot_io_wrapper.cpp',
|
||||||
|
'jni_utils.cpp',
|
||||||
|
'plugin/godot_plugin_jni.cpp',
|
||||||
#'power_android.cpp'
|
#'power_android.cpp'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -692,6 +692,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
||||||
|
|
||||||
int xr_mode_index = p_preset->get("xr_features/xr_mode");
|
int xr_mode_index = p_preset->get("xr_features/xr_mode");
|
||||||
|
|
||||||
|
String plugins = p_preset->get("custom_template/plugins");
|
||||||
|
|
||||||
Vector<String> perms;
|
Vector<String> perms;
|
||||||
|
|
||||||
const char **aperms = android_perms;
|
const char **aperms = android_perms;
|
||||||
|
@ -860,6 +862,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tname == "meta-data" && attrname == "value" && value == "custom_template_plugins_value") {
|
||||||
|
// Update the meta-data 'android:value' attribute with the list of enabled plugins.
|
||||||
|
string_table.write[attr_value] = plugins;
|
||||||
|
}
|
||||||
|
|
||||||
iofs += 20;
|
iofs += 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1373,6 +1380,7 @@ public:
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false));
|
||||||
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/plugins", PROPERTY_HINT_PLACEHOLDER_TEXT, "Plugin1,Plugin2,..."), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
|
||||||
|
@ -2083,14 +2091,18 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build");
|
String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build");
|
||||||
|
String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
|
||||||
|
|
||||||
build_command = build_path.plus_file(build_command);
|
build_command = build_path.plus_file(build_command);
|
||||||
|
|
||||||
String package_name = get_package_name(p_preset->get("package/unique_name"));
|
String package_name = get_package_name(p_preset->get("package/unique_name"));
|
||||||
|
String plugins = p_preset->get("custom_template/plugins");
|
||||||
|
|
||||||
List<String> cmdline;
|
List<String> cmdline;
|
||||||
cmdline.push_back("build");
|
cmdline.push_back("build");
|
||||||
cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
|
cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
|
||||||
|
cmdline.push_back("-Pcustom_template_plugins_dir=" + plugins_dir); // argument to specify the plugins directory.
|
||||||
|
cmdline.push_back("-Pcustom_template_plugins=" + plugins); // argument to specify the list of plugins to enable.
|
||||||
cmdline.push_back("-p"); // argument to specify the start directory.
|
cmdline.push_back("-p"); // argument to specify the start directory.
|
||||||
cmdline.push_back(build_path); // start directory.
|
cmdline.push_back(build_path); // start directory.
|
||||||
/*{ used for debug
|
/*{ used for debug
|
||||||
|
|
|
@ -37,6 +37,11 @@
|
||||||
android:name="xr_mode_metadata_name"
|
android:name="xr_mode_metadata_name"
|
||||||
android:value="xr_mode_metadata_value" />
|
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
|
<activity
|
||||||
android:name=".GodotApp"
|
android:name=".GodotApp"
|
||||||
android:label="@string/godot_project_name_string"
|
android:label="@string/godot_project_name_string"
|
||||||
|
|
|
@ -34,15 +34,30 @@ allprojects {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation libraries.supportCoreUtils
|
implementation libraries.supportCoreUtils
|
||||||
|
implementation libraries.v4Support
|
||||||
|
|
||||||
if (rootProject.findProject(":lib")) {
|
if (rootProject.findProject(":lib")) {
|
||||||
implementation project(":lib")
|
implementation project(":lib")
|
||||||
|
} else if (rootProject.findProject(":godot:lib")) {
|
||||||
|
implementation project(":godot:lib")
|
||||||
} else {
|
} else {
|
||||||
// Custom build mode. In this scenario this project is the only one around and the Godot
|
// 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.
|
// library is available through the pre-generated godot-lib.*.aar android archive files.
|
||||||
debugImplementation fileTree(dir: 'libs/debug', include: ['*.jar', '*.aar'])
|
debugImplementation fileTree(dir: 'libs/debug', include: ['*.jar', '*.aar'])
|
||||||
releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar'])
|
releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar'])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
//CHUNK_DEPENDENCIES_BEGIN
|
//CHUNK_DEPENDENCIES_BEGIN
|
||||||
//CHUNK_DEPENDENCIES_END
|
//CHUNK_DEPENDENCIES_END
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,63 @@
|
||||||
ext.versions = [
|
ext.versions = [
|
||||||
androidGradlePlugin : '3.4.2',
|
androidGradlePlugin: '3.4.2',
|
||||||
compileSdk : 28,
|
compileSdk : 28,
|
||||||
minSdk : 18,
|
minSdk : 18,
|
||||||
targetSdk : 28,
|
targetSdk : 28,
|
||||||
buildTools : '28.0.3',
|
buildTools : '28.0.3',
|
||||||
supportCoreUtils : '28.0.0'
|
supportCoreUtils : '28.0.0',
|
||||||
|
v4Support : '28.0.0'
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
ext.libraries = [
|
ext.libraries = [
|
||||||
androidGradlePlugin : "com.android.tools.build:gradle:$versions.androidGradlePlugin",
|
androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin",
|
||||||
supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils"
|
supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils",
|
||||||
|
v4Support : "com.android.support:support-v4:$versions.v4Support"
|
||||||
]
|
]
|
||||||
|
|
||||||
ext.getExportPackageName = { ->
|
ext.getExportPackageName = { ->
|
||||||
// Retrieve the app id from the project property set by the Godot build command.
|
// 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") : ""
|
String appId = project.hasProperty("export_package_name") ? project.property("export_package_name") : ""
|
||||||
// Check if the app id is valid, otherwise use the default.
|
// Check if the app id is valid, otherwise use the default.
|
||||||
if (appId == null || appId.isEmpty()) {
|
if (appId == null || appId.isEmpty()) {
|
||||||
appId = "com.godot.game"
|
appId = "com.godot.game"
|
||||||
}
|
}
|
||||||
return appId
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ ext {
|
||||||
sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : ""
|
sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : ""
|
||||||
|
|
||||||
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
|
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
|
||||||
supportedTargets = ['release':"release", 'debug':"release_debug"]
|
supportedTargets = ['release': "release", 'debug': "release_debug"]
|
||||||
|
|
||||||
// Used by gradle to specify which architecture to build for by default when running `./gradlew build`.
|
// Used by gradle to specify which architecture to build for by default when running `./gradlew build`.
|
||||||
// This command is usually used by Android Studio.
|
// This command is usually used by Android Studio.
|
||||||
|
@ -64,10 +64,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.
|
* 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'
|
dependsOn ':lib:assembleDebug'
|
||||||
from('lib/build/outputs/aar')
|
from('lib/build/outputs/aar')
|
||||||
into('app/libs/debug')
|
into('app/libs/debug')
|
||||||
|
@ -75,16 +75,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.
|
* 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'
|
dependsOn ':lib:assembleRelease'
|
||||||
from('lib/build/outputs/aar')
|
from('lib/build/outputs/aar')
|
||||||
into('app/libs/release')
|
into('app/libs/release')
|
||||||
include('godot-lib.release.aar')
|
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
|
* 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'.
|
* as the AAR files generated by 'copyDebugAAR' and 'copyReleaseAAR'.
|
||||||
|
@ -95,7 +124,7 @@ task zipCustomBuild(type: Zip) {
|
||||||
doFirst {
|
doFirst {
|
||||||
logger.lifecycle("Generating Godot custom build template")
|
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 '**/*'
|
include '**/*'
|
||||||
archiveName 'android_source.zip'
|
archiveName 'android_source.zip'
|
||||||
destinationDir(file(binDir))
|
destinationDir(file(binDir))
|
||||||
|
@ -110,19 +139,24 @@ task generateGodotTemplates(type: GradleBuild) {
|
||||||
startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType)
|
startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks = []
|
tasks = ["copyGodotPaymentPluginToAppModule"]
|
||||||
|
|
||||||
// Only build the apks and aar files for which we have native shared libraries.
|
// Only build the apks and aar files for which we have native shared libraries.
|
||||||
for (String target : supportedTargets.keySet()) {
|
for (String target : supportedTargets.keySet()) {
|
||||||
File targetLibs = new File("lib/libs/" + target)
|
File targetLibs = new File("lib/libs/" + target)
|
||||||
if (targetLibs != null && targetLibs.isDirectory()) {
|
if (targetLibs != null
|
||||||
File[] targetLibsContents = targetLibs.listFiles()
|
&& targetLibs.isDirectory()
|
||||||
if (targetLibsContents != null && targetLibsContents.length > 0) {
|
&& targetLibs.listFiles() != null
|
||||||
// Copy the generated aar library files to the custom build directory.
|
&& targetLibs.listFiles().length > 0) {
|
||||||
tasks += "copy" + target.capitalize() + "AAR"
|
String capitalizedTarget = target.capitalize()
|
||||||
// Copy the prebuilt binary templates to the bin directory.
|
// Copy the generated aar library files to the custom build directory.
|
||||||
tasks += "copy" + target.capitalize() + "BinaryToBin"
|
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 +167,28 @@ task generateGodotTemplates(type: GradleBuild) {
|
||||||
* Clean the generated artifacts.
|
* Clean the generated artifacts.
|
||||||
*/
|
*/
|
||||||
task cleanGodotTemplates(type: Delete) {
|
task cleanGodotTemplates(type: Delete) {
|
||||||
// Delete the generated native libs
|
// Delete the generated native libs
|
||||||
delete("lib/libs")
|
delete("lib/libs")
|
||||||
|
|
||||||
// Delete the library generated AAR files
|
// Delete the library generated AAR files
|
||||||
delete("lib/build/outputs/aar")
|
delete("lib/build/outputs/aar")
|
||||||
|
|
||||||
// Delete the app libs directory contents
|
// Delete the godotpayment libs directory contents
|
||||||
delete("app/libs")
|
delete("plugins/godotpayment/libs")
|
||||||
|
|
||||||
// Delete the generated binary apks
|
// Delete the generated godotpayment aar
|
||||||
delete("app/build/outputs/apk")
|
delete("plugins/godotpayment/build/outputs/aar")
|
||||||
|
|
||||||
// Delete the Godot templates in the Godot bin directory
|
// Delete the app libs directory contents
|
||||||
delete("$binDir/android_debug.apk")
|
delete("app/libs")
|
||||||
delete("$binDir/android_release.apk")
|
|
||||||
delete("$binDir/android_source.zip")
|
// 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("$binDir/godot-lib.debug.aar")
|
||||||
|
delete("$binDir/godot-lib.release.aar")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ apply plugin: 'com.android.library'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation libraries.supportCoreUtils
|
implementation libraries.supportCoreUtils
|
||||||
|
implementation libraries.v4Support
|
||||||
}
|
}
|
||||||
|
|
||||||
def pathToRootDir = "../../../../"
|
def pathToRootDir = "../../../../"
|
||||||
|
|
|
@ -61,8 +61,11 @@ import android.os.Messenger;
|
||||||
import android.os.VibrationEffect;
|
import android.os.VibrationEffect;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.provider.Settings.Secure;
|
import android.provider.Settings.Secure;
|
||||||
|
import android.support.annotation.CallSuper;
|
||||||
import android.support.annotation.Keep;
|
import android.support.annotation.Keep;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
@ -96,11 +99,13 @@ import java.util.Locale;
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
import org.godotengine.godot.input.GodotEditText;
|
import org.godotengine.godot.input.GodotEditText;
|
||||||
import org.godotengine.godot.payments.PaymentsManager;
|
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.GodotNetUtils;
|
||||||
import org.godotengine.godot.utils.PermissionsUtil;
|
import org.godotengine.godot.utils.PermissionsUtil;
|
||||||
import org.godotengine.godot.xr.XRMode;
|
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;
|
static final int MAX_SINGLETONS = 64;
|
||||||
private IStub mDownloaderClientStub;
|
private IStub mDownloaderClientStub;
|
||||||
|
@ -129,6 +134,8 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
// Used to dispatch events to the main thread.
|
// Used to dispatch events to the main thread.
|
||||||
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
|
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
private GodotPluginRegistry pluginRegistry;
|
||||||
|
|
||||||
static private Intent mCurrentIntent;
|
static private Intent mCurrentIntent;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -158,7 +165,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
|
|
||||||
protected void registerClass(String p_name, String[] p_methods) {
|
protected void registerClass(String p_name, String[] p_methods) {
|
||||||
|
|
||||||
GodotLib.singleton(p_name, this);
|
GodotPlugin.nativeRegisterSingleton(p_name, this);
|
||||||
|
|
||||||
Class clazz = getClass();
|
Class clazz = getClass();
|
||||||
Method[] methods = clazz.getDeclaredMethods();
|
Method[] methods = clazz.getDeclaredMethods();
|
||||||
|
@ -184,7 +191,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
String[] pt = new String[ptr.size()];
|
String[] pt = new String[ptr.size()];
|
||||||
ptr.toArray(pt);
|
ptr.toArray(pt);
|
||||||
|
|
||||||
GodotLib.method(p_name, method.getName(), method.getReturnType().getName(), pt);
|
GodotPlugin.nativeRegisterMethod(p_name, method.getName(), method.getReturnType().getName(), pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
Godot.singletons[Godot.singleton_count++] = this;
|
Godot.singletons[Godot.singleton_count++] = this;
|
||||||
|
@ -269,6 +276,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
|
|
||||||
singletons[i].onMainActivityResult(requestCode, resultCode, data);
|
singletons[i].onMainActivityResult(requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onMainActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -276,12 +286,25 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
for (int i = 0; i < singleton_count; i++) {
|
for (int i = 0; i < singleton_count; i++) {
|
||||||
singletons[i].onMainRequestPermissionsResult(requestCode, permissions, grantResults);
|
singletons[i].onMainRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
}
|
}
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < permissions.length; i++) {
|
for (int i = 0; i < permissions.length; i++) {
|
||||||
GodotLib.requestPermissionResult(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
|
GodotLib.requestPermissionResult(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked on the GL thread when the Godot main loop has started.
|
||||||
|
*/
|
||||||
|
@CallSuper
|
||||||
|
protected void onGLGodotMainLoopStarted() {
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onGLGodotMainLoopStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by the native code (java_godot_lib_jni.cpp) to complete initialization of the GLSurfaceView view and renderer.
|
* Used by the native code (java_godot_lib_jni.cpp) to complete initialization of the GLSurfaceView view and renderer.
|
||||||
*/
|
*/
|
||||||
|
@ -304,14 +327,13 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
edittext.setView(mView);
|
edittext.setView(mView);
|
||||||
io.setEdit(edittext);
|
io.setEdit(edittext);
|
||||||
|
|
||||||
final Godot godot = this;
|
|
||||||
mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onGlobalLayout() {
|
public void onGlobalLayout() {
|
||||||
Point fullSize = new Point();
|
Point fullSize = new Point();
|
||||||
godot.getWindowManager().getDefaultDisplay().getSize(fullSize);
|
getWindowManager().getDefaultDisplay().getSize(fullSize);
|
||||||
Rect gameSize = new Rect();
|
Rect gameSize = new Rect();
|
||||||
godot.mView.getWindowVisibleDisplayFrame(gameSize);
|
mView.getWindowVisibleDisplayFrame(gameSize);
|
||||||
|
|
||||||
final int keyboardHeight = fullSize.y - gameSize.bottom;
|
final int keyboardHeight = fullSize.y - gameSize.bottom;
|
||||||
GodotLib.setVirtualKeyboardHeight(keyboardHeight);
|
GodotLib.setVirtualKeyboardHeight(keyboardHeight);
|
||||||
|
@ -323,6 +345,12 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
GodotLib.setup(current_command_line);
|
GodotLib.setup(current_command_line);
|
||||||
|
|
||||||
|
// Must occur after GodotLib.setup has completed.
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onGLRegisterPluginWithGodotNative();
|
||||||
|
}
|
||||||
|
|
||||||
setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
|
setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
|
||||||
|
|
||||||
// The Godot Android plugins are setup on completion of GodotLib.setup
|
// The Godot Android plugins are setup on completion of GodotLib.setup
|
||||||
|
@ -340,6 +368,14 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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) {
|
public void setKeepScreenOn(final boolean p_enabled) {
|
||||||
|
@ -537,6 +573,7 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
Window window = getWindow();
|
Window window = getWindow();
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||||
mClipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
|
mClipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this);
|
||||||
|
|
||||||
//check for apk expansion API
|
//check for apk expansion API
|
||||||
if (true) {
|
if (true) {
|
||||||
|
@ -680,6 +717,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
for (int i = 0; i < singleton_count; i++) {
|
for (int i = 0; i < singleton_count; i++) {
|
||||||
singletons[i].onMainDestroy();
|
singletons[i].onMainDestroy();
|
||||||
}
|
}
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onMainDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
GodotLib.ondestroy(this);
|
GodotLib.ondestroy(this);
|
||||||
|
|
||||||
|
@ -708,6 +748,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
for (int i = 0; i < singleton_count; i++) {
|
for (int i = 0; i < singleton_count; i++) {
|
||||||
singletons[i].onMainPause();
|
singletons[i].onMainPause();
|
||||||
}
|
}
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onMainPause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getClipboard() {
|
public String getClipboard() {
|
||||||
|
@ -761,6 +804,9 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
|
|
||||||
singletons[i].onMainResume();
|
singletons[i].onMainResume();
|
||||||
}
|
}
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onMainResume();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UiChangeListener() {
|
public void UiChangeListener() {
|
||||||
|
@ -857,6 +903,11 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
shouldQuit = false;
|
shouldQuit = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
if (plugin.onMainBackPressed()) {
|
||||||
|
shouldQuit = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldQuit && mView != null) {
|
if (shouldQuit && mView != null) {
|
||||||
mView.queueEvent(new Runnable() {
|
mView.queueEvent(new Runnable() {
|
||||||
|
@ -868,6 +919,17 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue a runnable to be run on the GL thread.
|
||||||
|
* <p>
|
||||||
|
* This must be called after the GL thread has started.
|
||||||
|
*/
|
||||||
|
public final void runOnGLThread(@NonNull Runnable action) {
|
||||||
|
if (mView != null) {
|
||||||
|
mView.queueEvent(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void forceQuit() {
|
private void forceQuit() {
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,22 +175,6 @@ public class GodotLib {
|
||||||
*/
|
*/
|
||||||
public static native void audio();
|
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.
|
* Used to access Godot global properties.
|
||||||
* @param p_key Property key
|
* @param p_key Property key
|
||||||
|
|
|
@ -33,6 +33,8 @@ package org.godotengine.godot;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
import javax.microedition.khronos.egl.EGLConfig;
|
import javax.microedition.khronos.egl.EGLConfig;
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
import org.godotengine.godot.plugin.GodotPlugin;
|
||||||
|
import org.godotengine.godot.plugin.GodotPluginRegistry;
|
||||||
import org.godotengine.godot.utils.GLUtils;
|
import org.godotengine.godot.utils.GLUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,8 +42,13 @@ import org.godotengine.godot.utils.GLUtils;
|
||||||
*/
|
*/
|
||||||
class GodotRenderer implements GLSurfaceView.Renderer {
|
class GodotRenderer implements GLSurfaceView.Renderer {
|
||||||
|
|
||||||
|
private final GodotPluginRegistry pluginRegistry;
|
||||||
private boolean activityJustResumed = false;
|
private boolean activityJustResumed = false;
|
||||||
|
|
||||||
|
GodotRenderer() {
|
||||||
|
this.pluginRegistry = GodotPluginRegistry.getPluginRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
public void onDrawFrame(GL10 gl) {
|
public void onDrawFrame(GL10 gl) {
|
||||||
if (activityJustResumed) {
|
if (activityJustResumed) {
|
||||||
GodotLib.onRendererResumed();
|
GodotLib.onRendererResumed();
|
||||||
|
@ -52,18 +59,26 @@ class GodotRenderer implements GLSurfaceView.Renderer {
|
||||||
for (int i = 0; i < Godot.singleton_count; i++) {
|
for (int i = 0; i < Godot.singleton_count; i++) {
|
||||||
Godot.singletons[i].onGLDrawFrame(gl);
|
Godot.singletons[i].onGLDrawFrame(gl);
|
||||||
}
|
}
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onGLDrawFrame(gl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
||||||
|
|
||||||
GodotLib.resize(width, height);
|
GodotLib.resize(width, height);
|
||||||
for (int i = 0; i < Godot.singleton_count; i++) {
|
for (int i = 0; i < Godot.singleton_count; i++) {
|
||||||
Godot.singletons[i].onGLSurfaceChanged(gl, width, height);
|
Godot.singletons[i].onGLSurfaceChanged(gl, width, height);
|
||||||
}
|
}
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onGLSurfaceChanged(gl, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||||
GodotLib.newcontext(GLUtils.use_32);
|
GodotLib.newcontext(GLUtils.use_32);
|
||||||
|
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||||
|
plugin.onGLSurfaceCreated(gl, config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onActivityResumed() {
|
void onActivityResumed() {
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* GodotPaymentInterface.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.payments;
|
||||||
|
|
||||||
|
public interface GodotPaymentInterface {
|
||||||
|
void purchase(String sku, String transactionId);
|
||||||
|
|
||||||
|
void consumeUnconsumedPurchases();
|
||||||
|
|
||||||
|
String getSignature();
|
||||||
|
|
||||||
|
void callbackSuccess(String ticket, String signature, String sku);
|
||||||
|
|
||||||
|
void callbackSuccessProductMassConsumed(String ticket, String signature, String sku);
|
||||||
|
|
||||||
|
void callbackSuccessNoUnconsumedPurchases();
|
||||||
|
|
||||||
|
void callbackFailConsume(String message);
|
||||||
|
|
||||||
|
void callbackFail(String message);
|
||||||
|
|
||||||
|
void callbackCancel();
|
||||||
|
|
||||||
|
void callbackAlreadyOwned(String sku);
|
||||||
|
|
||||||
|
int getPurchaseCallbackId();
|
||||||
|
|
||||||
|
void setPurchaseCallbackId(int purchaseCallbackId);
|
||||||
|
|
||||||
|
String getPurchaseValidationUrlPrefix();
|
||||||
|
|
||||||
|
void setPurchaseValidationUrlPrefix(String url);
|
||||||
|
|
||||||
|
String getAccessToken();
|
||||||
|
|
||||||
|
void setAccessToken(String accessToken);
|
||||||
|
|
||||||
|
void setTransactionId(String transactionId);
|
||||||
|
|
||||||
|
String getTransactionId();
|
||||||
|
|
||||||
|
// request purchased items are not consumed
|
||||||
|
void requestPurchased();
|
||||||
|
|
||||||
|
// callback for requestPurchased()
|
||||||
|
void callbackPurchased(String receipt, String signature, String sku);
|
||||||
|
|
||||||
|
void callbackDisconnected();
|
||||||
|
|
||||||
|
void callbackConnected();
|
||||||
|
|
||||||
|
// true if connected, false otherwise
|
||||||
|
boolean isConnected();
|
||||||
|
|
||||||
|
// consume item automatically after purchase. default is true.
|
||||||
|
void setAutoConsume(boolean autoConsume);
|
||||||
|
|
||||||
|
// consume a specific item
|
||||||
|
void consume(String sku);
|
||||||
|
|
||||||
|
// query in app item detail info
|
||||||
|
void querySkuDetails(String[] list);
|
||||||
|
|
||||||
|
void addSkuDetail(String itemJson);
|
||||||
|
|
||||||
|
void completeSkuDetail();
|
||||||
|
|
||||||
|
void errorSkuDetail(String errorMessage);
|
||||||
|
}
|
|
@ -43,7 +43,6 @@ import android.util.Log;
|
||||||
import com.android.vending.billing.IInAppBillingService;
|
import com.android.vending.billing.IInAppBillingService;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import org.godotengine.godot.GodotPaymentV3;
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
@ -90,9 +89,9 @@ public class PaymentsManager {
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
mService = null;
|
mService = null;
|
||||||
|
|
||||||
// At this stage, godotPaymentV3 might not have been initialized yet.
|
// At this stage, godotPayment might not have been initialized yet.
|
||||||
if (godotPaymentV3 != null) {
|
if (godotPayment != null) {
|
||||||
godotPaymentV3.callbackDisconnected();
|
godotPayment.callbackDisconnected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,9 +99,9 @@ public class PaymentsManager {
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
mService = IInAppBillingService.Stub.asInterface(service);
|
mService = IInAppBillingService.Stub.asInterface(service);
|
||||||
|
|
||||||
// At this stage, godotPaymentV3 might not have been initialized yet.
|
// At this stage, godotPayment might not have been initialized yet.
|
||||||
if (godotPaymentV3 != null) {
|
if (godotPayment != null) {
|
||||||
godotPaymentV3.callbackConnected();
|
godotPayment.callbackConnected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -111,17 +110,17 @@ public class PaymentsManager {
|
||||||
new PurchaseTask(mService, activity) {
|
new PurchaseTask(mService, activity) {
|
||||||
@Override
|
@Override
|
||||||
protected void error(String message) {
|
protected void error(String message) {
|
||||||
godotPaymentV3.callbackFail(message);
|
godotPayment.callbackFail(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void canceled() {
|
protected void canceled() {
|
||||||
godotPaymentV3.callbackCancel();
|
godotPayment.callbackCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void alreadyOwned() {
|
protected void alreadyOwned() {
|
||||||
godotPaymentV3.callbackAlreadyOwned(sku);
|
godotPayment.callbackAlreadyOwned(sku);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.purchase(sku, transactionId);
|
.purchase(sku, transactionId);
|
||||||
|
@ -135,19 +134,19 @@ public class PaymentsManager {
|
||||||
new ReleaseAllConsumablesTask(mService, activity) {
|
new ReleaseAllConsumablesTask(mService, activity) {
|
||||||
@Override
|
@Override
|
||||||
protected void success(String sku, String receipt, String signature, String token) {
|
protected void success(String sku, String receipt, String signature, String token) {
|
||||||
godotPaymentV3.callbackSuccessProductMassConsumed(receipt, signature, sku);
|
godotPayment.callbackSuccessProductMassConsumed(receipt, signature, sku);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void error(String message) {
|
protected void error(String message) {
|
||||||
Log.d("godot", "consumeUnconsumedPurchases :" + message);
|
Log.d("godot", "consumeUnconsumedPurchases :" + message);
|
||||||
godotPaymentV3.callbackFailConsume(message);
|
godotPayment.callbackFailConsume(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void notRequired() {
|
protected void notRequired() {
|
||||||
Log.d("godot", "callbackSuccessNoUnconsumedPurchases :");
|
Log.d("godot", "callbackSuccessNoUnconsumedPurchases :");
|
||||||
godotPaymentV3.callbackSuccessNoUnconsumedPurchases();
|
godotPayment.callbackSuccessNoUnconsumedPurchases();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.consumeItAll();
|
.consumeItAll();
|
||||||
|
@ -168,7 +167,7 @@ public class PaymentsManager {
|
||||||
final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
|
final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
|
||||||
|
|
||||||
if (myPurchases == null || myPurchases.size() == 0) {
|
if (myPurchases == null || myPurchases.size() == 0) {
|
||||||
godotPaymentV3.callbackPurchased("", "", "");
|
godotPayment.callbackPurchased("", "", "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +185,7 @@ public class PaymentsManager {
|
||||||
pc.setConsumableFlag("block", sku, true);
|
pc.setConsumableFlag("block", sku, true);
|
||||||
pc.setConsumableValue("token", sku, token);
|
pc.setConsumableValue("token", sku, token);
|
||||||
|
|
||||||
godotPaymentV3.callbackPurchased(receipt, signature, sku);
|
godotPayment.callbackPurchased(receipt, signature, sku);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,7 +202,7 @@ public class PaymentsManager {
|
||||||
new HandlePurchaseTask(activity) {
|
new HandlePurchaseTask(activity) {
|
||||||
@Override
|
@Override
|
||||||
protected void success(final String sku, final String signature, final String ticket) {
|
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) {
|
if (auto_consume) {
|
||||||
new ConsumeTask(mService, activity) {
|
new ConsumeTask(mService, activity) {
|
||||||
|
@ -213,7 +212,7 @@ public class PaymentsManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void error(String message) {
|
protected void error(String message) {
|
||||||
godotPaymentV3.callbackFail(message);
|
godotPayment.callbackFail(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.consume(sku);
|
.consume(sku);
|
||||||
|
@ -222,12 +221,12 @@ public class PaymentsManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void error(String message) {
|
protected void error(String message) {
|
||||||
godotPaymentV3.callbackFail(message);
|
godotPayment.callbackFail(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void canceled() {
|
protected void canceled() {
|
||||||
godotPaymentV3.callbackCancel();
|
godotPayment.callbackCancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.handlePurchaseRequest(resultCode, data);
|
.handlePurchaseRequest(resultCode, data);
|
||||||
|
@ -235,19 +234,19 @@ public class PaymentsManager {
|
||||||
|
|
||||||
public void validatePurchase(String purchaseToken, final String sku) {
|
public void validatePurchase(String purchaseToken, final String sku) {
|
||||||
|
|
||||||
new ValidateTask(activity, godotPaymentV3) {
|
new ValidateTask(activity, godotPayment) {
|
||||||
@Override
|
@Override
|
||||||
protected void success() {
|
protected void success() {
|
||||||
|
|
||||||
new ConsumeTask(mService, activity) {
|
new ConsumeTask(mService, activity) {
|
||||||
@Override
|
@Override
|
||||||
protected void success(String ticket) {
|
protected void success(String ticket) {
|
||||||
godotPaymentV3.callbackSuccess(ticket, null, sku);
|
godotPayment.callbackSuccess(ticket, null, sku);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void error(String message) {
|
protected void error(String message) {
|
||||||
godotPaymentV3.callbackFail(message);
|
godotPayment.callbackFail(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.consume(sku);
|
.consume(sku);
|
||||||
|
@ -255,12 +254,12 @@ public class PaymentsManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void error(String message) {
|
protected void error(String message) {
|
||||||
godotPaymentV3.callbackFail(message);
|
godotPayment.callbackFail(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void canceled() {
|
protected void canceled() {
|
||||||
godotPaymentV3.callbackCancel();
|
godotPayment.callbackCancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.validatePurchase(sku);
|
.validatePurchase(sku);
|
||||||
|
@ -274,12 +273,12 @@ public class PaymentsManager {
|
||||||
new ConsumeTask(mService, activity) {
|
new ConsumeTask(mService, activity) {
|
||||||
@Override
|
@Override
|
||||||
protected void success(String ticket) {
|
protected void success(String ticket) {
|
||||||
godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku);
|
godotPayment.callbackSuccessProductMassConsumed(ticket, "", sku);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void error(String message) {
|
protected void error(String message) {
|
||||||
godotPaymentV3.callbackFailConsume(message);
|
godotPayment.callbackFailConsume(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.consume(sku);
|
.consume(sku);
|
||||||
|
@ -387,9 +386,9 @@ public class PaymentsManager {
|
||||||
if (!skuDetails.containsKey("DETAILS_LIST")) {
|
if (!skuDetails.containsKey("DETAILS_LIST")) {
|
||||||
int response = getResponseCodeFromBundle(skuDetails);
|
int response = getResponseCodeFromBundle(skuDetails);
|
||||||
if (response != BILLING_RESPONSE_RESULT_OK) {
|
if (response != BILLING_RESPONSE_RESULT_OK) {
|
||||||
godotPaymentV3.errorSkuDetail(getResponseDesc(response));
|
godotPayment.errorSkuDetail(getResponseDesc(response));
|
||||||
} else {
|
} else {
|
||||||
godotPaymentV3.errorSkuDetail("No error but no detail list.");
|
godotPayment.errorSkuDetail("No error but no detail list.");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -398,22 +397,22 @@ public class PaymentsManager {
|
||||||
|
|
||||||
for (String thisResponse : responseList) {
|
for (String thisResponse : responseList) {
|
||||||
Log.d("godot", "response = " + thisResponse);
|
Log.d("godot", "response = " + thisResponse);
|
||||||
godotPaymentV3.addSkuDetail(thisResponse);
|
godotPayment.addSkuDetail(thisResponse);
|
||||||
}
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
godotPaymentV3.errorSkuDetail("RemoteException error!");
|
godotPayment.errorSkuDetail("RemoteException error!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
godotPaymentV3.completeSkuDetail();
|
godotPayment.completeSkuDetail();
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private GodotPaymentV3 godotPaymentV3;
|
private GodotPaymentInterface godotPayment;
|
||||||
|
|
||||||
public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) {
|
public void setBaseSingleton(GodotPaymentInterface godotPaymentInterface) {
|
||||||
this.godotPaymentV3 = godotPaymentV3;
|
this.godotPayment = godotPaymentInterface;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ import android.app.Activity;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import org.godotengine.godot.GodotPaymentV3;
|
|
||||||
import org.godotengine.godot.utils.HttpRequester;
|
import org.godotengine.godot.utils.HttpRequester;
|
||||||
import org.godotengine.godot.utils.RequestParams;
|
import org.godotengine.godot.utils.RequestParams;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
@ -43,7 +42,7 @@ import org.json.JSONObject;
|
||||||
abstract public class ValidateTask {
|
abstract public class ValidateTask {
|
||||||
|
|
||||||
private Activity context;
|
private Activity context;
|
||||||
private GodotPaymentV3 godotPaymentsV3;
|
private GodotPaymentInterface godotPayments;
|
||||||
private ProgressDialog dialog;
|
private ProgressDialog dialog;
|
||||||
private String mSku;
|
private String mSku;
|
||||||
|
|
||||||
|
@ -80,9 +79,9 @@ abstract public class ValidateTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidateTask(Activity context, GodotPaymentV3 godotPaymentsV3) {
|
public ValidateTask(Activity context, GodotPaymentInterface godotPayments) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.godotPaymentsV3 = godotPaymentsV3;
|
this.godotPayments = godotPayments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validatePurchase(final String sku) {
|
public void validatePurchase(final String sku) {
|
||||||
|
@ -96,7 +95,7 @@ abstract public class ValidateTask {
|
||||||
|
|
||||||
private String doInBackground(String... params) {
|
private String doInBackground(String... params) {
|
||||||
PaymentsCache pc = new PaymentsCache(context);
|
PaymentsCache pc = new PaymentsCache(context);
|
||||||
String url = godotPaymentsV3.getPurchaseValidationUrlPrefix();
|
String url = godotPayments.getPurchaseValidationUrlPrefix();
|
||||||
RequestParams param = new RequestParams();
|
RequestParams param = new RequestParams();
|
||||||
param.setUrl(url);
|
param.setUrl(url);
|
||||||
param.put("ticket", pc.getConsumableValue("ticket", mSku));
|
param.put("ticket", pc.getConsumableValue("ticket", mSku));
|
||||||
|
|
|
@ -0,0 +1,256 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* GodotPlugin.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.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
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 javax.microedition.khronos.egl.EGLConfig;
|
||||||
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
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 final Godot godot;
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
public final void onGLRegisterPluginWithGodotNative() {
|
||||||
|
nativeRegisterSingleton(getPluginName(), this);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 GL thread when the Godot main loop has started.
|
||||||
|
*/
|
||||||
|
public void onGLGodotMainLoopStarted() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 abstract List<String> getPluginMethods();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 GL thread.
|
||||||
|
*
|
||||||
|
* @param action the action to run on the GL thread
|
||||||
|
*/
|
||||||
|
protected void runOnGLThread(Runnable action) {
|
||||||
|
godot.runOnGLThread(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to setup a {@link GodotPlugin} instance.
|
||||||
|
* @param p_name Name of the instance.
|
||||||
|
*/
|
||||||
|
public static native void nativeRegisterSingleton(String p_name, Object object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
public static 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);
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* GodotPluginRegistry.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.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
platform/android/java/plugins/godotpayment/build.gradle
Normal file
31
platform/android/java/plugins/godotpayment/build.gradle
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion versions.compileSdk
|
||||||
|
buildToolsVersion versions.buildTools
|
||||||
|
|
||||||
|
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'])
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -1,5 +1,5 @@
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
/* GodotPaymentV3.java */
|
/* GodotPayment.java */
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
/* This file is part of: */
|
/* This file is part of: */
|
||||||
/* GODOT ENGINE */
|
/* GODOT ENGINE */
|
||||||
|
@ -28,20 +28,24 @@
|
||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
/* 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.support.annotation.NonNull;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.godotengine.godot.Dictionary;
|
||||||
|
import org.godotengine.godot.Godot;
|
||||||
|
import org.godotengine.godot.GodotLib;
|
||||||
|
import org.godotengine.godot.payments.GodotPaymentInterface;
|
||||||
import org.godotengine.godot.payments.PaymentsManager;
|
import org.godotengine.godot.payments.PaymentsManager;
|
||||||
|
import org.godotengine.godot.plugin.GodotPlugin;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class GodotPaymentV3 extends Godot.SingletonBase {
|
public class GodotPayment extends GodotPlugin implements GodotPaymentInterface {
|
||||||
|
|
||||||
private Godot activity;
|
|
||||||
private Integer purchaseCallbackId = 0;
|
private Integer purchaseCallbackId = 0;
|
||||||
private String accessToken;
|
private String accessToken;
|
||||||
private String purchaseValidationUrlPrefix;
|
private String purchaseValidationUrlPrefix;
|
||||||
|
@ -49,8 +53,15 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
|
||||||
private PaymentsManager mPaymentManager;
|
private PaymentsManager mPaymentManager;
|
||||||
private Dictionary mSkuDetails = new Dictionary();
|
private Dictionary mSkuDetails = new Dictionary();
|
||||||
|
|
||||||
|
public GodotPayment(Godot godot) {
|
||||||
|
super(godot);
|
||||||
|
mPaymentManager = godot.getPaymentsManager();
|
||||||
|
mPaymentManager.setBaseSingleton(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void purchase(final String sku, final String transactionId) {
|
public void purchase(final String sku, final String transactionId) {
|
||||||
activity.runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mPaymentManager.requestPurchase(sku, transactionId);
|
mPaymentManager.requestPurchase(sku, transactionId);
|
||||||
|
@ -58,21 +69,9 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static public Godot.SingletonBase initialize(Activity p_activity) {
|
@Override
|
||||||
|
|
||||||
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() {
|
public void consumeUnconsumedPurchases() {
|
||||||
activity.runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mPaymentManager.consumeUnconsumedPurchases();
|
mPaymentManager.consumeUnconsumedPurchases();
|
||||||
|
@ -82,74 +81,91 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
|
||||||
|
|
||||||
private String signature;
|
private String signature;
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getSignature() {
|
public String getSignature() {
|
||||||
return this.signature;
|
return this.signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void callbackSuccess(String ticket, String signature, String sku) {
|
public void callbackSuccess(String ticket, String signature, String sku) {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[] { ticket, signature, sku });
|
GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[] { ticket, signature, sku });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) {
|
public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) {
|
||||||
Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku);
|
Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku);
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[] { ticket, signature, sku });
|
GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[] { ticket, signature, sku });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void callbackSuccessNoUnconsumedPurchases() {
|
public void callbackSuccessNoUnconsumedPurchases() {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {});
|
GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void callbackFailConsume(String message) {
|
public void callbackFailConsume(String message) {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] { message });
|
GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] { message });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void callbackFail(String message) {
|
public void callbackFail(String message) {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] { message });
|
GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] { message });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void callbackCancel() {
|
public void callbackCancel() {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[] {});
|
GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void callbackAlreadyOwned(String sku) {
|
public void callbackAlreadyOwned(String sku) {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku });
|
GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getPurchaseCallbackId() {
|
public int getPurchaseCallbackId() {
|
||||||
return purchaseCallbackId;
|
return purchaseCallbackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setPurchaseCallbackId(int purchaseCallbackId) {
|
public void setPurchaseCallbackId(int purchaseCallbackId) {
|
||||||
this.purchaseCallbackId = purchaseCallbackId;
|
this.purchaseCallbackId = purchaseCallbackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getPurchaseValidationUrlPrefix() {
|
public String getPurchaseValidationUrlPrefix() {
|
||||||
return this.purchaseValidationUrlPrefix;
|
return this.purchaseValidationUrlPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setPurchaseValidationUrlPrefix(String url) {
|
public void setPurchaseValidationUrlPrefix(String url) {
|
||||||
this.purchaseValidationUrlPrefix = url;
|
this.purchaseValidationUrlPrefix = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getAccessToken() {
|
public String getAccessToken() {
|
||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setAccessToken(String accessToken) {
|
public void setAccessToken(String accessToken) {
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setTransactionId(String transactionId) {
|
public void setTransactionId(String transactionId) {
|
||||||
this.transactionId = transactionId;
|
this.transactionId = transactionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getTransactionId() {
|
public String getTransactionId() {
|
||||||
return this.transactionId;
|
return this.transactionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// request purchased items are not consumed
|
// request purchased items are not consumed
|
||||||
|
@Override
|
||||||
public void requestPurchased() {
|
public void requestPurchased() {
|
||||||
activity.runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mPaymentManager.requestPurchased();
|
mPaymentManager.requestPurchased();
|
||||||
|
@ -158,34 +174,41 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback for requestPurchased()
|
// callback for requestPurchased()
|
||||||
|
@Override
|
||||||
public void callbackPurchased(String receipt, String signature, String sku) {
|
public void callbackPurchased(String receipt, String signature, String sku) {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[] { receipt, signature, sku });
|
GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[] { receipt, signature, sku });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void callbackDisconnected() {
|
public void callbackDisconnected() {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[] {});
|
GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void callbackConnected() {
|
public void callbackConnected() {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[] {});
|
GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// true if connected, false otherwise
|
// true if connected, false otherwise
|
||||||
|
@Override
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return mPaymentManager.isConnected();
|
return mPaymentManager.isConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
// consume item automatically after purchase. default is true.
|
// consume item automatically after purchase. default is true.
|
||||||
|
@Override
|
||||||
public void setAutoConsume(boolean autoConsume) {
|
public void setAutoConsume(boolean autoConsume) {
|
||||||
mPaymentManager.setAutoConsume(autoConsume);
|
mPaymentManager.setAutoConsume(autoConsume);
|
||||||
}
|
}
|
||||||
|
|
||||||
// consume a specific item
|
// consume a specific item
|
||||||
|
@Override
|
||||||
public void consume(String sku) {
|
public void consume(String sku) {
|
||||||
mPaymentManager.consume(sku);
|
mPaymentManager.consume(sku);
|
||||||
}
|
}
|
||||||
|
|
||||||
// query in app item detail info
|
// query in app item detail info
|
||||||
|
@Override
|
||||||
public void querySkuDetails(String[] list) {
|
public void querySkuDetails(String[] list) {
|
||||||
List<String> nKeys = Arrays.asList(list);
|
List<String> nKeys = Arrays.asList(list);
|
||||||
List<String> cKeys = Arrays.asList(mSkuDetails.get_keys());
|
List<String> cKeys = Arrays.asList(mSkuDetails.get_keys());
|
||||||
|
@ -202,6 +225,7 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addSkuDetail(String itemJson) {
|
public void addSkuDetail(String itemJson) {
|
||||||
JSONObject o = null;
|
JSONObject o = null;
|
||||||
try {
|
try {
|
||||||
|
@ -220,11 +244,25 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void completeSkuDetail() {
|
public void completeSkuDetail() {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[] { mSkuDetails });
|
GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[] { mSkuDetails });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void errorSkuDetail(String errorMessage) {
|
public void errorSkuDetail(String errorMessage) {
|
||||||
GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[] { 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");
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -3,3 +3,4 @@ rootProject.name = "Godot"
|
||||||
|
|
||||||
include ':app'
|
include ':app'
|
||||||
include ':lib'
|
include ':lib'
|
||||||
|
include ':plugins:godotpayment'
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -37,36 +37,34 @@
|
||||||
// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
|
// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
|
||||||
// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
|
// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jobject obj, jobject activity);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jint p_x, jint p_y);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jobject obj, jint p_x, jint p_y);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint p_y);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jobject obj, jint p_x, jint p_y);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_scancode, jint p_unicode_char, jboolean p_pressed);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, jint p_device, jboolean p_connected, jstring p_name);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jclass clazz, jint p_device, jboolean p_connected, jstring p_name);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env, jobject obj, jstring name, jobject p_object);
|
JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params);
|
||||||
JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jobject obj, jstring path);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height);
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jobject p_obj, jstring p_permission, jboolean p_result);
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) {
|
||||||
_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
|
_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
|
||||||
_vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V");
|
_vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V");
|
||||||
_get_input_fallback_mapping = p_env->GetMethodID(cls, "getInputFallbackMapping", "()Ljava/lang/String;");
|
_get_input_fallback_mapping = p_env->GetMethodID(cls, "getInputFallbackMapping", "()Ljava/lang/String;");
|
||||||
|
_on_gl_godot_main_loop_started = p_env->GetMethodID(cls, "onGLGodotMainLoopStarted", "()V");
|
||||||
}
|
}
|
||||||
|
|
||||||
GodotJavaWrapper::~GodotJavaWrapper() {
|
GodotJavaWrapper::~GodotJavaWrapper() {
|
||||||
|
@ -114,6 +115,15 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
|
||||||
p_env->CallVoidMethod(godot_instance, _on_video_init);
|
p_env->CallVoidMethod(godot_instance, _on_video_init);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GodotJavaWrapper::on_gl_godot_main_loop_started(JNIEnv *p_env) {
|
||||||
|
if (_on_gl_godot_main_loop_started) {
|
||||||
|
if (p_env == NULL) {
|
||||||
|
p_env = ThreadAndroid::get_env();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p_env->CallVoidMethod(godot_instance, _on_gl_godot_main_loop_started);
|
||||||
|
}
|
||||||
|
|
||||||
void GodotJavaWrapper::restart(JNIEnv *p_env) {
|
void GodotJavaWrapper::restart(JNIEnv *p_env) {
|
||||||
if (_restart)
|
if (_restart)
|
||||||
if (p_env == NULL)
|
if (p_env == NULL)
|
||||||
|
|
|
@ -61,6 +61,7 @@ private:
|
||||||
jmethodID _is_activity_resumed = 0;
|
jmethodID _is_activity_resumed = 0;
|
||||||
jmethodID _vibrate = 0;
|
jmethodID _vibrate = 0;
|
||||||
jmethodID _get_input_fallback_mapping = 0;
|
jmethodID _get_input_fallback_mapping = 0;
|
||||||
|
jmethodID _on_gl_godot_main_loop_started = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance);
|
GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance);
|
||||||
|
@ -73,6 +74,7 @@ public:
|
||||||
|
|
||||||
void gfx_init(bool gl2);
|
void gfx_init(bool gl2);
|
||||||
void on_video_init(JNIEnv *p_env = NULL);
|
void on_video_init(JNIEnv *p_env = NULL);
|
||||||
|
void on_gl_godot_main_loop_started(JNIEnv *p_env = NULL);
|
||||||
void restart(JNIEnv *p_env = NULL);
|
void restart(JNIEnv *p_env = NULL);
|
||||||
void force_quit(JNIEnv *p_env = NULL);
|
void force_quit(JNIEnv *p_env = NULL);
|
||||||
void set_keep_screen_on(bool p_enabled);
|
void set_keep_screen_on(bool p_enabled);
|
||||||
|
|
432
platform/android/jni_utils.cpp
Normal file
432
platform/android/jni_utils.cpp
Normal file
|
@ -0,0 +1,432 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* jni_utils.cpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* 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. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#include "jni_utils.h"
|
||||||
|
|
||||||
|
jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject) {
|
||||||
|
|
||||||
|
jvalret v;
|
||||||
|
|
||||||
|
switch (p_type) {
|
||||||
|
|
||||||
|
case Variant::BOOL: {
|
||||||
|
|
||||||
|
if (force_jobject) {
|
||||||
|
jclass bclass = env->FindClass("java/lang/Boolean");
|
||||||
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(Z)V");
|
||||||
|
jvalue val;
|
||||||
|
val.z = (bool)(*p_arg);
|
||||||
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
||||||
|
v.val.l = obj;
|
||||||
|
v.obj = obj;
|
||||||
|
env->DeleteLocalRef(bclass);
|
||||||
|
} else {
|
||||||
|
v.val.z = *p_arg;
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
case Variant::INT: {
|
||||||
|
|
||||||
|
if (force_jobject) {
|
||||||
|
|
||||||
|
jclass bclass = env->FindClass("java/lang/Integer");
|
||||||
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(I)V");
|
||||||
|
jvalue val;
|
||||||
|
val.i = (int)(*p_arg);
|
||||||
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
||||||
|
v.val.l = obj;
|
||||||
|
v.obj = obj;
|
||||||
|
env->DeleteLocalRef(bclass);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
v.val.i = *p_arg;
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
case Variant::REAL: {
|
||||||
|
|
||||||
|
if (force_jobject) {
|
||||||
|
|
||||||
|
jclass bclass = env->FindClass("java/lang/Double");
|
||||||
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(D)V");
|
||||||
|
jvalue val;
|
||||||
|
val.d = (double)(*p_arg);
|
||||||
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
||||||
|
v.val.l = obj;
|
||||||
|
v.obj = obj;
|
||||||
|
env->DeleteLocalRef(bclass);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
v.val.f = *p_arg;
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
case Variant::STRING: {
|
||||||
|
|
||||||
|
String s = *p_arg;
|
||||||
|
jstring jStr = env->NewStringUTF(s.utf8().get_data());
|
||||||
|
v.val.l = jStr;
|
||||||
|
v.obj = jStr;
|
||||||
|
} break;
|
||||||
|
case Variant::POOL_STRING_ARRAY: {
|
||||||
|
|
||||||
|
PoolVector<String> sarray = *p_arg;
|
||||||
|
jobjectArray arr = env->NewObjectArray(sarray.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
|
||||||
|
|
||||||
|
for (int j = 0; j < sarray.size(); j++) {
|
||||||
|
|
||||||
|
jstring str = env->NewStringUTF(sarray[j].utf8().get_data());
|
||||||
|
env->SetObjectArrayElement(arr, j, str);
|
||||||
|
env->DeleteLocalRef(str);
|
||||||
|
}
|
||||||
|
v.val.l = arr;
|
||||||
|
v.obj = arr;
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Variant::DICTIONARY: {
|
||||||
|
|
||||||
|
Dictionary dict = *p_arg;
|
||||||
|
jclass dclass = env->FindClass("org/godotengine/godot/Dictionary");
|
||||||
|
jmethodID ctor = env->GetMethodID(dclass, "<init>", "()V");
|
||||||
|
jobject jdict = env->NewObject(dclass, ctor);
|
||||||
|
|
||||||
|
Array keys = dict.keys();
|
||||||
|
|
||||||
|
jobjectArray jkeys = env->NewObjectArray(keys.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
|
||||||
|
for (int j = 0; j < keys.size(); j++) {
|
||||||
|
jstring str = env->NewStringUTF(String(keys[j]).utf8().get_data());
|
||||||
|
env->SetObjectArrayElement(jkeys, j, str);
|
||||||
|
env->DeleteLocalRef(str);
|
||||||
|
};
|
||||||
|
|
||||||
|
jmethodID set_keys = env->GetMethodID(dclass, "set_keys", "([Ljava/lang/String;)V");
|
||||||
|
jvalue val;
|
||||||
|
val.l = jkeys;
|
||||||
|
env->CallVoidMethodA(jdict, set_keys, &val);
|
||||||
|
env->DeleteLocalRef(jkeys);
|
||||||
|
|
||||||
|
jobjectArray jvalues = env->NewObjectArray(keys.size(), env->FindClass("java/lang/Object"), NULL);
|
||||||
|
|
||||||
|
for (int j = 0; j < keys.size(); j++) {
|
||||||
|
Variant var = dict[keys[j]];
|
||||||
|
jvalret v = _variant_to_jvalue(env, var.get_type(), &var, true);
|
||||||
|
env->SetObjectArrayElement(jvalues, j, v.val.l);
|
||||||
|
if (v.obj) {
|
||||||
|
env->DeleteLocalRef(v.obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
jmethodID set_values = env->GetMethodID(dclass, "set_values", "([Ljava/lang/Object;)V");
|
||||||
|
val.l = jvalues;
|
||||||
|
env->CallVoidMethodA(jdict, set_values, &val);
|
||||||
|
env->DeleteLocalRef(jvalues);
|
||||||
|
env->DeleteLocalRef(dclass);
|
||||||
|
|
||||||
|
v.val.l = jdict;
|
||||||
|
v.obj = jdict;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Variant::POOL_INT_ARRAY: {
|
||||||
|
|
||||||
|
PoolVector<int> array = *p_arg;
|
||||||
|
jintArray arr = env->NewIntArray(array.size());
|
||||||
|
PoolVector<int>::Read r = array.read();
|
||||||
|
env->SetIntArrayRegion(arr, 0, array.size(), r.ptr());
|
||||||
|
v.val.l = arr;
|
||||||
|
v.obj = arr;
|
||||||
|
|
||||||
|
} break;
|
||||||
|
case Variant::POOL_BYTE_ARRAY: {
|
||||||
|
PoolVector<uint8_t> array = *p_arg;
|
||||||
|
jbyteArray arr = env->NewByteArray(array.size());
|
||||||
|
PoolVector<uint8_t>::Read r = array.read();
|
||||||
|
env->SetByteArrayRegion(arr, 0, array.size(), reinterpret_cast<const signed char *>(r.ptr()));
|
||||||
|
v.val.l = arr;
|
||||||
|
v.obj = arr;
|
||||||
|
|
||||||
|
} break;
|
||||||
|
case Variant::POOL_REAL_ARRAY: {
|
||||||
|
|
||||||
|
PoolVector<float> array = *p_arg;
|
||||||
|
jfloatArray arr = env->NewFloatArray(array.size());
|
||||||
|
PoolVector<float>::Read r = array.read();
|
||||||
|
env->SetFloatArrayRegion(arr, 0, array.size(), r.ptr());
|
||||||
|
v.val.l = arr;
|
||||||
|
v.obj = arr;
|
||||||
|
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
|
||||||
|
v.val.i = 0;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _get_class_name(JNIEnv *env, jclass cls, bool *array) {
|
||||||
|
|
||||||
|
jclass cclass = env->FindClass("java/lang/Class");
|
||||||
|
jmethodID getName = env->GetMethodID(cclass, "getName", "()Ljava/lang/String;");
|
||||||
|
jstring clsName = (jstring)env->CallObjectMethod(cls, getName);
|
||||||
|
|
||||||
|
if (array) {
|
||||||
|
jmethodID isArray = env->GetMethodID(cclass, "isArray", "()Z");
|
||||||
|
jboolean isarr = env->CallBooleanMethod(cls, isArray);
|
||||||
|
(*array) = isarr ? true : false;
|
||||||
|
}
|
||||||
|
String name = jstring_to_string(clsName, env);
|
||||||
|
env->DeleteLocalRef(clsName);
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
|
||||||
|
|
||||||
|
if (obj == NULL) {
|
||||||
|
return Variant();
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass c = env->GetObjectClass(obj);
|
||||||
|
bool array;
|
||||||
|
String name = _get_class_name(env, c, &array);
|
||||||
|
|
||||||
|
if (name == "java.lang.String") {
|
||||||
|
|
||||||
|
return jstring_to_string((jstring)obj, env);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "[Ljava.lang.String;") {
|
||||||
|
|
||||||
|
jobjectArray arr = (jobjectArray)obj;
|
||||||
|
int stringCount = env->GetArrayLength(arr);
|
||||||
|
PoolVector<String> sarr;
|
||||||
|
|
||||||
|
for (int i = 0; i < stringCount; i++) {
|
||||||
|
jstring string = (jstring)env->GetObjectArrayElement(arr, i);
|
||||||
|
sarr.push_back(jstring_to_string(string, env));
|
||||||
|
env->DeleteLocalRef(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sarr;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "java.lang.Boolean") {
|
||||||
|
|
||||||
|
jmethodID boolValue = env->GetMethodID(c, "booleanValue", "()Z");
|
||||||
|
bool ret = env->CallBooleanMethod(obj, boolValue);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "java.lang.Integer" || name == "java.lang.Long") {
|
||||||
|
|
||||||
|
jclass nclass = env->FindClass("java/lang/Number");
|
||||||
|
jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J");
|
||||||
|
jlong ret = env->CallLongMethod(obj, longValue);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "[I") {
|
||||||
|
|
||||||
|
jintArray arr = (jintArray)obj;
|
||||||
|
int fCount = env->GetArrayLength(arr);
|
||||||
|
PoolVector<int> sarr;
|
||||||
|
sarr.resize(fCount);
|
||||||
|
|
||||||
|
PoolVector<int>::Write w = sarr.write();
|
||||||
|
env->GetIntArrayRegion(arr, 0, fCount, w.ptr());
|
||||||
|
w.release();
|
||||||
|
return sarr;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "[B") {
|
||||||
|
|
||||||
|
jbyteArray arr = (jbyteArray)obj;
|
||||||
|
int fCount = env->GetArrayLength(arr);
|
||||||
|
PoolVector<uint8_t> sarr;
|
||||||
|
sarr.resize(fCount);
|
||||||
|
|
||||||
|
PoolVector<uint8_t>::Write w = sarr.write();
|
||||||
|
env->GetByteArrayRegion(arr, 0, fCount, reinterpret_cast<signed char *>(w.ptr()));
|
||||||
|
w.release();
|
||||||
|
return sarr;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "java.lang.Float" || name == "java.lang.Double") {
|
||||||
|
|
||||||
|
jclass nclass = env->FindClass("java/lang/Number");
|
||||||
|
jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D");
|
||||||
|
double ret = env->CallDoubleMethod(obj, doubleValue);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "[D") {
|
||||||
|
|
||||||
|
jdoubleArray arr = (jdoubleArray)obj;
|
||||||
|
int fCount = env->GetArrayLength(arr);
|
||||||
|
PoolRealArray sarr;
|
||||||
|
sarr.resize(fCount);
|
||||||
|
|
||||||
|
PoolRealArray::Write w = sarr.write();
|
||||||
|
|
||||||
|
for (int i = 0; i < fCount; i++) {
|
||||||
|
|
||||||
|
double n;
|
||||||
|
env->GetDoubleArrayRegion(arr, i, 1, &n);
|
||||||
|
w.ptr()[i] = n;
|
||||||
|
};
|
||||||
|
return sarr;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "[F") {
|
||||||
|
|
||||||
|
jfloatArray arr = (jfloatArray)obj;
|
||||||
|
int fCount = env->GetArrayLength(arr);
|
||||||
|
PoolRealArray sarr;
|
||||||
|
sarr.resize(fCount);
|
||||||
|
|
||||||
|
PoolRealArray::Write w = sarr.write();
|
||||||
|
|
||||||
|
for (int i = 0; i < fCount; i++) {
|
||||||
|
|
||||||
|
float n;
|
||||||
|
env->GetFloatArrayRegion(arr, i, 1, &n);
|
||||||
|
w.ptr()[i] = n;
|
||||||
|
};
|
||||||
|
return sarr;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "[Ljava.lang.Object;") {
|
||||||
|
|
||||||
|
jobjectArray arr = (jobjectArray)obj;
|
||||||
|
int objCount = env->GetArrayLength(arr);
|
||||||
|
Array varr;
|
||||||
|
|
||||||
|
for (int i = 0; i < objCount; i++) {
|
||||||
|
jobject jobj = env->GetObjectArrayElement(arr, i);
|
||||||
|
Variant v = _jobject_to_variant(env, jobj);
|
||||||
|
varr.push_back(v);
|
||||||
|
env->DeleteLocalRef(jobj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return varr;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name == "java.util.HashMap" || name == "org.godotengine.godot.Dictionary") {
|
||||||
|
|
||||||
|
Dictionary ret;
|
||||||
|
jclass oclass = c;
|
||||||
|
jmethodID get_keys = env->GetMethodID(oclass, "get_keys", "()[Ljava/lang/String;");
|
||||||
|
jobjectArray arr = (jobjectArray)env->CallObjectMethod(obj, get_keys);
|
||||||
|
|
||||||
|
PoolStringArray keys = _jobject_to_variant(env, arr);
|
||||||
|
env->DeleteLocalRef(arr);
|
||||||
|
|
||||||
|
jmethodID get_values = env->GetMethodID(oclass, "get_values", "()[Ljava/lang/Object;");
|
||||||
|
arr = (jobjectArray)env->CallObjectMethod(obj, get_values);
|
||||||
|
|
||||||
|
Array vals = _jobject_to_variant(env, arr);
|
||||||
|
env->DeleteLocalRef(arr);
|
||||||
|
|
||||||
|
for (int i = 0; i < keys.size(); i++) {
|
||||||
|
|
||||||
|
ret[keys[i]] = vals[i];
|
||||||
|
};
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
env->DeleteLocalRef(c);
|
||||||
|
|
||||||
|
return Variant();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant::Type get_jni_type(const String &p_type) {
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
const char *name;
|
||||||
|
Variant::Type type;
|
||||||
|
} _type_to_vtype[] = {
|
||||||
|
{ "void", Variant::NIL },
|
||||||
|
{ "boolean", Variant::BOOL },
|
||||||
|
{ "int", Variant::INT },
|
||||||
|
{ "float", Variant::REAL },
|
||||||
|
{ "double", Variant::REAL },
|
||||||
|
{ "java.lang.String", Variant::STRING },
|
||||||
|
{ "[I", Variant::POOL_INT_ARRAY },
|
||||||
|
{ "[B", Variant::POOL_BYTE_ARRAY },
|
||||||
|
{ "[F", Variant::POOL_REAL_ARRAY },
|
||||||
|
{ "[Ljava.lang.String;", Variant::POOL_STRING_ARRAY },
|
||||||
|
{ "org.godotengine.godot.Dictionary", Variant::DICTIONARY },
|
||||||
|
{ NULL, Variant::NIL }
|
||||||
|
};
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
while (_type_to_vtype[idx].name) {
|
||||||
|
|
||||||
|
if (p_type == _type_to_vtype[idx].name)
|
||||||
|
return _type_to_vtype[idx].type;
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Variant::NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_jni_sig(const String &p_type) {
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
const char *name;
|
||||||
|
const char *sig;
|
||||||
|
} _type_to_vtype[] = {
|
||||||
|
{ "void", "V" },
|
||||||
|
{ "boolean", "Z" },
|
||||||
|
{ "int", "I" },
|
||||||
|
{ "float", "F" },
|
||||||
|
{ "double", "D" },
|
||||||
|
{ "java.lang.String", "Ljava/lang/String;" },
|
||||||
|
{ "org.godotengine.godot.Dictionary", "Lorg/godotengine/godot/Dictionary;" },
|
||||||
|
{ "[I", "[I" },
|
||||||
|
{ "[B", "[B" },
|
||||||
|
{ "[F", "[F" },
|
||||||
|
{ "[Ljava.lang.String;", "[Ljava/lang/String;" },
|
||||||
|
{ NULL, "V" }
|
||||||
|
};
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
while (_type_to_vtype[idx].name) {
|
||||||
|
|
||||||
|
if (p_type == _type_to_vtype[idx].name)
|
||||||
|
return _type_to_vtype[idx].sig;
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Ljava/lang/Object;";
|
||||||
|
}
|
55
platform/android/jni_utils.h
Normal file
55
platform/android/jni_utils.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* jni_utils.h */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* 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. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#ifndef JNI_UTILS_H
|
||||||
|
#define JNI_UTILS_H
|
||||||
|
|
||||||
|
#include "string_android.h"
|
||||||
|
#include <core/variant.h>
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
struct jvalret {
|
||||||
|
|
||||||
|
jobject obj;
|
||||||
|
jvalue val;
|
||||||
|
jvalret() { obj = NULL; }
|
||||||
|
};
|
||||||
|
|
||||||
|
jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject = false);
|
||||||
|
|
||||||
|
String _get_class_name(JNIEnv *env, jclass cls, bool *array);
|
||||||
|
|
||||||
|
Variant _jobject_to_variant(JNIEnv *env, jobject obj);
|
||||||
|
|
||||||
|
Variant::Type get_jni_type(const String &p_type);
|
||||||
|
|
||||||
|
const char *get_jni_sig(const String &p_type);
|
||||||
|
|
||||||
|
#endif // JNI_UTILS_H
|
300
platform/android/plugin/godot_plugin_jni.cpp
Normal file
300
platform/android/plugin/godot_plugin_jni.cpp
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* godot_plugin_jni.cpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* 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. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#include "godot_plugin_jni.h"
|
||||||
|
|
||||||
|
#include <core/engine.h>
|
||||||
|
#include <core/error_macros.h>
|
||||||
|
#include <core/project_settings.h>
|
||||||
|
#include <platform/android/jni_utils.h>
|
||||||
|
#include <platform/android/string_android.h>
|
||||||
|
|
||||||
|
class JNISingleton : public Object {
|
||||||
|
|
||||||
|
GDCLASS(JNISingleton, Object);
|
||||||
|
|
||||||
|
struct MethodData {
|
||||||
|
|
||||||
|
jmethodID method;
|
||||||
|
Variant::Type ret_type;
|
||||||
|
Vector<Variant::Type> argtypes;
|
||||||
|
};
|
||||||
|
|
||||||
|
jobject instance;
|
||||||
|
Map<StringName, MethodData> method_map;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(!instance, Variant());
|
||||||
|
|
||||||
|
r_error.error = Variant::CallError::CALL_OK;
|
||||||
|
|
||||||
|
Map<StringName, MethodData>::Element *E = method_map.find(p_method);
|
||||||
|
if (!E) {
|
||||||
|
|
||||||
|
r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
|
||||||
|
return Variant();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ac = E->get().argtypes.size();
|
||||||
|
if (ac < p_argcount) {
|
||||||
|
|
||||||
|
r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||||
|
r_error.argument = ac;
|
||||||
|
return Variant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ac > p_argcount) {
|
||||||
|
|
||||||
|
r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||||
|
r_error.argument = ac;
|
||||||
|
return Variant();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < p_argcount; i++) {
|
||||||
|
|
||||||
|
if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
|
||||||
|
|
||||||
|
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||||
|
r_error.argument = i;
|
||||||
|
r_error.expected = E->get().argtypes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jvalue *v = NULL;
|
||||||
|
|
||||||
|
if (p_argcount) {
|
||||||
|
|
||||||
|
v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEnv *env = ThreadAndroid::get_env();
|
||||||
|
|
||||||
|
int res = env->PushLocalFrame(16);
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(res != 0, Variant());
|
||||||
|
|
||||||
|
List<jobject> to_erase;
|
||||||
|
for (int i = 0; i < p_argcount; i++) {
|
||||||
|
|
||||||
|
jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]);
|
||||||
|
v[i] = vr.val;
|
||||||
|
if (vr.obj)
|
||||||
|
to_erase.push_back(vr.obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant ret;
|
||||||
|
|
||||||
|
switch (E->get().ret_type) {
|
||||||
|
|
||||||
|
case Variant::NIL: {
|
||||||
|
|
||||||
|
env->CallVoidMethodA(instance, E->get().method, v);
|
||||||
|
} break;
|
||||||
|
case Variant::BOOL: {
|
||||||
|
|
||||||
|
ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE;
|
||||||
|
} break;
|
||||||
|
case Variant::INT: {
|
||||||
|
|
||||||
|
ret = env->CallIntMethodA(instance, E->get().method, v);
|
||||||
|
} break;
|
||||||
|
case Variant::REAL: {
|
||||||
|
|
||||||
|
ret = env->CallFloatMethodA(instance, E->get().method, v);
|
||||||
|
} break;
|
||||||
|
case Variant::STRING: {
|
||||||
|
|
||||||
|
jobject o = env->CallObjectMethodA(instance, E->get().method, v);
|
||||||
|
ret = jstring_to_string((jstring)o, env);
|
||||||
|
env->DeleteLocalRef(o);
|
||||||
|
} break;
|
||||||
|
case Variant::POOL_STRING_ARRAY: {
|
||||||
|
|
||||||
|
jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v);
|
||||||
|
|
||||||
|
ret = _jobject_to_variant(env, arr);
|
||||||
|
|
||||||
|
env->DeleteLocalRef(arr);
|
||||||
|
} break;
|
||||||
|
case Variant::POOL_INT_ARRAY: {
|
||||||
|
|
||||||
|
jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v);
|
||||||
|
|
||||||
|
int fCount = env->GetArrayLength(arr);
|
||||||
|
PoolVector<int> sarr;
|
||||||
|
sarr.resize(fCount);
|
||||||
|
|
||||||
|
PoolVector<int>::Write w = sarr.write();
|
||||||
|
env->GetIntArrayRegion(arr, 0, fCount, w.ptr());
|
||||||
|
w.release();
|
||||||
|
ret = sarr;
|
||||||
|
env->DeleteLocalRef(arr);
|
||||||
|
} break;
|
||||||
|
case Variant::POOL_REAL_ARRAY: {
|
||||||
|
|
||||||
|
jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v);
|
||||||
|
|
||||||
|
int fCount = env->GetArrayLength(arr);
|
||||||
|
PoolVector<float> sarr;
|
||||||
|
sarr.resize(fCount);
|
||||||
|
|
||||||
|
PoolVector<float>::Write w = sarr.write();
|
||||||
|
env->GetFloatArrayRegion(arr, 0, fCount, w.ptr());
|
||||||
|
w.release();
|
||||||
|
ret = sarr;
|
||||||
|
env->DeleteLocalRef(arr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Variant::DICTIONARY: {
|
||||||
|
|
||||||
|
jobject obj = env->CallObjectMethodA(instance, E->get().method, v);
|
||||||
|
ret = _jobject_to_variant(env, obj);
|
||||||
|
env->DeleteLocalRef(obj);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
|
||||||
|
env->PopLocalFrame(NULL);
|
||||||
|
ERR_FAIL_V(Variant());
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (to_erase.size()) {
|
||||||
|
env->DeleteLocalRef(to_erase.front()->get());
|
||||||
|
to_erase.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
env->PopLocalFrame(NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject get_instance() const {
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
void set_instance(jobject p_instance) {
|
||||||
|
|
||||||
|
instance = p_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
|
||||||
|
|
||||||
|
MethodData md;
|
||||||
|
md.method = p_method;
|
||||||
|
md.argtypes = p_args;
|
||||||
|
md.ret_type = p_ret_type;
|
||||||
|
method_map[p_name] = md;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNISingleton() {
|
||||||
|
instance = NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static HashMap<String, JNISingleton *> jni_singletons;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj) {
|
||||||
|
|
||||||
|
String singname = jstring_to_string(name, env);
|
||||||
|
JNISingleton *s = memnew(JNISingleton);
|
||||||
|
s->set_instance(env->NewGlobalRef(obj));
|
||||||
|
jni_singletons[singname] = s;
|
||||||
|
|
||||||
|
Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s));
|
||||||
|
ProjectSettings::get_singleton()->set(singname, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args) {
|
||||||
|
|
||||||
|
String singname = jstring_to_string(sname, env);
|
||||||
|
|
||||||
|
ERR_FAIL_COND(!jni_singletons.has(singname));
|
||||||
|
|
||||||
|
JNISingleton *s = jni_singletons.get(singname);
|
||||||
|
|
||||||
|
String mname = jstring_to_string(name, env);
|
||||||
|
String retval = jstring_to_string(ret, env);
|
||||||
|
Vector<Variant::Type> types;
|
||||||
|
String cs = "(";
|
||||||
|
|
||||||
|
int stringCount = env->GetArrayLength(args);
|
||||||
|
|
||||||
|
for (int i = 0; i < stringCount; i++) {
|
||||||
|
|
||||||
|
jstring string = (jstring)env->GetObjectArrayElement(args, i);
|
||||||
|
const String rawString = jstring_to_string(string, env);
|
||||||
|
types.push_back(get_jni_type(rawString));
|
||||||
|
cs += get_jni_sig(rawString);
|
||||||
|
}
|
||||||
|
|
||||||
|
cs += ")";
|
||||||
|
cs += get_jni_sig(retval);
|
||||||
|
jclass cls = env->GetObjectClass(s->get_instance());
|
||||||
|
jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data());
|
||||||
|
if (!mid) {
|
||||||
|
|
||||||
|
print_line("Failed getting method ID " + mname);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->add_method(mname, mid, types, get_jni_type(retval));
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths) {
|
||||||
|
int gdnlib_count = env->GetArrayLength(gdnlib_paths);
|
||||||
|
if (gdnlib_count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the current list of gdnative libraries.
|
||||||
|
Array singletons = Array();
|
||||||
|
if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) {
|
||||||
|
singletons = ProjectSettings::get_singleton()->get("gdnative/singletons");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the libraries provided by the plugin
|
||||||
|
for (int i = 0; i < gdnlib_count; i++) {
|
||||||
|
jstring relative_path = (jstring)env->GetObjectArrayElement(gdnlib_paths, i);
|
||||||
|
|
||||||
|
String path = "res://" + jstring_to_string(relative_path, env);
|
||||||
|
if (!singletons.has(path)) {
|
||||||
|
singletons.push_back(path);
|
||||||
|
}
|
||||||
|
env->DeleteLocalRef(relative_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the updated list back into project settings.
|
||||||
|
ProjectSettings::get_singleton()->set("gdnative/singletons", singletons);
|
||||||
|
}
|
||||||
|
}
|
43
platform/android/plugin/godot_plugin_jni.h
Normal file
43
platform/android/plugin/godot_plugin_jni.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* godot_plugin_jni.h */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* 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. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GODOT_PLUGIN_JNI_H
|
||||||
|
#define GODOT_PLUGIN_JNI_H
|
||||||
|
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj);
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args);
|
||||||
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GODOT_PLUGIN_JNI_H
|
Loading…
Reference in a new issue