diff options
Diffstat (limited to 'platform/android')
24 files changed, 553 insertions, 267 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub index c8feac8690..60bb4bd613 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -79,6 +79,11 @@ for x in env.android_jni_dirs: gradle_asset_dirs_text="" +gradle_default_config_text="" + +for x in env.android_default_config: + gradle_default_config_text+=x+"\n\t\t" + gradle_text = gradle_text.replace("$$GRADLE_REPOSITORY_URLS$$",gradle_maven_repos_text) gradle_text = gradle_text.replace("$$GRADLE_DEPENDENCIES$$",gradle_maven_dependencies_text) gradle_text = gradle_text.replace("$$GRADLE_JAVA_DIRS$$",gradle_java_dirs_text) @@ -86,6 +91,7 @@ gradle_text = gradle_text.replace("$$GRADLE_RES_DIRS$$",gradle_res_dirs_text) gradle_text = gradle_text.replace("$$GRADLE_ASSET_DIRS$$",gradle_asset_dirs_text) gradle_text = gradle_text.replace("$$GRADLE_AIDL_DIRS$$",gradle_aidl_dirs_text) gradle_text = gradle_text.replace("$$GRADLE_JNI_DIRS$$",gradle_jni_dirs_text) +gradle_text = gradle_text.replace("$$GRADLE_DEFAULT_CONFIG$$",gradle_default_config_text) gradle_baseout.write( gradle_text ) @@ -103,4 +109,22 @@ pp_baseout.write( manifest ) env_android.SharedLibrary("#bin/libgodot",[android_objects],SHLIBSUFFIX=env["SHLIBSUFFIX"]) -#env.Command('#bin/libgodot_android.so', '#platform/android/libgodot_android.so', Copy('bin/libgodot_android.so', 'platform/android/libgodot_android.so')) + +lib_arch_dir = '' +if env['android_arch'] == 'armv6': + lib_arch_dir = 'armeabi' +elif env['android_arch'] == 'armv7': + lib_arch_dir = 'armeabi-v7a' +elif env['android_arch'] == 'x86': + lib_arch_dir = 'x86' +else: + print 'WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin' + +if lib_arch_dir != '': + if env['target'] == 'release': + lib_type_dir = 'release' + else: # release_debug, debug + lib_type_dir = 'debug' + + out_dir = '#platform/android/java/libs/'+lib_type_dir+'/'+lib_arch_dir + env_android.Command(out_dir+'/libgodot_android.so', '#bin/libgodot'+env['SHLIBSUFFIX'], Move("$TARGET", "$SOURCE")) diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp index 6d428e3fe5..419ed977b5 100644 --- a/platform/android/audio_driver_jandroid.cpp +++ b/platform/android/audio_driver_jandroid.cpp @@ -192,29 +192,31 @@ void AudioDriverAndroid::thread_func(JNIEnv *env) { env->CallVoidMethod(gob, _write_buffer, (jshortArray)audioBuffer); } - - } int AudioDriverAndroid::get_mix_rate() const { return mix_rate; } + AudioDriverSW::OutputFormat AudioDriverAndroid::get_output_format() const{ return OUTPUT_STEREO; } + void AudioDriverAndroid::lock(){ if (mutex) mutex->lock(); } + void AudioDriverAndroid::unlock() { if (mutex) mutex->unlock(); } + void AudioDriverAndroid::finish(){ JNIEnv *env = ThreadAndroid::get_env(); @@ -236,13 +238,11 @@ void AudioDriverAndroid::set_pause(bool p_pause) { } -AudioDriverAndroid::AudioDriverAndroid() -{ +AudioDriverAndroid::AudioDriverAndroid() { + s_ad=this; active=false; - - } #endif diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index a908f6193e..b7ef1424e5 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -207,6 +207,7 @@ Error AudioDriverOpenSL::init(){ return OK; } + void AudioDriverOpenSL::start(){ @@ -366,26 +367,31 @@ void AudioDriverOpenSL::start(){ active=true; } + int AudioDriverOpenSL::get_mix_rate() const { return 44100; } + AudioDriverSW::OutputFormat AudioDriverOpenSL::get_output_format() const{ return OUTPUT_STEREO; } + void AudioDriverOpenSL::lock(){ if (active && mutex) mutex->lock(); } + void AudioDriverOpenSL::unlock() { if (active && mutex) mutex->unlock(); } + void AudioDriverOpenSL::finish(){ (*sl)->Destroy(sl); diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template index 1e1461ef29..24951b921b 100644 --- a/platform/android/build.gradle.template +++ b/platform/android/build.gradle.template @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.1' + classpath 'com.android.tools.build:gradle:2.1.0' } } @@ -39,6 +39,12 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 23 + $$GRADLE_DEFAULT_CONFIG$$ + } + // Both signing and zip-aligning will be done at export time + buildTypes.all { buildType -> + buildType.zipAlignEnabled false + buildType.signingConfig null } sourceSets { main { @@ -65,8 +71,17 @@ android { $$GRADLE_JNI_DIRS$$ ] } - + debug.jniLibs.srcDirs = [ + 'libs/debug' + $$GRADLE_JNI_DIRS$$ + ] + release.jniLibs.srcDirs = [ + 'libs/release' + $$GRADLE_JNI_DIRS$$ + ] + } + applicationVariants.all { variant -> + // ApplicationVariant is undocumented, but this method is widely used; may break with another version of the Android Gradle plugin + variant.outputs.get(0).setOutputFile(new File("${projectDir}/../../../bin", "android_${variant.name}.apk")) } - - } diff --git a/platform/android/detect.py b/platform/android/detect.py index ba6b73a89f..49ffc86658 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -23,7 +23,7 @@ def get_opts(): ('ANDROID_NDK_ROOT', 'the path to Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)), ('NDK_TARGET', 'toolchain to use for the NDK',os.environ.get("NDK_TARGET", "arm-linux-androideabi-4.9")), ('NDK_TARGET_X86', 'toolchain to use for the NDK x86',os.environ.get("NDK_TARGET_X86", "x86-4.9")), - ('ndk_platform', 'compile for platform: (android-<api> , example: android-15)',"android-15"), + ('ndk_platform', 'compile for platform: (android-<api> , example: android-14)',"android-14"), ('android_arch', 'select compiler architecture: (armv7/armv6/x86)',"armv7"), ('android_neon','enable neon (armv7 only)',"yes"), ('android_stl','enable STL support in android port (for modules)',"no") @@ -211,16 +211,16 @@ def configure(env): if (env['android_stl']=='yes'): #env.Append(CCFLAGS=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/system/include"]) - env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/include"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.9/include"]) if env['android_arch']=='x86': - env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/x86/include"]) - env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/x86"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.9/libs/x86/include"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.9/libs/x86"]) elif env['android_arch']=='armv6': - env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi/include"]) - env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi/include"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi"]) elif env["android_arch"]=="armv7": - env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include"]) - env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a"]) env.Append(LIBS=["gnustl_static","supc++"]) env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cpufeatures"]) diff --git a/platform/android/dir_access_android.cpp b/platform/android/dir_access_android.cpp index 85df5dc37a..79ba83b3ee 100644 --- a/platform/android/dir_access_android.cpp +++ b/platform/android/dir_access_android.cpp @@ -61,6 +61,7 @@ String DirAccessAndroid::get_next(){ } + bool DirAccessAndroid::current_is_dir() const{ String sd; @@ -79,9 +80,11 @@ bool DirAccessAndroid::current_is_dir() const{ return false; } + bool DirAccessAndroid::current_is_hidden() const{ return current!="." && current!=".." && current.begins_with("."); } + void DirAccessAndroid::list_dir_end(){ if (aad==NULL) @@ -96,6 +99,7 @@ int DirAccessAndroid::get_drive_count(){ return 0; } + String DirAccessAndroid::get_drive(int p_drive){ return ""; @@ -164,6 +168,7 @@ Error DirAccessAndroid::rename(String p_from, String p_to){ ERR_FAIL_V(ERR_UNAVAILABLE); } + Error DirAccessAndroid::remove(String p_name){ ERR_FAIL_V(ERR_UNAVAILABLE); diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 8b7cb992d9..be2ffde2cd 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -76,6 +76,7 @@ String DirAccessJAndroid::get_next(){ return ret; } + bool DirAccessJAndroid::current_is_dir() const{ @@ -106,6 +107,7 @@ int DirAccessJAndroid::get_drive_count(){ return 0; } + String DirAccessJAndroid::get_drive(int p_drive){ return ""; @@ -215,6 +217,7 @@ Error DirAccessJAndroid::rename(String p_from, String p_to){ ERR_FAIL_V(ERR_UNAVAILABLE); } + Error DirAccessJAndroid::remove(String p_name){ ERR_FAIL_V(ERR_UNAVAILABLE); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 872f047c95..10d77aba6c 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1166,7 +1166,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d skip=true; } - if (file=="lib/armeabi/libgodot_android.so" && !export_arm) { + if (file.match("lib/armeabi*/libgodot_android.so") && !export_arm) { skip=true; } @@ -1482,6 +1482,7 @@ int EditorExportPlatformAndroid::get_device_count() const { return dc; } + String EditorExportPlatformAndroid::get_device_name(int p_device) const { ERR_FAIL_INDEX_V(p_device,devices.size(),""); @@ -1490,6 +1491,7 @@ String EditorExportPlatformAndroid::get_device_name(int p_device) const { device_lock->unlock(); return s; } + String EditorExportPlatformAndroid::get_device_info(int p_device) const { ERR_FAIL_INDEX_V(p_device,devices.size(),""); @@ -1573,7 +1575,7 @@ void EditorExportPlatformAndroid::_device_poll_thread(void *ud) { String dp; Error err = OS::get_singleton()->execute(adb,args,true,NULL,&dp,&ec); - print_line("RV: "+itos(ec)); + Vector<String> props = dp.split("\n"); String vendor; String device; @@ -1883,7 +1885,6 @@ bool EditorExportPlatformAndroid::can_export(String *r_error) const { EditorExportPlatformAndroid::~EditorExportPlatformAndroid() { - quit_request=true; Thread::wait_to_finish(device_thread); memdelete(device_lock); @@ -1912,6 +1913,5 @@ void register_android_exporter() { Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>( memnew(EditorExportPlatformAndroid) ); EditorImportExport::get_singleton()->add_export_platform(exporter); - } diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp index 334d32de0c..aefa16ca9c 100644 --- a/platform/android/file_access_android.cpp +++ b/platform/android/file_access_android.cpp @@ -74,6 +74,7 @@ void FileAccessAndroid::close() { AAsset_close(a); a=NULL; } + bool FileAccessAndroid::is_open() const { return a!=NULL; @@ -92,6 +93,7 @@ void FileAccessAndroid::seek(size_t p_position) { } } + void FileAccessAndroid::seek_end(int64_t p_position) { ERR_FAIL_COND(!a); @@ -99,10 +101,12 @@ void FileAccessAndroid::seek_end(int64_t p_position) { pos=len+p_position; } + size_t FileAccessAndroid::get_pos() const { return pos; } + size_t FileAccessAndroid::get_len() const { return len; @@ -128,6 +132,7 @@ uint8_t FileAccessAndroid::get_8() const { return byte; } + int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const { diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp index da8ceaff14..3d2e525bbc 100644 --- a/platform/android/file_access_jandroid.cpp +++ b/platform/android/file_access_jandroid.cpp @@ -90,6 +90,7 @@ void FileAccessJAndroid::close() { id=0; } + bool FileAccessJAndroid::is_open() const { return id!=0; @@ -102,6 +103,7 @@ void FileAccessJAndroid::seek(size_t p_position) { ERR_FAIL_COND(!is_open()); env->CallVoidMethod(io,_file_seek,id,p_position); } + void FileAccessJAndroid::seek_end(int64_t p_position) { ERR_FAIL_COND(!is_open()); @@ -109,6 +111,7 @@ void FileAccessJAndroid::seek_end(int64_t p_position) { seek(get_len()); } + size_t FileAccessJAndroid::get_pos() const { JNIEnv *env = ThreadAndroid::get_env(); @@ -116,13 +119,13 @@ size_t FileAccessJAndroid::get_pos() const { return env->CallIntMethod(io,_file_tell,id); } + size_t FileAccessJAndroid::get_len() const { JNIEnv *env = ThreadAndroid::get_env(); ERR_FAIL_COND_V(!is_open(),0); return env->CallIntMethod(io,_file_get_size,id); - } bool FileAccessJAndroid::eof_reached() const { @@ -167,7 +170,6 @@ Error FileAccessJAndroid::get_error() const { void FileAccessJAndroid::store_8(uint8_t p_dest) { - } bool FileAccessJAndroid::file_exists(const String& p_path) { @@ -182,8 +184,10 @@ bool FileAccessJAndroid::file_exists(const String& p_path) { jstring js = env->NewStringUTF(path.utf8().get_data()); int res = env->CallIntMethod(io,_file_open,js,false); - if (res<=0) + if (res<=0) { + env->DeleteLocalRef(js); return false; + } env->CallVoidMethod(io,_file_close,res); env->DeleteLocalRef(js); return true; @@ -193,7 +197,6 @@ bool FileAccessJAndroid::file_exists(const String& p_path) { void FileAccessJAndroid::setup( jobject p_io) { - io=p_io; JNIEnv *env = ThreadAndroid::get_env(); @@ -237,14 +240,12 @@ void FileAccessJAndroid::setup( jobject p_io) { } -FileAccessJAndroid::FileAccessJAndroid() -{ +FileAccessJAndroid::FileAccessJAndroid() { id=0; } -FileAccessJAndroid::~FileAccessJAndroid() -{ +FileAccessJAndroid::~FileAccessJAndroid() { if (is_open()) close(); diff --git a/platform/android/godot_android.cpp b/platform/android/godot_android.cpp index 9f909d7041..2371274d9d 100644 --- a/platform/android/godot_android.cpp +++ b/platform/android/godot_android.cpp @@ -314,6 +314,7 @@ struct engine { ASensorManager* sensorManager; const ASensor* accelerometerSensor; const ASensor* magnetometerSensor; + const ASensor* gyroscopeSensor; ASensorEventQueue* sensorEventQueue; bool display_active; @@ -746,6 +747,15 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) { engine->magnetometerSensor, (1000L/60)*1000); } + // And the gyroscope. + if (engine->gyroscopeSensor != NULL) { + ASensorEventQueue_enableSensor(engine->sensorEventQueue, + engine->gyroscopeSensor); + // We'd like to get 60 events per second (in us). + ASensorEventQueue_setEventRate(engine->sensorEventQueue, + engine->gyroscopeSensor, (1000L/60)*1000); + + } engine->animating = 1; break; case APP_CMD_LOST_FOCUS: @@ -759,6 +769,10 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) { ASensorEventQueue_disableSensor(engine->sensorEventQueue, engine->magnetometerSensor); } + if (engine->gyroscopeSensor != NULL) { + ASensorEventQueue_disableSensor(engine->sensorEventQueue, + engine->gyroscopeSensor); + } // Also stop animating. engine->animating = 0; engine_draw_frame(engine); @@ -788,6 +802,8 @@ void android_main(struct android_app* state) { ASENSOR_TYPE_ACCELEROMETER); engine.magnetometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, ASENSOR_TYPE_MAGNETIC_FIELD); + engine.gyroscopeSensor = ASensorManager_getDefaultSensor(engine.sensorManager, + ASENSOR_TYPE_GYROSCOPE); engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, state->looper, LOOPER_ID_USER, NULL, NULL); @@ -828,7 +844,7 @@ void android_main(struct android_app* state) { // If a sensor has data, process it now. // LOGI("events\n"); if (ident == LOOPER_ID_USER) { - if (engine.accelerometerSensor != NULL || engine.magnetometerSensor != NULL) { + if (engine.accelerometerSensor != NULL || engine.magnetometerSensor != NULL || engine.gyroscopeSensor != NULL) { ASensorEvent event; while (ASensorEventQueue_getEvents(engine.sensorEventQueue, &event, 1) > 0) { @@ -843,6 +859,10 @@ void android_main(struct android_app* state) { engine.os->process_magnetometer(Vector3(event.magnetic.x, event.magnetic.y, event.magnetic.z)); } + if (event.vector != NULL) { + engine.os->process_gyroscope(Vector3(event.vector.x, event.vector.y, + event.vector.z)); + } } } diff --git a/platform/android/ifaddrs_android.cpp b/platform/android/ifaddrs_android.cpp index c1e9eb3584..f6d5cdbe77 100644 --- a/platform/android/ifaddrs_android.cpp +++ b/platform/android/ifaddrs_android.cpp @@ -38,13 +38,16 @@ #include <errno.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> + struct netlinkrequest { nlmsghdr header; ifaddrmsg msg; }; + namespace { const int kMaxReadSize = 4096; -}; +} + static int set_ifname(struct ifaddrs* ifaddr, int interface) { char buf[IFNAMSIZ] = {0}; char* name = if_indextoname(interface, buf); @@ -55,6 +58,7 @@ static int set_ifname(struct ifaddrs* ifaddr, int interface) { strncpy(ifaddr->ifa_name, name, strlen(name) + 1); return 0; } + static int set_flags(struct ifaddrs* ifaddr) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd == -1) { @@ -71,6 +75,7 @@ static int set_flags(struct ifaddrs* ifaddr) { ifaddr->ifa_flags = ifr.ifr_flags; return 0; } + static int set_addresses(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* data, size_t len) { if (msg->ifa_family == AF_INET) { @@ -89,6 +94,7 @@ static int set_addresses(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* data, } return 0; } + static int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) { char* prefix = NULL; if (family == AF_INET) { @@ -120,6 +126,7 @@ static int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) { *prefix = remainder; return 0; } + static int populate_ifaddrs(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* bytes, size_t len) { if (set_ifname(ifaddr, msg->ifa_index) != 0) { @@ -136,6 +143,7 @@ static int populate_ifaddrs(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* bytes, } return 0; } + int getifaddrs(struct ifaddrs** result) { int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { @@ -207,6 +215,7 @@ int getifaddrs(struct ifaddrs** result) { freeifaddrs(start); return -1; } + void freeifaddrs(struct ifaddrs* addrs) { struct ifaddrs* last = NULL; struct ifaddrs* cursor = addrs; diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index 0c71e760dc..d57051703e 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index 4c0f4878f5..4b80db7e33 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -217,6 +217,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC private SensorManager mSensorManager; private Sensor mAccelerometer; private Sensor mMagnetometer; + private Sensor mGyroscope; public FrameLayout layout; public RelativeLayout adLayout; @@ -387,6 +388,8 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME); + mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); + mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); result_callback = null; @@ -604,6 +607,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC mView.onResume(); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME); + mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); GodotLib.focusin(); if(use_immersive && Build.VERSION.SDK_INT >= 19.0){ // check if the application runs on an android 4.4+ Window window = getWindow(); @@ -670,6 +674,9 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC if (typeOfSensor == event.sensor.TYPE_MAGNETIC_FIELD) { GodotLib.magnetometer(x,y,z); } + if (typeOfSensor == event.sensor.TYPE_GYROSCOPE) { + GodotLib.gyroscope(x,y,z); + } } @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { diff --git a/platform/android/java/src/org/godotengine/godot/GodotIO.java b/platform/android/java/src/org/godotengine/godot/GodotIO.java index 3e6919c2ad..55e330924a 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/src/org/godotengine/godot/GodotIO.java @@ -40,6 +40,7 @@ import android.view.*; import android.view.inputmethod.InputMethodManager; import android.os.*; import android.util.Log; +import android.util.DisplayMetrics; import android.graphics.*; import android.text.method.*; import android.text.*; @@ -513,6 +514,11 @@ public class GodotIO { return Build.MODEL; } + public int getScreenDPI() { + DisplayMetrics metrics = applicationContext.getResources().getDisplayMetrics(); + return (int)(metrics.density * 160f); + } + public boolean needsReloadHooks() { return android.os.Build.VERSION.SDK_INT < 11; diff --git a/platform/android/java/src/org/godotengine/godot/GodotLib.java b/platform/android/java/src/org/godotengine/godot/GodotLib.java index df181ae1bb..9a2ea7df10 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/src/org/godotengine/godot/GodotLib.java @@ -52,6 +52,7 @@ public class GodotLib { public static native void touch(int what,int pointer,int howmany, int[] arr); public static native void accelerometer(float x, float y, float z); public static native void magnetometer(float x, float y, float z); + public static native void gyroscope(float x, float y, float z); public static native void key(int p_scancode, int p_unicode_char, boolean p_pressed); public static native void joybutton(int p_device, int p_but, boolean p_pressed); public static native void joyaxis(int p_device, int p_axis, float p_value); diff --git a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java index fde752acc9..b7de31ada3 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java +++ b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java @@ -28,99 +28,94 @@ /*************************************************************************/ package org.godotengine.godot; -import org.godotengine.godot.Dictionary; import android.app.Activity; import android.util.Log; +import org.godotengine.godot.payments.PaymentsManager; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class GodotPaymentV3 extends Godot.SingletonBase { private Godot activity; - private Integer purchaseCallbackId = 0; - private String accessToken; - private String purchaseValidationUrlPrefix; - private String transactionId; + private PaymentsManager mPaymentManager; + private Dictionary mSkuDetails = new Dictionary(); - public void purchase( String _sku, String _transactionId) { - final String sku = _sku; - final String transactionId = _transactionId; - activity.getPaymentsManager().setBaseSingleton(this); + public void purchase(final String sku, final String transactionId) { activity.runOnUiThread(new Runnable() { @Override public void run() { - activity.getPaymentsManager().requestPurchase(sku, transactionId); + mPaymentManager.requestPurchase(sku, transactionId); } }); } - -/* public string requestPurchasedTicket(){ - activity.getPaymentsManager() - } -*/ - static public Godot.SingletonBase initialize(Activity p_activity) { + static public Godot.SingletonBase initialize(Activity p_activity) { - return new GodotPaymentV3(p_activity); - } + return new GodotPaymentV3(p_activity); + } - public GodotPaymentV3(Activity p_activity) { - registerClass("GodotPayments", new String[] {"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume"}); - activity=(Godot) p_activity; + registerClass("GodotPayments", new String[]{"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails"}); + activity = (Godot) p_activity; + mPaymentManager = activity.getPaymentsManager(); + mPaymentManager.setBaseSingleton(this); } - public void consumeUnconsumedPurchases(){ - activity.getPaymentsManager().setBaseSingleton(this); + public void consumeUnconsumedPurchases() { activity.runOnUiThread(new Runnable() { @Override public void run() { - activity.getPaymentsManager().consumeUnconsumedPurchases(); + mPaymentManager.consumeUnconsumedPurchases(); } }); } private String signature; - public String getSignature(){ - return this.signature; + + public String getSignature() { + return this.signature; } - - - public void callbackSuccess(String ticket, String signature, String sku){ -// Log.d(this.getClass().getName(), "PRE-Send callback to purchase success"); + + public void callbackSuccess(String ticket, String signature, String sku) { GodotLib.callobject(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature, sku}); -// Log.d(this.getClass().getName(), "POST-Send callback to purchase success"); -} + } + + public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) { + Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku); + GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[]{ticket, signature, sku}); + } - public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku){ -// Log.d(this.getClass().getName(), "PRE-Send callback to consume success"); - Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > "+ticket+","+signature+","+sku); - GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[]{ticket, signature, sku}); -// Log.d(this.getClass().getName(), "POST-Send callback to consume success"); + public void callbackSuccessNoUnconsumedPurchases() { + GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[]{}); } - public void callbackSuccessNoUnconsumedPurchases(){ - GodotLib.calldeferred(purchaseCallbackId, "no_validation_required", new Object[]{}); + public void callbackFailConsume() { + GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[]{}); } - - public void callbackFail(){ + + public void callbackFail() { GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[]{}); -// GodotLib.callobject(purchaseCallbackId, "purchase_fail", new Object[]{}); } - - public void callbackCancel(){ + + public void callbackCancel() { GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[]{}); -// GodotLib.callobject(purchaseCallbackId, "purchase_cancel", new Object[]{}); } - - public void callbackAlreadyOwned(String sku){ + + public void callbackAlreadyOwned(String sku) { GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[]{sku}); } - + public int getPurchaseCallbackId() { return purchaseCallbackId; } @@ -129,11 +124,11 @@ public class GodotPaymentV3 extends Godot.SingletonBase { this.purchaseCallbackId = purchaseCallbackId; } - public String getPurchaseValidationUrlPrefix(){ - return this.purchaseValidationUrlPrefix ; + public String getPurchaseValidationUrlPrefix() { + return this.purchaseValidationUrlPrefix; } - public void setPurchaseValidationUrlPrefix(String url){ + public void setPurchaseValidationUrlPrefix(String url) { this.purchaseValidationUrlPrefix = url; } @@ -144,39 +139,80 @@ public class GodotPaymentV3 extends Godot.SingletonBase { public void setAccessToken(String accessToken) { this.accessToken = accessToken; } - - public void setTransactionId(String transactionId){ + + public void setTransactionId(String transactionId) { this.transactionId = transactionId; } - - public String getTransactionId(){ + + public String getTransactionId() { return this.transactionId; } - + // request purchased items are not consumed - public void requestPurchased(){ - activity.getPaymentsManager().setBaseSingleton(this); + public void requestPurchased() { activity.runOnUiThread(new Runnable() { @Override public void run() { - activity.getPaymentsManager().requestPurchased(); + mPaymentManager.requestPurchased(); } }); } - + // callback for requestPurchased() - 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}); } - + // consume item automatically after purchase. default is true. - public void setAutoConsume(boolean autoConsume){ - activity.getPaymentsManager().setAutoConsume(autoConsume); + public void setAutoConsume(boolean autoConsume) { + mPaymentManager.setAutoConsume(autoConsume); } - + // consume a specific item - public void consume(String sku){ - activity.getPaymentsManager().consume(sku); + public void consume(String sku) { + mPaymentManager.consume(sku); + } + + // query in app item detail info + public void querySkuDetails(String[] list) { + List<String> nKeys = Arrays.asList(list); + List<String> cKeys = Arrays.asList(mSkuDetails.get_keys()); + ArrayList<String> fKeys = new ArrayList<String>(); + for (String key : nKeys) { + if (!cKeys.contains(key)) { + fKeys.add(key); + } + } + if (fKeys.size() > 0) { + mPaymentManager.querySkuDetails(fKeys.toArray(new String[0])); + } else { + completeSkuDetail(); + } + } + + public void addSkuDetail(String itemJson) { + JSONObject o = null; + try { + o = new JSONObject(itemJson); + Dictionary item = new Dictionary(); + item.put("type", o.optString("type")); + item.put("product_id", o.optString("productId")); + item.put("title", o.optString("title")); + item.put("description", o.optString("description")); + item.put("price", o.optString("price")); + item.put("price_currency_code", o.optString("price_currency_code")); + item.put("price_amount", 0.000001d * o.optLong("price_amount_micros")); + mSkuDetails.put(item.get("product_id").toString(), item); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public void completeSkuDetail() { + GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[]{mSkuDetails}); + } + + public void errorSkuDetail(String errorMessage) { + GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[]{errorMessage}); } } - diff --git a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java index eb33b37ecc..753c0a6f93 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java +++ b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java @@ -28,283 +28,370 @@ /*************************************************************************/ package org.godotengine.godot.payments; -import java.util.ArrayList; -import java.util.List; - -import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; +import android.text.TextUtils; import android.util.Log; -import android.os.Bundle; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONStringer; +import com.android.vending.billing.IInAppBillingService; -import org.godotengine.godot.Dictionary; import org.godotengine.godot.Godot; import org.godotengine.godot.GodotPaymentV3; -import com.android.vending.billing.IInAppBillingService; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; public class PaymentsManager { public static final int BILLING_RESPONSE_RESULT_OK = 0; public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001; private static boolean auto_consume = true; - + private Activity activity; IInAppBillingService mService; - public void setActivity(Activity activity){ + public void setActivity(Activity activity) { this.activity = activity; } - public static PaymentsManager createManager(Activity activity){ + public static PaymentsManager createManager(Activity activity) { PaymentsManager manager = new PaymentsManager(activity); return manager; } - - private PaymentsManager(Activity activity){ + + private PaymentsManager(Activity activity) { this.activity = activity; } - - public PaymentsManager initService(){ + + public PaymentsManager initService() { Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); intent.setPackage("com.android.vending"); activity.bindService( - intent, - mServiceConn, + intent, + mServiceConn, Context.BIND_AUTO_CREATE); return this; } - public void destroy(){ + public void destroy() { if (mService != null) { - activity.unbindService(mServiceConn); - } + activity.unbindService(mServiceConn); + } } - + ServiceConnection mServiceConn = new ServiceConnection() { - @Override - public void onServiceDisconnected(ComponentName name) { - mService = null; - } + @Override + public void onServiceDisconnected(ComponentName name) { + mService = null; + } - @Override - public void onServiceConnected(ComponentName name, IBinder service) { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { mService = IInAppBillingService.Stub.asInterface(service); - } + } }; - - public void requestPurchase(final String sku, String transactionId){ + + public void requestPurchase(final String sku, String transactionId) { new PurchaseTask(mService, Godot.getInstance()) { - + @Override protected void error(String message) { godotPaymentV3.callbackFail(); - + } - + @Override protected void canceled() { godotPaymentV3.callbackCancel(); } - + @Override protected void alreadyOwned() { godotPaymentV3.callbackAlreadyOwned(sku); } - + }.purchase(sku, transactionId); } - public void consumeUnconsumedPurchases(){ + public void consumeUnconsumedPurchases() { new ReleaseAllConsumablesTask(mService, activity) { - + @Override protected void success(String sku, String receipt, String signature, String token) { godotPaymentV3.callbackSuccessProductMassConsumed(receipt, signature, sku); } - + @Override protected void error(String message) { - godotPaymentV3.callbackFail(); - + Log.d("godot", "consumeUnconsumedPurchases :" + message); + godotPaymentV3.callbackFailConsume(); + } @Override protected void notRequired() { + Log.d("godot", "callbackSuccessNoUnconsumedPurchases :"); godotPaymentV3.callbackSuccessNoUnconsumedPurchases(); - + } }.consumeItAll(); } - - public void requestPurchased(){ - try{ + + public void requestPurchased() { + try { PaymentsCache pc = new PaymentsCache(Godot.getInstance()); -// Log.d("godot", "requestPurchased for " + activity.getPackageName()); - Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp",null); + String continueToken = null; -/* - for (String key : bundle.keySet()) { - Object value = bundle.get(key); - Log.d("godot", String.format("%s %s (%s)", key, value.toString(), value.getClass().getName())); - } -*/ - - if (bundle.getInt("RESPONSE_CODE") == 0){ + do { + Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp", continueToken); - final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); - final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); - + if (bundle.getInt("RESPONSE_CODE") == 0) { - if (myPurchases == null || myPurchases.size() == 0){ -// Log.d("godot", "No purchases!"); - godotPaymentV3.callbackPurchased("", "", ""); - return; - } - -// Log.d("godot", "# products are purchased:" + myPurchases.size()); - for (int i=0;i<myPurchases.size();i++) - { - - try{ - String receipt = myPurchases.get(i); - JSONObject inappPurchaseData = new JSONObject(receipt); - String sku = inappPurchaseData.getString("productId"); - String token = inappPurchaseData.getString("purchaseToken"); - String signature = mySignatures.get(i); -// Log.d("godot", "purchased item:" + token + "\n" + receipt); - - pc.setConsumableValue("ticket_signautre", sku, signature); - pc.setConsumableValue("ticket", sku, receipt); - pc.setConsumableFlag("block", sku, true); - pc.setConsumableValue("token", sku, token); - - godotPaymentV3.callbackPurchased(receipt, signature, sku); - } catch (JSONException e) { + final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); + final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); + + if (myPurchases == null || myPurchases.size() == 0) { + godotPaymentV3.callbackPurchased("", "", ""); + return; } - } - } - }catch(Exception e){ + for (int i = 0; i < myPurchases.size(); i++) { + + try { + String receipt = myPurchases.get(i); + JSONObject inappPurchaseData = new JSONObject(receipt); + String sku = inappPurchaseData.getString("productId"); + String token = inappPurchaseData.getString("purchaseToken"); + String signature = mySignatures.get(i); + + pc.setConsumableValue("ticket_signautre", sku, signature); + pc.setConsumableValue("ticket", sku, receipt); + pc.setConsumableFlag("block", sku, true); + pc.setConsumableValue("token", sku, token); + + godotPaymentV3.callbackPurchased(receipt, signature, sku); + } catch (JSONException e) { + } + } + } + continueToken = bundle.getString("INAPP_CONTINUATION_TOKEN"); + Log.d("godot", "continue token = " + continueToken); + } while (!TextUtils.isEmpty(continueToken)); + } catch (Exception e) { Log.d("godot", "Error requesting purchased products:" + e.getClass().getName() + ":" + e.getMessage()); } } - + public void processPurchaseResponse(int resultCode, Intent data) { - new HandlePurchaseTask(activity){ + new HandlePurchaseTask(activity) { @Override protected void success(final String sku, final String signature, final String ticket) { godotPaymentV3.callbackSuccess(ticket, signature, sku); - if (auto_consume){ + if (auto_consume) { new ConsumeTask(mService, activity) { - + @Override protected void success(String ticket) { -// godotPaymentV3.callbackSuccess(""); } - + @Override protected void error(String message) { godotPaymentV3.callbackFail(); - + } }.consume(sku); } - -// godotPaymentV3.callbackSuccess(new PaymentsCache(activity).getConsumableValue("ticket", sku),signature); -// godotPaymentV3.callbackSuccess(ticket); - //validatePurchase(purchaseToken, sku); } @Override protected void error(String message) { godotPaymentV3.callbackFail(); - } @Override protected void canceled() { godotPaymentV3.callbackCancel(); - } }.handlePurchaseRequest(resultCode, data); } - - public void validatePurchase(String purchaseToken, final String sku){ - - new ValidateTask(activity, godotPaymentV3){ + + public void validatePurchase(String purchaseToken, final String sku) { + + new ValidateTask(activity, godotPaymentV3) { @Override protected void success() { - + new ConsumeTask(mService, activity) { - + @Override protected void success(String ticket) { godotPaymentV3.callbackSuccess(ticket, null, sku); - } - + @Override protected void error(String message) { godotPaymentV3.callbackFail(); - } }.consume(sku); - + } @Override protected void error(String message) { godotPaymentV3.callbackFail(); - } @Override protected void canceled() { godotPaymentV3.callbackCancel(); - } }.validatePurchase(sku); } - - public void setAutoConsume(boolean autoConsume){ + + public void setAutoConsume(boolean autoConsume) { auto_consume = autoConsume; } - - public void consume(final String sku){ + + public void consume(final String sku) { new ConsumeTask(mService, activity) { - + @Override protected void success(String ticket) { godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku); - } - + @Override protected void error(String message) { - godotPaymentV3.callbackFail(); - + godotPaymentV3.callbackFailConsume(); } }.consume(sku); } - + + // Workaround to bug where sometimes response codes come as Long instead of Integer + int getResponseCodeFromBundle(Bundle b) { + Object o = b.get("RESPONSE_CODE"); + if (o == null) { + //logDebug("Bundle with null response code, assuming OK (known issue)"); + return BILLING_RESPONSE_RESULT_OK; + } else if (o instanceof Integer) return ((Integer) o).intValue(); + else if (o instanceof Long) return (int) ((Long) o).longValue(); + else { + //logError("Unexpected type for bundle response code."); + //logError(o.getClass().getName()); + throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName()); + } + } + + /** + * Returns a human-readable description for the given response code. + * + * @param code The response code + * @return A human-readable string explaining the result code. + * It also includes the result code numerically. + */ + public static String getResponseDesc(int code) { + String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" + + "3:Billing Unavailable/4:Item unavailable/" + + "5:Developer Error/6:Error/7:Item Already Owned/" + + "8:Item not owned").split("/"); + String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" + + "-1002:Bad response received/" + + "-1003:Purchase signature verification failed/" + + "-1004:Send intent failed/" + + "-1005:User cancelled/" + + "-1006:Unknown purchase response/" + + "-1007:Missing token/" + + "-1008:Unknown error/" + + "-1009:Subscriptions not available/" + + "-1010:Invalid consumption attempt").split("/"); + + if (code <= -1000) { + int index = -1000 - code; + if (index >= 0 && index < iabhelper_msgs.length) return iabhelper_msgs[index]; + else return String.valueOf(code) + ":Unknown IAB Helper Error"; + } else if (code < 0 || code >= iab_msgs.length) + return String.valueOf(code) + ":Unknown"; + else + return iab_msgs[code]; + } + + public void querySkuDetails(final String[] list) { + (new Thread(new Runnable() { + @Override + public void run() { + ArrayList<String> skuList = new ArrayList<String>(Arrays.asList(list)); + if (skuList.size() == 0) { + return; + } + // Split the sku list in blocks of no more than 20 elements. + ArrayList<ArrayList<String>> packs = new ArrayList<ArrayList<String>>(); + ArrayList<String> tempList; + int n = skuList.size() / 20; + int mod = skuList.size() % 20; + for (int i = 0; i < n; i++) { + tempList = new ArrayList<String>(); + for (String s : skuList.subList(i * 20, i * 20 + 20)) { + tempList.add(s); + } + packs.add(tempList); + } + if (mod != 0) { + tempList = new ArrayList<String>(); + for (String s : skuList.subList(n * 20, n * 20 + mod)) { + tempList.add(s); + } + packs.add(tempList); + + for (ArrayList<String> skuPartList : packs) { + Bundle querySkus = new Bundle(); + querySkus.putStringArrayList("ITEM_ID_LIST", skuPartList); + Bundle skuDetails = null; + try { + skuDetails = mService.getSkuDetails(3, activity.getPackageName(), "inapp", querySkus); + if (!skuDetails.containsKey("DETAILS_LIST")) { + int response = getResponseCodeFromBundle(skuDetails); + if (response != BILLING_RESPONSE_RESULT_OK) { + godotPaymentV3.errorSkuDetail(getResponseDesc(response)); + } else { + godotPaymentV3.errorSkuDetail("No error but no detail list."); + } + return; + } + + ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST"); + + for (String thisResponse : responseList) { + Log.d("godot", "response = "+thisResponse); + godotPaymentV3.addSkuDetail(thisResponse); + } + } catch (RemoteException e) { + e.printStackTrace(); + godotPaymentV3.errorSkuDetail("RemoteException error!"); + } + } + godotPaymentV3.completeSkuDetail(); + } + } + })).start(); + } + private GodotPaymentV3 godotPaymentV3; - + public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) { this.godotPaymentV3 = godotPaymentV3; } } - diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index 4fda13fec0..cc1e5b61d1 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -540,25 +540,21 @@ Variant JavaClass::call(const StringName& p_method,const Variant** p_args,int p_ JavaClass::JavaClass() { - } ///////////////////// Variant JavaObject::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error){ - return Variant(); } JavaObject::JavaObject(const Ref<JavaClass>& p_base,jobject *p_instance) { - } JavaObject::~JavaObject(){ - } @@ -1356,5 +1352,4 @@ JavaClassWrapper::JavaClassWrapper(jobject p_activity) { bclass = env->FindClass("java/lang/Double"); Double_doubleValue = env->GetMethodID(bclass, "doubleValue", "()D"); - } diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index 5fd2ab8910..e5b655e5d5 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -215,7 +215,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant* p_a } return v; -}; +} String _get_class_name(JNIEnv * env, jclass cls, bool* array) { @@ -233,11 +233,15 @@ String _get_class_name(JNIEnv * env, jclass cls, bool* array) { 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); @@ -259,8 +263,7 @@ Variant _jobject_to_variant(JNIEnv * env, jobject obj) { for (int i=0; i<stringCount; i++) { jstring string = (jstring) env->GetObjectArrayElement(arr, i); - const char *rawString = env->GetStringUTFChars(string, 0); - sarr.push_back(String(rawString)); + sarr.push_back(String::utf8(env->GetStringUTFChars(string, NULL))); env->DeleteLocalRef(string); } @@ -403,7 +406,7 @@ Variant _jobject_to_variant(JNIEnv * env, jobject obj) { env->DeleteLocalRef(c); return Variant(); -}; +} class JNISingleton : public Object { @@ -506,7 +509,7 @@ public: } break; case Variant::BOOL: { - ret = env->CallBooleanMethodA(instance,E->get().method,v); + ret = env->CallBooleanMethodA(instance,E->get().method,v)==JNI_TRUE; //print_line("call bool"); } break; case Variant::INT: { @@ -521,8 +524,7 @@ public: case Variant::STRING: { jobject o = env->CallObjectMethodA(instance,E->get().method,v); - String str = env->GetStringUTFChars((jstring)o, NULL ); - ret=str; + ret = String::utf8(env->GetStringUTFChars((jstring)o, NULL)); env->DeleteLocalRef(o); } break; case Variant::STRING_ARRAY: { @@ -651,6 +653,7 @@ static bool quit_request=false; static Size2 new_size; static Vector3 accelerometer; static Vector3 magnetometer; +static Vector3 gyroscope; static HashMap<String,JNISingleton*> jni_singletons; static jobject godot_io; @@ -665,6 +668,7 @@ static jmethodID _openURI=0; static jmethodID _getDataDir=0; static jmethodID _getLocale=0; static jmethodID _getModel=0; +static jmethodID _getScreenDPI=0; static jmethodID _showKeyboard=0; static jmethodID _hideKeyboard=0; static jmethodID _setScreenOrientation=0; @@ -709,6 +713,12 @@ static String _get_model() { return String(env->GetStringUTFChars( s, NULL )); } +static int _get_screen_dpi() { + + JNIEnv *env = ThreadAndroid::get_env(); + return env->CallIntMethod(godot_io,_getScreenDPI); +} + static String _get_unique_id() { JNIEnv *env = ThreadAndroid::get_env(); @@ -721,27 +731,27 @@ static void _show_vk(const String& p_existing) { JNIEnv* env = ThreadAndroid::get_env(); jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); env->CallVoidMethod(godot_io, _showKeyboard, jStr); -}; +} static void _set_screen_orient(int p_orient) { JNIEnv* env = ThreadAndroid::get_env(); env->CallVoidMethod(godot_io, _setScreenOrientation, p_orient ); -}; +} static String _get_system_dir(int p_dir) { JNIEnv *env = ThreadAndroid::get_env(); jstring s =(jstring)env->CallObjectMethod(godot_io,_getSystemDir,p_dir); return String(env->GetStringUTFChars( s, NULL )); -}; +} static void _hide_vk() { JNIEnv* env = ThreadAndroid::get_env(); env->CallVoidMethod(godot_io, _hideKeyboard); -}; +} // virtual Error native_video_play(String p_path); // virtual bool native_video_is_playing(); @@ -818,6 +828,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv * e _getDataDir = env->GetMethodID(c,"getDataDir","()Ljava/lang/String;"); _getLocale = env->GetMethodID(c,"getLocale","()Ljava/lang/String;"); _getModel = env->GetMethodID(c,"getModel","()Ljava/lang/String;"); + _getScreenDPI = env->GetMethodID(c, "getScreenDPI","()I"); _getUniqueID = env->GetMethodID(c,"getUniqueID","()Ljava/lang/String;"); _showKeyboard = env->GetMethodID(c,"showKeyboard","(Ljava/lang/String;)V"); _hideKeyboard = env->GetMethodID(c,"hideKeyboard","()V"); @@ -872,7 +883,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv * e __android_log_print(ANDROID_LOG_INFO,"godot","CMDLINE LEN %i - APK EXPANSION %I\n",cmdlen,int(use_apk_expansion)); - os_android = new OS_Android(_gfx_init_func,env,_open_uri,_get_data_dir,_get_locale, _get_model,_show_vk, _hide_vk,_set_screen_orient,_get_unique_id, _get_system_dir, _play_video,_is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, use_apk_expansion); + os_android = new OS_Android(_gfx_init_func,env,_open_uri,_get_data_dir,_get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk,_set_screen_orient,_get_unique_id, _get_system_dir, _play_video,_is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, use_apk_expansion); os_android->set_need_reload_hooks(p_need_reload_hook); char wd[500]; @@ -942,7 +953,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv * e os_android->reload_gfx(); } - } @@ -957,7 +967,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_quit(JNIEnv * env, jo static void _initialize_java_modules() { - String modules = Globals::get_singleton()->get("android/modules"); Vector<String> mods = modules.split(",",false); print_line("ANDROID MODULES : " + modules); @@ -1091,6 +1100,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv * env, jo os_android->process_magnetometer(magnetometer); + os_android->process_gyroscope(gyroscope); + if (os_android->main_loop_iterate()==true) { jclass cls = env->FindClass("org/godotengine/godot/Godot"); @@ -1404,7 +1415,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv * en input_mutex->lock(); joy_events.push_back(jevent); input_mutex->unlock(); -}; +} JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv * env, jobject obj, jint p_device, jint p_axis, jfloat p_value) { @@ -1417,7 +1428,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv * env, input_mutex->lock(); joy_events.push_back(jevent); input_mutex->unlock(); -}; +} JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv * env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) { OS_Android::JoystickEvent jevent; @@ -1480,7 +1491,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv * env, job input_mutex->lock(); key_events.push_back(ievent); input_mutex->unlock(); -}; +} JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv * env, jobject obj, jfloat x, jfloat y, jfloat z) { @@ -1499,6 +1510,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv * } +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv * env, jobject obj, jfloat x, jfloat y, jfloat z) { + + input_mutex->lock(); + gyroscope=Vector3(x,y,z); + input_mutex->unlock(); + +} + JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv * env, jobject obj){ if (!suspend_mutex) @@ -1701,7 +1720,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv * e env->PopLocalFrame(NULL); -}; +} JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * env, jobject p_obj, jint ID, jstring method, jobjectArray params) { @@ -1736,7 +1755,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * // something env->PopLocalFrame(NULL); -}; +} //Main::cleanup(); diff --git a/platform/android/java_glue.h b/platform/android/java_glue.h index f7916efe2c..f1c83f01e8 100644 --- a/platform/android/java_glue.h +++ b/platform/android/java_glue.h @@ -50,6 +50,7 @@ extern "C" { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv * env, jobject obj); 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_magnetometer(JNIEnv * env, jobject obj, 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_focusin(JNIEnv * env, jobject obj); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv * env, jobject obj); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv * env, jobject obj, jstring name,jobject p_object); @@ -57,7 +58,7 @@ extern "C" { JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv * env, jobject obj, jstring path); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv * env, jobject obj, jint ID, jstring method, jobjectArray params); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * env, jobject obj, jint ID, jstring method, jobjectArray params); -}; +} #endif diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 4e6dfb2db2..4e395a6f9f 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -53,6 +53,7 @@ int OS_Android::get_video_driver_count() const { return 1; } + const char * OS_Android::get_video_driver_name(int p_driver) const { return "GLES2"; @@ -271,17 +272,18 @@ bool OS_Android::is_mouse_grab_enabled() const { //*sigh* technology has evolved so much since i was a kid.. return false; } + Point2 OS_Android::get_mouse_pos() const { return Point2(); } + int OS_Android::get_mouse_button_state() const { return 0; } void OS_Android::set_window_title(const String& p_title) { - } //interesting byt not yet @@ -290,13 +292,13 @@ void OS_Android::set_window_title(const String& p_title) { void OS_Android::set_video_mode(const VideoMode& p_video_mode,int p_screen) { - } OS::VideoMode OS_Android::get_video_mode(int p_screen) const { return default_videomode; } + void OS_Android::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { p_list->push_back(default_videomode); @@ -340,6 +342,7 @@ void OS_Android::main_loop_begin() { if (main_loop) main_loop->init(); } + bool OS_Android::main_loop_iterate() { if (!main_loop) @@ -394,7 +397,7 @@ void OS_Android::process_event(InputEvent p_event) { p_event.ID = last_id++; input->parse_input_event(p_event); -}; +} void OS_Android::process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points) { @@ -614,6 +617,11 @@ void OS_Android::process_magnetometer(const Vector3& p_magnetometer) { input->set_magnetometer(p_magnetometer); } +void OS_Android::process_gyroscope(const Vector3& p_gyroscope) { + + input->set_gyroscope(p_gyroscope); +} + bool OS_Android::has_touchscreen_ui_hint() const { return true; @@ -622,7 +630,7 @@ bool OS_Android::has_touchscreen_ui_hint() const { bool OS_Android::has_virtual_keyboard() const { return true; -}; +} void OS_Android::show_virtual_keyboard(const String& p_existing_text,const Rect2& p_screen_rect) { @@ -632,7 +640,7 @@ void OS_Android::show_virtual_keyboard(const String& p_existing_text,const Rect2 ERR_PRINT("Virtual keyboard not available"); }; -}; +} void OS_Android::hide_virtual_keyboard() { @@ -643,7 +651,7 @@ void OS_Android::hide_virtual_keyboard() { ERR_PRINT("Virtual keyboard not available"); }; -}; +} void OS_Android::init_video_mode(int p_video_width,int p_video_height) { @@ -679,7 +687,7 @@ Error OS_Android::shell_open(String p_uri) { if (open_uri_func) return open_uri_func(p_uri)?ERR_CANT_OPEN:OK; return ERR_UNAVAILABLE; -}; +} String OS_Android::get_resource_dir() const { @@ -700,6 +708,13 @@ String OS_Android::get_model_name() const { return OS_Unix::get_model_name(); } +int OS_Android::get_screen_dpi(int p_screen) const { + + if (get_screen_dpi_func) { + return get_screen_dpi_func(); + } + return 160; +} void OS_Android::set_need_reload_hooks(bool p_needs_them) { @@ -708,16 +723,40 @@ void OS_Android::set_need_reload_hooks(bool p_needs_them) { String OS_Android::get_data_dir() const { - if (get_data_dir_func) - return get_data_dir_func(); + if (data_dir_cache!=String()) + return data_dir_cache; + + if (get_data_dir_func) { + String data_dir=get_data_dir_func(); + + //store current dir + char real_current_dir_name[2048]; + getcwd(real_current_dir_name,2048); + + //go to data dir + chdir(data_dir.utf8().get_data()); + + //get actual data dir, so we resolve potential symlink (Android 6.0+ seems to use symlink) + char data_current_dir_name[2048]; + getcwd(data_current_dir_name,2048); + + //cache by parsing utf8 + data_dir_cache.parse_utf8(data_current_dir_name); + + //restore original dir so we don't mess things up + chdir(real_current_dir_name); + + return data_dir_cache; + } + + return "."; //return Globals::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); -}; +} void OS_Android::set_screen_orientation(ScreenOrientation p_orientation) { - if (set_screen_orientation_func) set_screen_orientation_func(p_orientation); } @@ -778,8 +817,7 @@ String OS_Android::get_joy_guid(int p_device) const { return input->get_joy_guid_remapped(p_device); } -OS_Android::OS_Android(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func,GetModelFunc p_get_model_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, SetScreenOrientationFunc p_screen_orient,GetUniqueIDFunc p_get_unique_id,GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, bool p_use_apk_expansion) { - +OS_Android::OS_Android(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func,GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, SetScreenOrientationFunc p_screen_orient,GetUniqueIDFunc p_get_unique_id,GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, bool p_use_apk_expansion) { use_apk_expansion=p_use_apk_expansion; default_videomode.width=800; @@ -799,6 +837,7 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFu get_data_dir_func=p_get_data_dir_func; get_locale_func=p_get_locale_func; get_model_func=p_get_model_func; + get_screen_dpi_func = p_get_screen_dpi_func; get_unique_id_func=p_get_unique_id; get_system_dir_func=p_get_sdir_func; @@ -818,5 +857,4 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFu OS_Android::~OS_Android() { - } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 843b3c4788..d383fd2036 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -63,6 +63,7 @@ typedef int (*OpenURIFunc)(const String&); typedef String (*GetDataDirFunc)(); typedef String (*GetLocaleFunc)(); typedef String (*GetModelFunc)(); +typedef int (*GetScreenDPIFunc)(); typedef String (*GetUniqueIDFunc)(); typedef void (*ShowVirtualKeyboardFunc)(const String&); typedef void (*HideVirtualKeyboardFunc)(); @@ -123,6 +124,8 @@ private: PhysicsServer *physics_server; Physics2DServer *physics_2d_server; + mutable String data_dir_cache; + #if 0 AudioDriverAndroid audio_driver_android; #else @@ -139,6 +142,7 @@ private: GetDataDirFunc get_data_dir_func; GetLocaleFunc get_locale_func; GetModelFunc get_model_func; + GetScreenDPIFunc get_screen_dpi_func; ShowVirtualKeyboardFunc show_virtual_keyboard_func; HideVirtualKeyboardFunc hide_virtual_keyboard_func; SetScreenOrientationFunc set_screen_orientation_func; @@ -232,6 +236,7 @@ public: virtual String get_resource_dir() const; virtual String get_locale() const; virtual String get_model_name() const; + virtual int get_screen_dpi(int p_screen=0) const; virtual String get_unique_ID() const; @@ -240,6 +245,7 @@ public: void process_accelerometer(const Vector3& p_accelerometer); void process_magnetometer(const Vector3& p_magnetometer); + void process_gyroscope(const Vector3& p_gyroscope); void process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points); void process_joy_event(JoystickEvent p_event); void process_event(InputEvent p_event); @@ -254,7 +260,7 @@ public: virtual String get_joy_guid(int p_device) const; void joy_connection_changed(int p_device, bool p_connected, String p_name); - OS_Android(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func,GetModelFunc p_get_model_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, SetScreenOrientationFunc p_screen_orient,GetUniqueIDFunc p_get_unique_id,GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, bool p_use_apk_expansion); + OS_Android(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func,GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, SetScreenOrientationFunc p_screen_orient,GetUniqueIDFunc p_get_unique_id,GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, bool p_use_apk_expansion); ~OS_Android(); }; diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp index 61ee237586..73818b282f 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -64,10 +64,12 @@ Thread* ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback,void return tr; } + Thread::ID ThreadAndroid::get_thread_ID_func_jandroid() { return (ID)pthread_self(); } + void ThreadAndroid::wait_to_finish_func_jandroid(Thread* p_thread) { ThreadAndroid *tp=static_cast<ThreadAndroid*>(p_thread); |