diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /platform/android | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'platform/android')
116 files changed, 11976 insertions, 0 deletions
diff --git a/platform/android/.old/SCsub b/platform/android/.old/SCsub new file mode 100644 index 0000000000..59e6d10b85 --- /dev/null +++ b/platform/android/.old/SCsub @@ -0,0 +1,22 @@ +Import('env') + +android_files = [ + + 'audio_driver_android.cpp', + 'file_access_jandroid.cpp', + 'dir_access_jandroid.cpp', + 'os_android.cpp', + 'java_glue.cpp' +] + +#env.Depends('#core/math/vector3.h', 'vector3_psp.h') + +#obj = env.SharedObject('godot_android.cpp') + +android_objects=[] +for x in android_files: + android_objects.append( env.SharedObject( x ) ) + +prog = None + +env.SharedLibrary("#platform/jandroid/libgodot_android.so",[android_objects]) diff --git a/platform/android/.old/audio_driver_android.cpp b/platform/android/.old/audio_driver_android.cpp new file mode 100644 index 0000000000..7e2d065d65 --- /dev/null +++ b/platform/android/.old/audio_driver_android.cpp @@ -0,0 +1,104 @@ +#include "audio_driver_android.h" + +AudioDriverAndroid* AudioDriverAndroid::s_ad=NULL; + +const char* AudioDriverAndroid::get_name() const { + + return "Android"; +} + +#if 0 +int AudioDriverAndroid::thread_func(SceSize args, void *argp) { + + AudioDriverAndroid* ad = s_ad; + sceAudioOutput2Reserve(AUDIO_OUTPUT_SAMPLE); + + int half=0; + while(!ad->exit_thread) { + + int16_t *ptr = &ad->outbuff[AUDIO_OUTPUT_SAMPLE*2*half]; + + + + if (!ad->active) { + + for(int i=0;i<AUDIO_OUTPUT_SAMPLE*2;i++) { + ptr[i]=0; + } + + } else { + + //printf("samples: %i\n",AUDIO_OUTPUT_SAMPLE); + ad->lock(); + + ad->audio_server_process(AUDIO_OUTPUT_SAMPLE,ad->outbuff_32); + + ad->unlock(); + + const int32_t* src_buff=ad->outbuff_32; + + for(int i=0;i<AUDIO_OUTPUT_SAMPLE*2;i++) { + + ptr[i]=src_buff[i]>>16; + } + } + + + /* Output 16-bit PCM STEREO data that is in pcmBuf without changing the volume */ + sceAudioOutput2OutputBlocking( + SCE_AUDIO_VOLUME_0dB*3, //0db at 0x8000, that's obvious + ptr + ); + + if (half) + half=0; + else + half=1; + + } + + sceAudioOutput2Release(); + + sceKernelExitThread(SCE_KERNEL_EXIT_SUCCESS); + ad->thread_exited=true; + return SCE_KERNEL_EXIT_SUCCESS; + +} + +#endif +Error AudioDriverAndroid::init(){ + + return OK; + + +} +void AudioDriverAndroid::start(){ + + +} +int AudioDriverAndroid::get_mix_rate() const { + + return 44100; +} +AudioDriverSW::OutputFormat AudioDriverAndroid::get_output_format() const{ + + return OUTPUT_STEREO; +} +void AudioDriverAndroid::lock(){ + + +} +void AudioDriverAndroid::unlock() { + + +} +void AudioDriverAndroid::finish(){ + + } + + +AudioDriverAndroid::AudioDriverAndroid() +{ + s_ad=this; +} + diff --git a/platform/android/.old/audio_driver_android.h b/platform/android/.old/audio_driver_android.h new file mode 100644 index 0000000000..08fbaa927e --- /dev/null +++ b/platform/android/.old/audio_driver_android.h @@ -0,0 +1,29 @@ +#ifndef AUDIO_DRIVER_ANDROID_H +#define AUDIO_DRIVER_ANDROID_H + +#include "servers/audio/audio_server_sw.h" + +class AudioDriverAndroid : public AudioDriverSW { + + + static AudioDriverAndroid* s_ad; + +public: + + void set_singleton(); + + virtual const char* get_name() const; + + virtual Error init(); + virtual void start(); + virtual int get_mix_rate() const ; + virtual OutputFormat get_output_format() const; + virtual void lock(); + virtual void unlock(); + virtual void finish(); + + + AudioDriverAndroid(); +}; + +#endif // AUDIO_DRIVER_ANDROID_H diff --git a/platform/android/.old/context_gl_android.cpp b/platform/android/.old/context_gl_android.cpp new file mode 100644 index 0000000000..e48ed2e7f9 --- /dev/null +++ b/platform/android/.old/context_gl_android.cpp @@ -0,0 +1,38 @@ +#include "context_gl_android.h" + +#include <GLES2/gl2.h> +#include "os/os.h" +void ContextGLAndroid::make_current() { + +}; + +int ContextGLAndroid::get_window_width() { + + return OS::get_singleton()->get_default_video_mode().width; +}; + +int ContextGLAndroid::get_window_height() { + + return OS::get_singleton()->get_default_video_mode().height; + +}; + +void ContextGLAndroid::swap_buffers() { + +}; + +Error ContextGLAndroid::initialize() { + + return OK; +}; + + +ContextGLAndroid::ContextGLAndroid() { + + +}; + +ContextGLAndroid::~ContextGLAndroid() { + +}; + diff --git a/platform/android/.old/context_gl_android.h b/platform/android/.old/context_gl_android.h new file mode 100644 index 0000000000..bf09d08ad2 --- /dev/null +++ b/platform/android/.old/context_gl_android.h @@ -0,0 +1,24 @@ +#ifndef CONTEXT_GL_ANDROID_H +#define CONTEXT_GL_ANDROID_H + +class ContextGLAndroid : public ContextGL { + + enum { + COMMAND_BUFFER_SIZE = 1024 * 1024, + }; + +public: + + virtual void make_current(); + + virtual int get_window_width(); + virtual int get_window_height(); + virtual void swap_buffers(); + + virtual Error initialize(); + + ContextGLAndroid(); + ~ContextGLAndroid(); +}; + +#endif // CONTEXT_GL_ANDROID_H diff --git a/platform/android/.old/detect.py b/platform/android/.old/detect.py new file mode 100644 index 0000000000..c29b56aed4 --- /dev/null +++ b/platform/android/.old/detect.py @@ -0,0 +1,107 @@ +import os +import sys +import string + +def can_build(): + + import os + if (not os.environ.has_key("ANDROID_NDK_ROOT")): + print("ANDROID_NDK_ROOT not present, Android disabled.") + return False + return True + +def get_opts(): + + return [ + ('ANDROID_NDK_ROOT', 'the path to Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)), + ('NDK_TOOLCHAIN', 'toolchain to use for the NDK',"arm-eabi-4.4.0"), + #android 2.2 +# ('NDK_PLATFORM', 'platform to use for the NDK',"android-8"), +# ('NDK_TARGET', 'toolchain to use for the NDK',"arm-linux-androideabi-4.4.3"), + ] + +def get_flags(): + + return [ + ('lua', 'no'), + ('tools', 'no'), + ('nedmalloc', 'no'), + ] + + + +def configure(env): + + + print("Godot Android!!!!!") + + env.Append(CPPPATH=['#platform/android']) + + env['OBJSUFFIX'] = ".jandroid.o" + env['LIBSUFFIX'] = ".jandroid.a" + env['PROGSUFFIX'] = ".jandroid" + + gcc_path=env["ANDROID_NDK_ROOT"]+"/toolchains/"+env["NDK_TARGET"]+"/prebuilt/"; + + if (sys.platform.find("linux")==0): + gcc_path=gcc_path+"/linux-x86/bin" + elif (sys.platform=="darwin"): + gcc_path=gcc_path+"/darwin-x86/bin" #this may be wrong + elif (os.name=="nt"): + gcc_path=gcc_path+"/windows-x86/bin" #this may be wrong + + + + env['ENV']['PATH'] = gcc_path+":"+env['ENV']['PATH'] + + env['CC'] = gcc_path+'/arm-linux-androideabi-gcc' + env['CXX'] = gcc_path+'/arm-linux-androideabi-g++' + env['AR'] = gcc_path+"/arm-linux-androideabi-ar" + + import string + #include path + gcc_include=env["ANDROID_NDK_ROOT"]+"/platforms/"+env["NDK_PLATFORM"]+"/arch-arm/usr/include" + ld_sysroot=env["ANDROID_NDK_ROOT"]+"/platforms/"+env["NDK_PLATFORM"]+"/arch-arm" + ld_path=env["ANDROID_NDK_ROOT"]+"/platforms/"+env["NDK_PLATFORM"]+"/arch-arm/usr/lib" + #cxx_include=env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/system/include" + #env.Append(CPPPATH=[gcc_include,cxx_include]) + env['CCFLAGS'] = string.split('-DNO_THREADS -DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -Wno-psabi -march=armv5te -mtune=xscale -msoft-float -fno-exceptions -mthumb -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED ') + + env.Append(LDPATH=[ld_path]) + env.Append(LIBS=['c','m','stdc++','log','GLESv2']) + + env["LINKFLAGS"]= string.split(" -g --sysroot="+ld_sysroot+" -Wl,--no-undefined -Wl,-z,noexecstack") + env.Append(LINKFLAGS=["-Wl,-soname,libgodot_android.so"]) + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O2', '-ffast-math','-fomit-frame-pointer']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-D_DEBUG', '-g', '-Wall', '-O0', '-DDEBUG_ENABLED']) + env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']) + + env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DNO_FCNTL']) + + env['neon_enabled']=True + env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL','-DMPC_FIXED_POINT']) +# env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DMPC_FIXED_POINT']) + 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.4.3/include"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi/include"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi"]) + env.Append(LIBS=["gnustl_static","supc++"]) + #env.Append(CCFLAGS=["-I"+env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/stlport/stlport"]) + #env.Append(CCFLAGS=["-I"+env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/libs/armeabi/include"]) + #env.Append(LINKFLAGS=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/libs/armeabi/libstdc++.a"]) + else: + + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/include"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cpufeatures"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/libs/armeabi"]) + env.Append(LIBS=['gabi++_static']) + env.Append(CCFLAGS=["-fno-exceptions",'-DNO_SAFE_CAST']) diff --git a/platform/android/.old/dir_access_jandroid.cpp b/platform/android/.old/dir_access_jandroid.cpp new file mode 100644 index 0000000000..649a25a902 --- /dev/null +++ b/platform/android/.old/dir_access_jandroid.cpp @@ -0,0 +1,217 @@ +#include "dir_access_jandroid.h" +#include "file_access_jandroid.h" + +jobject DirAccessJAndroid::io=NULL; +jclass DirAccessJAndroid::cls=NULL; +JNIEnv * DirAccessJAndroid::env=NULL; +jmethodID DirAccessJAndroid::_dir_open=NULL; +jmethodID DirAccessJAndroid::_dir_next=NULL; +jmethodID DirAccessJAndroid::_dir_close=NULL; + + +DirAccess *DirAccessJAndroid::create_fs() { + + return memnew(DirAccessJAndroid); +} + +bool DirAccessJAndroid::list_dir_begin() { + + list_dir_end(); + + jstring js = env->NewStringUTF(current_dir.utf8().get_data()); + int res = env->CallIntMethod(io,_dir_open,js); + if (res<=0) + return true; + + id=res; + + return false; +} + +String DirAccessJAndroid::get_next(){ + + ERR_FAIL_COND_V(id==0,""); + + jstring str= (jstring)env->CallObjectMethod(io,_dir_next,id); + if (!str) + return ""; + + int sl = env->GetStringLength(str); + if (sl==0) { + env->DeleteLocalRef((jobject)str); + return ""; + } + + CharString cs; + cs.resize(sl+1); + env->GetStringRegion(str,0,sl,(jchar*)&cs[0]); + cs[sl]=0; + + String ret; + ret.parse_utf8(&cs[0]); + env->DeleteLocalRef((jobject)str); + + return ret; + +} +bool DirAccessJAndroid::current_is_dir() const{ + + String sd; + if (current_dir=="") + sd=current; + else + sd=current_dir+"/"+current; + + jstring js = env->NewStringUTF(sd.utf8().get_data()); + + int res = env->CallIntMethod(io,_dir_open,js); + if (res<=0) + return false; + + env->CallObjectMethod(io,_dir_close,res); + + + return true; +} +void DirAccessJAndroid::list_dir_end(){ + + if (id==0) + return; + + env->CallObjectMethod(io,_dir_close,id); + id=0; + + +} + +int DirAccessJAndroid::get_drive_count(){ + + return 0; +} +String DirAccessJAndroid::get_drive(int p_drive){ + + return ""; +} + +Error DirAccessJAndroid::change_dir(String p_dir){ + + p_dir=p_dir.simplify_path(); + + if (p_dir=="" || p_dir=="." || (p_dir==".." && current_dir=="")) + return OK; + + String new_dir; + + if (p_dir.begins_with("/")) + new_dir=p_dir.substr(1,p_dir.length()); + else if (p_dir.begins_with("res://")) + new_dir=p_dir.substr(6,p_dir.length()); + else //relative + new_dir=new_dir+"/"+p_dir; + +//test if newdir exists + new_dir=new_dir.simplify_path(); + + jstring js = env->NewStringUTF(new_dir.utf8().get_data()); + int res = env->CallIntMethod(io,_dir_open,js); + if (res<=0) + return ERR_INVALID_PARAMETER; + + env->CallObjectMethod(io,_dir_close,res); + + + + return OK; +} + +String DirAccessJAndroid::get_current_dir(){ + + return "/"+current_dir; +} +String DirAccessJAndroid::get_dir_separator() const{ + + return "/"; +} + +bool DirAccessJAndroid::file_exists(String p_file){ + + String sd; + if (current_dir=="") + sd=p_file; + else + sd=current_dir+"/"+p_file; + + FileAccessJAndroid *f = memnew(FileAccessJAndroid); + bool exists = f->file_exists(sd); + memdelete(f); + + return exists; +} + +uint64_t DirAccessJAndroid::get_modified_time(String p_file){ + + return 0; +} + +Error DirAccessJAndroid::make_dir(String p_dir){ + + ERR_FAIL_V(ERR_UNAVAILABLE); +} + +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); +} + +//FileType get_file_type() const; +size_t DirAccessJAndroid::get_space_left() { + + return 0; +} + +void DirAccessJAndroid::make_default() { + + instance_func=create_fs; +} + +void DirAccessJAndroid::setup( JNIEnv *p_env, jobject p_io) { + + + env=p_env; + io=p_io; + __android_log_print(ANDROID_LOG_INFO,"godot","STEP7"); + + jclass c = env->GetObjectClass(io); + cls = (jclass)env->NewGlobalRef(c); + __android_log_print(ANDROID_LOG_INFO,"godot","STEP8"); + + _dir_open = env->GetMethodID(cls, "dir_open", "(Ljava/lang/String;)I"); + if(_dir_open != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_open ok!!"); + } + _dir_next = env->GetMethodID(cls, "dir_next", "(I)Ljava/lang/String;"); + if(_dir_next != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_next ok!!"); + } + _dir_close = env->GetMethodID(cls, "dir_close", "(I)V"); + if(_dir_close != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_close ok!!"); + } + +// (*env)->CallVoidMethod(env,obj,aMethodID, myvar); +} + + +DirAccessJAndroid::DirAccessJAndroid() { + + id=0; +} + +DirAccessJAndroid::~DirAccessJAndroid() { + + list_dir_end();; +} diff --git a/platform/android/.old/dir_access_jandroid.h b/platform/android/.old/dir_access_jandroid.h new file mode 100644 index 0000000000..80973004aa --- /dev/null +++ b/platform/android/.old/dir_access_jandroid.h @@ -0,0 +1,63 @@ +#ifndef DIR_ACCESS_JANDROID_H +#define DIR_ACCESS_JANDROID_H + +#include "java_glue.h" +#include "os/dir_access.h" +#include <stdio.h> + + +class DirAccessJAndroid : public DirAccess { + + //AAssetDir* aad; + + static jobject io; + static jclass cls; + + static jmethodID _dir_open; + static jmethodID _dir_next; + static jmethodID _dir_close; + + static JNIEnv * env; + + int id; + + String current_dir; + String current; + + static DirAccess *create_fs(); + +public: + + virtual bool list_dir_begin(); ///< This starts dir listing + virtual String get_next(); + virtual bool current_is_dir() const; + virtual void list_dir_end(); ///< + + virtual int get_drive_count(); + virtual String get_drive(int p_drive); + + virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success + virtual String get_current_dir(); ///< return current dir location + virtual String get_dir_separator() const ; + + virtual bool file_exists(String p_file); + virtual uint64_t get_modified_time(String p_file); + + virtual Error make_dir(String p_dir); + + virtual Error rename(String p_from, String p_to); + virtual Error remove(String p_name); + + //virtual FileType get_file_type() const; + size_t get_space_left(); + + static void make_default(); + + static void setup( JNIEnv *env, jobject io); + static void update_env( JNIEnv *p_env) { env=p_env; } + + DirAccessJAndroid(); + ~DirAccessJAndroid(); +}; + +#endif // DIR_ACCESS_JANDROID_H diff --git a/platform/android/.old/file_access_jandroid.cpp b/platform/android/.old/file_access_jandroid.cpp new file mode 100644 index 0000000000..96c064f9f5 --- /dev/null +++ b/platform/android/.old/file_access_jandroid.cpp @@ -0,0 +1,204 @@ +#include "file_access_jandroid.h" +#include "os/os.h" +#include <unistd.h> + +jobject FileAccessJAndroid::io=NULL; +jclass FileAccessJAndroid::cls; +jmethodID FileAccessJAndroid::_file_open=0; +jmethodID FileAccessJAndroid::_file_get_size=0; +jmethodID FileAccessJAndroid::_file_seek=0; +jmethodID FileAccessJAndroid::_file_read=0; +jmethodID FileAccessJAndroid::_file_tell=0; +jmethodID FileAccessJAndroid::_file_eof=0; +jmethodID FileAccessJAndroid::_file_close=0; + + +JNIEnv * FileAccessJAndroid::env=NULL; + +void FileAccessJAndroid::make_default() { + + create_func=create_jandroid; +} + +FileAccess* FileAccessJAndroid::create_jandroid() { + + return memnew(FileAccessJAndroid); +} + +Error FileAccessJAndroid::open(const String& p_path, int p_mode_flags) { + + if (is_open()) + close(); + + String path=fix_path(p_path).simplify_path(); + if (path.begins_with("/")) + path=path.substr(1,path.length()); + else if (path.begins_with("res://")) + path=path.substr(6,path.length()); + + //OS::get_singleton()->print("env: %p, io %p, fo: %p\n",env,io,_file_open); + + jstring js = env->NewStringUTF(path.utf8().get_data()); + int res = env->CallIntMethod(io,_file_open,js,p_mode_flags&WRITE?true:false); + + if (res<=0) + return ERR_FILE_CANT_OPEN; + id=res; + + + return OK; +} + +void FileAccessJAndroid::close() { + + if (io==0) + return; + + env->CallVoidMethod(io,_file_close,id); + id=0; + +} +bool FileAccessJAndroid::is_open() const { + + return id!=0; +} + +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()); + + seek(get_len()); + +} +size_t FileAccessJAndroid::get_pos() const { + + ERR_FAIL_COND_V(!is_open(),0); + return env->CallIntMethod(io,_file_tell,id); + +} +size_t FileAccessJAndroid::get_len() const { + + ERR_FAIL_COND_V(!is_open(),0); + return env->CallIntMethod(io,_file_get_size,id); + + +} + +bool FileAccessJAndroid::eof_reached() const { + + ERR_FAIL_COND_V(!is_open(),0); + return env->CallIntMethod(io,_file_eof,id); + +} + +uint8_t FileAccessJAndroid::get_8() const { + + ERR_FAIL_COND_V(!is_open(),0); + uint8_t byte; + get_buffer(&byte,1); + return byte; +} +int FileAccessJAndroid::get_buffer(uint8_t *p_dst, int p_length) const { + + ERR_FAIL_COND_V(!is_open(),0); + if (p_length==0) + return 0; + + jbyteArray jca = (jbyteArray)env->CallObjectMethod(io,_file_read,id,p_length); + + int len = env->GetArrayLength(jca); + env->GetByteArrayRegion(jca,0,len,(jbyte*)p_dst); + env->DeleteLocalRef((jobject)jca); + + return len; + +} + +Error FileAccessJAndroid::get_error() const { + + if (eof_reached()) + return ERR_FILE_EOF; + return OK; +} + +void FileAccessJAndroid::store_8(uint8_t p_dest) { + + +} + +bool FileAccessJAndroid::file_exists(const String& p_path) { + + jstring js = env->NewStringUTF(p_path.utf8().get_data()); + int res = env->CallIntMethod(io,_file_open,js,false); + if (res<=0) + return false; + env->CallVoidMethod(io,_file_close,res); + return true; + +} + + +void FileAccessJAndroid::setup( JNIEnv *p_env, jobject p_io) { + + io=p_io; + env=p_env; + + __android_log_print(ANDROID_LOG_INFO,"godot","STEP5"); + + jclass c = env->GetObjectClass(io); + __android_log_print(ANDROID_LOG_INFO,"godot","STEP6"); + cls=(jclass)env->NewGlobalRef(c); + + _file_open = env->GetMethodID(cls, "file_open", "(Ljava/lang/String;Z)I"); + if(_file_open != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_open ok!!"); + } + _file_get_size = env->GetMethodID(cls, "file_get_size", "(I)I"); + if(_file_get_size != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_get_size ok!!"); + } + _file_tell = env->GetMethodID(cls, "file_tell", "(I)I"); + if(_file_tell != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_tell ok!!"); + } + _file_eof = env->GetMethodID(cls, "file_eof", "(I)Z"); + + if(_file_eof != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_eof ok!!"); + } + _file_seek = env->GetMethodID(cls, "file_seek", "(II)V"); + if(_file_seek != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_seek ok!!"); + } + _file_read = env->GetMethodID(cls, "file_read", "(II)[B"); + if(_file_read != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_read ok!!"); + } + _file_close = env->GetMethodID(cls, "file_close", "(I)V"); + if(_file_close != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_close ok!!"); + } + +// (*env)->CallVoidMethod(env,obj,aMethodID, myvar); +} + + +FileAccessJAndroid::FileAccessJAndroid() +{ + + id=0; +} + +FileAccessJAndroid::~FileAccessJAndroid() +{ + + if (is_open()) + close(); +} + + diff --git a/platform/android/.old/file_access_jandroid.h b/platform/android/.old/file_access_jandroid.h new file mode 100644 index 0000000000..7b432fc4fd --- /dev/null +++ b/platform/android/.old/file_access_jandroid.h @@ -0,0 +1,56 @@ +#ifndef FILE_ACCESS_JANDROID_H +#define FILE_ACCESS_JANDROID_H + +#include "java_glue.h" +#include "os/file_access.h" +class FileAccessJAndroid : public FileAccess { + + static jobject io; + static jclass cls; + + static jmethodID _file_open; + static jmethodID _file_get_size; + static jmethodID _file_seek; + static jmethodID _file_tell; + static jmethodID _file_eof; + static jmethodID _file_read; + static jmethodID _file_close; + + + static JNIEnv * env; + + int id; + static FileAccess* create_jandroid(); + + +public: + + virtual Error open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + virtual int get_buffer(uint8_t *p_dst, int p_length) const; + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + + virtual bool file_exists(const String& p_path); ///< return true if a file exists + + static void make_default(); + + static void setup( JNIEnv *env, jobject io); + static void update_env( JNIEnv *p_env) { env=p_env; } + FileAccessJAndroid(); + ~FileAccessJAndroid(); +}; + +#endif // FILE_ACCESS_JANDROID_H diff --git a/platform/android/.old/gdb.setup.base b/platform/android/.old/gdb.setup.base new file mode 100644 index 0000000000..a622b1caf6 --- /dev/null +++ b/platform/android/.old/gdb.setup.base @@ -0,0 +1,3 @@ +set solib-search-path . +directory ../.. +file app_process diff --git a/platform/android/.old/java/AndroidManifest.xml b/platform/android/.old/java/AndroidManifest.xml new file mode 100644 index 0000000000..c05a681fe3 --- /dev/null +++ b/platform/android/.old/java/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.godot" + android:versionCode="1" + android:versionName="1.0"> + <application android:label="@string/godot" android:icon="@drawable/icon"> + <activity android:name="Godot" + android:label="@string/godot" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:launchMode="singleTask" + android:configChanges="orientation|keyboardHidden"> + + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + <uses-feature android:glEsVersion="0x00020000"/> + <uses-sdk android:minSdkVersion="8"/> + +</manifest> diff --git a/platform/android/.old/java/build.properties b/platform/android/.old/java/build.properties new file mode 100644 index 0000000000..ee52d86d94 --- /dev/null +++ b/platform/android/.old/java/build.properties @@ -0,0 +1,17 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked in Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + diff --git a/platform/android/.old/java/build.xml b/platform/android/.old/java/build.xml new file mode 100644 index 0000000000..6dc5eb874c --- /dev/null +++ b/platform/android/.old/java/build.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="Godot" default="help"> + +<!-- The local.properties file is created and updated by the 'android' + tool. + It contains the path to the SDK. It should *NOT* be checked into + Version Control Systems. --> + <property file="local.properties" /> + <property name="asset-dir" value="assets" /> + <!-- The build.properties file can be created by you and is never touched + by the 'android' tool. This is the place to change some of the + default property values used by the Ant rules. + Here are some properties you may want to change/update: + + source.dir + The name of the source directory. Default is 'src'. + out.dir + The name of the output directory. Default is 'bin'. + + Properties related to the SDK location or the project target should + be updated using the 'android' tool with the 'update' action. + + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. + + --> + <property file="build.properties" /> + + <!-- The default.properties file is created and updated by the 'android' + tool, as well as ADT. + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. --> + <property file="default.properties" /> + + + <!-- Required pre-setup import --> + <import file="${sdk.dir}/tools/ant/pre_setup.xml" /> + + +<!-- extension targets. Uncomment the ones where you want to do custom work + in between standard targets --> +<!-- + <target name="-pre-build"> + </target> + <target name="-pre-compile"> + </target> + + [This is typically used for code obfuscation. + Compiled code location: ${out.classes.absolute.dir} + If this is not done in place, override ${out.dex.input.absolute.dir}] + <target name="-post-compile"> + </target> +--> + + <!-- Execute the Android Setup task that will setup some properties + specific to the target, and import the build rules files. + + The rules file is imported from + <SDK>/tools/ant/ + Depending on the project type it can be either: + - main_rules.xml + - lib_rules.xml + - test_rules.xml + + To customize existing targets, there are two options: + - Customize only one target: + - copy/paste the target into this file, *before* the + <setup> task. + - customize it to your needs. + - Customize the whole script. + - copy/paste the content of the rules files (minus the top node) + into this file, *after* the <setup> task + - disable the import of the rules by changing the setup task + below to <setup import="false" />. + - customize to your needs. + --> + <setup /> + +</project> diff --git a/platform/android/.old/java/default.properties b/platform/android/.old/java/default.properties new file mode 100644 index 0000000000..e2e8061f26 --- /dev/null +++ b/platform/android/.old/java/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-8 diff --git a/platform/android/.old/java/libs/armeabi/gdbserver b/platform/android/.old/java/libs/armeabi/gdbserver Binary files differnew file mode 100755 index 0000000000..39634a8147 --- /dev/null +++ b/platform/android/.old/java/libs/armeabi/gdbserver diff --git a/platform/android/.old/java/local.properties b/platform/android/.old/java/local.properties new file mode 100644 index 0000000000..18e2b6b0cb --- /dev/null +++ b/platform/android/.old/java/local.properties @@ -0,0 +1,10 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must *NOT* be checked in Version Control Systems, +# as it contains information specific to your local configuration. + +# location of the SDK. This is only used by Ant +# For customization when using a Version Control System, please read the +# header note. +sdk.dir=/home/red/bin/android-sdk-linux_x86 diff --git a/platform/android/.old/java/proguard.cfg b/platform/android/.old/java/proguard.cfg new file mode 100644 index 0000000000..12dd0392c0 --- /dev/null +++ b/platform/android/.old/java/proguard.cfg @@ -0,0 +1,36 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native <methods>; +} + +-keepclasseswithmembernames class * { + public <init>(android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public <init>(android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/platform/android/.old/java/res/drawable-hdpi/icon.png b/platform/android/.old/java/res/drawable-hdpi/icon.png Binary files differnew file mode 100644 index 0000000000..8074c4c571 --- /dev/null +++ b/platform/android/.old/java/res/drawable-hdpi/icon.png diff --git a/platform/android/.old/java/res/drawable-ldpi/icon.png b/platform/android/.old/java/res/drawable-ldpi/icon.png Binary files differnew file mode 100644 index 0000000000..1095584ec2 --- /dev/null +++ b/platform/android/.old/java/res/drawable-ldpi/icon.png diff --git a/platform/android/.old/java/res/drawable-mdpi/icon.png b/platform/android/.old/java/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 0000000000..a07c69fa5a --- /dev/null +++ b/platform/android/.old/java/res/drawable-mdpi/icon.png diff --git a/platform/android/.old/java/res/values/strings.xml b/platform/android/.old/java/res/values/strings.xml new file mode 100644 index 0000000000..f92332ad4c --- /dev/null +++ b/platform/android/.old/java/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot">Godot</string> +</resources> diff --git a/platform/android/.old/java/src/com/android/godot/Godot.java b/platform/android/.old/java/src/com/android/godot/Godot.java new file mode 100644 index 0000000000..475caf3362 --- /dev/null +++ b/platform/android/.old/java/src/com/android/godot/Godot.java @@ -0,0 +1,85 @@ +package com.android.godot; + +import android.app.Activity; +import android.os.Bundle; +import android.view.MotionEvent; + +public class Godot extends Activity +{ + + + GodotView mView; + + static public GodotIO io; + + + @Override protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + io = new GodotIO(getAssets()); + GodotLib.io=io; + mView = new GodotView(getApplication(),io); + setContentView(mView); + } + + @Override protected void onPause() { + super.onPause(); + mView.onPause(); + } + + @Override protected void onResume() { + super.onResume(); + mView.onResume(); + } + + @Override public boolean dispatchTouchEvent (MotionEvent event) { + + super.onTouchEvent(event); + int evcount=event.getPointerCount(); + if (evcount==0) + return true; + + int[] arr = new int[event.getPointerCount()*3]; + + for(int i=0;i<event.getPointerCount();i++) { + + arr[i*3+0]=(int)event.getPointerId(i); + arr[i*3+1]=(int)event.getX(i); + arr[i*3+2]=(int)event.getY(i); + } + + //System.out.printf("gaction: %d\n",event.getAction()); + switch(event.getAction()&MotionEvent.ACTION_MASK) { + + case MotionEvent.ACTION_DOWN: { + GodotLib.touch(0,0,evcount,arr); + //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY()); + } break; + case MotionEvent.ACTION_MOVE: { + GodotLib.touch(1,0,evcount,arr); + //for(int i=0;i<event.getPointerCount();i++) { + // System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i)); + //} + } break; + case MotionEvent.ACTION_POINTER_UP: { + int pointer_idx = event.getActionIndex(); + GodotLib.touch(4,pointer_idx,evcount,arr); + //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case MotionEvent.ACTION_POINTER_DOWN: { + int pointer_idx = event.getActionIndex(); + GodotLib.touch(3,pointer_idx,evcount,arr); + //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { + GodotLib.touch(2,0,evcount,arr); + //for(int i=0;i<event.getPointerCount();i++) { + // System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i)); + //} + } break; + + } + return true; + } + +} diff --git a/platform/android/.old/java/src/com/android/godot/GodotIO.java b/platform/android/.old/java/src/com/android/godot/GodotIO.java new file mode 100644 index 0000000000..a673f79b8d --- /dev/null +++ b/platform/android/.old/java/src/com/android/godot/GodotIO.java @@ -0,0 +1,277 @@ + +package com.android.godot; +import java.util.HashMap; + +import android.content.res.AssetManager; +import java.io.InputStream; +import java.io.IOException; + +// Wrapper for native library + +public class GodotIO { + + + AssetManager am; + + + /// FILES + + public int last_file_id=1; + + class AssetData { + + + public boolean eof=false; + public String path; + public InputStream is; + public int len; + public int pos; + } + + + HashMap<Integer,AssetData> streams; + + + public int file_open(String path,boolean write) { + + System.out.printf("file_open: Attempt to Open %s\n",path); + + if (write) + return -1; + + + AssetData ad = new AssetData(); + + try { + ad.is = am.open(path); + + } catch (Exception e) { + + System.out.printf("Exception on file_open: %s\n",e); + return -1; + } + + try { + ad.len=ad.is.available(); + } catch (Exception e) { + + System.out.printf("Exception availabling on file_open: %s\n",e); + return -1; + } + + ad.path=path; + ad.pos=0; + ++last_file_id; + streams.put(last_file_id,ad); + + return last_file_id; + } + public int file_get_size(int id) { + + if (!streams.containsKey(id)) { + System.out.printf("file_get_size: Invalid file id: %d\n",id); + return -1; + } + + return streams.get(id).len; + + } + public void file_seek(int id,int bytes) { + + if (!streams.containsKey(id)) { + System.out.printf("file_get_size: Invalid file id: %d\n",id); + return; + } + //seek sucks + AssetData ad = streams.get(id); + if (bytes>ad.len) + bytes=ad.len; + if (bytes<0) + bytes=0; + + try { + + if (bytes > (int)ad.pos) { + int todo=bytes-(int)ad.pos; + while(todo>0) { + todo-=ad.is.skip(todo); + } + ad.pos=bytes; + } else if (bytes<(int)ad.pos) { + + ad.is=am.open(ad.path); + + ad.pos=bytes; + int todo=bytes; + while(todo>0) { + todo-=ad.is.skip(todo); + } + } + + ad.eof=false; + } catch (IOException e) { + + System.out.printf("Exception on file_seek: %s\n",e); + return; + } + + + } + + public int file_tell(int id) { + + if (!streams.containsKey(id)) { + System.out.printf("file_read: Can't tell eof for invalid file id: %d\n",id); + return 0; + } + + AssetData ad = streams.get(id); + return ad.pos; + } + public boolean file_eof(int id) { + + if (!streams.containsKey(id)) { + System.out.printf("file_read: Can't check eof for invalid file id: %d\n",id); + return false; + } + + AssetData ad = streams.get(id); + return ad.eof; + } + + public byte[] file_read(int id, int bytes) { + + if (!streams.containsKey(id)) { + System.out.printf("file_read: Can't read invalid file id: %d\n",id); + return new byte[0]; + } + + + AssetData ad = streams.get(id); + + if (ad.pos + bytes > ad.len) { + + bytes=ad.len-ad.pos; + ad.eof=true; + } + + + if (bytes==0) { + + return new byte[0]; + } + + + + byte[] buf1=new byte[bytes]; + int r=0; + try { + r = ad.is.read(buf1); + } catch (IOException e) { + + System.out.printf("Exception on file_read: %s\n",e); + return new byte[bytes]; + } + + if (r==0) { + return new byte[0]; + } + + ad.pos+=r; + + if (r<bytes) { + + byte[] buf2=new byte[r]; + for(int i=0;i<r;i++) + buf2[i]=buf1[i]; + return buf2; + } else { + + return buf1; + } + + } + + public void file_close(int id) { + + if (!streams.containsKey(id)) { + System.out.printf("file_close: Can't close invalid file id: %d\n",id); + return; + } + + streams.remove(id); + + } + + + /// DIRECTORIES + + + class AssetDir { + + public String[] files; + public int current; + } + + public int last_dir_id=1; + + HashMap<Integer,AssetDir> dirs; + + public int dir_open(String path) { + + AssetDir ad = new AssetDir(); + ad.current=0; + + try { + ad.files = am.list(path); + } catch (IOException e) { + + System.out.printf("Exception on dir_open: %s\n",e); + return -1; + } + + ++last_dir_id; + dirs.put(last_dir_id,ad); + + return last_dir_id; + + } + + public String dir_next(int id) { + + if (!dirs.containsKey(id)) { + System.out.printf("dir_next: invalid dir id: %d\n",id); + return ""; + } + + AssetDir ad = dirs.get(id); + if (ad.current>=ad.files.length) + return ""; + String r = ad.files[ad.current]; + ad.current++; + return r; + + } + + public void dir_close(int id) { + + if (!dirs.containsKey(id)) { + System.out.printf("dir_close: invalid dir id: %d\n",id); + return; + } + + dirs.remove(id); + } + + + GodotIO(AssetManager p_am) { + + am=p_am; + streams=new HashMap<Integer,AssetData>(); + dirs=new HashMap<Integer,AssetDir>(); + + + } + + +} diff --git a/platform/android/.old/java/src/com/android/godot/GodotLib.java b/platform/android/.old/java/src/com/android/godot/GodotLib.java new file mode 100644 index 0000000000..2ef49a5222 --- /dev/null +++ b/platform/android/.old/java/src/com/android/godot/GodotLib.java @@ -0,0 +1,22 @@ +package com.android.godot; + +// Wrapper for native library + +public class GodotLib { + + + public static GodotIO io; + + static { + System.loadLibrary("godot_android"); + } + + /** + * @param width the current view width + * @param height the current view height + */ + + public static native void init(int width, int height); + public static native void step(); + public static native void touch(int what,int pointer,int howmany, int[] arr); +} diff --git a/platform/android/.old/java/src/com/android/godot/GodotView.java b/platform/android/.old/java/src/com/android/godot/GodotView.java new file mode 100644 index 0000000000..c5eb3d17ad --- /dev/null +++ b/platform/android/.old/java/src/com/android/godot/GodotView.java @@ -0,0 +1,319 @@ +package com.android.godot; +import android.content.Context; +import android.graphics.PixelFormat; +import android.opengl.GLSurfaceView; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.content.ContextWrapper; + +import java.io.File; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.opengles.GL10; + +/** + * A simple GLSurfaceView sub-class that demonstrate how to perform + * OpenGL ES 2.0 rendering into a GL Surface. Note the following important + * details: + * + * - The class must use a custom context factory to enable 2.0 rendering. + * See ContextFactory class definition below. + * + * - The class must use a custom EGLConfigChooser to be able to select + * an EGLConfig that supports 2.0. This is done by providing a config + * specification to eglChooseConfig() that has the attribute + * EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT flag + * set. See ConfigChooser class definition below. + * + * - The class must select the surface's format, then choose an EGLConfig + * that matches it exactly (with regards to red/green/blue/alpha channels + * bit depths). Failure to do so would result in an EGL_BAD_MATCH error. + */ +class GodotView extends GLSurfaceView { + private static String TAG = "GodotView"; + private static final boolean DEBUG = false; + private static Context ctx; + + private static GodotIO io; + + public GodotView(Context context,GodotIO p_io) { + super(context); + ctx=context; + io=p_io; + + init(false, 0, 0); + } + + public GodotView(Context context, boolean translucent, int depth, int stencil) { + super(context); + init(translucent, depth, stencil); + } + + private void init(boolean translucent, int depth, int stencil) { + + /* By default, GLSurfaceView() creates a RGB_565 opaque surface. + * If we want a translucent one, we should change the surface's + * format here, using PixelFormat.TRANSLUCENT for GL Surfaces + * is interpreted as any 32-bit surface with alpha by SurfaceFlinger. + */ + if (translucent) { + this.getHolder().setFormat(PixelFormat.TRANSLUCENT); + } + + /* Setup the context factory for 2.0 rendering. + * See ContextFactory class definition below + */ + setEGLContextFactory(new ContextFactory()); + + /* We need to choose an EGLConfig that matches the format of + * our surface exactly. This is going to be done in our + * custom config chooser. See ConfigChooser class definition + * below. + */ + setEGLConfigChooser( translucent ? + new ConfigChooser(8, 8, 8, 8, depth, stencil) : + new ConfigChooser(5, 6, 5, 0, depth, stencil) ); + + /* Set the renderer responsible for frame rendering */ + setRenderer(new Renderer()); + } + + private static class ContextFactory implements GLSurfaceView.EGLContextFactory { + private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { + Log.w(TAG, "creating OpenGL ES 2.0 context"); + checkEglError("Before eglCreateContext", egl); + int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); + checkEglError("After eglCreateContext", egl); + return context; + } + + public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { + egl.eglDestroyContext(display, context); + } + } + + private static void checkEglError(String prompt, EGL10 egl) { + int error; + while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { + Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); + } + } + + private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser { + + public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) { + mRedSize = r; + mGreenSize = g; + mBlueSize = b; + mAlphaSize = a; + mDepthSize = depth; + mStencilSize = stencil; + } + + /* This EGL config specification is used to specify 2.0 rendering. + * We use a minimum size of 4 bits for red/green/blue, but will + * perform actual matching in chooseConfig() below. + */ + private static int EGL_OPENGL_ES2_BIT = 4; + private static int[] s_configAttribs2 = + { + EGL10.EGL_RED_SIZE, 4, + EGL10.EGL_GREEN_SIZE, 4, + EGL10.EGL_BLUE_SIZE, 4, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_NONE + }; + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + + /* Get the number of minimally matching EGL configurations + */ + int[] num_config = new int[1]; + egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config); + + int numConfigs = num_config[0]; + + if (numConfigs <= 0) { + throw new IllegalArgumentException("No configs match configSpec"); + } + + /* Allocate then read the array of minimally matching EGL configs + */ + EGLConfig[] configs = new EGLConfig[numConfigs]; + egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config); + + if (DEBUG) { + printConfigs(egl, display, configs); + } + /* Now return the "best" one + */ + return chooseConfig(egl, display, configs); + } + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + for(EGLConfig config : configs) { + int d = findConfigAttrib(egl, display, config, + EGL10.EGL_DEPTH_SIZE, 0); + int s = findConfigAttrib(egl, display, config, + EGL10.EGL_STENCIL_SIZE, 0); + + // We need at least mDepthSize and mStencilSize bits + if (d < mDepthSize || s < mStencilSize) + continue; + + // We want an *exact* match for red/green/blue/alpha + int r = findConfigAttrib(egl, display, config, + EGL10.EGL_RED_SIZE, 0); + int g = findConfigAttrib(egl, display, config, + EGL10.EGL_GREEN_SIZE, 0); + int b = findConfigAttrib(egl, display, config, + EGL10.EGL_BLUE_SIZE, 0); + int a = findConfigAttrib(egl, display, config, + EGL10.EGL_ALPHA_SIZE, 0); + + if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize) + return config; + } + return null; + } + + private int findConfigAttrib(EGL10 egl, EGLDisplay display, + EGLConfig config, int attribute, int defaultValue) { + + if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { + return mValue[0]; + } + return defaultValue; + } + + private void printConfigs(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + int numConfigs = configs.length; + Log.w(TAG, String.format("%d configurations", numConfigs)); + for (int i = 0; i < numConfigs; i++) { + Log.w(TAG, String.format("Configuration %d:\n", i)); + printConfig(egl, display, configs[i]); + } + } + + private void printConfig(EGL10 egl, EGLDisplay display, + EGLConfig config) { + int[] attributes = { + EGL10.EGL_BUFFER_SIZE, + EGL10.EGL_ALPHA_SIZE, + EGL10.EGL_BLUE_SIZE, + EGL10.EGL_GREEN_SIZE, + EGL10.EGL_RED_SIZE, + EGL10.EGL_DEPTH_SIZE, + EGL10.EGL_STENCIL_SIZE, + EGL10.EGL_CONFIG_CAVEAT, + EGL10.EGL_CONFIG_ID, + EGL10.EGL_LEVEL, + EGL10.EGL_MAX_PBUFFER_HEIGHT, + EGL10.EGL_MAX_PBUFFER_PIXELS, + EGL10.EGL_MAX_PBUFFER_WIDTH, + EGL10.EGL_NATIVE_RENDERABLE, + EGL10.EGL_NATIVE_VISUAL_ID, + EGL10.EGL_NATIVE_VISUAL_TYPE, + 0x3030, // EGL10.EGL_PRESERVED_RESOURCES, + EGL10.EGL_SAMPLES, + EGL10.EGL_SAMPLE_BUFFERS, + EGL10.EGL_SURFACE_TYPE, + EGL10.EGL_TRANSPARENT_TYPE, + EGL10.EGL_TRANSPARENT_RED_VALUE, + EGL10.EGL_TRANSPARENT_GREEN_VALUE, + EGL10.EGL_TRANSPARENT_BLUE_VALUE, + 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB, + 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA, + 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL, + 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL, + EGL10.EGL_LUMINANCE_SIZE, + EGL10.EGL_ALPHA_MASK_SIZE, + EGL10.EGL_COLOR_BUFFER_TYPE, + EGL10.EGL_RENDERABLE_TYPE, + 0x3042 // EGL10.EGL_CONFORMANT + }; + String[] names = { + "EGL_BUFFER_SIZE", + "EGL_ALPHA_SIZE", + "EGL_BLUE_SIZE", + "EGL_GREEN_SIZE", + "EGL_RED_SIZE", + "EGL_DEPTH_SIZE", + "EGL_STENCIL_SIZE", + "EGL_CONFIG_CAVEAT", + "EGL_CONFIG_ID", + "EGL_LEVEL", + "EGL_MAX_PBUFFER_HEIGHT", + "EGL_MAX_PBUFFER_PIXELS", + "EGL_MAX_PBUFFER_WIDTH", + "EGL_NATIVE_RENDERABLE", + "EGL_NATIVE_VISUAL_ID", + "EGL_NATIVE_VISUAL_TYPE", + "EGL_PRESERVED_RESOURCES", + "EGL_SAMPLES", + "EGL_SAMPLE_BUFFERS", + "EGL_SURFACE_TYPE", + "EGL_TRANSPARENT_TYPE", + "EGL_TRANSPARENT_RED_VALUE", + "EGL_TRANSPARENT_GREEN_VALUE", + "EGL_TRANSPARENT_BLUE_VALUE", + "EGL_BIND_TO_TEXTURE_RGB", + "EGL_BIND_TO_TEXTURE_RGBA", + "EGL_MIN_SWAP_INTERVAL", + "EGL_MAX_SWAP_INTERVAL", + "EGL_LUMINANCE_SIZE", + "EGL_ALPHA_MASK_SIZE", + "EGL_COLOR_BUFFER_TYPE", + "EGL_RENDERABLE_TYPE", + "EGL_CONFORMANT" + }; + int[] value = new int[1]; + for (int i = 0; i < attributes.length; i++) { + int attribute = attributes[i]; + String name = names[i]; + if ( egl.eglGetConfigAttrib(display, config, attribute, value)) { + Log.w(TAG, String.format(" %s: %d\n", name, value[0])); + } else { + // Log.w(TAG, String.format(" %s: failed\n", name)); + while (egl.eglGetError() != EGL10.EGL_SUCCESS); + } + } + } + + // Subclasses can adjust these values: + protected int mRedSize; + protected int mGreenSize; + protected int mBlueSize; + protected int mAlphaSize; + protected int mDepthSize; + protected int mStencilSize; + private int[] mValue = new int[1]; + } + + private static class Renderer implements GLSurfaceView.Renderer { + + + public void onDrawFrame(GL10 gl) { + GodotLib.step(); + } + + public void onSurfaceChanged(GL10 gl, int width, int height) { + + + System.out.printf("** CONTENT DIR %s\n",ctx.getFilesDir().getPath()); + GodotLib.init(width, height); + } + + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + // Do nothing. + } + } +} diff --git a/platform/android/.old/java_glue.cpp b/platform/android/.old/java_glue.cpp new file mode 100644 index 0000000000..38dbcc104f --- /dev/null +++ b/platform/android/.old/java_glue.cpp @@ -0,0 +1,181 @@ +#include "java_glue.h" +#include "os_android.h" +#include "main/main.h" +#include <unistd.h> +#include "file_access_jandroid.h" +#include "dir_access_jandroid.h" + +static OS_Android *os_android=NULL; + + +struct TST { + + int a; + TST() { + + a=5; + } +}; + +TST tst; + +struct JAndroidPointerEvent { + + Vector<OS_Android::TouchPos> points; + int pointer; + int what; +}; + +static List<JAndroidPointerEvent> pointer_events; +static bool initialized=false; +static Mutex *input_mutex=NULL; + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_init(JNIEnv * env, jobject obj, jint width, jint height) +{ + + + if (initialized) // wtf + return; + + __android_log_print(ANDROID_LOG_INFO,"godot","**INIT EVENT! - %p\n",env); + + + initialized=true; + + __android_log_print(ANDROID_LOG_INFO,"godot","***************** HELLO FROM JNI!!!!!!!!"); + + { + //setup IO Object + + jclass cls = env->FindClass("com/android/godot/Godot"); + if (cls) { + + cls=(jclass)env->NewGlobalRef(cls); + __android_log_print(ANDROID_LOG_INFO,"godot","*******CLASS FOUND!!!"); + } + __android_log_print(ANDROID_LOG_INFO,"godot","STEP2, %p",cls); + jfieldID fid = env->GetStaticFieldID(cls, "io", "Lcom/android/godot/GodotIO;"); + __android_log_print(ANDROID_LOG_INFO,"godot","STEP3 %i",fid); + jobject ob = env->GetStaticObjectField(cls,fid); + __android_log_print(ANDROID_LOG_INFO,"godot","STEP4, %p",ob); + jobject gob = env->NewGlobalRef(ob); + + + FileAccessJAndroid::setup(env,gob); + DirAccessJAndroid::setup(env,gob); + } + + + + os_android = new OS_Android(width,height); + + char wd[500]; + getcwd(wd,500); + + __android_log_print(ANDROID_LOG_INFO,"godot","test construction %i\n",tst.a); + __android_log_print(ANDROID_LOG_INFO,"godot","running from dir %s\n",wd); + + __android_log_print(ANDROID_LOG_INFO,"godot","**SETUP"); + + + +#if 0 + char *args[]={"-test","render",NULL}; + __android_log_print(ANDROID_LOG_INFO,"godot","pre asdasd setup..."); + Error err = Main::setup("apk",2,args); +#else + Error err = Main::setup("apk",0,NULL); +#endif + if (err!=OK) { + __android_log_print(ANDROID_LOG_INFO,"godot","*****UNABLE TO SETUP"); + + return; //should exit instead and print the error + } + + __android_log_print(ANDROID_LOG_INFO,"godot","**START"); + + + if (!Main::start()) { + + return; //should exit instead and print the error + } + input_mutex=Mutex::create(); + + os_android->main_loop_begin(); + + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_step(JNIEnv * env, jobject obj) +{ + + __android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_ID()); + + + + + { + + FileAccessJAndroid::update_env(env); + DirAccessJAndroid::update_env(env); + } + + input_mutex->lock(); + + while(pointer_events.size()) { + + JAndroidPointerEvent jpe=pointer_events.front()->get(); + os_android->process_touch(jpe.what,jpe.pointer,jpe.points); + pointer_events.pop_front(); + } + + input_mutex->unlock(); + + + if (os_android->main_loop_iterate()==true) { + + return; //should exit instead + } + + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_touch(JNIEnv * env, jobject obj, jint ev,jint pointer, jint count, jintArray positions) { + + + + __android_log_print(ANDROID_LOG_INFO,"godot","**TOUCH EVENT! - %p-%i\n",env,Thread::get_caller_ID()); + + + + + + Vector<OS_Android::TouchPos> points; + for(int i=0;i<count;i++) { + + jint p[3]; + env->GetIntArrayRegion(positions,i*3,3,p); + OS_Android::TouchPos tp; + tp.pos=Point2(p[1],p[2]); + tp.id=p[0]; + points.push_back(tp); + } + + JAndroidPointerEvent jpe; + jpe.pointer=pointer; + jpe.points=points; + jpe.what=ev; + + input_mutex->lock(); + + pointer_events.push_back(jpe); + + input_mutex->unlock(); + //if (os_android) +// os_android->process_touch(ev,pointer,points); + +} + +//Main::cleanup(); + +//return os.get_exit_code(); diff --git a/platform/android/.old/java_glue.h b/platform/android/.old/java_glue.h new file mode 100644 index 0000000000..9940af3d7d --- /dev/null +++ b/platform/android/.old/java_glue.h @@ -0,0 +1,16 @@ +#ifndef JAVA_GLUE_H +#define JAVA_GLUE_H + +#include <jni.h> +#include <android/log.h> + + +extern "C" { + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_init(JNIEnv * env, jobject obj, jint width, jint height); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_step(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_touch(JNIEnv * env, jobject obj, jint ev,jint pointer, jint count, jintArray positions); +}; + + + +#endif // JAVA_GLUE_H diff --git a/platform/android/.old/os_android.cpp b/platform/android/.old/os_android.cpp new file mode 100644 index 0000000000..ecd87a3af9 --- /dev/null +++ b/platform/android/.old/os_android.cpp @@ -0,0 +1,426 @@ + +#include "os_android.h" +#include "java_glue.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "servers/visual/visual_server_raster.h" + +#include "file_access_jandroid.h" +#include "dir_access_jandroid.h" +#include "core/io/file_access_buffered_fa.h" +#include "main/main.h" +int OS_Android::get_video_driver_count() const { + + return 1; +} +const char * OS_Android::get_video_driver_name(int p_driver) const { + + return "GLES2"; +} + +OS::VideoMode OS_Android::get_default_video_mode() const { + + return OS::VideoMode(); +} + +int OS_Android::get_audio_driver_count() const { + + return 1; +} +const char * OS_Android::get_audio_driver_name(int p_driver) const { + + return "Android"; +} + +void OS_Android::initialize_core() { + + OS_Unix::initialize_core(); + //FileAccessJAndroid::make_default(); + DirAccessJAndroid::make_default(); + FileAccessBufferedFA<FileAccessJAndroid>::make_default(); + + +} + +void OS_Android::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + AudioDriverManagerSW::add_driver(&audio_driver_android); + + rasterizer = memnew( RasterizerGLES2 ); + visual_server = memnew( VisualServerRaster(rasterizer) ); + visual_server->init(); + visual_server->cursor_set_visible(false, 0); + + AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton(); + + if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) { + + ERR_PRINT("Initializing audio failed."); + } + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + + audio_server->set_mixer_params(AudioMixerSW::INTERPOLATION_LINEAR,false); + audio_server->init(); + + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + + +} + +void OS_Android::set_main_loop( MainLoop * p_main_loop ) { + + + + main_loop=p_main_loop; +} + +void OS_Android::delete_main_loop() { + + memdelete( main_loop ); +} + +void OS_Android::finalize() { + + memdelete(input); + +} + + +void OS_Android::vprint(const char* p_format, va_list p_list, bool p_stderr) { + + __android_log_vprint(p_stderr?ANDROID_LOG_ERROR:ANDROID_LOG_INFO,"godot",p_format,p_list); +} + +void OS_Android::print(const char *p_format, ... ) { + + va_list argp; + va_start(argp, p_format); + __android_log_vprint(ANDROID_LOG_INFO,"godot",p_format,argp); + va_end(argp); + +} + +void OS_Android::alert(const String& p_alert) { + + print("ALERT: %s\n",p_alert.utf8().get_data()); +} + + +void OS_Android::set_mouse_show(bool p_show) { + + //android has no mouse... +} + +void OS_Android::set_mouse_grab(bool p_grab) { + + //it really has no mouse...! +} + +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 +//void set_clipboard(const String& p_text); +//String get_clipboard() const; + +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); +} + +String OS_Android::get_name() { + + return "Android"; +} + +MainLoop *OS_Android::get_main_loop() const { + + return main_loop; +} + +bool OS_Android::can_draw() const { + + return true; //always? +} + +void OS_Android::set_cursor_shape(CursorShape p_shape) { + + //android really really really has no mouse.. how amazing.. +} + +void OS_Android::main_loop_begin() { + + if (main_loop) + main_loop->init(); +} +bool OS_Android::main_loop_iterate() { + + if (!main_loop) + return false; + return Main::iteration(); +} + +void OS_Android::main_loop_end() { + + if (main_loop) + main_loop->finish(); + +} + +void OS_Android::process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points) { + + + + switch(p_what) { + case 0: { //gesture begin + + if (touch.size()) { + //end all if exist + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.ID=++last_id; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.button_mask=BUTTON_MASK_LEFT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=touch[0].pos.x; + ev.mouse_button.y=touch[0].pos.y; + ev.mouse_button.global_x=touch[0].pos.x; + ev.mouse_button.global_y=touch[0].pos.y; + main_loop->input_event(ev); + + + for(int i=0;i<touch.size();i++) { + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=++last_id; + ev.screen_touch.index=touch[i].id; + ev.screen_touch.pressed=false; + ev.screen_touch.x=touch[i].pos.x; + ev.screen_touch.y=touch[i].pos.y; + main_loop->input_event(ev); + + } + } + + touch.resize(p_points.size()); + for(int i=0;i<p_points.size();i++) { + touch[i].id=p_points[i].id; + touch[i].pos=p_points[i].pos; + } + + { + //send mouse + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.ID=++last_id; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.button_mask=BUTTON_MASK_LEFT; + ev.mouse_button.pressed=true; + ev.mouse_button.x=touch[0].pos.x; + ev.mouse_button.y=touch[0].pos.y; + ev.mouse_button.global_x=touch[0].pos.x; + ev.mouse_button.global_y=touch[0].pos.y; + last_mouse=touch[0].pos; + main_loop->input_event(ev); + } + + + //send touch + for(int i=0;i<touch.size();i++) { + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=++last_id; + ev.screen_touch.index=touch[i].id; + ev.screen_touch.pressed=true; + ev.screen_touch.x=touch[i].pos.x; + ev.screen_touch.y=touch[i].pos.y; + main_loop->input_event(ev); + } + + } break; + case 1: { //motion + + + if (p_points.size()) { + //send mouse, should look for point 0? + InputEvent ev; + ev.type=InputEvent::MOUSE_MOTION; + ev.ID=++last_id; + ev.mouse_motion.button_mask=BUTTON_MASK_LEFT; + ev.mouse_motion.x=p_points[0].pos.x; + ev.mouse_motion.y=p_points[0].pos.y; + input->set_mouse_pos(Point2(ev.mouse_motion.x,ev.mouse_motion.y)); + ev.mouse_motion.speed_x=input->get_mouse_speed().x; + ev.mouse_motion.speed_y=input->get_mouse_speed().y; + ev.mouse_motion.relative_x=p_points[0].pos.x-last_mouse.x; + ev.mouse_motion.relative_y=p_points[0].pos.y-last_mouse.y; + last_mouse=p_points[0].pos; + main_loop->input_event(ev); + } + + ERR_FAIL_COND(touch.size()!=p_points.size()); + + for(int i=0;i<touch.size();i++) { + + int idx=-1; + for(int j=0;j<p_points.size();j++) { + + if (touch[i].id==p_points[j].id) { + idx=j; + break; + } + + } + + ERR_CONTINUE(idx==-1); + + if (touch[i].pos==p_points[idx].pos) + continue; //no move unncesearily + + InputEvent ev; + ev.type=InputEvent::SCREEN_DRAG; + ev.ID=++last_id; + ev.screen_drag.index=touch[i].id; + ev.screen_drag.x=p_points[idx].pos.x; + ev.screen_drag.y=p_points[idx].pos.y; + ev.screen_drag.x=p_points[idx].pos.x - touch[i].pos.x; + ev.screen_drag.y=p_points[idx].pos.y - touch[i].pos.y; + main_loop->input_event(ev); + touch[i].pos=p_points[idx].pos; + } + + + } break; + case 2: { //release + + + + if (touch.size()) { + //end all if exist + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.ID=++last_id; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.button_mask=BUTTON_MASK_LEFT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=touch[0].pos.x; + ev.mouse_button.y=touch[0].pos.y; + ev.mouse_button.global_x=touch[0].pos.x; + ev.mouse_button.global_y=touch[0].pos.y; + main_loop->input_event(ev); + + + for(int i=0;i<touch.size();i++) { + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=++last_id; + ev.screen_touch.index=touch[i].id; + ev.screen_touch.pressed=false; + ev.screen_touch.x=touch[i].pos.x; + ev.screen_touch.y=touch[i].pos.y; + main_loop->input_event(ev); + + } + } + + } break; + case 3: { // add tuchi + + + + + + ERR_FAIL_INDEX(p_pointer,p_points.size()); + + TouchPos tp=p_points[p_pointer]; + touch.push_back(tp); + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=++last_id; + ev.screen_touch.index=tp.id; + ev.screen_touch.pressed=true; + ev.screen_touch.x=tp.pos.x; + ev.screen_touch.y=tp.pos.y; + main_loop->input_event(ev); + + } break; + case 4: { + + + for(int i=0;i<touch.size();i++) { + if (touch[i].id==p_pointer) { + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=++last_id; + ev.screen_touch.index=touch[i].id; + ev.screen_touch.pressed=false; + ev.screen_touch.x=touch[i].pos.x; + ev.screen_touch.y=touch[i].pos.y; + main_loop->input_event(ev); + touch.remove(i); + i--; + } + } + + } break; + + } + +} + +OS_Android::OS_Android(int p_video_width,int p_video_height) { + + default_videomode.width=p_video_width; + default_videomode.height=p_video_height; + default_videomode.fullscreen=true; + default_videomode.resizable=false; + main_loop=NULL; + last_id=1; +} + +OS_Android::~OS_Android() { + + +} diff --git a/platform/android/.old/os_android.h b/platform/android/.old/os_android.h new file mode 100644 index 0000000000..25ed84851a --- /dev/null +++ b/platform/android/.old/os_android.h @@ -0,0 +1,108 @@ +#ifndef OS_ANDROID_H +#define OS_ANDROID_H + +#include "os/input.h" +#include "drivers/unix/os_unix.h" +#include "os/main_loop.h" +#include "servers/physics/physics_server_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/physics_2d/physics_2d_server_sw.h" +#include "servers/visual/rasterizer.h" +#include "audio_driver_android.h" + +class OS_Android : public OS_Unix { +public: + + struct TouchPos { + int id; + Point2 pos; + }; +private: + + Vector<TouchPos> touch; + + Point2 last_mouse; + unsigned int last_id; + + + Rasterizer *rasterizer; + VisualServer *visual_server; +// AudioDriverPSP audio_driver_psp; + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + AudioDriverAndroid audio_driver_android; + InputDefault *input; + + VideoMode default_videomode; + MainLoop * main_loop; +public: + + + void initialize_core(); + + // functions used by main to initialize/deintialize the OS + virtual int get_video_driver_count() const; + virtual const char * get_video_driver_name(int p_driver) const; + + virtual VideoMode get_default_video_mode() const; + + virtual int get_audio_driver_count() const; + virtual const char * get_audio_driver_name(int p_driver) const; + + virtual void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver); + + virtual void set_main_loop( MainLoop * p_main_loop ); + virtual void delete_main_loop(); + + virtual void finalize(); + + + typedef int64_t ProcessID; + + static OS* get_singleton(); + + virtual void vprint(const char* p_format, va_list p_list, bool p_stderr=false); + virtual void print(const char *p_format, ... ); + virtual void alert(const String& p_alert); + + + virtual void set_mouse_show(bool p_show); + virtual void set_mouse_grab(bool p_grab); + virtual bool is_mouse_grab_enabled() const; + virtual Point2 get_mouse_pos() const; + virtual int get_mouse_button_state() const; + virtual void set_window_title(const String& p_title); + + //virtual void set_clipboard(const String& p_text); + //virtual String get_clipboard() const; + + virtual void set_screen_orientation(ScreenOrientation p_orientation); + + virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen=0); + virtual VideoMode get_video_mode(int p_screen=0) const; + virtual void get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen=0) const; + + virtual String get_name(); + virtual MainLoop *get_main_loop() const; + + virtual bool can_draw() const; + + virtual void set_cursor_shape(CursorShape p_shape); + + void main_loop_begin(); + bool main_loop_iterate(); + void main_loop_end(); + + void process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points); + OS_Android(int p_video_width,int p_video_height); + ~OS_Android(); + +}; + +#endif diff --git a/platform/android/.old/platform_config.h b/platform/android/.old/platform_config.h new file mode 100644 index 0000000000..dad24432a5 --- /dev/null +++ b/platform/android/.old/platform_config.h @@ -0,0 +1 @@ +#include <alloca.h> diff --git a/platform/android/.old/run_debug.sh b/platform/android/.old/run_debug.sh new file mode 100755 index 0000000000..e39193bd55 --- /dev/null +++ b/platform/android/.old/run_debug.sh @@ -0,0 +1,40 @@ + +# com.android.godot +# for this, need to push gdbserver to device +# adb push [location]/gdbserver /data/local + +# run + +DEBUG_PORT=5039 +GDB_PATH="$ANDROID_NDK_ROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-gdb" + +#kill existing previous gdbserver if exists +adb shell killall gdbserver +run-as com.android.godot killall com.android.godot +run-as com.android.godot killall gdbserver +#get data dir of the app +adb pull /system/bin/app_process app_process + +DATA_DIR=`adb shell run-as com.android.godot /system/bin/sh -c pwd | tr -d '\n\r'` +echo "DATA DIR IS $DATA_DIR" +#start app +adb shell am start -n com.android.godot/com.android.godot.Godot +#get the pid of the app +PID=`adb shell pidof com.android.godot | tr -d '\n\r'` +echo "PID IS: $PID hoho" +#launch gdbserver +DEBUG_SOCKET=debug-socket +#echo adb shell /data/local/gdbserver +debug-socket --attach $PID +adb shell run-as com.android.godot lib/gdbserver +$DEBUG_SOCKET --attach $PID & +sleep 2s +#adb shell /data/local/gdbserver localhost:$DEBUG_PORT --attach $PID & +#setup network connection +adb forward tcp:$DEBUG_PORT localfilesystem:$DATA_DIR/$DEBUG_SOCKET +cp gdb.setup.base gdb.setup +echo "target remote :$DEBUG_PORT" >> gdb.setup +#echo "file +$GDB_PATH -x gdb.setup + + + + diff --git a/platform/android/AndroidManifest.xml.template b/platform/android/AndroidManifest.xml.template new file mode 100644 index 0000000000..208d24059e --- /dev/null +++ b/platform/android/AndroidManifest.xml.template @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.godot.game" + android:versionCode="1" + android:versionName="1.0" + android:installLocation="preferExternal" + > + <application android:label="@string/godot_project_name_string" android:icon="@drawable/icon"> + <activity android:name="com.android.godot.Godot" + android:label="@string/godot_project_name_string" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:launchMode="singleTask" + android:screenOrientation="landscape" + android:configChanges="orientation|keyboardHidden"> + + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + + + +$$ADD_APPLICATION_CHUNKS$$ + + </application> + <uses-feature android:glEsVersion="0x00020000"/> + <uses-permission android:name="android.permission.INTERNET"></uses-permission> + <uses-permission android:name="android.permission.READ_CONTACTS"/> + <uses-permission android:name="com.android.vending.BILLING" /> + <uses-permission android:name="android.permission.READ_PHONE_STATE"/> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> + <uses-permission android:name="android.permission.RECEIVE_SMS"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + + + <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="11"/> + +</manifest> diff --git a/platform/android/SCsub b/platform/android/SCsub new file mode 100644 index 0000000000..327b1ffe09 --- /dev/null +++ b/platform/android/SCsub @@ -0,0 +1,66 @@ +import shutil + +Import('env') + +android_files = [ + + 'os_android.cpp', + 'godot_android.cpp', + 'file_access_android.cpp', + 'dir_access_android.cpp', + 'audio_driver_android.cpp', + 'file_access_jandroid.cpp', + 'dir_access_jandroid.cpp', + 'thread_jandroid.cpp', + 'audio_driver_jandroid.cpp', + 'android_native_app_glue.c', + 'java_glue.cpp' +] + +#env.Depends('#core/math/vector3.h', 'vector3_psp.h') + +#obj = env.SharedObject('godot_android.cpp') + +env_android = env.Clone() +if env['target'] == "profile": + env_android.Append(CPPFLAGS=['-DPROFILER_ENABLED']) + +android_objects=[] +for x in android_files: + android_objects.append( env_android.SharedObject( x ) ) + +prog = None + +abspath=env.Dir(".").abspath + + +pp_basein = open(abspath+"/project.properties.template","rb") +pp_baseout = open(abspath+"/java/project.properties","wb") +pp_baseout.write( pp_basein.read() ) +refcount=1 +for x in env.android_source_modules: + pp_baseout.write("android.library.reference."+str(refcount)+"="+x+"\n") + refcount+=1 + + + +pp_baseout.close() + + +pp_basein = open(abspath+"/AndroidManifest.xml.template","rb") +pp_baseout = open(abspath+"/java/AndroidManifest.xml","wb") +manifest = pp_basein.read() +manifest = manifest.replace("$$ADD_APPLICATION_CHUNKS$$",env.android_manifest_chunk) +pp_baseout.write( manifest ) + + +for x in env.android_source_files: + shutil.copy(x,abspath+"/java/src/com/android/godot") + +for x in env.android_module_libraries: + shutil.copy(x,abspath+"/java/libs") + + +env_android.SharedLibrary("#platform/android/libgodot_android.so",[android_objects]) + +env.Command('#bin/libgodot_android.so', '#platform/android/libgodot_android.so', Copy('bin/libgodot_android.so', 'platform/android/libgodot_android.so')) diff --git a/platform/android/android_native_app_glue.c b/platform/android/android_native_app_glue.c new file mode 100644 index 0000000000..965f6284cd --- /dev/null +++ b/platform/android/android_native_app_glue.c @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef ANDROID_NATIVE_ACTIVITY + +#include <jni.h> + + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/resource.h> + +#include "android_native_app_glue.h" +#include <android/log.h> + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) + +static void free_saved_state(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->savedState != NULL) { + free(android_app->savedState); + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + pthread_mutex_unlock(&android_app->mutex); +} + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { + switch (cmd) { + case APP_CMD_SAVE_STATE: + free_saved_state(android_app); + break; + } + return cmd; + } else { + LOGI("No data on command pipe!"); + } + return -1; +} + +static void print_cur_config(struct android_app* android_app) { + char lang[2], country[2]; + AConfiguration_getLanguage(android_app->config, lang); + AConfiguration_getCountry(android_app->config, country); + + LOGI("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " + "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " + "modetype=%d modenight=%d", + AConfiguration_getMcc(android_app->config), + AConfiguration_getMnc(android_app->config), + lang[0], lang[1], country[0], country[1], + AConfiguration_getOrientation(android_app->config), + AConfiguration_getTouchscreen(android_app->config), + AConfiguration_getDensity(android_app->config), + AConfiguration_getKeyboard(android_app->config), + AConfiguration_getNavigation(android_app->config), + AConfiguration_getKeysHidden(android_app->config), + AConfiguration_getNavHidden(android_app->config), + AConfiguration_getSdkVersion(android_app->config), + AConfiguration_getScreenSize(android_app->config), + AConfiguration_getScreenLong(android_app->config), + AConfiguration_getUiModeType(android_app->config), + AConfiguration_getUiModeNight(android_app->config)); +} + +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGI("APP_CMD_INPUT_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGI("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, LOOPER_ID_INPUT, NULL, + &android_app->inputPollSource); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_INIT_WINDOW: + LOGI("APP_CMD_INIT_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_TERM_WINDOW: + LOGI("APP_CMD_TERM_WINDOW\n"); + pthread_cond_broadcast(&android_app->cond); + break; + + case APP_CMD_RESUME: + case APP_CMD_START: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGI("activityState=%d\n", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_CONFIG_CHANGED: + LOGI("APP_CMD_CONFIG_CHANGED\n"); + AConfiguration_fromAssetManager(android_app->config, + android_app->activity->assetManager); + print_cur_config(android_app); + break; + + case APP_CMD_DESTROY: + LOGI("APP_CMD_DESTROY\n"); + android_app->destroyRequested = 1; + break; + } +} + +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_TERM_WINDOW: + LOGI("APP_CMD_TERM_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = NULL; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_SAVE_STATE: + LOGI("APP_CMD_SAVE_STATE\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_RESUME: + free_saved_state(android_app); + break; + } +} + +void app_dummy() { + +} + +static void android_app_destroy(struct android_app* android_app) { + LOGI("android_app_destroy!"); + free_saved_state(android_app); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + AConfiguration_delete(android_app->config); + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +static void process_input(struct android_app* app, struct android_poll_source* source) { + AInputEvent* event = NULL; + if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { + LOGI("New input event: type=%d\n", AInputEvent_getType(event)); + if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { + return; + } + int32_t handled = 0; + if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); + AInputQueue_finishEvent(app->inputQueue, event, handled); + } else { + LOGI("Failure reading next input event: %s\n", strerror(errno)); + } +} + +static void process_cmd(struct android_app* app, struct android_poll_source* source) { + int8_t cmd = android_app_read_cmd(app); + android_app_pre_exec_cmd(app, cmd); + if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); + android_app_post_exec_cmd(app, cmd); +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + android_app->config = AConfiguration_new(); + AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); + + print_cur_config(android_app); + + android_app->cmdPollSource.id = LOOPER_ID_MAIN; + android_app->cmdPollSource.app = android_app; + android_app->cmdPollSource.process = process_cmd; + android_app->inputPollSource.id = LOOPER_ID_INPUT; + android_app->inputPollSource.app = android_app; + android_app->inputPollSource.process = process_input; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, + &android_app->cmdPollSource); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + + android_app_destroy(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); + memset(android_app, 0, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + if (savedState != NULL) { + android_app->savedState = malloc(savedStateSize); + android_app->savedStateSize = savedStateSize; + memcpy(android_app->savedState, savedState, savedStateSize); + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGI("could not create pipe: %s", strerror(errno)); + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGI("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->pendingWindow != NULL) { + android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); + } + android_app->pendingWindow = window; + if (window != NULL) { + android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); + } + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static void onDestroy(ANativeActivity* activity) { + LOGI("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGI("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGI("Resume: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + struct android_app* android_app = (struct android_app*)activity->instance; + void* savedState = NULL; + + LOGI("SaveInstanceState: %p\n", activity); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 0; + android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); + while (!android_app->stateSaved) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + + if (android_app->savedState != NULL) { + savedState = android_app->savedState; + *outLen = android_app->savedStateSize; + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + + pthread_mutex_unlock(&android_app->mutex); + + return savedState; +} + +static void onPause(ANativeActivity* activity) { + LOGI("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGI("Stop: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); +} + +static void onConfigurationChanged(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGI("ConfigurationChanged: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); +} + +static void onLowMemory(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGI("LowMemory: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGI("WindowFocusChanged: %p -- %d\n", activity, focused); + android_app_write_cmd((struct android_app*)activity->instance, + focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +void ANativeActivity_onCreate(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + LOGI("Creating: %p\n", activity); + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onStart = onStart; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onPause = onPause; + activity->callbacks->onStop = onStop; + activity->callbacks->onConfigurationChanged = onConfigurationChanged; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + + activity->instance = android_app_create(activity, savedState, savedStateSize); +} +#endif diff --git a/platform/android/android_native_app_glue.h b/platform/android/android_native_app_glue.h new file mode 100644 index 0000000000..fe8684b5d2 --- /dev/null +++ b/platform/android/android_native_app_glue.h @@ -0,0 +1,378 @@ +/*************************************************************************/ +/* android_native_app_glue.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/*************************************************************************/ + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _ANDROID_NATIVE_APP_GLUE_H +#define _ANDROID_NATIVE_APP_GLUE_H +#ifdef ANDROID_NATIVE_ACTIVITY + +#include <poll.h> +#include <pthread.h> +#include <sched.h> + +#include <android/configuration.h> +#include <android/looper.h> +#include <android/native_activity.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The native activity interface provided by <android/native_activity.h> + * is based on a set of application-provided callbacks that will be called + * by the Activity's main thread when certain events occur. + * + * This means that each one of this callbacks _should_ _not_ block, or they + * risk having the system force-close the application. This programming + * model is direct, lightweight, but constraining. + * + * The 'threaded_native_app' static library is used to provide a different + * execution model where the application can implement its own main event + * loop in a different thread instead. Here's how it works: + * + * 1/ The application must provide a function named "android_main()" that + * will be called when the activity is created, in a new thread that is + * distinct from the activity's main thread. + * + * 2/ android_main() receives a pointer to a valid "android_app" structure + * that contains references to other important objects, e.g. the + * ANativeActivity obejct instance the application is running in. + * + * 3/ the "android_app" object holds an ALooper instance that already + * listens to two important things: + * + * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX + * declarations below. + * + * - input events coming from the AInputQueue attached to the activity. + * + * Each of these correspond to an ALooper identifier returned by + * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, + * respectively. + * + * Your application can use the same ALooper to listen to additional + * file-descriptors. They can either be callback based, or with return + * identifiers starting with LOOPER_ID_USER. + * + * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, + * the returned data will point to an android_poll_source structure. You + * can call the process() function on it, and fill in android_app->onAppCmd + * and android_app->onInputEvent to be called for your own processing + * of the event. + * + * Alternatively, you can call the low-level functions to read and process + * the data directly... look at the process_cmd() and process_input() + * implementations in the glue to see how to do this. + * + * See the sample named "native-activity" that comes with the NDK with a + * full usage example. Also look at the JavaDoc of NativeActivity. + */ + +struct android_app; + +/** + * Data associated with an ALooper fd that will be returned as the "outData" + * when that source has data ready. + */ +struct android_poll_source { + // The identifier of this source. May be LOOPER_ID_MAIN or + // LOOPER_ID_INPUT. + int32_t id; + + // The android_app this ident is associated with. + struct android_app* app; + + // Function to call to perform the standard processing of data from + // this source. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // Fill this in with the function to process main app commands (APP_CMD_*) + void (*onAppCmd)(struct android_app* app, int32_t cmd); + + // Fill this in with the function to process input events. At this point + // the event has already been pre-dispatched, and it will be finished upon + // return. Return 1 if you have handled the event, 0 for any default + // dispatching. + int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The current configuration the app is running in. + AConfiguration* config; + + // This is the last instance's saved state, as provided at creation time. + // It is NULL if there was no state. You can use this as you need; the + // memory will remain around until you call android_app_exec_cmd() for + // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. + // These variables should only be changed when processing a APP_CMD_SAVE_STATE, + // at which point they will be initialized to NULL and you can malloc your + // state and place the information here. In that case the memory will be + // freed for you later. + void* savedState; + size_t savedStateSize; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current content rectangle of the window; this is the area where the + // window's content should be placed to be seen by the user. + ARect contentRect; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + struct android_poll_source cmdPollSource; + struct android_poll_source inputPollSource; + + int running; + int stateSaved; + int destroyed; + int redrawNeeded; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; + ARect pendingContentRect; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread, which + * is returned as an identifier from ALooper_pollOnce(). The data for this + * identifier is a pointer to an android_poll_source structure. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window, which is returned as an identifier from + * ALooper_pollOnce(). The data for this identifier is a pointer to an + * android_poll_source structure. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_INPUT = 2, + + /** + * Start of user-defined ALooper identifiers. + */ + LOOPER_ID_USER = 3, +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: a new ANativeWindow is ready for use. Upon + * receiving this command, android_app->window will contain the new window + * surface. + */ + APP_CMD_INIT_WINDOW, + + /** + * Command from main thread: the existing ANativeWindow needs to be + * terminated. Upon receiving this command, android_app->window still + * contains the existing window; after calling android_app_exec_cmd + * it will be set to NULL. + */ + APP_CMD_TERM_WINDOW, + + /** + * Command from main thread: the current ANativeWindow has been resized. + * Please redraw with its new size. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Command from main thread: the system needs that the current ANativeWindow + * be redrawn. You should redraw the window before handing this to + * android_app_exec_cmd() in order to avoid transient drawing glitches. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Command from main thread: the content area of the window has changed, + * such as from the soft input window being shown or hidden. You can + * find the new content rect in android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the current device configuration has changed. + */ + APP_CMD_CONFIG_CHANGED, + + /** + * Command from main thread: the system is running low on memory. + * Try to reduce your memory use. + */ + APP_CMD_LOW_MEMORY, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app should generate a new saved state + * for itself, to restore from later if needed. If you have saved state, + * allocate it with malloc and place it in android_app.savedState with + * the size in android_app.savedStateSize. The will be freed for you + * later. + */ + APP_CMD_SAVE_STATE, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * initial pre-processing of the given command. You can perform your own + * actions for the command after calling this function. + */ +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * final post-processing of the given command. You must have done your own + * actions for the command before calling this function. + */ +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Dummy function you can call to ensure glue code isn't stripped. + */ +void app_dummy(); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_NATIVE_APP_GLUE_H */ +#endif diff --git a/platform/android/audio_driver_android.cpp b/platform/android/audio_driver_android.cpp new file mode 100644 index 0000000000..4f7b4ee348 --- /dev/null +++ b/platform/android/audio_driver_android.cpp @@ -0,0 +1,393 @@ +/*************************************************************************/ +/* audio_driver_android.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "audio_driver_android.h" +#include <string.h> +#ifdef ANDROID_NATIVE_ACTIVITY + + + + + +#define MAX_NUMBER_INTERFACES 3 +#define MAX_NUMBER_OUTPUT_DEVICES 6 + +/* Structure for passing information to callback function */ + + +void AudioDriverAndroid::_buffer_callback( + SLAndroidSimpleBufferQueueItf queueItf + /* SLuint32 eventFlags, + const void * pBuffer, + SLuint32 bufferSize, + SLuint32 dataUsed*/) { + + + + if (mutex) + mutex->lock(); + + audio_server_process(buffer_size,mixdown_buffer); + + if (mutex) + mutex->unlock(); + + + const int32_t* src_buff=mixdown_buffer; + + int16_t *ptr = (int16_t*)buffers[last_free]; + last_free=(last_free+1)%BUFFER_COUNT; + + for(int i=0;i<buffer_size*2;i++) { + + ptr[i]=src_buff[i]>>16; + } + + (*queueItf)->Enqueue(queueItf, ptr, 4 * buffer_size); + + +#if 0 + SLresult res; + CallbackCntxt *pCntxt = (CallbackCntxt*)pContext; + if(pCntxt->pData < (pCntxt->pDataBase + pCntxt->size)) + { + res = (*queueItf)->Enqueue(queueItf, (void*) pCntxt->pData, + 2 * AUDIO_DATA_BUFFER_SIZE, SL_BOOLEAN_FALSE); /* Size given + in bytes. */ + CheckErr(res); + /* Increase data pointer by buffer size */ + pCntxt->pData += AUDIO_DATA_BUFFER_SIZE; + } + } +#endif +} + +void AudioDriverAndroid::_buffer_callbacks( + SLAndroidSimpleBufferQueueItf queueItf, + /*SLuint32 eventFlags, + const void * pBuffer, + SLuint32 bufferSize, + SLuint32 dataUsed,*/ + void *pContext) { + + + AudioDriverAndroid *ad = (AudioDriverAndroid*)pContext; + +// ad->_buffer_callback(queueItf,eventFlags,pBuffer,bufferSize,dataUsed); + ad->_buffer_callback(queueItf); + +} + + +AudioDriverAndroid* AudioDriverAndroid::s_ad=NULL; + +const char* AudioDriverAndroid::get_name() const { + + return "Android"; +} + +#if 0 +int AudioDriverAndroid::thread_func(SceSize args, void *argp) { + + AudioDriverAndroid* ad = s_ad; + sceAudioOutput2Reserve(AUDIO_OUTPUT_SAMPLE); + + int half=0; + while(!ad->exit_thread) { + + int16_t *ptr = &ad->outbuff[AUDIO_OUTPUT_SAMPLE*2*half]; + + + + if (!ad->active) { + + for(int i=0;i<AUDIO_OUTPUT_SAMPLE*2;i++) { + ptr[i]=0; + } + + } else { + + //printf("samples: %i\n",AUDIO_OUTPUT_SAMPLE); + ad->lock(); + + ad->audio_server_process(AUDIO_OUTPUT_SAMPLE,ad->outbuff_32); + + ad->unlock(); + + const int32_t* src_buff=ad->outbuff_32; + + for(int i=0;i<AUDIO_OUTPUT_SAMPLE*2;i++) { + + ptr[i]=src_buff[i]>>16; + } + } + + + /* Output 16-bit PCM STEREO data that is in pcmBuf without changing the volume */ + sceAudioOutput2OutputBlocking( + SCE_AUDIO_VOLUME_0dB*3, //0db at 0x8000, that's obvious + ptr + ); + + if (half) + half=0; + else + half=1; + + } + + sceAudioOutput2Release(); + + sceKernelExitThread(SCE_KERNEL_EXIT_SUCCESS); + ad->thread_exited=true; + return SCE_KERNEL_EXIT_SUCCESS; + +} + +#endif +Error AudioDriverAndroid::init(){ + + SLresult + res; + SLEngineOption EngineOption[] = { + (SLuint32) SL_ENGINEOPTION_THREADSAFE, + (SLuint32) SL_BOOLEAN_TRUE + + }; + res = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL); + if (res!=SL_RESULT_SUCCESS) { + + ERR_EXPLAIN("Could not Initialize OpenSL"); + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } + res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE); + if (res!=SL_RESULT_SUCCESS) { + + ERR_EXPLAIN("Could not Realize OpenSL"); + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } + + print_line("OpenSL Init OK!"); + + return OK; + +} +void AudioDriverAndroid::start(){ + + + mutex = Mutex::create(); + active=false; + + + SLint32 numOutputs = 0; + SLuint32 deviceID = 0; + SLresult res; + + + buffer_size = 1024; + + for(int i=0;i<BUFFER_COUNT;i++) { + + buffers[i]=memnew_arr( int16_t,buffer_size*2 ); + memset(buffers[i],0,buffer_size*4); + } + + mixdown_buffer = memnew_arr( int32_t,buffer_size* 2); + + /* Callback context for the buffer queue callback function */ + + /* Get the SL Engine Interface which is implicit */ + res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf); + + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + /* Initialize arrays required[] and iidArray[] */ + int i; + SLboolean required[MAX_NUMBER_INTERFACES]; + SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; + +#if 0 + + for (i=0; i<MAX_NUMBER_INTERFACES; i++) + { + required[i] = SL_BOOLEAN_FALSE; + iidArray[i] = SL_IID_NULL; + } + // Set arrays required[] and iidArray[] for VOLUME interface + required[0] = SL_BOOLEAN_TRUE; + iidArray[0] = SL_IID_VOLUME; + + // Create Output Mix object to be used by player + res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 1, + iidArray, required); +#else + + { + const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB}; + const SLboolean req[1] = {SL_BOOLEAN_FALSE}; + res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0, + ids, req); + } + + +#endif + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + // Realizing the Output Mix object in synchronous mode. + res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, BUFFER_COUNT}; +// bufferQueue.locatorType = SL_DATALOCATOR_BUFFERQUEUE; +// bufferQueue.numBuffers = BUFFER_COUNT; /* Four buffers in our buffer queue */ + /* Setup the format of the content in the buffer queue */ + pcm.formatType = SL_DATAFORMAT_PCM; + pcm.numChannels = 2; + pcm.samplesPerSec = SL_SAMPLINGRATE_44_1; + pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; + pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; +#ifdef BIG_ENDIAN_ENABLED + pcm.endianness = SL_BYTEORDER_BIGENDIAN; +#else + pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; +#endif + audioSource.pFormat = (void *)&pcm; + audioSource.pLocator = (void *)&loc_bufq; + + + /* Setup the data sink structure */ + locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; + locator_outputmix.outputMix= OutputMix; + audioSink.pLocator = (void *)&locator_outputmix; + audioSink.pFormat = NULL; + /* Initialize the context for Buffer queue callbacks */ +// cntxt.pDataBase = (void*)&pcmData; + //cntxt.pData = cntxt.pDataBase; + //cntxt.size = sizeof(pcmData); + /* Set arrays required[] and iidArray[] for SEEK interface + (PlayItf is implicit) */ + required[0] = SL_BOOLEAN_TRUE; + iidArray[0] = SL_IID_BUFFERQUEUE; + /* Create the music player */ + + { + const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND}; + const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + + res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, + &audioSource, &audioSink, 1, ids, req); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + } + /* Realizing the player in synchronous mode. */ + res = (*player)->Realize(player, SL_BOOLEAN_FALSE); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + /* Get seek and play interfaces */ + res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, + (void*)&bufferQueueItf); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + /* Setup to receive buffer queue event callbacks */ + res = (*bufferQueueItf)->RegisterCallback(bufferQueueItf, + _buffer_callbacks, this); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + /* Before we start set volume to -3dB (-300mB) */ +#if 0 + res = (*OutputMix)->GetInterface(OutputMix, SL_IID_VOLUME, + (void*)&volumeItf); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + /* Setup the data source structure for the buffer queue */ + + res = (*volumeItf)->SetVolumeLevel(volumeItf, -300); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); +#endif + last_free=0; +#if 1 + //fill up buffers + for(int i=0;i<BUFFER_COUNT;i++) { + /* Enqueue a few buffers to get the ball rolling */ + res = (*bufferQueueItf)->Enqueue(bufferQueueItf, buffers[i], + 4 * buffer_size); /* Size given in */ + + } +#endif + + res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + +#if 0 + res = (*bufferQueueItf)->GetState(bufferQueueItf, &state); + ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); + while(state.count) + { + (*bufferQueueItf)->GetState(bufferQueueItf, &state); + } + /* Make sure player is stopped */ + res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); + CheckErr(res); + /* Destroy the player */ + (*player)->Destroy(player); + /* Destroy Output Mix object */ + (*OutputMix)->Destroy(OutputMix); +#endif + + active=true; +} +int AudioDriverAndroid::get_mix_rate() const { + + return 44100; +} +AudioDriverSW::OutputFormat AudioDriverAndroid::get_output_format() const{ + + return OUTPUT_STEREO; +} +void AudioDriverAndroid::lock(){ + + //if (active && mutex) + // mutex->lock(); + +} +void AudioDriverAndroid::unlock() { + + //if (active && mutex) + // mutex->unlock(); + +} +void AudioDriverAndroid::finish(){ + + (*sl)->Destroy(sl); + +} + + +AudioDriverAndroid::AudioDriverAndroid() +{ + s_ad=this; + mutex=NULL; +} + +#endif diff --git a/platform/android/audio_driver_android.h b/platform/android/audio_driver_android.h new file mode 100644 index 0000000000..655772c772 --- /dev/null +++ b/platform/android/audio_driver_android.h @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* audio_driver_android.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 AUDIO_DRIVER_ANDROID_H +#define AUDIO_DRIVER_ANDROID_H + +#ifdef ANDROID_NATIVE_ACTIVITY + +#include "servers/audio/audio_server_sw.h" +#include "os/mutex.h" +#include <SLES/OpenSLES.h> +#include "SLES/OpenSLES_Android.h" +class AudioDriverAndroid : public AudioDriverSW { + + bool active; + Mutex *mutex; + + enum { + + BUFFER_COUNT=2 + }; + + + + + uint32_t buffer_size; + int16_t *buffers[BUFFER_COUNT]; + int32_t *mixdown_buffer; + int last_free; + + + SLPlayItf playItf; + SLObjectItf sl; + SLEngineItf EngineItf; + SLObjectItf OutputMix; + SLVolumeItf volumeItf; + SLObjectItf player; + SLAndroidSimpleBufferQueueItf bufferQueueItf; + SLDataSource audioSource; + SLDataFormat_PCM pcm; + SLDataSink audioSink; + SLDataLocator_OutputMix locator_outputmix; + SLBufferQueueState state; + + static AudioDriverAndroid* s_ad; + + void _buffer_callback( + SLAndroidSimpleBufferQueueItf queueItf + /* SLuint32 eventFlags, + const void * pBuffer, + SLuint32 bufferSize, + SLuint32 dataUsed*/); + + static void _buffer_callbacks( + SLAndroidSimpleBufferQueueItf queueItf, + /*SLuint32 eventFlags, + const void * pBuffer, + SLuint32 bufferSize, + SLuint32 dataUsed,*/ + void *pContext); +public: + + void set_singleton(); + + virtual const char* get_name() const; + + virtual Error init(); + virtual void start(); + virtual int get_mix_rate() const ; + virtual OutputFormat get_output_format() const; + virtual void lock(); + virtual void unlock(); + virtual void finish(); + + + AudioDriverAndroid(); +}; + +#endif // AUDIO_DRIVER_ANDROID_H +#endif diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp new file mode 100644 index 0000000000..6e3f6f9505 --- /dev/null +++ b/platform/android/audio_driver_jandroid.cpp @@ -0,0 +1,248 @@ +/*************************************************************************/ +/* audio_driver_jandroid.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "audio_driver_jandroid.h" +#include "globals.h" +#include "os/os.h" +#include "thread_jandroid.h" +#ifndef ANDROID_NATIVE_ACTIVITY + +AudioDriverAndroid* AudioDriverAndroid::s_ad=NULL; + +jobject AudioDriverAndroid::io; +jmethodID AudioDriverAndroid::_init_audio; +jmethodID AudioDriverAndroid::_write_buffer; +jmethodID AudioDriverAndroid::_quit; +jmethodID AudioDriverAndroid::_pause; +bool AudioDriverAndroid::active=false; +jclass AudioDriverAndroid::cls; +int AudioDriverAndroid::audioBufferFrames=0; +int AudioDriverAndroid::mix_rate=44100; +bool AudioDriverAndroid::quit=false; +jobject AudioDriverAndroid::audioBuffer = NULL; +void* AudioDriverAndroid::audioBufferPinned = NULL; +Mutex *AudioDriverAndroid::mutex=NULL; +int32_t* AudioDriverAndroid::audioBuffer32=NULL; + + +const char* AudioDriverAndroid::get_name() const { + + return "Android"; +} + + +Error AudioDriverAndroid::init(){ + + mutex = Mutex::create(); +/* + // TODO: pass in/return a (Java) device ID, also whether we're opening for input or output + this->spec.samples = Android_JNI_OpenAudioDevice(this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples); + SDL_CalculateAudioSpec(&this->spec); + + if (this->spec.samples == 0) { + // Init failed? + SDL_SetError("Java-side initialization failed!"); + return 0; + } +*/ + +// Android_JNI_SetupThread(); + + + // __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device"); + + + JNIEnv *env = ThreadAndroid::get_env(); + int mix_rate = GLOBAL_DEF("audio/mix_rate",44100); + + int latency = GLOBAL_DEF("audio/output_latency",25); + latency=50; + unsigned int buffer_size = nearest_power_of_2( latency * mix_rate / 1000 ); + if (OS::get_singleton()->is_stdout_verbose()) { + print_line("audio buffer size: "+itos(buffer_size)); + } + + __android_log_print(ANDROID_LOG_INFO,"godot","Initializing audio! params: %i,%i ",mix_rate,buffer_size); + audioBuffer = env->CallObjectMethod(io,_init_audio, mix_rate, buffer_size); + + + ERR_FAIL_COND_V( audioBuffer == NULL, ERR_INVALID_PARAMETER); + + audioBuffer = env->NewGlobalRef(audioBuffer); + + jboolean isCopy = JNI_FALSE; + audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy); + audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer); + audioBuffer32 = memnew_arr(int32_t,audioBufferFrames); + + return OK; +} + +void AudioDriverAndroid::start(){ + active=true; + +} + +void AudioDriverAndroid::setup( jobject p_io) { + + JNIEnv *env = ThreadAndroid::get_env(); + io=p_io; + + jclass c = env->GetObjectClass(io); + cls = (jclass)env->NewGlobalRef(c); + + + __android_log_print(ANDROID_LOG_INFO,"godot","starting to attempt get methods"); + + _init_audio = env->GetMethodID(cls, "audioInit", "(II)Ljava/lang/Object;"); + if(_init_audio != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _init_audio ok!!"); + } else { + __android_log_print(ANDROID_LOG_INFO,"godot","audioinit ok!"); + } + + _write_buffer = env->GetMethodID(cls, "audioWriteShortBuffer", "([S)V"); + if(_write_buffer != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _write_buffer ok!!"); + } + + + _quit = env->GetMethodID(cls, "audioQuit", "()V"); + if(_quit != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _quit ok!!"); + } + + _pause = env->GetMethodID(cls, "audioPause", "(Z)V"); + if(_quit != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _pause ok!!"); + } + + +} + +void AudioDriverAndroid::thread_func(JNIEnv *env) { + + jclass cls = env->FindClass("com/android/godot/Godot"); + if (cls) { + + cls=(jclass)env->NewGlobalRef(cls); + __android_log_print(ANDROID_LOG_INFO,"godot","*******CLASS FOUND!!!"); + } + jfieldID fid = env->GetStaticFieldID(cls, "io", "Lcom/android/godot/GodotIO;"); + jobject ob = env->GetStaticObjectField(cls,fid); + jobject gob = env->NewGlobalRef(ob); + jclass c = env->GetObjectClass(gob); + jclass lcls = (jclass)env->NewGlobalRef(c); + _write_buffer = env->GetMethodID(lcls, "audioWriteShortBuffer", "([S)V"); + if(_write_buffer != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _write_buffer ok!!"); + } + + while(!quit) { + + + int16_t* ptr = (int16_t*)audioBufferPinned; + int fc = audioBufferFrames; + + if (!s_ad->active || mutex->try_lock()!=OK) { + + for(int i=0;i<fc;i++) { + ptr[i]=0; + } + + } else { + + + s_ad->audio_server_process(fc/2,audioBuffer32); + + mutex->unlock(); + + for(int i=0;i<fc;i++) { + + ptr[i]=audioBuffer32[i]>>16; + } + + } + env->ReleaseShortArrayElements((jshortArray)audioBuffer, (jshort *)ptr, JNI_COMMIT); + 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(); + env->CallVoidMethod(io, _quit); + + if (audioBuffer) { + env->DeleteGlobalRef(audioBuffer); + audioBuffer = NULL; + audioBufferPinned = NULL; + } + + active=false; +} + +void AudioDriverAndroid::set_pause(bool p_pause) { + + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(io, _pause,p_pause); + +} + +AudioDriverAndroid::AudioDriverAndroid() +{ + s_ad=this; + active=false; + + + +} + +#endif diff --git a/platform/android/audio_driver_jandroid.h b/platform/android/audio_driver_jandroid.h new file mode 100644 index 0000000000..f102acf154 --- /dev/null +++ b/platform/android/audio_driver_jandroid.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* audio_driver_jandroid.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 AUDIO_DRIVER_ANDROID_H +#define AUDIO_DRIVER_ANDROID_H + +#include "servers/audio/audio_server_sw.h" +#ifndef ANDROID_NATIVE_ACTIVITY + +#include "java_glue.h" + +class AudioDriverAndroid : public AudioDriverSW { + + + static Mutex *mutex; + static AudioDriverAndroid* s_ad; + static jobject io; + static jmethodID _init_audio; + static jmethodID _write_buffer; + static jmethodID _quit; + static jmethodID _pause; + static bool active; + static bool quit; + + static jclass cls; + + static jobject audioBuffer; + static void* audioBufferPinned; + static int32_t* audioBuffer32; + static int audioBufferFrames; + static int mix_rate; + + +public: + + void set_singleton(); + + virtual const char* get_name() const; + + virtual Error init(); + virtual void start(); + virtual int get_mix_rate() const ; + virtual OutputFormat get_output_format() const; + virtual void lock(); + virtual void unlock(); + virtual void finish(); + + virtual void set_pause(bool p_pause); + + static void setup( jobject act); + static void thread_func(JNIEnv *env); + + AudioDriverAndroid(); +}; + +#endif +#endif // AUDIO_DRIVER_ANDROID_H diff --git a/platform/android/detect.py b/platform/android/detect.py new file mode 100644 index 0000000000..b89024a81a --- /dev/null +++ b/platform/android/detect.py @@ -0,0 +1,175 @@ +import os +import sys +import string +import platform + +def is_active(): + return True + +def get_name(): + return "Android" + +def can_build(): + + import os + if (not os.environ.has_key("ANDROID_NDK_ROOT")): + return False + return True + +def get_opts(): + + return [ + ('ANDROID_NDK_ROOT', 'the path to Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)), + ('NDK_TOOLCHAIN', 'toolchain to use for the NDK',"arm-eabi-4.4.0"), + #android 2.3 + ('ndk_platform', 'compile for platform: (2.2,2.3)',"2.2"), + ('NDK_TARGET', 'toolchain to use for the NDK',"arm-linux-androideabi-4.7"), + ('android_stl','enable STL support in android port (for modules)','no'), + ('armv6','compile for older phones running arm v6 (instead of v7+neon+smp)','no') + + ] + +def get_flags(): + + return [ + ('lua', 'no'), + ('tools', 'no'), + ('nedmalloc', 'no'), + ('builtin_zlib', 'no'), + ] + + +def create(env): + tools = env['TOOLS'] + if "mingw" in tools: + tools.remove('mingw') + if "applelink" in tools: + tools.remove("applelink") + env.Tool('gcc') + return env.Clone(tools=tools); + +def configure(env): + + if env['PLATFORM'] == 'win32': + import methods + env.Tool('gcc') + env['SPAWN'] = methods.win32_spawn + + ndk_platform="" + + if (env["ndk_platform"]=="2.2"): + ndk_platform="android-8" + else: + ndk_platform="android-9" + env.Append(CPPFLAGS=["-DANDROID_NATIVE_ACTIVITY"]) + + print("Godot Android!!!!!") + + env.Append(CPPPATH=['#platform/android']) + + env['OBJSUFFIX'] = ".android.o" + env['LIBSUFFIX'] = ".android.a" + env['PROGSUFFIX'] = ".android" + env['SHLIBSUFFIX'] = ".so" + + gcc_path=env["ANDROID_NDK_ROOT"]+"/toolchains/"+env["NDK_TARGET"]+"/prebuilt/"; + + import os + if (sys.platform.find("linux")==0): + if (platform.architecture()[0]=='64bit' or os.path.isdir(gcc_path+"linux-x86_64/bin")): # check was not working + gcc_path=gcc_path+"/linux-x86_64/bin" + else: + gcc_path=gcc_path+"/linux-x86/bin" + elif (sys.platform=="darwin"): + gcc_path=gcc_path+"/darwin-x86_64/bin" #this may be wrong + env['SHLINKFLAGS'][1] = '-shared' + elif (os.name=="nt"): + gcc_path=gcc_path+"/windows/bin" #this may be wrong + + + + env['ENV']['PATH'] = gcc_path+":"+env['ENV']['PATH'] + + env['CC'] = gcc_path+'/arm-linux-androideabi-gcc' + env['CXX'] = gcc_path+'/arm-linux-androideabi-g++' + env['AR'] = gcc_path+"/arm-linux-androideabi-ar" + env['RANLIB'] = gcc_path+"/arm-linux-androideabi-ranlib" + env['AS'] = gcc_path+"/arm-linux-androideabi-as" + + import string + #include path + gcc_include=env["ANDROID_NDK_ROOT"]+"/platforms/"+ndk_platform+"/arch-arm/usr/include" + ld_sysroot=env["ANDROID_NDK_ROOT"]+"/platforms/"+ndk_platform+"/arch-arm" + #glue_include=env["ANDROID_NDK_ROOT"]+"/sources/android/native_app_glue" + ld_path=env["ANDROID_NDK_ROOT"]+"/platforms/"+ndk_platform+"/arch-arm/usr/lib" + env.Append(CPPPATH=[gcc_include]) +# env['CCFLAGS'] = string.split('-DNO_THREADS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -Wno-psabi -march=armv5te -mtune=xscale -msoft-float -fno-exceptions -mthumb -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED ') + print("********* armv6", env['armv6']) + if env["armv6"]!="no": + env['CCFLAGS'] = string.split('-DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_6__ -D__GLIBC__ -Wno-psabi -march=armv6 -mfpu=vfp -mfloat-abi=softfp -funsafe-math-optimizations -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED -DGLES1_ENABLED') + else: + env['CCFLAGS'] = string.split('-DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_7__ -D__GLIBC__ -Wno-psabi -march=armv6 -mfpu=neon -mfloat-abi=softfp -ftree-vectorize -funsafe-math-optimizations -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED -DGLES1_ENABLED') + + env.Append(LDPATH=[ld_path]) +# env.Append(LIBS=['c','m','stdc++','log','EGL','GLESv1_CM','GLESv2','OpenSLES','supc++','android']) + if (env["ndk_platform"]!="2.2"): + env.Append(LIBS=['EGL','OpenSLES','android']) + env.Append(LIBS=['c','m','stdc++','log','GLESv1_CM','GLESv2', 'z']) + + env["LINKFLAGS"]= string.split(" -g --sysroot="+ld_sysroot+" -Wl,--no-undefined -Wl,-z,noexecstack ") + env.Append(LINKFLAGS=["-Wl,-soname,libgodot_android.so"]) + + if (env["target"]=="release"): + + env.Append(CCFLAGS=['-O2', '-ffast-math','-fomit-frame-pointer']) + env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX'] + + elif (env["target"]=="release_debug"): + + env.Append(CCFLAGS=['-O2', '-ffast-math','-DDEBUG_ENABLED']) + env['OBJSUFFIX'] = "_optd"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_optd"+env['LIBSUFFIX'] + + elif (env["target"]=="profile"): + + env.Append(CCFLAGS=['-O2', '-ffast-math','-fomit-frame-pointer', '-g1']) + env.Append(LIBPATH=['#platform/android/armeabi']) + env.Append(LIBS=['andprof']) + env['OBJSUFFIX'] = "_prof"+env['OBJSUFFIX'] + env['LIBSUFFIX'] = "_prof"+env['LIBSUFFIX'] + env['SHLIBSUFFIX'] = "_prof"+env['SHLIBSUFFIX'] + + elif (env["target"]=="debug"): + + env.Append(CCFLAGS=['-D_DEBUG', '-g1', '-Wall', '-O0', '-DDEBUG_ENABLED']) + env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']) + + if env["armv6"] == "no": + env['neon_enabled']=True + env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL','-DMPC_FIXED_POINT']) +# env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DMPC_FIXED_POINT']) + 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.4.3/include"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi/include"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi"]) + env.Append(LIBS=["gnustl_static","supc++"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cpufeatures"]) + + #env.Append(CCFLAGS=["-I"+env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/stlport/stlport"]) + #env.Append(CCFLAGS=["-I"+env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/libs/armeabi/include"]) + #env.Append(LINKFLAGS=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/libs/armeabi/libstdc++.a"]) + else: + + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/include"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cpufeatures"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/libs/armeabi"]) + env.Append(LIBS=['gabi++_static']) + env.Append(CCFLAGS=["-fno-exceptions",'-DNO_SAFE_CAST']) + + import methods + env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + env.Append( BUILDERS = { 'GLSL' : env.Builder(action = methods.build_glsl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) + diff --git a/platform/android/dir_access_android.cpp b/platform/android/dir_access_android.cpp new file mode 100644 index 0000000000..60bde61fc4 --- /dev/null +++ b/platform/android/dir_access_android.cpp @@ -0,0 +1,189 @@ +/*************************************************************************/ +/* dir_access_android.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/*************************************************************************/ +#ifdef ANDROID_NATIVE_ACTIVITY +#include "dir_access_android.h" +#include "file_access_android.h" + + + +DirAccess *DirAccessAndroid::create_fs() { + + return memnew(DirAccessAndroid); +} + +bool DirAccessAndroid::list_dir_begin() { + + list_dir_end(); + + AAssetDir* aad = AAssetManager_openDir(FileAccessAndroid::asset_manager,current_dir.utf8().get_data()); + if (!aad) + return true; //nothing + + + return false; +} + +String DirAccessAndroid::get_next(){ + + const char* fn= AAssetDir_getNextFileName(aad); + if (!fn) + return ""; + String s; + s.parse_utf8(fn); + current=s; + return s; + + +} +bool DirAccessAndroid::current_is_dir() const{ + + String sd; + if (current_dir=="") + sd=current; + else + sd=current_dir+"/"+current; + + AAssetDir* aad2 = AAssetManager_openDir(FileAccessAndroid::asset_manager,sd.utf8().get_data()); + if (aad2) { + + AAssetDir_close(aad2); + return true; + } + + return false; + +} +void DirAccessAndroid::list_dir_end(){ + + if (aad==NULL) + return; + + AAssetDir_close(aad); + aad=NULL; + +} + +int DirAccessAndroid::get_drive_count(){ + + return 0; +} +String DirAccessAndroid::get_drive(int p_drive){ + + return ""; +} + +Error DirAccessAndroid::change_dir(String p_dir){ + + p_dir=p_dir.simplify_path(); + + if (p_dir=="" || p_dir=="." || (p_dir==".." && current_dir=="")) + return OK; + + String new_dir; + + if (p_dir.begins_with("/")) + new_dir=p_dir.substr(1,p_dir.length()); + else if (p_dir.begins_with("res://")) + new_dir=p_dir.substr(6,p_dir.length()); + else //relative + new_dir=new_dir+"/"+p_dir; + +//test if newdir exists + new_dir=new_dir.simplify_path(); + + AAssetDir* aad = AAssetManager_openDir(FileAccessAndroid::asset_manager,new_dir.utf8().get_data()); + if (aad) { + + current_dir=new_dir; + AAssetDir_close(aad); + return OK; + } + + return ERR_INVALID_PARAMETER; +} + +String DirAccessAndroid::get_current_dir(){ + + return "/"+current_dir; +} + + +bool DirAccessAndroid::file_exists(String p_file){ + + String sd; + if (current_dir=="") + sd=p_file; + else + sd=current_dir+"/"+p_file; + + AAsset *a=AAssetManager_open(FileAccessAndroid::asset_manager,sd.utf8().get_data(),AASSET_MODE_STREAMING); + if (a) { + AAsset_close(a); + return true; + } + + return false; +} + + +Error DirAccessAndroid::make_dir(String p_dir){ + + ERR_FAIL_V(ERR_UNAVAILABLE); +} + +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); +} + +//FileType get_file_type() const; +size_t DirAccessAndroid::get_space_left() { + + return 0; +} + +void DirAccessAndroid::make_default() { + + instance_func=create_fs; +} + +DirAccessAndroid::DirAccessAndroid() { + + aad=NULL; +} + +DirAccessAndroid::~DirAccessAndroid() { + + list_dir_end();; +} +#endif diff --git a/platform/android/dir_access_android.h b/platform/android/dir_access_android.h new file mode 100644 index 0000000000..a6aead6eb3 --- /dev/null +++ b/platform/android/dir_access_android.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* dir_access_android.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 DIR_ACCESS_ANDROID_H +#define DIR_ACCESS_ANDROID_H + +#ifdef ANDROID_NATIVE_ACTIVITY + +#include "os/dir_access.h" +#include <stdio.h> +#include <android/asset_manager.h> +#include <android/log.h> +#include <android_native_app_glue.h> + + + +class DirAccessAndroid : public DirAccess { + + AAssetDir* aad; + String current_dir; + String current; + + static DirAccess *create_fs(); + +public: + + virtual bool list_dir_begin(); ///< This starts dir listing + virtual String get_next(); + virtual bool current_is_dir() const; + virtual void list_dir_end(); ///< + + virtual int get_drive_count(); + virtual String get_drive(int p_drive); + + virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success + virtual String get_current_dir(); ///< return current dir location + + + virtual bool file_exists(String p_file); + + + virtual Error make_dir(String p_dir); + + virtual Error rename(String p_from, String p_to); + virtual Error remove(String p_name); + + //virtual FileType get_file_type() const; + size_t get_space_left(); + + static void make_default(); + + DirAccessAndroid(); + ~DirAccessAndroid(); +}; + +#endif +#endif // DIR_ACCESS_ANDROID_H diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp new file mode 100644 index 0000000000..0c8a5785f8 --- /dev/null +++ b/platform/android/dir_access_jandroid.cpp @@ -0,0 +1,241 @@ +/*************************************************************************/ +/* dir_access_jandroid.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 ANDROID_NATIVE_ACTIVITY + +#include "dir_access_jandroid.h" +#include "file_access_jandroid.h" +#include "thread_jandroid.h" +jobject DirAccessJAndroid::io=NULL; +jclass DirAccessJAndroid::cls=NULL; +jmethodID DirAccessJAndroid::_dir_open=NULL; +jmethodID DirAccessJAndroid::_dir_next=NULL; +jmethodID DirAccessJAndroid::_dir_close=NULL; + + +DirAccess *DirAccessJAndroid::create_fs() { + + return memnew(DirAccessJAndroid); +} + +bool DirAccessJAndroid::list_dir_begin() { + + list_dir_end(); + JNIEnv *env = ThreadAndroid::get_env(); + + jstring js = env->NewStringUTF(current_dir.utf8().get_data()); + int res = env->CallIntMethod(io,_dir_open,js); + if (res<=0) + return true; + + id=res; + + return false; +} + +String DirAccessJAndroid::get_next(){ + + ERR_FAIL_COND_V(id==0,""); + + JNIEnv *env = ThreadAndroid::get_env(); + jstring str= (jstring)env->CallObjectMethod(io,_dir_next,id); + if (!str) + return ""; + + int sl = env->GetStringLength(str); + if (sl==0) { + env->DeleteLocalRef((jobject)str); + return ""; + } + + CharString cs; + cs.resize(sl+1); + env->GetStringRegion(str,0,sl,(jchar*)&cs[0]); + cs[sl]=0; + + String ret; + ret.parse_utf8(&cs[0]); + env->DeleteLocalRef((jobject)str); + + return ret; + +} +bool DirAccessJAndroid::current_is_dir() const{ + + JNIEnv *env = ThreadAndroid::get_env(); + String sd; + if (current_dir=="") + sd=current; + else + sd=current_dir+"/"+current; + + jstring js = env->NewStringUTF(sd.utf8().get_data()); + + int res = env->CallIntMethod(io,_dir_open,js); + if (res<=0) + return false; + + env->CallObjectMethod(io,_dir_close,res); + + + return true; +} +void DirAccessJAndroid::list_dir_end(){ + + if (id==0) + return; + + JNIEnv *env = ThreadAndroid::get_env(); + env->CallObjectMethod(io,_dir_close,id); + id=0; + + +} + +int DirAccessJAndroid::get_drive_count(){ + + return 0; +} +String DirAccessJAndroid::get_drive(int p_drive){ + + return ""; +} + +Error DirAccessJAndroid::change_dir(String p_dir){ + + JNIEnv *env = ThreadAndroid::get_env(); + p_dir=p_dir.simplify_path(); + + if (p_dir=="" || p_dir=="." || (p_dir==".." && current_dir=="")) + return OK; + + String new_dir; + + if (p_dir.begins_with("/")) + new_dir=p_dir.substr(1,p_dir.length()); + else if (p_dir.begins_with("res://")) + new_dir=p_dir.substr(6,p_dir.length()); + else //relative + new_dir=new_dir+"/"+p_dir; + +//test if newdir exists + new_dir=new_dir.simplify_path(); + + jstring js = env->NewStringUTF(new_dir.utf8().get_data()); + int res = env->CallIntMethod(io,_dir_open,js); + if (res<=0) + return ERR_INVALID_PARAMETER; + + env->CallObjectMethod(io,_dir_close,res); + + + + return OK; +} + +String DirAccessJAndroid::get_current_dir(){ + + return "/"+current_dir; +} + +bool DirAccessJAndroid::file_exists(String p_file){ + + JNIEnv *env = ThreadAndroid::get_env(); + String sd; + if (current_dir=="") + sd=p_file; + else + sd=current_dir+"/"+p_file; + + FileAccessJAndroid *f = memnew(FileAccessJAndroid); + bool exists = f->file_exists(sd); + memdelete(f); + + return exists; +} + + +Error DirAccessJAndroid::make_dir(String p_dir){ + + ERR_FAIL_V(ERR_UNAVAILABLE); +} + +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); +} + +//FileType get_file_type() const; +size_t DirAccessJAndroid::get_space_left() { + + return 0; +} + + +void DirAccessJAndroid::setup( jobject p_io) { + + + JNIEnv *env = ThreadAndroid::get_env(); + io=p_io; + __android_log_print(ANDROID_LOG_INFO,"godot","STEP7"); + + jclass c = env->GetObjectClass(io); + cls = (jclass)env->NewGlobalRef(c); + __android_log_print(ANDROID_LOG_INFO,"godot","STEP8"); + + _dir_open = env->GetMethodID(cls, "dir_open", "(Ljava/lang/String;)I"); + if(_dir_open != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_open ok!!"); + } + _dir_next = env->GetMethodID(cls, "dir_next", "(I)Ljava/lang/String;"); + if(_dir_next != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_next ok!!"); + } + _dir_close = env->GetMethodID(cls, "dir_close", "(I)V"); + if(_dir_close != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_close ok!!"); + } + +// (*env)->CallVoidMethod(env,obj,aMethodID, myvar); +} + + +DirAccessJAndroid::DirAccessJAndroid() { + + id=0; +} + +DirAccessJAndroid::~DirAccessJAndroid() { + + list_dir_end();; +} +#endif diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h new file mode 100644 index 0000000000..b6e3fe393f --- /dev/null +++ b/platform/android/dir_access_jandroid.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* dir_access_jandroid.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 DIR_ACCESS_JANDROID_H +#define DIR_ACCESS_JANDROID_H + +#ifndef ANDROID_NATIVE_ACTIVITY + + +#include "java_glue.h" +#include "os/dir_access.h" +#include <stdio.h> + + +class DirAccessJAndroid : public DirAccess { + + //AAssetDir* aad; + + static jobject io; + static jclass cls; + + static jmethodID _dir_open; + static jmethodID _dir_next; + static jmethodID _dir_close; + + int id; + + String current_dir; + String current; + + static DirAccess *create_fs(); + +public: + + virtual bool list_dir_begin(); ///< This starts dir listing + virtual String get_next(); + virtual bool current_is_dir() const; + virtual void list_dir_end(); ///< + + virtual int get_drive_count(); + virtual String get_drive(int p_drive); + + virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success + virtual String get_current_dir(); ///< return current dir location + + + virtual bool file_exists(String p_file); + + virtual Error make_dir(String p_dir); + + virtual Error rename(String p_from, String p_to); + virtual Error remove(String p_name); + + //virtual FileType get_file_type() const; + size_t get_space_left(); + + + static void setup( jobject io); + + DirAccessJAndroid(); + ~DirAccessJAndroid(); +}; + +#endif // DIR_ACCESS_JANDROID_H +#endif diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp new file mode 100644 index 0000000000..a9b96a116b --- /dev/null +++ b/platform/android/export/export.cpp @@ -0,0 +1,1229 @@ +#include "version.h" +#include "export.h" +#include "tools/editor/editor_settings.h" +#include "tools/editor/editor_import_export.h" +#include "tools/editor/editor_node.h" +#include "io/zip_io.h" +#include "io/marshalls.h" +#include "globals.h" +#include "os/file_access.h" +#include "os/os.h" +#include "platform/android/logo.h" + +class EditorExportPlatformAndroid : public EditorExportPlatform { + + OBJ_TYPE( EditorExportPlatformAndroid,EditorExportPlatform ); + + String custom_release_package; + String custom_debug_package; + + int version_code; + String version_name; + String package; + String name; + String icon; + bool _signed; + int orientation; + + String release_keystore; + String release_username; + + struct APKExportData { + + zipFile apk; + EditorProgress *ep; + }; + + struct Device { + + String id; + String name; + String description; + }; + + Vector<Device> devices; + bool devices_changed; + Mutex *device_lock; + Thread *device_thread; + Ref<ImageTexture> logo; + + volatile bool quit_request; + + + static void _device_poll_thread(void *ud); + + String get_project_name() const; + void _fix_manifest(Vector<uint8_t>& p_manifest); + void _fix_resources(Vector<uint8_t>& p_manifest); + static Error save_apk_file(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total); + +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + + virtual String get_name() const { return "Android"; } + virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_ETC1; } + virtual Ref<Texture> get_logo() const { return logo; } + + + virtual bool poll_devices(); + virtual int get_device_count() const; + virtual String get_device_name(int p_device) const; + virtual String get_device_info(int p_device) const; + virtual Error run(int p_device); + + virtual bool requieres_password(bool p_debug) const { return !p_debug; } + virtual String get_binary_extension() const { return "apk"; } + virtual Error export_project(const String& p_path,bool p_debug,const String& p_password=""); + + virtual bool can_export(String *r_error=NULL) const; + + EditorExportPlatformAndroid(); + ~EditorExportPlatformAndroid(); +}; + +bool EditorExportPlatformAndroid::_set(const StringName& p_name, const Variant& p_value) { + + String n=p_name; + + if (n=="version/code") + version_code=p_value; + else if (n=="version/name") + version_name=p_value; + else if (n=="package/unique_name") + package=p_value; + else if (n=="package/name") + name=p_value; + else if (n=="package/icon") + icon=p_value; + else if (n=="package/signed") + _signed=p_value; + else if (n=="screen/orientation") + orientation=p_value; + else if (n=="keystore/release") + release_keystore=p_value; + else if (n=="keystore/release_user") + release_username=p_value; + else + return false; + + return true; +} + +bool EditorExportPlatformAndroid::_get(const StringName& p_name,Variant &r_ret) const{ + + String n=p_name; + + if (n=="version/code") + r_ret=version_code; + else if (n=="version/name") + r_ret=version_name; + else if (n=="package/unique_name") + r_ret=package; + else if (n=="package/name") + r_ret=name; + else if (n=="package/icon") + r_ret=icon; + else if (n=="package/signed") + r_ret=_signed; + else if (n=="screen/orientation") + r_ret=orientation; + else if (n=="keystore/release") + r_ret=release_keystore; + else if (n=="keystore/release_user") + r_ret=release_username; + else + return false; + + return true; +} +void EditorExportPlatformAndroid::_get_property_list( List<PropertyInfo> *p_list) const{ + + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_FILE,"apk")); + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_FILE,"apk")); + p_list->push_back( PropertyInfo( Variant::INT, "version/code", PROPERTY_HINT_RANGE,"1,65535,1")); + p_list->push_back( PropertyInfo( Variant::STRING, "version/name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/unique_name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/name") ); + p_list->push_back( PropertyInfo( Variant::STRING, "package/icon",PROPERTY_HINT_FILE,"png") ); + p_list->push_back( PropertyInfo( Variant::BOOL, "package/signed") ); + p_list->push_back( PropertyInfo( Variant::INT, "screen/orientation",PROPERTY_HINT_ENUM,"Landscape,Portrait") ); + p_list->push_back( PropertyInfo( Variant::STRING, "keystore/release",PROPERTY_HINT_FILE,"keystore") ); + p_list->push_back( PropertyInfo( Variant::STRING, "keystore/release_user" ) ); + + //p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)")); + +} + + +static String _parse_string(const uint8_t *p_bytes,bool p_utf8) { + + uint32_t offset=0; + uint32_t len = decode_uint16(&p_bytes[offset]); + + if (p_utf8) { + //don't know how to read extended utf8, this will have to be for now + len>>=8; + + } + offset+=2; + printf("len %i, unicode: %i\n",len,int(p_utf8)); + + if (p_utf8) { + + Vector<uint8_t> str8; + str8.resize(len+1); + for(int i=0;i<len;i++) { + str8[i]=p_bytes[offset+i]; + } + str8[len]=0; + String str; + str.parse_utf8((const char*)str8.ptr()); + return str; + } else { + + String str; + for(int i=0;i<len;i++) { + CharType c = decode_uint16(&p_bytes[offset+i*2]); + if (c==0) + break; + str += String::chr(c); + } + return str; + } + +} + +void EditorExportPlatformAndroid::_fix_resources(Vector<uint8_t>& p_manifest) { + + + const int UTF8_FLAG = 0x00000100; + print_line("*******************GORRRGLE***********************"); + + uint32_t header = decode_uint32(&p_manifest[0]); + uint32_t filesize = decode_uint32(&p_manifest[4]); + uint32_t string_block_len = decode_uint32(&p_manifest[16]); + uint32_t string_count = decode_uint32(&p_manifest[20]); + uint32_t string_flags = decode_uint32(&p_manifest[28]); + const uint32_t string_table_begins = 40; + + Vector<String> string_table; + + printf("stirng block len: %i\n",string_block_len); + printf("stirng count: %i\n",string_count); + printf("flags: %x\n",string_flags); + + for(int i=0;i<string_count;i++) { + + uint32_t offset = decode_uint32(&p_manifest[string_table_begins+i*4]); + offset+=string_table_begins+string_count*4; + + String str = _parse_string(&p_manifest[offset],string_flags&UTF8_FLAG); + + if (str.begins_with("godot-project-name")) { + + + if (str=="godot-project-name") { + //project name + str = get_project_name(); + + } else { + + String lang = str.substr(str.find_last("-")+1,str.length()).replace("-","_"); + String prop = "application/name_"+lang; + if (Globals::get_singleton()->has(prop)) { + str = Globals::get_singleton()->get(prop); + } else { + str = get_project_name(); + } + } + } + + string_table.push_back(str); + + } + + //write a new string table, but use 16 bits + Vector<uint8_t> ret; + ret.resize(string_table_begins+string_table.size()*4); + + for(int i=0;i<string_table_begins;i++) { + + ret[i]=p_manifest[i]; + } + + int ofs=0; + for(int i=0;i<string_table.size();i++) { + + encode_uint32(ofs,&ret[string_table_begins+i*4]); + ofs+=string_table[i].length()*2+2+2; + } + + ret.resize(ret.size()+ofs); + uint8_t *chars=&ret[ret.size()-ofs]; + for(int i=0;i<string_table.size();i++) { + + String s = string_table[i]; + encode_uint16(s.length(),chars); + chars+=2; + for(int j=0;j<s.length();j++) { + encode_uint16(s[j],chars); + chars+=2; + } + encode_uint16(0,chars); + chars+=2; + } + + //pad + while(ret.size()%4) + ret.push_back(0); + + //change flags to not use utf8 + encode_uint32(string_flags&~0x100,&ret[28]); + //change length + encode_uint32(ret.size()-12,&ret[16]); + //append the rest... + int rest_from = 12+string_block_len; + int rest_to = ret.size(); + int rest_len = (p_manifest.size() - rest_from); + ret.resize(ret.size() + (p_manifest.size() - rest_from) ); + for(int i=0;i<rest_len;i++) { + ret[rest_to+i]=p_manifest[rest_from+i]; + } + //finally update the size + encode_uint32(ret.size(),&ret[4]); + + + p_manifest=ret; + printf("end\n"); +} + +String EditorExportPlatformAndroid::get_project_name() const { + + String aname; + if (this->name!="") { + aname=this->name; + } else { + aname = Globals::get_singleton()->get("application/name"); + + } + + if (aname=="") { + aname=_MKSTR(VERSION_NAME); + } + + return aname; +} + + +void EditorExportPlatformAndroid::_fix_manifest(Vector<uint8_t>& p_manifest) { + + + const int CHUNK_AXML_FILE = 0x00080003; + const int CHUNK_RESOURCEIDS = 0x00080180; + const int CHUNK_STRINGS = 0x001C0001; + const int CHUNK_XML_END_NAMESPACE = 0x00100101; + const int CHUNK_XML_END_TAG = 0x00100103; + const int CHUNK_XML_START_NAMESPACE = 0x00100100; + const int CHUNK_XML_START_TAG = 0x00100102; + const int CHUNK_XML_TEXT = 0x00100104; + const int UTF8_FLAG = 0x00000100; + + Vector<String> string_table; + + uint32_t ofs=0; + + + uint32_t header = decode_uint32(&p_manifest[ofs]); + uint32_t filesize = decode_uint32(&p_manifest[ofs+4]); + ofs+=8; + +// print_line("FILESIZE: "+itos(filesize)+" ACTUAL: "+itos(p_manifest.size())); + + uint32_t string_count; + uint32_t styles_count; + uint32_t string_flags; + uint32_t string_data_offset; + + uint32_t styles_offset; + uint32_t string_table_begins; + uint32_t string_table_ends; + Vector<uint8_t> stable_extra; + + while(ofs < p_manifest.size()) { + + uint32_t chunk = decode_uint32(&p_manifest[ofs]); + uint32_t size = decode_uint32(&p_manifest[ofs+4]); + + + switch(chunk) { + + case CHUNK_STRINGS: { + + + int iofs=ofs+8; + + uint32_t string_count=decode_uint32(&p_manifest[iofs]); + uint32_t styles_count=decode_uint32(&p_manifest[iofs+4]); + uint32_t string_flags=decode_uint32(&p_manifest[iofs+8]); + uint32_t string_data_offset=decode_uint32(&p_manifest[iofs+12]); + uint32_t styles_offset=decode_uint32(&p_manifest[iofs+16]); +/* + printf("string count: %i\n",string_count); + printf("flags: %i\n",string_flags); + printf("sdata ofs: %i\n",string_data_offset); + printf("styles ofs: %i\n",styles_offset); +*/ + uint32_t st_offset=iofs+20; + string_table.resize(string_count); + uint32_t string_end=0; + + string_table_begins=st_offset; + + + for(int i=0;i<string_count;i++) { + + uint32_t string_at = decode_uint32(&p_manifest[st_offset+i*4]); + string_at+=st_offset+string_count*4; + + ERR_EXPLAIN("Unimplemented, can't read utf8 string table."); + ERR_FAIL_COND(string_flags&UTF8_FLAG); + + if (string_flags&UTF8_FLAG) { + + + + } else { + uint32_t len = decode_uint16(&p_manifest[string_at]); + Vector<CharType> ucstring; + ucstring.resize(len+1); + for(int j=0;j<len;j++) { + uint16_t c=decode_uint16(&p_manifest[string_at+2+2*j]); + ucstring[j]=c; + } + string_end=MAX(string_at+2+2*len,string_end); + ucstring[len]=0; + string_table[i]=ucstring.ptr(); + } + + +// print_line("String "+itos(i)+": "+string_table[i]); + } + + for(int i=string_end;i<(ofs+size);i++) { + stable_extra.push_back(p_manifest[i]); + } + +// printf("stable extra: %i\n",int(stable_extra.size())); + string_table_ends=ofs+size; + +// print_line("STABLE SIZE: "+itos(size)+" ACTUAL: "+itos(string_table_ends)); + + } break; + case CHUNK_XML_START_TAG: { + + int iofs=ofs+8; + uint32_t line=decode_uint32(&p_manifest[iofs]); + uint32_t nspace=decode_uint32(&p_manifest[iofs+8]); + uint32_t name=decode_uint32(&p_manifest[iofs+12]); + uint32_t check=decode_uint32(&p_manifest[iofs+16]); + + String tname=string_table[name]; + +// printf("NSPACE: %i\n",nspace); + //printf("NAME: %i (%s)\n",name,tname.utf8().get_data()); + //printf("CHECK: %x\n",check); + uint32_t attrcount=decode_uint32(&p_manifest[iofs+20]); + iofs+=28; + //printf("ATTRCOUNT: %x\n",attrcount); + for(int i=0;i<attrcount;i++) { + uint32_t attr_nspace=decode_uint32(&p_manifest[iofs]); + uint32_t attr_name=decode_uint32(&p_manifest[iofs+4]); + uint32_t attr_value=decode_uint32(&p_manifest[iofs+8]); + uint32_t attr_flags=decode_uint32(&p_manifest[iofs+12]); + uint32_t attr_resid=decode_uint32(&p_manifest[iofs+16]); + + + String value; + if (attr_value!=0xFFFFFFFF) + value=string_table[attr_value]; + else + value="Res #"+itos(attr_resid); + String attrname = string_table[attr_name]; + String nspace; + if (attr_nspace!=0xFFFFFFFF) + nspace=string_table[attr_nspace]; + else + nspace=""; + + printf("ATTR %i NSPACE: %i\n",i,attr_nspace); + printf("ATTR %i NAME: %i (%s)\n",i,attr_name,attrname.utf8().get_data()); + printf("ATTR %i VALUE: %i (%s)\n",i,attr_value,value.utf8().get_data()); + printf("ATTR %i FLAGS: %x\n",i,attr_flags); + printf("ATTR %i RESID: %x\n",i,attr_resid); + + //replace project information + if (tname=="manifest" && attrname=="package") { + + print_line("FOUND PACKAGE"); + string_table[attr_value]=package; + } + + //print_line("tname: "+tname); + //print_line("nspace: "+nspace); + //print_line("attrname: "+attrname); + if (tname=="manifest" && /*nspace=="android" &&*/ attrname=="versionCode") { + + print_line("FOUND versioncode"); + encode_uint32(version_code,&p_manifest[iofs+16]); + } + + + if (tname=="manifest" && /*nspace=="android" &&*/ attrname=="versionName") { + + print_line("FOUND versionname"); + if (attr_value==0xFFFFFFFF) { + WARN_PRINT("Version name in a resource, should be plaintext") + } else + string_table[attr_value]=version_name; + } + + if (tname=="activity" && /*nspace=="android" &&*/ attrname=="screenOrientation") { + + print_line("FOUND screen orientation"); + if (attr_value==0xFFFFFFFF) { + WARN_PRINT("Version name in a resource, should be plaintext") + } else { + string_table[attr_value]=(orientation==0?"landscape":"portrait"); + } + } + + if (tname=="application" && /*nspace=="android" &&*/ attrname=="label") { + + print_line("FOUND application"); + if (attr_value==0xFFFFFFFF) { + WARN_PRINT("Application name in a resource, should be plaintext.") + } else { + + String aname = get_project_name(); + string_table[attr_value]=aname; + } + } + if (tname=="activity" && /*nspace=="android" &&*/ attrname=="label") { + + print_line("FOUND activity name"); + if (attr_value==0xFFFFFFFF) { + WARN_PRINT("Activity name in a resource, should be plaintext") + } else { + String aname; + if (this->name!="") { + aname=this->name; + } else { + aname = Globals::get_singleton()->get("application/name"); + + } + + if (aname=="") { + aname=_MKSTR(VERSION_NAME); + } + + print_line("APP NAME IS..."+aname); + string_table[attr_value]=aname; + } + } + + iofs+=20; + } + + } break; + } + printf("chunk %x: size: %d\n",chunk,size); + + ofs+=size; + } + + printf("end\n"); + + //create new andriodmanifest binary + + Vector<uint8_t> ret; + ret.resize(string_table_begins+string_table.size()*4); + + for(int i=0;i<string_table_begins;i++) { + + ret[i]=p_manifest[i]; + } + + ofs=0; + for(int i=0;i<string_table.size();i++) { + + encode_uint32(ofs,&ret[string_table_begins+i*4]); + ofs+=string_table[i].length()*2+2+2; + print_line("ofs: "+itos(i)+": "+itos(ofs)); + } + ret.resize(ret.size()+ofs); + uint8_t *chars=&ret[ret.size()-ofs]; + for(int i=0;i<string_table.size();i++) { + + String s = string_table[i]; + print_line("savint string :"+s); + encode_uint16(s.length(),chars); + chars+=2; + for(int j=0;j<s.length();j++) { //include zero? + encode_uint16(s[j],chars); + chars+=2; + } + encode_uint16(0,chars); + chars+=2; + + } + + + ret.resize(ret.size()+stable_extra.size()); + while(ret.size()%4) + ret.push_back(0); + + for(int i=0;i<stable_extra.size();i++) { + + chars[i]=stable_extra[i]; + } + + + uint32_t new_stable_end=ret.size(); + + uint32_t extra = (p_manifest.size()-string_table_ends); + ret.resize(new_stable_end + extra); + for(int i=0;i<extra;i++) + ret[new_stable_end+i]=p_manifest[string_table_ends+i]; + + while(ret.size()%4) + ret.push_back(0); + encode_uint32(ret.size(),&ret[4]); //update new file size + + encode_uint32(new_stable_end-8,&ret[12]); //update new string table size + + print_line("file size: "+itos(ret.size())); + + p_manifest=ret; + + + + + + +#if 0 + uint32_t header[9]; + for(int i=0;i<9;i++) { + header[i]=decode_uint32(&p_manifest[i*4]); + } + + print_line("STO: "+itos(header[3])); + uint32_t st_offset=9*4; + //ERR_FAIL_COND(header[3]!=0x24) + uint32_t string_count=header[4]; + + + string_table.resize(string_count); + + for(int i=0;i<string_count;i++) { + + uint32_t string_at = decode_uint32(&p_manifest[st_offset+i*4]); + string_at+=st_offset+string_count*4; + uint32_t len = decode_uint16(&p_manifest[string_at]); + Vector<CharType> ucstring; + ucstring.resize(len+1); + for(int j=0;j<len;j++) { + uint16_t c=decode_uint16(&p_manifest[string_at+2+2*j]); + ucstring[j]=c; + } + ucstring[len]=0; + string_table[i]=ucstring.ptr(); + } + + +#endif + +} + + + +Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total) { + + APKExportData *ed=(APKExportData*)p_userdata; + String dst_path=p_path; + dst_path=dst_path.replace_first("res://","assets/"); + + zipOpenNewFileInZip(ed->apk, + dst_path.utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + + zipWriteInFileInZip(ed->apk,p_data.ptr(),p_data.size()); + zipCloseFileInZip(ed->apk); + ed->ep->step("File: "+p_path,3+p_file*100/p_total); + return OK; + +} + + + +Error EditorExportPlatformAndroid::export_project(const String& p_path,bool p_debug,const String& p_password) { + + String src_apk; + + EditorProgress ep("export","Exporting for Android",104); + + String apk_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (p_debug) { + + src_apk=custom_debug_package!=""?custom_debug_package:apk_path+"android_debug.apk"; + } else { + + src_apk=custom_release_package!=""?custom_release_package:apk_path+"android_release.apk"; + + } + + + FileAccess *src_f=NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + + + ep.step("Creating APK",0); + + unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io); + if (!pkg) { + + EditorNode::add_io_error("Could not find template APK to export:\n"+src_apk); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(pkg); + + zlib_filefunc_def io2=io; + FileAccess *dst_f=NULL; + io2.opaque=&dst_f; + zipFile apk=zipOpen2(p_path.utf8().get_data(),APPEND_STATUS_CREATE,NULL,&io2); + + + while(ret==UNZ_OK) { + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0); + + String file=fname; + + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg,data.ptr(),data.size()); + unzCloseCurrentFile(pkg); + + //write + + if (file=="AndroidManifest.xml") { + + _fix_manifest(data); + } + + if (file=="resources.arsc") { + + _fix_resources(data); + } + + if (file=="res/drawable/icon.png") { + bool found=false; + + if (this->icon!="" && this->icon.ends_with(".png")) { + + FileAccess *f = FileAccess::open(this->icon,FileAccess::READ); + if (f) { + + data.resize(f->get_len()); + f->get_buffer(data.ptr(),data.size()); + memdelete(f); + found=true; + } + + } + + if (!found) { + + String appicon = Globals::get_singleton()->get("application/icon"); + if (appicon!="" && appicon.ends_with(".png")) { + FileAccess*f = FileAccess::open(appicon,FileAccess::READ); + if (f) { + data.resize(f->get_len()); + f->get_buffer(data.ptr(),data.size()); + memdelete(f); + } + } + } + } + + print_line("ADDING: "+file); + zipOpenNewFileInZip(apk, + file.utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + zipWriteInFileInZip(apk,data.ptr(),data.size()); + zipCloseFileInZip(apk); + + ret = unzGoToNextFile(pkg); + } + + + ep.step("Adding Files..",1); + + + + APKExportData ed; + ed.ep=&ep; + ed.apk=apk; + + Error err = export_project_files(save_apk_file,&ed,false); + + zipClose(apk,NULL); + unzClose(pkg); + + if (err) { + return err; + } + + + + if (_signed) { + + + String jarsigner=EditorSettings::get_singleton()->get("android/jarsigner"); + if (!FileAccess::exists(jarsigner)) { + EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the editor settings.\nResulting apk is unsigned."); + return OK; + } + + String keystore; + String password; + String user; + if (p_debug) { + keystore=EditorSettings::get_singleton()->get("android/debug_keystore"); + password=EditorSettings::get_singleton()->get("android/debug_keystore_pass"); + user=EditorSettings::get_singleton()->get("android/debug_keystore_user"); + + ep.step("Signing Debug APK..",103); + + } else { + keystore=release_keystore; + password=p_password; + user=release_username; + + ep.step("Signing Release APK..",103); + + } + + if (!FileAccess::exists(keystore)) { + EditorNode::add_io_error("Could not find keytore, unable to export."); + return ERR_FILE_CANT_OPEN; + } + + List<String> args; + args.push_back("-digestalg"); + args.push_back("SHA1"); + args.push_back("-sigalg"); + args.push_back("MD5withRSA"); + args.push_back("-verbose"); + args.push_back("-keystore"); + args.push_back(keystore); + args.push_back("-storepass"); + args.push_back(password); + args.push_back(p_path); + args.push_back(user); + int retval; + int err = OS::get_singleton()->execute(jarsigner,args,true,NULL,NULL,&retval); + if (retval) { + EditorNode::add_io_error("'jarsigner' returned with error #"+itos(retval)); + return ERR_CANT_CREATE; + } + + ep.step("Verifying APK..",104); + + args.clear(); + args.push_back("-verify"); + args.push_back(p_path); + args.push_back("-verbose"); + + err = OS::get_singleton()->execute(jarsigner,args,true,NULL,NULL,&retval); + if (retval) { + EditorNode::add_io_error("'jarsigner' verificaiton of APK failed. Make sure to use jarsigner from Java 6."); + return ERR_CANT_CREATE; + } + + } + return OK; + +} + + +bool EditorExportPlatformAndroid::poll_devices() { + + bool dc=devices_changed; + devices_changed=false; + return dc; +} + +int EditorExportPlatformAndroid::get_device_count() const { + + device_lock->lock(); + int dc=devices.size(); + device_lock->unlock(); + + return dc; + +} +String EditorExportPlatformAndroid::get_device_name(int p_device) const { + + ERR_FAIL_INDEX_V(p_device,devices.size(),""); + device_lock->lock(); + String s=devices[p_device].name; + device_lock->unlock(); + return s; +} +String EditorExportPlatformAndroid::get_device_info(int p_device) const { + + ERR_FAIL_INDEX_V(p_device,devices.size(),""); + device_lock->lock(); + String s=devices[p_device].description; + device_lock->unlock(); + return s; +} + +void EditorExportPlatformAndroid::_device_poll_thread(void *ud) { + + EditorExportPlatformAndroid *ea=(EditorExportPlatformAndroid *)ud; + + + while(!ea->quit_request) { + + String adb=EditorSettings::get_singleton()->get("android/adb"); + if (!FileAccess::exists(adb)) { + OS::get_singleton()->delay_usec(3000000); + continue; //adb not configured + } + + String devices; + List<String> args; + args.push_back("devices"); + int ec; + Error err = OS::get_singleton()->execute(adb,args,true,NULL,&devices,&ec); + Vector<String> ds = devices.split("\n"); + Vector<String> ldevices; + for(int i=1;i<ds.size();i++) { + + String d = ds[i]; + int dpos = d.find("device"); + if (dpos==-1) + continue; + d=d.substr(0,dpos).strip_edges(); +// print_line("found devuce: "+d); + ldevices.push_back(d); + } + + ea->device_lock->lock(); + + bool different=false; + + if (devices.size()!=ldevices.size()) { + + different=true; + } else { + + for(int i=0;i<ea->devices.size();i++) { + + if (ea->devices[i].id!=ldevices[i]) { + different=true; + break; + } + } + } + + if (different) { + + + Vector<Device> ndevices; + + for(int i=0;i<ldevices.size();i++) { + + Device d; + d.id=ldevices[i]; + for(int j=0;j<ea->devices.size();j++) { + if (ea->devices[j].id==ldevices[i]) { + d.description=ea->devices[j].description; + d.name=ea->devices[j].name; + } + } + + if (d.description=="") { + //in the oven, request! + args.clear(); + args.push_back("-s"); + args.push_back(d.id); + args.push_back("shell"); + args.push_back("cat"); + args.push_back("/system/build.prop"); + int ec; + 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; + d.description+"Device ID: "+d.id+"\n"; + for(int j=0;j<props.size();j++) { + + String p = props[j]; + if (p.begins_with("ro.product.model=")) { + device=p.get_slice("=",1).strip_edges(); + } else if (p.begins_with("ro.product.brand=")) { + vendor=p.get_slice("=",1).strip_edges().capitalize(); + } else if (p.begins_with("ro.build.display.id=")) { + d.description+="Build: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.build.version.release=")) { + d.description+="Release: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.product.cpu.abi=")) { + d.description+="CPU: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.product.manufacturer=")) { + d.description+="Manufacturer: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.board.platform=")) { + d.description+="Chipset: "+p.get_slice("=",1).strip_edges()+"\n"; + } else if (p.begins_with("ro.opengles.version=")) { + uint32_t opengl = p.get_slice("=",1).to_int(); + d.description+="OpenGL: "+itos(opengl>>16)+"."+itos((opengl>>8)&0xFF)+"."+itos((opengl)&0xFF)+"\n"; + } + } + + d.name=vendor+" "+device; +// print_line("name: "+d.name); +// print_line("description: "+d.description); + + } + + ndevices.push_back(d); + + } + + ea->devices=ndevices; + ea->devices_changed=true; + } + + ea->device_lock->unlock(); + + OS::get_singleton()->delay_usec(3000000); + } + +} + +Error EditorExportPlatformAndroid::run(int p_device) { + + ERR_FAIL_INDEX_V(p_device,devices.size(),ERR_INVALID_PARAMETER); + device_lock->lock(); + + EditorProgress ep("run","Running on "+devices[p_device].name,3); + + String adb=EditorSettings::get_singleton()->get("android/adb"); + if (adb=="") { + + EditorNode::add_io_error("ADB executable not configured in settings, can't run."); + device_lock->unlock(); + return ERR_UNCONFIGURED; + } + + //export_temp + ep.step("Exporting APK",0); + + String export_to=EditorSettings::get_singleton()->get_settings_path()+"/tmp/tmpexport.apk"; + Error err = export_project(export_to,true); + if (err) { + device_lock->unlock(); + return err; + } + + ep.step("Uninstalling..",1); + + print_line("Uninstalling previous version: "+devices[p_device].name); + List<String> args; + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("uninstall"); + args.push_back(package); + int rv; + err = OS::get_singleton()->execute(adb,args,true,NULL,NULL,&rv); +#if 0 + if (err || rv!=0) { + EditorNode::add_io_error("Could not install to device."); + device_lock->unlock(); + return ERR_CANT_CREATE; + } +#endif + print_line("Installing into device (please wait..): "+devices[p_device].name); + ep.step("Installing to Device (please wait..)..",2); + + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("install"); + args.push_back(export_to); + rv; + err = OS::get_singleton()->execute(adb,args,true,NULL,NULL,&rv); + if (err || rv!=0) { + EditorNode::add_io_error("Could not install to device."); + device_lock->unlock(); + return ERR_CANT_CREATE; + } + + ep.step("Running on Device..",3); + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("shell"); + args.push_back("am"); + args.push_back("start"); + args.push_back("-a"); + args.push_back("android.intent.action.MAIN"); + args.push_back("-n"); + args.push_back(package+"/com.android.godot.Godot"); + + err = OS::get_singleton()->execute(adb,args,true,NULL,NULL,&rv); + if (err || rv!=0) { + EditorNode::add_io_error("Could not execute ondevice."); + device_lock->unlock(); + return ERR_CANT_CREATE; + } + device_lock->unlock(); + return OK; +} + + +EditorExportPlatformAndroid::EditorExportPlatformAndroid() { + + version_code=1; + version_name="1.0"; + package="com.android.noname"; + name=""; + _signed=true; + device_lock = Mutex::create(); + quit_request=false; + orientation=0; + + device_thread=Thread::create(_device_poll_thread,this); + devices_changed=true; + + Image img( _android_logo ); + logo = Ref<ImageTexture>( memnew( ImageTexture )); + logo->create_from_image(img); +} + +bool EditorExportPlatformAndroid::can_export(String *r_error) const { + + bool valid=true; + String adb=EditorSettings::get_singleton()->get("android/adb"); + String err; + + if (!FileAccess::exists(adb)) { + + valid=false; + err+="ADB executable not configured in editor settings.\n"; + } + + String js = EditorSettings::get_singleton()->get("android/jarsigner"); + + if (!FileAccess::exists(js)) { + + valid=false; + err+="OpenJDK 6 jarsigner not configured in editor settings.\n"; + } + + String dk = EditorSettings::get_singleton()->get("android/debug_keystore"); + + if (!FileAccess::exists(dk)) { + + valid=false; + err+="Debug Keystore not configured in editor settings.\n"; + } + + + String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (!FileAccess::exists(exe_path+"android_debug.apk") || !FileAccess::exists(exe_path+"android_release.apk")) { + valid=false; + err+="No export templates found.\nDownload and install export templates.\n"; + } + + if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) { + valid=false; + err+="Custom debug package not found.\n"; + } + + if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) { + valid=false; + err+="Custom release package not found.\n"; + } + + if (r_error) + *r_error=err; + + return valid; +} + + +EditorExportPlatformAndroid::~EditorExportPlatformAndroid() { + + quit_request=true; + Thread::wait_to_finish(device_thread); +} + + +void register_android_exporter() { + + String exe_ext=OS::get_singleton()->get_name()=="Windows"?"exe":""; + EDITOR_DEF("android/adb",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"android/adb",PROPERTY_HINT_GLOBAL_FILE,exe_ext)); + EDITOR_DEF("android/jarsigner",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"android/jarsigner",PROPERTY_HINT_GLOBAL_FILE,exe_ext)); + EDITOR_DEF("android/debug_keystore",""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"android/debug_keystore",PROPERTY_HINT_GLOBAL_FILE,"keystore")); + EDITOR_DEF("android/debug_keystore_user","androiddebugkey"); + EDITOR_DEF("android/debug_keystore_pass","android"); + //EDITOR_DEF("android/release_keystore",""); + //EDITOR_DEF("android/release_username",""); + //EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"android/release_keystore",PROPERTY_HINT_GLOBAL_FILE,"*.keystore")); + + Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>( memnew(EditorExportPlatformAndroid) ); + EditorImportExport::get_singleton()->add_export_platform(exporter); + + +} + diff --git a/platform/android/export/export.h b/platform/android/export/export.h new file mode 100644 index 0000000000..88581802b8 --- /dev/null +++ b/platform/android/export/export.h @@ -0,0 +1,3 @@ + + +void register_android_exporter(); diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp new file mode 100644 index 0000000000..f1a2bf5882 --- /dev/null +++ b/platform/android/file_access_android.cpp @@ -0,0 +1,187 @@ +/*************************************************************************/ +/* file_access_android.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "file_access_android.h" +#include "print_string.h" + +#ifdef ANDROID_NATIVE_ACTIVITY + + +AAssetManager *FileAccessAndroid::asset_manager=NULL; + + +void FileAccessAndroid::make_default() { + + create_func=create_android; +} + +FileAccess* FileAccessAndroid::create_android() { + + return memnew(FileAccessAndroid); +} + + +Error FileAccessAndroid::open(const String& p_path, int p_mode_flags) { + + String path=fix_path(p_path).simplify_path(); + if (path.begins_with("/")) + path=path.substr(1,path.length()); + else if (path.begins_with("res://")) + path=path.substr(6,path.length()); + + + + ERR_FAIL_COND_V(p_mode_flags&FileAccess::WRITE,ERR_UNAVAILABLE); //can't write on android.. + a=AAssetManager_open(asset_manager,path.utf8().get_data(),AASSET_MODE_STREAMING); + if (!a) + return ERR_CANT_OPEN; + //ERR_FAIL_COND_V(!a,ERR_FILE_NOT_FOUND); + len=AAsset_getLength(a); + pos=0; + eof=false; + + return OK; +} + +void FileAccessAndroid::close() { + + if (!a) + return; + AAsset_close(a); + a=NULL; +} +bool FileAccessAndroid::is_open() const { + + return a!=NULL; +} + +void FileAccessAndroid::seek(size_t p_position) { + + ERR_FAIL_COND(!a); + AAsset_seek(a,p_position,SEEK_SET); + pos=p_position; + if (pos>len) { + pos=len; + eof=true; + } else { + eof=false; + } + +} +void FileAccessAndroid::seek_end(int64_t p_position) { + + ERR_FAIL_COND(!a); + AAsset_seek(a,p_position,SEEK_END); + pos=len+p_position; + +} +size_t FileAccessAndroid::get_pos() const { + + return pos; +} +size_t FileAccessAndroid::get_len() const { + + return len; +} + +bool FileAccessAndroid::eof_reached() const { + + return eof; +} + +uint8_t FileAccessAndroid::get_8() const { + + + if (pos>=len) { + eof=true; + return 0; + } + + + uint8_t byte; + AAsset_read(a,&byte,1); + pos++; + return byte; + +} +int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const { + + + off_t r = AAsset_read(a,p_dst,p_length); + if (r>=0) { + pos+=r; + if (pos>len) { + pos=len; + eof=true; + } + } + return r; + +} + +Error FileAccessAndroid::get_error() const { + + return eof?ERR_FILE_EOF:OK; //not sure what else it may happen +} + +void FileAccessAndroid::store_8(uint8_t p_dest) { + + ERR_FAIL(); + +} + +bool FileAccessAndroid::file_exists(const String& p_path) { + + String path=fix_path(p_path).simplify_path(); + if (path.begins_with("/")) + path=path.substr(1,path.length()); + else if (path.begins_with("res://")) + path=path.substr(6,path.length()); + + AAsset *at=AAssetManager_open(asset_manager,path.utf8().get_data(),AASSET_MODE_STREAMING); + + if (!at) + return false; + + AAsset_close(at); + return true; + +} + + +FileAccessAndroid::FileAccessAndroid() { + a=NULL; + eof=false; +} + + +FileAccessAndroid::~FileAccessAndroid() +{ + close(); +} +#endif diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h new file mode 100644 index 0000000000..080ab1a6ce --- /dev/null +++ b/platform/android/file_access_android.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* file_access_android.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 FILE_ACCESS_ANDROID_H +#define FILE_ACCESS_ANDROID_H + +#ifdef ANDROID_NATIVE_ACTIVITY + + +#include "os/file_access.h" +#include <stdio.h> +#include <android/asset_manager.h> +#include <android/log.h> +#include <android_native_app_glue.h> + + +class FileAccessAndroid : public FileAccess { + + static FileAccess* create_android(); + mutable AAsset *a; + mutable size_t len; + mutable size_t pos; + mutable bool eof; + +public: + + static AAssetManager *asset_manager; + + virtual Error open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + virtual int get_buffer(uint8_t *p_dst, int p_length) const; + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + + virtual bool file_exists(const String& p_path); ///< return true if a file exists + + + static void make_default(); + + FileAccessAndroid(); + ~FileAccessAndroid(); +}; + +#endif // FILE_ACCESS_ANDROID_H +#endif diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp new file mode 100644 index 0000000000..e1dec4f601 --- /dev/null +++ b/platform/android/file_access_jandroid.cpp @@ -0,0 +1,253 @@ +/*************************************************************************/ +/* file_access_jandroid.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 ANDROID_NATIVE_ACTIVITY + +#include "file_access_jandroid.h" +#include "os/os.h" +#include <unistd.h> +#include "thread_jandroid.h" + +jobject FileAccessJAndroid::io=NULL; +jclass FileAccessJAndroid::cls; +jmethodID FileAccessJAndroid::_file_open=0; +jmethodID FileAccessJAndroid::_file_get_size=0; +jmethodID FileAccessJAndroid::_file_seek=0; +jmethodID FileAccessJAndroid::_file_read=0; +jmethodID FileAccessJAndroid::_file_tell=0; +jmethodID FileAccessJAndroid::_file_eof=0; +jmethodID FileAccessJAndroid::_file_close=0; + + +FileAccess* FileAccessJAndroid::create_jandroid() { + + return memnew(FileAccessJAndroid); +} + +Error FileAccessJAndroid::_open(const String& p_path, int p_mode_flags) { + + if (is_open()) + close(); + + String path=fix_path(p_path).simplify_path(); + if (path.begins_with("/")) + path=path.substr(1,path.length()); + else if (path.begins_with("res://")) + path=path.substr(6,path.length()); + + JNIEnv *env = ThreadAndroid::get_env(); + + //OS::get_singleton()->print("env: %p, io %p, fo: %p\n",env,io,_file_open); + + + jstring js = env->NewStringUTF(path.utf8().get_data()); + int res = env->CallIntMethod(io,_file_open,js,p_mode_flags&WRITE?true:false); + + env->DeleteLocalRef(js); + + if (res<=0) + return ERR_FILE_CANT_OPEN; + id=res; + + + return OK; +} + +void FileAccessJAndroid::close() { + + if (!is_open()) + return; + + JNIEnv *env = ThreadAndroid::get_env(); + + env->CallVoidMethod(io,_file_close,id); + id=0; + +} +bool FileAccessJAndroid::is_open() const { + + return id!=0; +} + +void FileAccessJAndroid::seek(size_t p_position) { + + JNIEnv *env = ThreadAndroid::get_env(); + + 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()); + + seek(get_len()); + +} +size_t FileAccessJAndroid::get_pos() const { + + JNIEnv *env = ThreadAndroid::get_env(); + ERR_FAIL_COND_V(!is_open(),0); + 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 { + + JNIEnv *env = ThreadAndroid::get_env(); + ERR_FAIL_COND_V(!is_open(),0); + return env->CallIntMethod(io,_file_eof,id); + +} + +uint8_t FileAccessJAndroid::get_8() const { + + ERR_FAIL_COND_V(!is_open(),0); + uint8_t byte; + get_buffer(&byte,1); + return byte; +} +int FileAccessJAndroid::get_buffer(uint8_t *p_dst, int p_length) const { + + ERR_FAIL_COND_V(!is_open(),0); + if (p_length==0) + return 0; + JNIEnv *env = ThreadAndroid::get_env(); + + jbyteArray jca = (jbyteArray)env->CallObjectMethod(io,_file_read,id,p_length); + + + int len = env->GetArrayLength(jca); + env->GetByteArrayRegion(jca,0,len,(jbyte*)p_dst); + env->DeleteLocalRef((jobject)jca); + + return len; + +} + +Error FileAccessJAndroid::get_error() const { + + if (eof_reached()) + return ERR_FILE_EOF; + return OK; +} + +void FileAccessJAndroid::store_8(uint8_t p_dest) { + + +} + +bool FileAccessJAndroid::file_exists(const String& p_path) { + + JNIEnv *env = ThreadAndroid::get_env(); + + String path=fix_path(p_path).simplify_path(); + if (path.begins_with("/")) + path=path.substr(1,path.length()); + else if (path.begins_with("res://")) + path=path.substr(6,path.length()); + + jstring js = env->NewStringUTF(path.utf8().get_data()); + int res = env->CallIntMethod(io,_file_open,js,false); + if (res<=0) + return false; + env->CallVoidMethod(io,_file_close,res); + env->DeleteLocalRef(js); + return true; + +} + + +void FileAccessJAndroid::setup( jobject p_io) { + + + io=p_io; + JNIEnv *env = ThreadAndroid::get_env(); + + __android_log_print(ANDROID_LOG_INFO,"godot","STEP5"); + + jclass c = env->GetObjectClass(io); + __android_log_print(ANDROID_LOG_INFO,"godot","STEP6"); + cls=(jclass)env->NewGlobalRef(c); + + _file_open = env->GetMethodID(cls, "file_open", "(Ljava/lang/String;Z)I"); + if(_file_open != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_open ok!!"); + } + _file_get_size = env->GetMethodID(cls, "file_get_size", "(I)I"); + if(_file_get_size != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_get_size ok!!"); + } + _file_tell = env->GetMethodID(cls, "file_tell", "(I)I"); + if(_file_tell != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_tell ok!!"); + } + _file_eof = env->GetMethodID(cls, "file_eof", "(I)Z"); + + if(_file_eof != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_eof ok!!"); + } + _file_seek = env->GetMethodID(cls, "file_seek", "(II)V"); + if(_file_seek != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_seek ok!!"); + } + _file_read = env->GetMethodID(cls, "file_read", "(II)[B"); + if(_file_read != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_read ok!!"); + } + _file_close = env->GetMethodID(cls, "file_close", "(I)V"); + if(_file_close != 0) { + __android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_close ok!!"); + } + +// (*env)->CallVoidMethod(env,obj,aMethodID, myvar); +} + + +FileAccessJAndroid::FileAccessJAndroid() +{ + + id=0; +} + +FileAccessJAndroid::~FileAccessJAndroid() +{ + + if (is_open()) + close(); +} + + +#endif diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h new file mode 100644 index 0000000000..3cbeab5ffc --- /dev/null +++ b/platform/android/file_access_jandroid.h @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* file_access_jandroid.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 FILE_ACCESS_JANDROID_H +#define FILE_ACCESS_JANDROID_H + +#ifndef ANDROID_NATIVE_ACTIVITY + +#include "java_glue.h" +#include "os/file_access.h" +class FileAccessJAndroid : public FileAccess { + + static jobject io; + static jclass cls; + + static jmethodID _file_open; + static jmethodID _file_get_size; + static jmethodID _file_seek; + static jmethodID _file_tell; + static jmethodID _file_eof; + static jmethodID _file_read; + static jmethodID _file_close; + + int id; + static FileAccess* create_jandroid(); + + +public: + + virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + virtual int get_buffer(uint8_t *p_dst, int p_length) const; + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + + virtual bool file_exists(const String& p_path); ///< return true if a file exists + + + + static void setup( jobject io); + + virtual uint64_t _get_modified_time(const String& p_file) { return 0; } + + FileAccessJAndroid(); + ~FileAccessJAndroid(); +}; + +#endif + +#endif // FILE_ACCESS_JANDROID_H diff --git a/platform/android/globals/global_defaults.cpp b/platform/android/globals/global_defaults.cpp new file mode 100644 index 0000000000..c6f852a592 --- /dev/null +++ b/platform/android/globals/global_defaults.cpp @@ -0,0 +1,13 @@ + +#include "global_defaults.h" +#include "globals.h" + + +void register_android_global_defaults() { + + GLOBAL_DEF("rasterizer.Android/use_fragment_lighting",false); + GLOBAL_DEF("display.Android/driver","GLES2"); + GLOBAL_DEF("rasterizer.Android/trilinear_mipmap_filter",false); + + Globals::get_singleton()->set_custom_property_info("display.Android/driver",PropertyInfo(Variant::STRING,"display.Android/driver",PROPERTY_HINT_ENUM,"GLES1,GLES2")); +} diff --git a/platform/android/globals/global_defaults.h b/platform/android/globals/global_defaults.h new file mode 100644 index 0000000000..64eb26c482 --- /dev/null +++ b/platform/android/globals/global_defaults.h @@ -0,0 +1,3 @@ + + +void register_android_global_defaults();
\ No newline at end of file diff --git a/platform/android/godot_android.cpp b/platform/android/godot_android.cpp new file mode 100644 index 0000000000..673ff91641 --- /dev/null +++ b/platform/android/godot_android.cpp @@ -0,0 +1,993 @@ +/*************************************************************************/ +/* godot_android.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/*************************************************************************/ +#ifdef ANDROID_NATIVE_ACTIVITY + +#include <jni.h> +#include <errno.h> + +#include <EGL/egl.h> +#include <GLES2/gl2.h> + +#include <android/sensor.h> +#include <android/window.h> +#include <android/log.h> +#include <android_native_app_glue.h> +#include "file_access_android.h" +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include "os_android.h" +#include "globals.h" +#include "main/main.h" +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "godot", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "godot", __VA_ARGS__)) + + +extern "C" { + JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerSingleton(JNIEnv * env, jobject obj, jstring name,jobject p_object); + JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerMethod(JNIEnv * env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args); + JNIEXPORT jstring JNICALL Java_com_android_godot_Godot_getGlobal(JNIEnv * env, jobject obj, jstring path); +}; + +class JNISingleton : public Object { + + OBJ_TYPE( JNISingleton, Object ); + + + struct MethodData { + + + jmethodID method; + Variant::Type ret_type; + Vector<Variant::Type> argtypes; + }; + + jobject instance; + Map<StringName,MethodData> method_map; + JNIEnv *env; + +public: + + void update_env(JNIEnv *p_env) { env=p_env; } + + virtual Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) { + + print_line("attempt to call "+String(p_method)); + + r_error.error=Variant::CallError::CALL_OK; + + Map<StringName,MethodData >::Element *E=method_map.find(p_method); + if (!E) { + + print_line("no exists"); + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + + + int ac = E->get().argtypes.size(); + if (ac<p_argcount) { + + print_line("fewargs"); + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=ac; + return Variant(); + } + + if (ac>p_argcount) { + + print_line("manyargs"); + 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 ); + } + + for(int i=0;i<p_argcount;i++) { + + + switch(E->get().argtypes[i]) { + + case Variant::BOOL: { + + v[i].z=*p_args[i]; + } break; + case Variant::INT: { + + v[i].i=*p_args[i]; + } break; + case Variant::REAL: { + + v[i].f=*p_args[i]; + } break; + case Variant::STRING: { + + String s = *p_args[i]; + jstring jStr = env->NewStringUTF(s.utf8().get_data()); + v[i].l=jStr; + } break; + case Variant::STRING_ARRAY: { + + DVector<String> sarray = *p_args[i]; + jobjectArray arr = env->NewObjectArray(sarray.size(),env->FindClass("java/lang/String"),env->NewStringUTF("")); + + for(int j=0;j<sarray.size();j++) { + + env->SetObjectArrayElement(arr,j,env->NewStringUTF( sarray[i].utf8().get_data() )); + } + v[i].l=arr; + + } break; + case Variant::INT_ARRAY: { + + DVector<int> array = *p_args[i]; + jintArray arr = env->NewIntArray(array.size()); + DVector<int>::Read r = array.read(); + env->SetIntArrayRegion(arr,0,array.size(),r.ptr()); + v[i].l=arr; + + } break; + case Variant::REAL_ARRAY: { + + DVector<float> array = *p_args[i]; + jfloatArray arr = env->NewFloatArray(array.size()); + DVector<float>::Read r = array.read(); + env->SetFloatArrayRegion(arr,0,array.size(),r.ptr()); + v[i].l=arr; + + } break; + default: { + + ERR_FAIL_V(Variant()); + } break; + + } + } + + print_line("calling method!!"); + + Variant ret; + + switch(E->get().ret_type) { + + case Variant::NIL: { + + + print_line("call void"); + env->CallVoidMethodA(instance,E->get().method,v); + } break; + case Variant::BOOL: { + + ret = env->CallBooleanMethodA(instance,E->get().method,v); + print_line("call bool"); + } break; + case Variant::INT: { + + ret = env->CallIntMethodA(instance,E->get().method,v); + print_line("call int"); + } 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); + String singname = env->GetStringUTFChars((jstring)o, NULL ); + } break; + case Variant::STRING_ARRAY: { + + jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance,E->get().method,v); + + int stringCount = env->GetArrayLength(arr); + DVector<String> sarr; + + 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)); + } + + ret=sarr; + + } break; + case Variant::INT_ARRAY: { + + jintArray arr = (jintArray)env->CallObjectMethodA(instance,E->get().method,v); + + int fCount = env->GetArrayLength(arr); + DVector<int> sarr; + sarr.resize(fCount); + + DVector<int>::Write w = sarr.write(); + env->GetIntArrayRegion(arr,0,fCount,w.ptr()); + w = DVector<int>::Write(); + ret=sarr; + } break; + case Variant::REAL_ARRAY: { + + jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance,E->get().method,v); + + int fCount = env->GetArrayLength(arr); + DVector<float> sarr; + sarr.resize(fCount); + + DVector<float>::Write w = sarr.write(); + env->GetFloatArrayRegion(arr,0,fCount,w.ptr()); + w = DVector<float>::Write(); + ret=sarr; + } break; + default: { + + + print_line("failure.."); + ERR_FAIL_V(Variant()); + } break; + } + + print_line("success"); + + 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() {} + +}; + +//JNIEnv *JNISingleton::env=NULL; + +static HashMap<String,JNISingleton*> jni_singletons; + + +struct engine { + struct android_app* app; + OS_Android *os; + JNIEnv *jni; + + ASensorManager* sensorManager; + const ASensor* accelerometerSensor; + ASensorEventQueue* sensorEventQueue; + + bool display_active; + bool requested_quit; + int animating; + EGLDisplay display; + EGLSurface surface; + EGLContext context; + int32_t width; + int32_t height; + +}; + +/** + * Initialize an EGL context for the current display. + */ +static int engine_init_display(struct engine* engine,bool p_gl2) { + // initialize OpenGL ES and EGL + + /* + * Here specify the attributes of the desired configuration. + * Below, we select an EGLConfig with at least 8 bits per color + * component compatible with on-screen windows + */ + const EGLint gl2_attribs[] = { + // EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 4, + EGL_GREEN_SIZE, 4, + EGL_RED_SIZE, 4, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, EGL_DONT_CARE, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + const EGLint gl1_attribs[] = { + // EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 4, + EGL_GREEN_SIZE, 4, + EGL_RED_SIZE, 4, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, EGL_DONT_CARE, + EGL_NONE + }; + + const EGLint *attribs=p_gl2?gl2_attribs:gl1_attribs; + + + EGLint w, h, dummy, format; + EGLint numConfigs; + EGLConfig config; + EGLSurface surface; + EGLContext context; + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + eglInitialize(display, 0, 0); + + + /* Here, the application chooses the configuration it desires. In this + * sample, we have a very simplified selection process, where we pick + * the first EGLConfig that matches our criteria */ + + eglChooseConfig(display, attribs, &config, 1, &numConfigs); + + LOGI("Num configs: %i\n",numConfigs); + + /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is + * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). + * As soon as we picked a EGLConfig, we can safely reconfigure the + * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ + eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); + + ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format); + //ANativeWindow_setFlags(engine->app->window, 0, 0, format|); + + surface = eglCreateWindowSurface(display, config, engine->app->window, NULL); + + const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION,2, + EGL_NONE + }; + context = eglCreateContext(display, config, EGL_NO_CONTEXT, p_gl2?context_attribs:NULL); + + if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { + LOGW("Unable to eglMakeCurrent"); + return -1; + } + + eglQuerySurface(display, surface, EGL_WIDTH, &w); + eglQuerySurface(display, surface, EGL_HEIGHT, &h); + print_line("INIT VIDEO MODE: "+itos(w)+","+itos(h)); + + //engine->os->set_egl_extensions(eglQueryString(display,EGL_EXTENSIONS)); + engine->os->init_video_mode(w,h); + + + engine->display = display; + engine->context = context; + engine->surface = surface; + engine->width = w; + engine->height = h; + engine->display_active=true; + + //engine->state.angle = 0; + + // Initialize GL state. + //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + glEnable(GL_CULL_FACE); + // glShadeModel(GL_SMOOTH); + glDisable(GL_DEPTH_TEST); + LOGI("GL Version: %s - %s %s\n", glGetString(GL_VERSION),glGetString(GL_VENDOR), glGetString(GL_RENDERER)); + + return 0; +} + + +static void engine_draw_frame(struct engine* engine) { + if (engine->display == NULL) { + // No display. + return; + } + + // Just fill the screen with a color. + //glClearColor(0,1,0,1); + //glClear(GL_COLOR_BUFFER_BIT); + if (engine->os && engine->os->main_loop_iterate()==true) { + + engine->requested_quit=true; + return; //should exit instead + } + + eglSwapBuffers(engine->display, engine->surface); +} + + +static void engine_term_display(struct engine* engine) { + if (engine->display != EGL_NO_DISPLAY) { + eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (engine->context != EGL_NO_CONTEXT) { + eglDestroyContext(engine->display, engine->context); + } + if (engine->surface != EGL_NO_SURFACE) { + eglDestroySurface(engine->display, engine->surface); + } + eglTerminate(engine->display); + } + + engine->animating = 0; + engine->display = EGL_NO_DISPLAY; + engine->context = EGL_NO_CONTEXT; + engine->surface = EGL_NO_SURFACE; + engine->display_active=false; + +} + +/** + * Process the next input event. + */ +static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { + struct engine* engine = (struct engine*)app->userData; + + if (!engine->os) + return 0; + + switch(AInputEvent_getType(event)) { + + case AINPUT_EVENT_TYPE_KEY: { + + int ac = AKeyEvent_getAction(event); + switch(ac) { + + case AKEY_EVENT_ACTION_DOWN: { + + int32_t code = AKeyEvent_getKeyCode(event); + if (code==AKEYCODE_BACK) { + + //AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled); + if (engine->os) + engine->os->main_loop_request_quit(); + return 1; + + } + + + } break; + case AKEY_EVENT_ACTION_UP: { + + + } break; + } + + + } break; + case AINPUT_EVENT_TYPE_MOTION: { + + + Vector<OS_Android::TouchPos> touchvec; + + int pc = AMotionEvent_getPointerCount(event); + + touchvec.resize(pc); + + for(int i=0;i<pc;i++) { + + touchvec[i].pos.x=AMotionEvent_getX(event,i); + touchvec[i].pos.y=AMotionEvent_getY(event,i); + touchvec[i].id=AMotionEvent_getPointerId(event,i); + } + + + //System.out.printf("gaction: %d\n",event.getAction()); + int pidx=(AMotionEvent_getAction(event)&AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)>>8; + switch(AMotionEvent_getAction(event)&AMOTION_EVENT_ACTION_MASK) { + + case AMOTION_EVENT_ACTION_DOWN: { + engine->os->process_touch(0,0,touchvec); + + //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY()); + } break; + case AMOTION_EVENT_ACTION_MOVE: { + engine->os->process_touch(1,0,touchvec); + //for(int i=0;i<event.getPointerCount();i++) { + // System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i)); + //} + } break; + case AMOTION_EVENT_ACTION_POINTER_UP: { + + engine->os->process_touch(4,pidx,touchvec); + //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + engine->os->process_touch(3,pidx,touchvec); + //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_UP: { + engine->os->process_touch(2,0,touchvec); + //for(int i=0;i<event.getPointerCount();i++) { + // System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i)); + //} + } break; + } + + return 1; + } break; + + } + + return 0; +} + +/** + * Process the next main command. + */ + +static void _gfx_init(void *ud,bool p_gl2) { + + struct engine* engine = (struct engine*)ud; + engine_init_display(engine,p_gl2); +} + +static void engine_handle_cmd(struct android_app* app, int32_t cmd) { + struct engine* engine = (struct engine*)app->userData; + // LOGI("**** CMD %i\n",cmd); + switch (cmd) { + case APP_CMD_SAVE_STATE: + // The system has asked us to save our current state. Do so. + //engine->app->savedState = malloc(sizeof(struct saved_state)); + //*((struct saved_state*)engine->app->savedState) = engine->state; + //engine->app->savedStateSize = sizeof(struct saved_state); + break; + case APP_CMD_CONFIG_CHANGED: + case APP_CMD_WINDOW_RESIZED: { + +#if 0 +// android blows + if (engine->display_active) { + + EGLint w,h; + eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w); + eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h); + engine->os->init_video_mode(w,h); + //print_line("RESIZED VIDEO MODE: "+itos(w)+","+itos(h)); + engine_draw_frame(engine); + + } +#else + + if (engine->display_active) { + + + EGLint w,h; + eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w); + eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h); + // if (w==engine->os->get_video_mode().width && h==engine->os->get_video_mode().height) + // break; + + engine_term_display(engine); + + + } + + + engine->os->reload_gfx(); + engine_draw_frame(engine); + engine->animating=1; + + /* + EGLint w,h; + eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w); + eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h); + engine->os->init_video_mode(w,h); + //print_line("RESIZED VIDEO MODE: "+itos(w)+","+itos(h)); + + }*/ + +#endif + + } break; + case APP_CMD_INIT_WINDOW: + //The window is being shown, get it ready. + // LOGI("INIT WINDOW"); + if (engine->app->window != NULL) { + + if (engine->os==NULL) { + + //do initialization here, when there's OpenGL! hackish but the only way + engine->os = new OS_Android(_gfx_init,engine); + + // char *args[]={"-test","gui",NULL}; + __android_log_print(ANDROID_LOG_INFO,"godot","pre asdasd setup..."); +#if 0 + Error err = Main::setup("apk",2,args); +#else + Error err = Main::setup("apk",0,NULL); + + String modules = Globals::get_singleton()->get("android/modules"); + Vector<String> mods = modules.split(",",false); + mods.push_back("GodotOS"); + __android_log_print(ANDROID_LOG_INFO,"godot","mod count: %i",mods.size()); + + if (mods.size()) { + + jclass activityClass = engine->jni->FindClass("android/app/NativeActivity"); + + jmethodID getClassLoader = engine->jni->GetMethodID(activityClass,"getClassLoader", "()Ljava/lang/ClassLoader;"); + + jobject cls = engine->jni->CallObjectMethod(app->activity->clazz, getClassLoader); + + jclass classLoader = engine->jni->FindClass("java/lang/ClassLoader"); + + jmethodID findClass = engine->jni->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + + + static JNINativeMethod methods[] = { + {"registerSingleton", "(Ljava/lang/String;Ljava/lang/Object;)V",(void *)&Java_com_android_godot_Godot_registerSingleton}, + {"registerMethod", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",(void *)&Java_com_android_godot_Godot_registerMethod}, + {"getGlobal", "(Ljava/lang/String;)Ljava/lang/String;", (void *)&Java_com_android_godot_Godot_getGlobal}, + }; + + jstring gstrClassName = engine->jni->NewStringUTF("com/android/godot/Godot"); + jclass GodotClass = (jclass)engine->jni->CallObjectMethod(cls, findClass, gstrClassName); + + __android_log_print(ANDROID_LOG_INFO,"godot","godot ****^*^*?^*^*class data %x",GodotClass); + + engine->jni->RegisterNatives(GodotClass,methods,sizeof(methods)/sizeof(methods[0])); + + for (int i=0;i<mods.size();i++) { + + String m = mods[i]; + //jclass singletonClass = engine->jni->FindClass(m.utf8().get_data()); + + jstring strClassName = engine->jni->NewStringUTF(m.utf8().get_data()); + jclass singletonClass = (jclass)engine->jni->CallObjectMethod(cls, findClass, strClassName); + + __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class data %x",singletonClass); + jmethodID initialize = engine->jni->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lcom/android/godot/Godot$SingletonBase;"); + + + jobject obj = engine->jni->CallStaticObjectMethod(singletonClass,initialize,app->activity->clazz); + __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class instance %x",obj); + jobject gob = engine->jni->NewGlobalRef(obj); + + + } + + } + +#endif + + + if (!Main::start()) + return; //should exit instead and print the error + + engine->os->main_loop_begin(); + } else { + //i guess recreate resources? + engine->os->reload_gfx(); + } + + + engine->animating=1; + engine_draw_frame(engine); + } + break; + case APP_CMD_TERM_WINDOW: + // The window is being hidden or closed, clean it up. + // LOGI("TERM WINDOW"); + engine_term_display(engine); + break; + case APP_CMD_GAINED_FOCUS: + // When our app gains focus, we start monitoring the accelerometer. + if (engine->accelerometerSensor != NULL) { + ASensorEventQueue_enableSensor(engine->sensorEventQueue, + engine->accelerometerSensor); + // We'd like to get 60 events per second (in us). + ASensorEventQueue_setEventRate(engine->sensorEventQueue, + engine->accelerometerSensor, (1000L/60)*1000); + + } + engine->animating = 1; + break; + case APP_CMD_LOST_FOCUS: + // When our app loses focus, we stop monitoring the accelerometer. + // This is to avoid consuming battery while not being used. + if (engine->accelerometerSensor != NULL) { + ASensorEventQueue_disableSensor(engine->sensorEventQueue, + engine->accelerometerSensor); + } + // Also stop animating. + engine->animating = 0; + engine_draw_frame(engine); + break; + } +} + +void android_main(struct android_app* state) { + struct engine engine; + // Make sure glue isn't stripped. + app_dummy(); + + memset(&engine, 0, sizeof(engine)); + state->userData = &engine; + state->onAppCmd = engine_handle_cmd; + state->onInputEvent = engine_handle_input; + engine.app = state; + engine.requested_quit=false; + engine.os=NULL; + engine.display_active=false; + + FileAccessAndroid::asset_manager=state->activity->assetManager; + + // Prepare to monitor accelerometer + engine.sensorManager = ASensorManager_getInstance(); + engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, + ASENSOR_TYPE_ACCELEROMETER); + engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, + state->looper, LOOPER_ID_USER, NULL, NULL); + + + ANativeActivity_setWindowFlags(state->activity,AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON,0); + + state->activity->vm->AttachCurrentThread(&engine.jni, NULL); + + + + // loop waiting for stuff to do. + + while (1) { + // Read all pending events. + int ident; + int events; + struct android_poll_source* source; + + // If not animating, we will block forever waiting for events. + // If animating, we loop until all events are read, then continue + // to draw the next frame of animation. + + int nullmax=50; + while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, + (void**)&source)) >= 0) { + + // Process this event. + + if (source != NULL) { + // LOGI("process\n"); + source->process(state, source); + } else { + nullmax--; + if (nullmax<0) + break; + } + + // If a sensor has data, process it now. + // LOGI("events\n"); + if (ident == LOOPER_ID_USER) { + if (engine.accelerometerSensor != NULL) { + ASensorEvent event; + while (ASensorEventQueue_getEvents(engine.sensorEventQueue, + &event, 1) > 0) { + + + if (engine.os) { + engine.os->process_accelerometer(Vector3(event.acceleration.x, event.acceleration.y, + event.acceleration.z)); + + } + + } + } + } + + // Check if we are exiting. + if (state->destroyRequested != 0) { + if (engine.os) { + engine.os->main_loop_request_quit(); + } + state->destroyRequested=0; + } + + if (engine.requested_quit) { + engine_term_display(&engine); + exit(0); + return; + } + +// LOGI("end\n"); + + + } + +// LOGI("engine animating? %i\n",engine.animating); + + if (engine.animating) { + //do os render + + engine_draw_frame(&engine); + //LOGI("TERM WINDOW"); + + } + } + +} + + + + +JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerSingleton(JNIEnv * env, jobject obj, jstring name,jobject p_object){ + + String singname = env->GetStringUTFChars( name, NULL ); + JNISingleton *s = memnew( JNISingleton ); + s->update_env(env); + s->set_instance(env->NewGlobalRef(p_object)); + jni_singletons[singname]=s; + + Globals::get_singleton()->add_singleton(Globals::Singleton(singname,s)); + +} + + +static 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}, + {"java.lang.String",Variant::STRING}, + {"[I",Variant::INT_ARRAY}, + {"[F",Variant::REAL_ARRAY}, + {"[java.lang.String",Variant::STRING_ARRAY}, + {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; +} + + +static 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"}, + {"java.lang.String","Ljava/lang/String;"}, + {"[I","[I"}, + {"[F","[F"}, + {"[java.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 ""; +} + +JNIEXPORT jstring JNICALL Java_com_android_godot_Godot_getGlobal(JNIEnv * env, jobject obj, jstring path) { + + String js = env->GetStringUTFChars( path, NULL ); + + return env->NewStringUTF(Globals::get_singleton()->get(js).operator String().utf8().get_data()); + + +} + + + +JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerMethod(JNIEnv * env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args){ + + String singname = env->GetStringUTFChars( sname, NULL ); + + ERR_FAIL_COND(!jni_singletons.has(singname)); + + JNISingleton *s = jni_singletons.get(singname); + + + String mname = env->GetStringUTFChars( name, NULL ); + String retval = env->GetStringUTFChars( ret, NULL ); + Vector<Variant::Type> types; + String cs="("; + + + int stringCount = env->GetArrayLength(args); + + print_line("Singl: "+singname+" Method: "+mname+" RetVal: "+retval); + for (int i=0; i<stringCount; i++) { + + jstring string = (jstring) env->GetObjectArrayElement(args, i); + const char *rawString = env->GetStringUTFChars(string, 0); + types.push_back(get_jni_type(String(rawString))); + cs+=get_jni_sig(String(rawString)); + } + + cs+=")"; + cs+=get_jni_sig(retval); + jclass cls = env->GetObjectClass(s->get_instance()); + print_line("METHOD: "+mname+" sig: "+cs); + jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data()); + if (!mid) { + + print_line("FAILED GETTING METHOID "+mname); + } + + s->add_method(mname,mid,types,get_jni_type(retval)); + + +} + +#endif diff --git a/platform/android/java/ant.properties b/platform/android/java/ant.properties new file mode 100644 index 0000000000..950b8b0199 --- /dev/null +++ b/platform/android/java/ant.properties @@ -0,0 +1,22 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked into Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + +key.store=my-release-key.keystore +key.alias=mykey + +key.store.password=123456 +key.alias.password=123456 diff --git a/platform/android/java/build.properties b/platform/android/java/build.properties new file mode 100644 index 0000000000..ee52d86d94 --- /dev/null +++ b/platform/android/java/build.properties @@ -0,0 +1,17 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked in Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + diff --git a/platform/android/java/build.xml b/platform/android/java/build.xml new file mode 100644 index 0000000000..424e2827dc --- /dev/null +++ b/platform/android/java/build.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="Godot" default="help"> + + <!-- The local.properties file is created and updated by the 'android' tool. + It contains the path to the SDK. It should *NOT* be checked into + Version Control Systems. --> + <property file="local.properties" /> + + <!-- The ant.properties file can be created by you. It is only edited by the + 'android' tool to add properties to it. + This is the place to change some Ant specific build properties. + Here are some properties you may want to change/update: + + source.dir + The name of the source directory. Default is 'src'. + out.dir + The name of the output directory. Default is 'bin'. + + For other overridable properties, look at the beginning of the rules + files in the SDK, at tools/ant/build.xml + + Properties related to the SDK location or the project target should + be updated using the 'android' tool with the 'update' action. + + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. + + --> + <property file="ant.properties" /> + + <!-- if sdk.dir was not set from one of the property file, then + get it from the ANDROID_HOME env var. + This must be done before we load project.properties since + the proguard config can use sdk.dir --> + <property environment="env" /> + <condition property="sdk.dir" value="${env.ANDROID_HOME}"> + <isset property="env.ANDROID_HOME" /> + </condition> + + <!-- The project.properties file is created and updated by the 'android' + tool, as well as ADT. + + This contains project specific properties such as project target, and library + dependencies. Lower level build properties are stored in ant.properties + (or in .classpath for Eclipse projects). + + This file is an integral part of the build system for your + application and should be checked into Version Control Systems. --> + <loadproperties srcFile="project.properties" /> + + <!-- quick check on sdk.dir --> + <fail + message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable." + unless="sdk.dir" + /> + + <!-- + Import per project custom build rules if present at the root of the project. + This is the place to put custom intermediary targets such as: + -pre-build + -pre-compile + -post-compile (This is typically used for code obfuscation. + Compiled code location: ${out.classes.absolute.dir} + If this is not done in place, override ${out.dex.input.absolute.dir}) + -post-package + -post-build + -pre-clean + --> + <import file="custom_rules.xml" optional="true" /> + + <!-- Import the actual build file. + + To customize existing targets, there are two options: + - Customize only one target: + - copy/paste the target into this file, *before* the + <import> task. + - customize it to your needs. + - Customize the whole content of build.xml + - copy/paste the content of the rules files (minus the top node) + into this file, replacing the <import> task. + - customize to your needs. + + *********************** + ****** IMPORTANT ****** + *********************** + In all cases you must update the value of version-tag below to read 'custom' instead of an integer, + in order to avoid having your file be overridden by tools such as "android update project" + --> + <!-- version-tag: 1 --> + <import file="${sdk.dir}/tools/ant/build.xml" /> + +</project> diff --git a/platform/android/java/default.properties b/platform/android/java/default.properties new file mode 100644 index 0000000000..e2e8061f26 --- /dev/null +++ b/platform/android/java/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-8 diff --git a/platform/android/java/my-release-key.keystore b/platform/android/java/my-release-key.keystore Binary files differnew file mode 100644 index 0000000000..410cccd865 --- /dev/null +++ b/platform/android/java/my-release-key.keystore diff --git a/platform/android/java/proguard-project.txt b/platform/android/java/proguard-project.txt new file mode 100644 index 0000000000..f2fe1559a2 --- /dev/null +++ b/platform/android/java/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/platform/android/java/proguard.cfg b/platform/android/java/proguard.cfg new file mode 100644 index 0000000000..12dd0392c0 --- /dev/null +++ b/platform/android/java/proguard.cfg @@ -0,0 +1,36 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native <methods>; +} + +-keepclasseswithmembernames class * { + public <init>(android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public <init>(android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/platform/android/java/res/drawable/icon.png b/platform/android/java/res/drawable/icon.png Binary files differnew file mode 100644 index 0000000000..050a1cf930 --- /dev/null +++ b/platform/android/java/res/drawable/icon.png diff --git a/platform/android/java/res/values-ar/strings.xml b/platform/android/java/res/values-ar/strings.xml new file mode 100644 index 0000000000..9f3dc6d6ac --- /dev/null +++ b/platform/android/java/res/values-ar/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ar</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-bg/strings.xml b/platform/android/java/res/values-bg/strings.xml new file mode 100644 index 0000000000..bd8109277e --- /dev/null +++ b/platform/android/java/res/values-bg/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-bg</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ca/strings.xml b/platform/android/java/res/values-ca/strings.xml new file mode 100644 index 0000000000..494cb88468 --- /dev/null +++ b/platform/android/java/res/values-ca/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ca</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-cs/strings.xml b/platform/android/java/res/values-cs/strings.xml new file mode 100644 index 0000000000..30ce00f895 --- /dev/null +++ b/platform/android/java/res/values-cs/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-cs</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-da/strings.xml b/platform/android/java/res/values-da/strings.xml new file mode 100644 index 0000000000..4c2a1cf0f4 --- /dev/null +++ b/platform/android/java/res/values-da/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-da</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-de/strings.xml b/platform/android/java/res/values-de/strings.xml new file mode 100644 index 0000000000..52946d4cce --- /dev/null +++ b/platform/android/java/res/values-de/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-de</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-el/strings.xml b/platform/android/java/res/values-el/strings.xml new file mode 100644 index 0000000000..181dc51762 --- /dev/null +++ b/platform/android/java/res/values-el/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-el</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-en/strings.xml b/platform/android/java/res/values-en/strings.xml new file mode 100644 index 0000000000..976a565013 --- /dev/null +++ b/platform/android/java/res/values-en/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-en</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-es-rES/strings.xml b/platform/android/java/res/values-es-rES/strings.xml new file mode 100644 index 0000000000..73f63a08f8 --- /dev/null +++ b/platform/android/java/res/values-es-rES/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-es_ES</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-es/strings.xml b/platform/android/java/res/values-es/strings.xml new file mode 100644 index 0000000000..07b718a641 --- /dev/null +++ b/platform/android/java/res/values-es/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-es</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-fi/strings.xml b/platform/android/java/res/values-fi/strings.xml new file mode 100644 index 0000000000..323d82aff1 --- /dev/null +++ b/platform/android/java/res/values-fi/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-fi</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-fr/strings.xml b/platform/android/java/res/values-fr/strings.xml new file mode 100644 index 0000000000..32bead2661 --- /dev/null +++ b/platform/android/java/res/values-fr/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-fr</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-he/strings.xml b/platform/android/java/res/values-he/strings.xml new file mode 100644 index 0000000000..f52ede2085 --- /dev/null +++ b/platform/android/java/res/values-he/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-he</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-hi/strings.xml b/platform/android/java/res/values-hi/strings.xml new file mode 100644 index 0000000000..8aab2a8c63 --- /dev/null +++ b/platform/android/java/res/values-hi/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-hi</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-hr/strings.xml b/platform/android/java/res/values-hr/strings.xml new file mode 100644 index 0000000000..caf55e2241 --- /dev/null +++ b/platform/android/java/res/values-hr/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-hr</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-hu/strings.xml b/platform/android/java/res/values-hu/strings.xml new file mode 100644 index 0000000000..e7f9e51226 --- /dev/null +++ b/platform/android/java/res/values-hu/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-hu</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-id/strings.xml b/platform/android/java/res/values-id/strings.xml new file mode 100644 index 0000000000..9e9a8b0c03 --- /dev/null +++ b/platform/android/java/res/values-id/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-id</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-it/strings.xml b/platform/android/java/res/values-it/strings.xml new file mode 100644 index 0000000000..1f5e5a049e --- /dev/null +++ b/platform/android/java/res/values-it/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-it</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ja/strings.xml b/platform/android/java/res/values-ja/strings.xml new file mode 100644 index 0000000000..7f85f57df7 --- /dev/null +++ b/platform/android/java/res/values-ja/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ja</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ko/strings.xml b/platform/android/java/res/values-ko/strings.xml new file mode 100644 index 0000000000..6d27498af8 --- /dev/null +++ b/platform/android/java/res/values-ko/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ko</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-lt/strings.xml b/platform/android/java/res/values-lt/strings.xml new file mode 100644 index 0000000000..6e3677fde7 --- /dev/null +++ b/platform/android/java/res/values-lt/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-lt</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-lv/strings.xml b/platform/android/java/res/values-lv/strings.xml new file mode 100644 index 0000000000..701fc271ac --- /dev/null +++ b/platform/android/java/res/values-lv/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-lv</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-nb/strings.xml b/platform/android/java/res/values-nb/strings.xml new file mode 100644 index 0000000000..73147ca1af --- /dev/null +++ b/platform/android/java/res/values-nb/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-nb</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-nl/strings.xml b/platform/android/java/res/values-nl/strings.xml new file mode 100644 index 0000000000..e501928a35 --- /dev/null +++ b/platform/android/java/res/values-nl/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-nl</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-pl/strings.xml b/platform/android/java/res/values-pl/strings.xml new file mode 100644 index 0000000000..ea5da73b6f --- /dev/null +++ b/platform/android/java/res/values-pl/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-pl</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-pt/strings.xml b/platform/android/java/res/values-pt/strings.xml new file mode 100644 index 0000000000..bdda7cd2c7 --- /dev/null +++ b/platform/android/java/res/values-pt/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-pt</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ro/strings.xml b/platform/android/java/res/values-ro/strings.xml new file mode 100644 index 0000000000..3686da4c19 --- /dev/null +++ b/platform/android/java/res/values-ro/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ro</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-ru/strings.xml b/platform/android/java/res/values-ru/strings.xml new file mode 100644 index 0000000000..954067658b --- /dev/null +++ b/platform/android/java/res/values-ru/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-ru</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-sk/strings.xml b/platform/android/java/res/values-sk/strings.xml new file mode 100644 index 0000000000..37d1283124 --- /dev/null +++ b/platform/android/java/res/values-sk/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-sk</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-sl/strings.xml b/platform/android/java/res/values-sl/strings.xml new file mode 100644 index 0000000000..0bb249c375 --- /dev/null +++ b/platform/android/java/res/values-sl/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-sl</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-sr/strings.xml b/platform/android/java/res/values-sr/strings.xml new file mode 100644 index 0000000000..0e83cab1a1 --- /dev/null +++ b/platform/android/java/res/values-sr/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-sr</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-sv/strings.xml b/platform/android/java/res/values-sv/strings.xml new file mode 100644 index 0000000000..e3a04ac2ec --- /dev/null +++ b/platform/android/java/res/values-sv/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-sv</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-th/strings.xml b/platform/android/java/res/values-th/strings.xml new file mode 100644 index 0000000000..0aa893b8bf --- /dev/null +++ b/platform/android/java/res/values-th/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-th</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-tl/strings.xml b/platform/android/java/res/values-tl/strings.xml new file mode 100644 index 0000000000..e7e2af4909 --- /dev/null +++ b/platform/android/java/res/values-tl/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-tl</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-tr/strings.xml b/platform/android/java/res/values-tr/strings.xml new file mode 100644 index 0000000000..97af1243a6 --- /dev/null +++ b/platform/android/java/res/values-tr/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-tr</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-uk/strings.xml b/platform/android/java/res/values-uk/strings.xml new file mode 100644 index 0000000000..3dea6908a9 --- /dev/null +++ b/platform/android/java/res/values-uk/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-uk</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-vi/strings.xml b/platform/android/java/res/values-vi/strings.xml new file mode 100644 index 0000000000..a6552130b0 --- /dev/null +++ b/platform/android/java/res/values-vi/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-vi</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values-zh/strings.xml b/platform/android/java/res/values-zh/strings.xml new file mode 100644 index 0000000000..397e662851 --- /dev/null +++ b/platform/android/java/res/values-zh/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name-zh</string> +</resources>
\ No newline at end of file diff --git a/platform/android/java/res/values/strings.xml b/platform/android/java/res/values/strings.xml new file mode 100644 index 0000000000..3a38d40599 --- /dev/null +++ b/platform/android/java/res/values/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="godot_project_name_string">godot-project-name</string> + <string name="testuf8">元気です</string> + <string name="testuf2">元気です元気です元気です</string> + +</resources> diff --git a/platform/android/java/src/com/android/godot/Dictionary.java b/platform/android/java/src/com/android/godot/Dictionary.java new file mode 100644 index 0000000000..4ed12f5818 --- /dev/null +++ b/platform/android/java/src/com/android/godot/Dictionary.java @@ -0,0 +1,80 @@ +/*************************************************************************/ +/* Dictionary.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 com.android.godot; + +import java.util.HashMap; +import java.util.Set; + + +public class Dictionary extends HashMap<String, Object> { + + protected String[] keys_cache; + + public String[] get_keys() { + + String[] ret = new String[size()]; + int i=0; + Set<String> keys = keySet(); + for (String key : keys) { + + ret[i] = key; + i++; + }; + + return ret; + }; + + public Object[] get_values() { + + Object[] ret = new Object[size()]; + int i=0; + Set<String> keys = keySet(); + for (String key : keys) { + + ret[i] = get(key); + i++; + }; + + return ret; + }; + + public void set_keys(String[] keys) { + keys_cache = keys; + }; + + public void set_values(Object[] vals) { + + int i=0; + for (String key : keys_cache) { + put(key, vals[i]); + i++; + }; + keys_cache = null; + }; +}; diff --git a/platform/android/java/src/com/android/godot/Godot.java b/platform/android/java/src/com/android/godot/Godot.java new file mode 100644 index 0000000000..cf1545df82 --- /dev/null +++ b/platform/android/java/src/com/android/godot/Godot.java @@ -0,0 +1,300 @@ +/*************************************************************************/ +/* Godot.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 com.android.godot; + +import android.app.Activity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.widget.RelativeLayout; +import android.widget.LinearLayout; +import android.view.ViewGroup.LayoutParams; + + +import android.app.*; +import android.content.*; +import android.view.*; +import android.view.inputmethod.InputMethodManager; +import android.os.*; +import android.util.Log; +import android.graphics.*; +import android.text.method.*; +import android.text.*; +import android.media.*; +import android.hardware.*; +import android.content.*; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.ArrayList; +import android.provider.Settings.Secure; + + +public class Godot extends Activity implements SensorEventListener +{ + + static public class SingletonBase { + + protected void registerClass(String p_name, String[] p_methods) { + + GodotLib.singleton(p_name,this); + + Class clazz = getClass(); + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + boolean found=false; + System.out.printf("METHOD: %s\n",method.getName()); + + for (String s : p_methods) { + System.out.printf("METHOD CMP WITH: %s\n",s); + if (s.equals(method.getName())) { + found=true; + System.out.printf("METHOD CMP VALID"); + break; + } + } + if (!found) + continue; + + System.out.printf("METHOD FOUND: %s\n",method.getName()); + + List<String> ptr = new ArrayList<String>(); + + Class[] paramTypes = method.getParameterTypes(); + for (Class c : paramTypes) { + ptr.add(c.getName()); + } + + String[] pt = new String[ptr.size()]; + ptr.toArray(pt); + + GodotLib.method(p_name,method.getName(),method.getReturnType().getName(),pt); + + + } + } + + public void registerMethods() {} + } + +/* + protected List<SingletonBase> singletons = new ArrayList<SingletonBase>(); + protected void instanceSingleton(SingletonBase s) { + + s.registerMethods(); + singletons.add(s); + } + +*/ + + public GodotView mView; + + private SensorManager mSensorManager; + private Sensor mAccelerometer; + + public RelativeLayout layout; + + + static public GodotIO io; + + public static void setWindowTitle(String title) { + //setTitle(title); + } + + public interface ResultCallback { + public void callback(int requestCode, int resultCode, Intent data); + }; + public ResultCallback result_callback; + + @Override protected void onActivityResult (int requestCode, int resultCode, Intent data) { + if (result_callback != null) { + result_callback.callback(requestCode, resultCode, data); + result_callback = null; + }; + }; + + public void onVideoInit(boolean use_gl2) { + +// mView = new GodotView(getApplication(),io,use_gl2); +// setContentView(mView); + + layout = new RelativeLayout(this); + layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); + setContentView(layout); + mView = new GodotView(getApplication(),io,use_gl2, this); + layout.addView(mView,new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); + mView.setKeepScreenOn(true); + + } + + @Override protected void onCreate(Bundle icicle) { + + + super.onCreate(icicle); + + + + Window window = getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + io = new GodotIO(this); + io.unique_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID); + GodotLib.io=io; + GodotLib.initialize(this,io.needsReloadHooks()); + mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); + + result_callback = null; + + // instanceSingleton( new GodotFacebook(this) ); + + + } + + @Override protected void onPause() { + super.onPause(); + mView.onPause(); + mSensorManager.unregisterListener(this); + GodotLib.focusout(); + + } + + @Override protected void onResume() { + super.onResume(); + mView.onResume(); + mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); + GodotLib.focusin(); + } + + @Override public void onSensorChanged(SensorEvent event) { + float x = event.values[0]; + float y = event.values[1]; + float z = event.values[2]; + GodotLib.accelerometer(x,y,z); + } + + @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { + // Do something here if sensor accuracy changes. + } + +/* + @Override public boolean dispatchKeyEvent(KeyEvent event) { + + if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) { + + System.out.printf("** BACK REQUEST!\n"); + + GodotLib.quit(); + return true; + } + System.out.printf("** OTHER KEY!\n"); + + return false; + } +*/ + + @Override public void onBackPressed() { + + GodotLib.quit(); + } + + public void forceQuit() { + + System.exit(0); + } + + + //@Override public boolean dispatchTouchEvent (MotionEvent event) { + public boolean gotTouchEvent(MotionEvent event) { + + super.onTouchEvent(event); + int evcount=event.getPointerCount(); + if (evcount==0) + return true; + + int[] arr = new int[event.getPointerCount()*3]; + + for(int i=0;i<event.getPointerCount();i++) { + + arr[i*3+0]=(int)event.getPointerId(i); + arr[i*3+1]=(int)event.getX(i); + arr[i*3+2]=(int)event.getY(i); + } + + //System.out.printf("gaction: %d\n",event.getAction()); + switch(event.getAction()&MotionEvent.ACTION_MASK) { + + case MotionEvent.ACTION_DOWN: { + GodotLib.touch(0,0,evcount,arr); + //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY()); + } break; + case MotionEvent.ACTION_MOVE: { + GodotLib.touch(1,0,evcount,arr); + //for(int i=0;i<event.getPointerCount();i++) { + // System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i)); + //} + } break; + case MotionEvent.ACTION_POINTER_UP: { + int pointer_idx = event.getActionIndex(); + GodotLib.touch(4,pointer_idx,evcount,arr); + //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case MotionEvent.ACTION_POINTER_DOWN: { + int pointer_idx = event.getActionIndex(); + GodotLib.touch(3,pointer_idx,evcount,arr); + //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { + GodotLib.touch(2,0,evcount,arr); + //for(int i=0;i<event.getPointerCount();i++) { + // System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i)); + //} + } break; + + } + return true; + } + + @Override public boolean onKeyUp(int keyCode, KeyEvent event) { + GodotLib.key(event.getUnicodeChar(0), false); + return super.onKeyUp(keyCode, event); + }; + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + GodotLib.key(event.getUnicodeChar(0), true); + return super.onKeyDown(keyCode, event); + }; + + + // Audio + + +} diff --git a/platform/android/java/src/com/android/godot/GodotIO.java b/platform/android/java/src/com/android/godot/GodotIO.java new file mode 100644 index 0000000000..1f3967cb8f --- /dev/null +++ b/platform/android/java/src/com/android/godot/GodotIO.java @@ -0,0 +1,514 @@ +/*************************************************************************/ +/* GodotIO.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 com.android.godot; +import java.util.HashMap; +import java.util.Locale; +import android.net.Uri; +import android.content.Intent; +import android.content.res.AssetManager; +import java.io.InputStream; +import java.io.IOException; +import android.app.*; +import android.content.*; +import android.view.*; +import android.view.inputmethod.InputMethodManager; +import android.os.*; +import android.util.Log; +import android.graphics.*; +import android.text.method.*; +import android.text.*; +import android.media.*; +import android.hardware.*; +import android.content.*; +import android.content.pm.ActivityInfo; +//android.os.Build + +// Wrapper for native library + +public class GodotIO { + + + AssetManager am; + Activity activity; + + final int SCREEN_LANDSCAPE=0; + final int SCREEN_PORTRAIT=1; + final int SCREEN_REVERSE_LANDSCAPE=2; + final int SCREEN_REVERSE_PORTRAIT=3; + final int SCREEN_SENSOR_LANDSCAPE=4; + final int SCREEN_SENSOR_PORTRAIT=5; + final int SCREEN_SENSOR=6; + + ///////////////////////// + /// FILES + ///////////////////////// + + public int last_file_id=1; + + class AssetData { + + + public boolean eof=false; + public String path; + public InputStream is; + public int len; + public int pos; + } + + + HashMap<Integer,AssetData> streams; + + + public int file_open(String path,boolean write) { + + //System.out.printf("file_open: Attempt to Open %s\n",path); + + if (write) + return -1; + + + AssetData ad = new AssetData(); + + try { + ad.is = am.open(path); + + } catch (Exception e) { + + //System.out.printf("Exception on file_open: %s\n",e); + return -1; + } + + try { + ad.len=ad.is.available(); + } catch (Exception e) { + + System.out.printf("Exception availabling on file_open: %s\n",e); + return -1; + } + + ad.path=path; + ad.pos=0; + ++last_file_id; + streams.put(last_file_id,ad); + + return last_file_id; + } + public int file_get_size(int id) { + + if (!streams.containsKey(id)) { + System.out.printf("file_get_size: Invalid file id: %d\n",id); + return -1; + } + + return streams.get(id).len; + + } + public void file_seek(int id,int bytes) { + + if (!streams.containsKey(id)) { + System.out.printf("file_get_size: Invalid file id: %d\n",id); + return; + } + //seek sucks + AssetData ad = streams.get(id); + if (bytes>ad.len) + bytes=ad.len; + if (bytes<0) + bytes=0; + + try { + + if (bytes > (int)ad.pos) { + int todo=bytes-(int)ad.pos; + while(todo>0) { + todo-=ad.is.skip(todo); + } + ad.pos=bytes; + } else if (bytes<(int)ad.pos) { + + ad.is=am.open(ad.path); + + ad.pos=bytes; + int todo=bytes; + while(todo>0) { + todo-=ad.is.skip(todo); + } + } + + ad.eof=false; + } catch (IOException e) { + + System.out.printf("Exception on file_seek: %s\n",e); + return; + } + + + } + + public int file_tell(int id) { + + if (!streams.containsKey(id)) { + System.out.printf("file_read: Can't tell eof for invalid file id: %d\n",id); + return 0; + } + + AssetData ad = streams.get(id); + return ad.pos; + } + public boolean file_eof(int id) { + + if (!streams.containsKey(id)) { + System.out.printf("file_read: Can't check eof for invalid file id: %d\n",id); + return false; + } + + AssetData ad = streams.get(id); + return ad.eof; + } + + public byte[] file_read(int id, int bytes) { + + if (!streams.containsKey(id)) { + System.out.printf("file_read: Can't read invalid file id: %d\n",id); + return new byte[0]; + } + + + AssetData ad = streams.get(id); + + if (ad.pos + bytes > ad.len) { + + bytes=ad.len-ad.pos; + ad.eof=true; + } + + + if (bytes==0) { + + return new byte[0]; + } + + + + byte[] buf1=new byte[bytes]; + int r=0; + try { + r = ad.is.read(buf1); + } catch (IOException e) { + + System.out.printf("Exception on file_read: %s\n",e); + return new byte[bytes]; + } + + if (r==0) { + return new byte[0]; + } + + ad.pos+=r; + + if (r<bytes) { + + byte[] buf2=new byte[r]; + for(int i=0;i<r;i++) + buf2[i]=buf1[i]; + return buf2; + } else { + + return buf1; + } + + } + + public void file_close(int id) { + + if (!streams.containsKey(id)) { + System.out.printf("file_close: Can't close invalid file id: %d\n",id); + return; + } + + streams.remove(id); + + } + + + ///////////////////////// + /// DIRECTORIES + ///////////////////////// + + + class AssetDir { + + public String[] files; + public int current; + } + + public int last_dir_id=1; + + HashMap<Integer,AssetDir> dirs; + + public int dir_open(String path) { + + AssetDir ad = new AssetDir(); + ad.current=0; + + try { + ad.files = am.list(path); + } catch (IOException e) { + + System.out.printf("Exception on dir_open: %s\n",e); + return -1; + } + + ++last_dir_id; + dirs.put(last_dir_id,ad); + + return last_dir_id; + + } + + public String dir_next(int id) { + + if (!dirs.containsKey(id)) { + System.out.printf("dir_next: invalid dir id: %d\n",id); + return ""; + } + + AssetDir ad = dirs.get(id); + if (ad.current>=ad.files.length) + return ""; + String r = ad.files[ad.current]; + ad.current++; + return r; + + } + + public void dir_close(int id) { + + if (!dirs.containsKey(id)) { + System.out.printf("dir_close: invalid dir id: %d\n",id); + return; + } + + dirs.remove(id); + } + + + + GodotIO(Activity p_activity) { + + am=p_activity.getAssets(); + activity=p_activity; + streams=new HashMap<Integer,AssetData>(); + dirs=new HashMap<Integer,AssetDir>(); + + + } + + + ///////////////////////// + // AUDIO + ///////////////////////// + + private Object buf; + private Thread mAudioThread; + private AudioTrack mAudioTrack; + + public Object audioInit(int sampleRate, int desiredFrames) { + int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_STEREO; + int audioFormat = AudioFormat.ENCODING_PCM_16BIT; + int frameSize = 4; + + System.out.printf("audioInit: initializing audio:\n"); + + //Log.v("Godot", "Godot audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); + + // Let the user pick a larger buffer if they really want -- but ye + // gods they probably shouldn't, the minimums are horrifyingly high + // latency already + desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); + + mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, + channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); + + audioStartThread(); + + //Log.v("Godot", "Godot audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer"); + + buf = new short[desiredFrames * 2]; + return buf; + } + + public void audioStartThread() { + mAudioThread = new Thread(new Runnable() { + public void run() { + mAudioTrack.play(); + GodotLib.audio(); + } + }); + + // I'd take REALTIME if I could get it! + mAudioThread.setPriority(Thread.MAX_PRIORITY); + mAudioThread.start(); + } + + public void audioWriteShortBuffer(short[] buffer) { + for (int i = 0; i < buffer.length; ) { + int result = mAudioTrack.write(buffer, i, buffer.length - i); + if (result > 0) { + i += result; + } else if (result == 0) { + try { + Thread.sleep(1); + } catch(InterruptedException e) { + // Nom nom + } + } else { + Log.w("Godot", "Godot audio: error return from write(short)"); + return; + } + } + } + + + + public void audioQuit() { + if (mAudioThread != null) { + try { + mAudioThread.join(); + } catch(Exception e) { + Log.v("Godot", "Problem stopping audio thread: " + e); + } + mAudioThread = null; + + //Log.v("Godot", "Finished waiting for audio thread"); + } + + if (mAudioTrack != null) { + mAudioTrack.stop(); + mAudioTrack = null; + } + } + + public void audioPause(boolean p_pause) { + + if (p_pause) + mAudioTrack.pause(); + else + mAudioTrack.play(); + } + + ///////////////////////// + // MISCELANEOUS OS IO + ///////////////////////// + + + + public int openURI(String p_uri) { + + try { + Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri); + Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(p_uri)); + activity.startActivity(myIntent); + return 0; + } catch (ActivityNotFoundException e) { + + return 1; + } + } + + public String getDataDir() { + + return activity.getFilesDir().getAbsolutePath(); + } + + public String getLocale() { + + return Locale.getDefault().toString(); + } + + public String getModel() { + return Build.MODEL; + } + + public boolean needsReloadHooks() { + + return android.os.Build.VERSION.SDK_INT < 11; + } + + public void showKeyboard(String p_existing_text) { + + InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); + inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + }; + + public void hideKeyboard() { + + InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); + inputMgr.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + }; + + public void setScreenOrientation(int p_orientation) { + + switch(p_orientation) { + + case SCREEN_LANDSCAPE: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } break; + case SCREEN_PORTRAIT: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } break; + case SCREEN_REVERSE_LANDSCAPE: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + } break; + case SCREEN_REVERSE_PORTRAIT: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); + } break; + case SCREEN_SENSOR_LANDSCAPE: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); + } break; + case SCREEN_SENSOR_PORTRAIT: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); + } break; + case SCREEN_SENSOR: { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); + } break; + + } + }; + + protected static final String PREFS_FILE = "device_id.xml"; + protected static final String PREFS_DEVICE_ID = "device_id"; + + public static String unique_id=""; + public String getUniqueID() { + + return unique_id; + } + +} diff --git a/platform/android/java/src/com/android/godot/GodotLib.java b/platform/android/java/src/com/android/godot/GodotLib.java new file mode 100644 index 0000000000..8d002c5ebc --- /dev/null +++ b/platform/android/java/src/com/android/godot/GodotLib.java @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* GodotLib.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 com.android.godot; + +// Wrapper for native library + +public class GodotLib { + + + public static GodotIO io; + + static { + System.loadLibrary("godot_android"); + } + + /** + * @param width the current view width + * @param height the current view height + */ + + public static native void initialize(Godot p_instance,boolean need_reload_hook); + public static native void resize(int width, int height,boolean reload); + public static native void newcontext(); + public static native void quit(); + public static native void step(); + 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 key(int p_unicode_char, boolean p_pressed); + public static native void focusin(); + public static native void focusout(); + public static native void audio(); + public static native void singleton(String p_name,Object p_object); + public static native void method(String p_sname,String p_name,String p_ret,String[] p_params); + public static native String getGlobal(String p_key); + public static native void callobject(int p_ID, String p_method, Object[] p_params); + +} diff --git a/platform/android/java/src/com/android/godot/GodotView.java b/platform/android/java/src/com/android/godot/GodotView.java new file mode 100644 index 0000000000..093757bfb0 --- /dev/null +++ b/platform/android/java/src/com/android/godot/GodotView.java @@ -0,0 +1,378 @@ +/*************************************************************************/ +/* GodotView.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 com.android.godot; +import android.content.Context; +import android.graphics.PixelFormat; +import android.opengl.GLSurfaceView; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.content.ContextWrapper; + +import java.io.File; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.opengles.GL10; + +/** + * A simple GLSurfaceView sub-class that demonstrate how to perform + * OpenGL ES 2.0 rendering into a GL Surface. Note the following important + * details: + * + * - The class must use a custom context factory to enable 2.0 rendering. + * See ContextFactory class definition below. + * + * - The class must use a custom EGLConfigChooser to be able to select + * an EGLConfig that supports 2.0. This is done by providing a config + * specification to eglChooseConfig() that has the attribute + * EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT flag + * set. See ConfigChooser class definition below. + * + * - The class must select the surface's format, then choose an EGLConfig + * that matches it exactly (with regards to red/green/blue/alpha channels + * bit depths). Failure to do so would result in an EGL_BAD_MATCH error. + */ +class GodotView extends GLSurfaceView { + private static String TAG = "GodotView"; + private static final boolean DEBUG = false; + private static Context ctx; + + private static GodotIO io; + private static boolean firsttime=true; + private static boolean use_gl2=false; + + private Godot activity; + + public GodotView(Context context,GodotIO p_io,boolean p_use_gl2, Godot p_activity) { + super(context); + ctx=context; + io=p_io; + use_gl2=p_use_gl2; + + activity = p_activity; + + if (!p_io.needsReloadHooks()) { + //will only work on SDK 11+!! + setPreserveEGLContextOnPause(true); + } + + init(false, 16, 0); + } + + public GodotView(Context context, boolean translucent, int depth, int stencil) { + super(context); + init(translucent, depth, stencil); + } + + @Override public boolean onTouchEvent (MotionEvent event) { + + return activity.gotTouchEvent(event); + }; + + private void init(boolean translucent, int depth, int stencil) { + + /* By default, GLSurfaceView() creates a RGB_565 opaque surface. + * If we want a translucent one, we should change the surface's + * format here, using PixelFormat.TRANSLUCENT for GL Surfaces + * is interpreted as any 32-bit surface with alpha by SurfaceFlinger. + */ + if (translucent) { + this.getHolder().setFormat(PixelFormat.TRANSLUCENT); + } + + /* Setup the context factory for 2.0 rendering. + * See ContextFactory class definition below + */ + setEGLContextFactory(new ContextFactory()); + + /* We need to choose an EGLConfig that matches the format of + * our surface exactly. This is going to be done in our + * custom config chooser. See ConfigChooser class definition + * below. + */ + setEGLConfigChooser( translucent ? + new ConfigChooser(8, 8, 8, 8, depth, stencil) : + new ConfigChooser(5, 6, 5, 0, depth, stencil) ); + + /* Set the renderer responsible for frame rendering */ + setRenderer(new Renderer()); + } + + private static class ContextFactory implements GLSurfaceView.EGLContextFactory { + private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { + if (use_gl2) + Log.w(TAG, "creating OpenGL ES 2.0 context :"); + else + Log.w(TAG, "creating OpenGL ES 1.1 context :"); + + checkEglError("Before eglCreateContext", egl); + int[] attrib_list2 = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl2?attrib_list2:null); + checkEglError("After eglCreateContext", egl); + return context; + } + + public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { + egl.eglDestroyContext(display, context); + } + } + + private static void checkEglError(String prompt, EGL10 egl) { + int error; + while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { + Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); + } + } + + private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser { + + public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) { + mRedSize = r; + mGreenSize = g; + mBlueSize = b; + mAlphaSize = a; + mDepthSize = depth; + mStencilSize = stencil; + } + + /* This EGL config specification is used to specify 2.0 rendering. + * We use a minimum size of 4 bits for red/green/blue, but will + * perform actual matching in chooseConfig() below. + */ + private static int EGL_OPENGL_ES2_BIT = 4; + private static int[] s_configAttribs2 = + { + EGL10.EGL_RED_SIZE, 4, + EGL10.EGL_GREEN_SIZE, 4, + EGL10.EGL_BLUE_SIZE, 4, + // EGL10.EGL_DEPTH_SIZE, 16, + // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_NONE + }; + private static int[] s_configAttribs = + { + EGL10.EGL_RED_SIZE, 4, + EGL10.EGL_GREEN_SIZE, 4, + EGL10.EGL_BLUE_SIZE, 4, + // EGL10.EGL_DEPTH_SIZE, 16, + // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, + EGL10.EGL_NONE + }; + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + + /* Get the number of minimally matching EGL configurations + */ + int[] num_config = new int[1]; + egl.eglChooseConfig(display, use_gl2?s_configAttribs2:s_configAttribs, null, 0, num_config); + + int numConfigs = num_config[0]; + + if (numConfigs <= 0) { + throw new IllegalArgumentException("No configs match configSpec"); + } + + /* Allocate then read the array of minimally matching EGL configs + */ + EGLConfig[] configs = new EGLConfig[numConfigs]; + egl.eglChooseConfig(display, use_gl2?s_configAttribs2:s_configAttribs, configs, numConfigs, num_config); + + if (DEBUG) { + printConfigs(egl, display, configs); + } + /* Now return the "best" one + */ + return chooseConfig(egl, display, configs); + } + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + for(EGLConfig config : configs) { + int d = findConfigAttrib(egl, display, config, + EGL10.EGL_DEPTH_SIZE, 0); + int s = findConfigAttrib(egl, display, config, + EGL10.EGL_STENCIL_SIZE, 0); + + // We need at least mDepthSize and mStencilSize bits + if (d < mDepthSize || s < mStencilSize) + continue; + + // We want an *exact* match for red/green/blue/alpha + int r = findConfigAttrib(egl, display, config, + EGL10.EGL_RED_SIZE, 0); + int g = findConfigAttrib(egl, display, config, + EGL10.EGL_GREEN_SIZE, 0); + int b = findConfigAttrib(egl, display, config, + EGL10.EGL_BLUE_SIZE, 0); + int a = findConfigAttrib(egl, display, config, + EGL10.EGL_ALPHA_SIZE, 0); + + if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize) + return config; + } + return null; + } + + private int findConfigAttrib(EGL10 egl, EGLDisplay display, + EGLConfig config, int attribute, int defaultValue) { + + if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { + return mValue[0]; + } + return defaultValue; + } + + private void printConfigs(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + int numConfigs = configs.length; + Log.w(TAG, String.format("%d configurations", numConfigs)); + for (int i = 0; i < numConfigs; i++) { + Log.w(TAG, String.format("Configuration %d:\n", i)); + printConfig(egl, display, configs[i]); + } + } + + private void printConfig(EGL10 egl, EGLDisplay display, + EGLConfig config) { + int[] attributes = { + EGL10.EGL_BUFFER_SIZE, + EGL10.EGL_ALPHA_SIZE, + EGL10.EGL_BLUE_SIZE, + EGL10.EGL_GREEN_SIZE, + EGL10.EGL_RED_SIZE, + EGL10.EGL_DEPTH_SIZE, + EGL10.EGL_STENCIL_SIZE, + EGL10.EGL_CONFIG_CAVEAT, + EGL10.EGL_CONFIG_ID, + EGL10.EGL_LEVEL, + EGL10.EGL_MAX_PBUFFER_HEIGHT, + EGL10.EGL_MAX_PBUFFER_PIXELS, + EGL10.EGL_MAX_PBUFFER_WIDTH, + EGL10.EGL_NATIVE_RENDERABLE, + EGL10.EGL_NATIVE_VISUAL_ID, + EGL10.EGL_NATIVE_VISUAL_TYPE, + 0x3030, // EGL10.EGL_PRESERVED_RESOURCES, + EGL10.EGL_SAMPLES, + EGL10.EGL_SAMPLE_BUFFERS, + EGL10.EGL_SURFACE_TYPE, + EGL10.EGL_TRANSPARENT_TYPE, + EGL10.EGL_TRANSPARENT_RED_VALUE, + EGL10.EGL_TRANSPARENT_GREEN_VALUE, + EGL10.EGL_TRANSPARENT_BLUE_VALUE, + 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB, + 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA, + 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL, + 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL, + EGL10.EGL_LUMINANCE_SIZE, + EGL10.EGL_ALPHA_MASK_SIZE, + EGL10.EGL_COLOR_BUFFER_TYPE, + EGL10.EGL_RENDERABLE_TYPE, + 0x3042 // EGL10.EGL_CONFORMANT + }; + String[] names = { + "EGL_BUFFER_SIZE", + "EGL_ALPHA_SIZE", + "EGL_BLUE_SIZE", + "EGL_GREEN_SIZE", + "EGL_RED_SIZE", + "EGL_DEPTH_SIZE", + "EGL_STENCIL_SIZE", + "EGL_CONFIG_CAVEAT", + "EGL_CONFIG_ID", + "EGL_LEVEL", + "EGL_MAX_PBUFFER_HEIGHT", + "EGL_MAX_PBUFFER_PIXELS", + "EGL_MAX_PBUFFER_WIDTH", + "EGL_NATIVE_RENDERABLE", + "EGL_NATIVE_VISUAL_ID", + "EGL_NATIVE_VISUAL_TYPE", + "EGL_PRESERVED_RESOURCES", + "EGL_SAMPLES", + "EGL_SAMPLE_BUFFERS", + "EGL_SURFACE_TYPE", + "EGL_TRANSPARENT_TYPE", + "EGL_TRANSPARENT_RED_VALUE", + "EGL_TRANSPARENT_GREEN_VALUE", + "EGL_TRANSPARENT_BLUE_VALUE", + "EGL_BIND_TO_TEXTURE_RGB", + "EGL_BIND_TO_TEXTURE_RGBA", + "EGL_MIN_SWAP_INTERVAL", + "EGL_MAX_SWAP_INTERVAL", + "EGL_LUMINANCE_SIZE", + "EGL_ALPHA_MASK_SIZE", + "EGL_COLOR_BUFFER_TYPE", + "EGL_RENDERABLE_TYPE", + "EGL_CONFORMANT" + }; + int[] value = new int[1]; + for (int i = 0; i < attributes.length; i++) { + int attribute = attributes[i]; + String name = names[i]; + if ( egl.eglGetConfigAttrib(display, config, attribute, value)) { + Log.w(TAG, String.format(" %s: %d\n", name, value[0])); + } else { + // Log.w(TAG, String.format(" %s: failed\n", name)); + while (egl.eglGetError() != EGL10.EGL_SUCCESS); + } + } + } + + // Subclasses can adjust these values: + protected int mRedSize; + protected int mGreenSize; + protected int mBlueSize; + protected int mAlphaSize; + protected int mDepthSize; + protected int mStencilSize; + private int[] mValue = new int[1]; + } + + private static class Renderer implements GLSurfaceView.Renderer { + + + public void onDrawFrame(GL10 gl) { + GodotLib.step(); + } + + public void onSurfaceChanged(GL10 gl, int width, int height) { + + GodotLib.resize(width, height,!firsttime); + firsttime=false; + } + + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + GodotLib.newcontext(); + } + } +} diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp new file mode 100644 index 0000000000..9c5003cb17 --- /dev/null +++ b/platform/android/java_glue.cpp @@ -0,0 +1,1153 @@ +/*************************************************************************/ +/* java_glue.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 ANDROID_NATIVE_ACTIVITY + +#include "java_glue.h" +#include "os_android.h" +#include "main/main.h" +#include <unistd.h> +#include "file_access_jandroid.h" +#include "dir_access_jandroid.h" +#include "audio_driver_jandroid.h" +#include "globals.h" +#include "thread_jandroid.h" +#include "core/os/keyboard.h" +static OS_Android *os_android=NULL; + + +jvalue _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant* p_arg, bool force_jobject = false) { + + jvalue 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.l = obj; + } else { + v.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.l = obj; + + } else { + v.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.l = obj; + + } else { + v.f=*p_arg; + }; + } break; + case Variant::STRING: { + + String s = *p_arg; + jstring jStr = env->NewStringUTF(s.utf8().get_data()); + v.l=jStr; + } break; + case Variant::STRING_ARRAY: { + + DVector<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++) { + + env->SetObjectArrayElement(arr,j,env->NewStringUTF( sarray[j].utf8().get_data() )); + } + v.l=arr; + + } break; + + case Variant::DICTIONARY: { + + Dictionary dict = *p_arg; + jclass dclass = env->FindClass("com/android/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++) { + env->SetObjectArrayElement(jkeys, j, env->NewStringUTF(String(keys[j]).utf8().get_data())); + }; + + jmethodID set_keys = env->GetMethodID(dclass, "set_keys", "([Ljava/lang/String;)V"); + jvalue val; + val.l = jkeys; + env->CallVoidMethodA(jdict, set_keys, &val); + + 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]]; + val = _variant_to_jvalue(env, var.get_type(), &var, true); + env->SetObjectArrayElement(jvalues, j, val.l); + }; + + jmethodID set_values = env->GetMethodID(dclass, "set_values", "([Ljava/lang/Object;)V"); + val.l = jvalues; + env->CallVoidMethodA(jdict, set_values, &val); + + v.l = jdict; + } break; + + case Variant::INT_ARRAY: { + + DVector<int> array = *p_arg; + jintArray arr = env->NewIntArray(array.size()); + DVector<int>::Read r = array.read(); + env->SetIntArrayRegion(arr,0,array.size(),r.ptr()); + v.l=arr; + + } break; + case Variant::REAL_ARRAY: { + + DVector<float> array = *p_arg; + jfloatArray arr = env->NewFloatArray(array.size()); + DVector<float>::Read r = array.read(); + env->SetFloatArrayRegion(arr,0,array.size(),r.ptr()); + v.l=arr; + + } break; + default: { + + v.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; + } + + return env->GetStringUTFChars( clsName, NULL ); +}; + + +Variant _jobject_to_variant(JNIEnv * env, jobject obj) { + + jclass c = env->GetObjectClass(obj); + bool array; + String name = _get_class_name(env, c, &array); + //print_line("name is " + name + ", array "+Variant(array)); + + if (name == "java.lang.String") { + + return String::utf8(env->GetStringUTFChars( (jstring)obj, NULL )); + }; + + + if (name == "[Ljava.lang.String;") { + + jobjectArray arr = (jobjectArray)obj; + int stringCount = env->GetArrayLength(arr); + //print_line("String array! " + String::num(stringCount)); + DVector<String> sarr; + + 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)); + } + + 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") { + + jclass nclass = env->FindClass("java/lang/Number"); + jmethodID intValue = env->GetMethodID(nclass, "intValue", "()I"); + int ret = env->CallIntMethod(obj, intValue); + return ret; + }; + + if (name == "[I") { + + jintArray arr = (jintArray)obj; + int fCount = env->GetArrayLength(arr); + DVector<int> sarr; + sarr.resize(fCount); + + DVector<int>::Write w = sarr.write(); + env->GetIntArrayRegion(arr,0,fCount,w.ptr()); + w = DVector<int>::Write(); + 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); + RealArray sarr; + sarr.resize(fCount); + + RealArray::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); + RealArray sarr; + sarr.resize(fCount); + + + RealArray::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); + } + + return varr; + }; + + if (name == "com.android.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); + + StringArray keys = _jobject_to_variant(env, 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); + + //print_line("adding " + String::num(keys.size()) + " to Dictionary!"); + for (int i=0; i<keys.size(); i++) { + + ret[keys[i]] = vals[i]; + }; + + return ret; + }; + + return Variant(); +}; + +class JNISingleton : public Object { + + OBJ_TYPE( 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) { + + //print_line("attempt to call "+String(p_method)); + + r_error.error=Variant::CallError::CALL_OK; + + Map<StringName,MethodData >::Element *E=method_map.find(p_method); + if (!E) { + + print_line("no exists"); + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + + + int ac = E->get().argtypes.size(); + if (ac<p_argcount) { + + print_line("fewargs"); + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=ac; + return Variant(); + } + + if (ac>p_argcount) { + + print_line("manyargs"); + 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(); + + //print_line("argcount "+String::num(p_argcount)); + for(int i=0;i<p_argcount;i++) { + + v[i] = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]); + } + + //print_line("calling method!!"); + + Variant ret; + + switch(E->get().ret_type) { + + case Variant::NIL: { + + + //print_line("call void"); + env->CallVoidMethodA(instance,E->get().method,v); + } break; + case Variant::BOOL: { + + ret = env->CallBooleanMethodA(instance,E->get().method,v); + //print_line("call bool"); + } break; + case Variant::INT: { + + ret = env->CallIntMethodA(instance,E->get().method,v); + //print_line("call int"); + } 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); + String singname = env->GetStringUTFChars((jstring)o, NULL ); + } break; + case Variant::STRING_ARRAY: { + + jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance,E->get().method,v); + + ret = _jobject_to_variant(env, arr); + + } break; + case Variant::INT_ARRAY: { + + jintArray arr = (jintArray)env->CallObjectMethodA(instance,E->get().method,v); + + int fCount = env->GetArrayLength(arr); + DVector<int> sarr; + sarr.resize(fCount); + + DVector<int>::Write w = sarr.write(); + env->GetIntArrayRegion(arr,0,fCount,w.ptr()); + w = DVector<int>::Write(); + ret=sarr; + } break; + case Variant::REAL_ARRAY: { + + jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance,E->get().method,v); + + int fCount = env->GetArrayLength(arr); + DVector<float> sarr; + sarr.resize(fCount); + + DVector<float>::Write w = sarr.write(); + env->GetFloatArrayRegion(arr,0,fCount,w.ptr()); + w = DVector<float>::Write(); + ret=sarr; + } break; + + case Variant::DICTIONARY: { + + //print_line("call dictionary"); + jobject obj = env->CallObjectMethodA(instance, E->get().method, v); + ret = _jobject_to_variant(env, obj); + + } break; + default: { + + + print_line("failure.."); + ERR_FAIL_V(Variant()); + } break; + } + + //print_line("success"); + + 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() {} + +}; + + +struct TST { + + int a; + TST() { + + a=5; + } +}; + +TST tst; + +struct JAndroidPointerEvent { + + Vector<OS_Android::TouchPos> points; + int pointer; + int what; +}; + +static List<JAndroidPointerEvent> pointer_events; +static List<InputEvent> key_events; +static bool initialized=false; +static Mutex *input_mutex=NULL; +static Mutex *suspend_mutex=NULL; +static int step=0; +static bool resized=false; +static bool resized_reload=false; +static bool quit_request=false; +static Size2 new_size; +static Vector3 accelerometer; +static HashMap<String,JNISingleton*> jni_singletons; +static jobject godot_io; + +typedef void (*GFXInitFunc)(void *ud,bool gl2); + +static jmethodID _on_video_init=0; +static jobject _godot_instance; + +static jmethodID _openURI=0; +static jmethodID _getDataDir=0; +static jmethodID _getLocale=0; +static jmethodID _getModel=0; +static jmethodID _showKeyboard=0; +static jmethodID _hideKeyboard=0; +static jmethodID _setScreenOrientation=0; +static jmethodID _getUniqueID=0; + + +static void _gfx_init_func(void* ud, bool gl2) { + +} + + +static int _open_uri(const String& p_uri) { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_uri.utf8().get_data()); + return env->CallIntMethod(godot_io,_openURI,jStr) ; +} + +static String _get_data_dir() { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring s =(jstring)env->CallObjectMethod(godot_io,_getDataDir); + return String(env->GetStringUTFChars( s, NULL )); +} + +static String _get_locale() { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring s =(jstring)env->CallObjectMethod(godot_io,_getLocale); + return String(env->GetStringUTFChars( s, NULL )); +} + +static String _get_model() { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring s =(jstring)env->CallObjectMethod(godot_io,_getModel); + return String(env->GetStringUTFChars( s, NULL )); +} + +static String _get_unique_id() { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring s =(jstring)env->CallObjectMethod(godot_io,_getUniqueID); + return String(env->GetStringUTFChars( s, NULL )); +} + +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 void _hide_vk() { + + JNIEnv* env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_io, _hideKeyboard); +}; + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_initialize(JNIEnv * env, jobject obj, jobject activity,jboolean p_need_reload_hook) { + + __android_log_print(ANDROID_LOG_INFO,"godot","**INIT EVENT! - %p\n",env); + + + initialized=true; + _godot_instance=activity; + + JavaVM *jvm; + env->GetJavaVM(&jvm); + + + __android_log_print(ANDROID_LOG_INFO,"godot","***************** HELLO FROM JNI!!!!!!!!"); + + { + //setup IO Object + + jclass cls = env->FindClass("com/android/godot/Godot"); + if (cls) { + + cls=(jclass)env->NewGlobalRef(cls); + __android_log_print(ANDROID_LOG_INFO,"godot","*******CLASS FOUND!!!"); + } + + __android_log_print(ANDROID_LOG_INFO,"godot","STEP2, %p",cls); + jfieldID fid = env->GetStaticFieldID(cls, "io", "Lcom/android/godot/GodotIO;"); + __android_log_print(ANDROID_LOG_INFO,"godot","STEP3 %i",fid); + jobject ob = env->GetStaticObjectField(cls,fid); + __android_log_print(ANDROID_LOG_INFO,"godot","STEP4, %p",ob); + jobject gob = env->NewGlobalRef(ob); + + __android_log_print(ANDROID_LOG_INFO,"godot","STEP4.5, %p",gob); + godot_io=gob; + + _on_video_init = env->GetMethodID(cls, "onVideoInit", "(Z)V"); + + jclass clsio = env->FindClass("com/android/godot/Godot"); + if (cls) { + jclass c = env->GetObjectClass(gob); + _openURI = env->GetMethodID(c,"openURI","(Ljava/lang/String;)I"); + _getDataDir = env->GetMethodID(c,"getDataDir","()Ljava/lang/String;"); + _getLocale = env->GetMethodID(c,"getLocale","()Ljava/lang/String;"); + _getModel = env->GetMethodID(c,"getModel","()Ljava/lang/String;"); + _getUniqueID = env->GetMethodID(c,"getUniqueID","()Ljava/lang/String;"); + _showKeyboard = env->GetMethodID(c,"showKeyboard","(Ljava/lang/String;)V"); + _hideKeyboard = env->GetMethodID(c,"hideKeyboard","()V"); + _setScreenOrientation = env->GetMethodID(c,"setScreenOrientation","(I)V"); + } + + ThreadAndroid::make_default(jvm); + FileAccessJAndroid::setup(gob); + DirAccessJAndroid::setup(gob); + AudioDriverAndroid::setup(gob); + } + + + + 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); + os_android->set_need_reload_hooks(p_need_reload_hook); + + char wd[500]; + getcwd(wd,500); + + __android_log_print(ANDROID_LOG_INFO,"godot","test construction %i\n",tst.a); + __android_log_print(ANDROID_LOG_INFO,"godot","running from dir %s\n",wd); + + __android_log_print(ANDROID_LOG_INFO,"godot","**SETUP"); + + +#if 0 + char *args[]={"-test","render",NULL}; + __android_log_print(ANDROID_LOG_INFO,"godot","pre asdasd setup..."); + Error err = Main::setup("apk",2,args,false); +#else + Error err = Main::setup("apk",0,NULL,false); +#endif + + if (err!=OK) { + __android_log_print(ANDROID_LOG_INFO,"godot","*****UNABLE TO SETUP"); + + return; //should exit instead and print the error + } + + __android_log_print(ANDROID_LOG_INFO,"godot","*****SETUP OK"); + + //video driver is determined here, because once initialized, it cant be changed + String vd = Globals::get_singleton()->get("display/driver"); + + + if (vd.to_upper()=="GLES1") + env->CallVoidMethod(_godot_instance, _on_video_init, (jboolean)false); + else + env->CallVoidMethod(_godot_instance, _on_video_init, (jboolean)true); + + __android_log_print(ANDROID_LOG_INFO,"godot","**START"); + + input_mutex=Mutex::create(); + suspend_mutex=Mutex::create(); + + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_resize(JNIEnv * env, jobject obj, jint width, jint height, jboolean reload) { + + __android_log_print(ANDROID_LOG_INFO,"godot","^_^_^_^_^ resize %lld, %i, %i\n",Thread::get_caller_ID(),width,height); + if (os_android) + os_android->set_display_size(Size2(width,height)); + + /*input_mutex->lock(); + resized=true; + if (reload) + resized_reload=true; + new_size=Size2(width,height); + input_mutex->unlock();*/ + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_newcontext(JNIEnv * env, jobject obj) { + + __android_log_print(ANDROID_LOG_INFO,"godot","^_^_^_^_^ newcontext %lld\n",Thread::get_caller_ID()); + if (os_android && step > 0) { + + os_android->reload_gfx(); + } + +} + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_quit(JNIEnv * env, jobject obj) { + + input_mutex->lock(); + quit_request=true; + input_mutex->unlock(); + +} + +static void _initialize_java_modules() { + + + String modules = Globals::get_singleton()->get("android/modules"); + Vector<String> mods = modules.split(",",false); + __android_log_print(ANDROID_LOG_INFO,"godot","mod count: %i",mods.size()); + + if (mods.size()) { + + JNIEnv *env = ThreadAndroid::get_env(); + + jclass activityClass = env->FindClass("com/android/godot/Godot"); + + jmethodID getClassLoader = env->GetMethodID(activityClass,"getClassLoader", "()Ljava/lang/ClassLoader;"); + + jobject cls = env->CallObjectMethod(_godot_instance, getClassLoader); + + jclass classLoader = env->FindClass("java/lang/ClassLoader"); + + jmethodID findClass = env->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + + for (int i=0;i<mods.size();i++) { + + String m = mods[i]; + //jclass singletonClass = env->FindClass(m.utf8().get_data()); + + print_line("LOADING MODULE: "+m); + jstring strClassName = env->NewStringUTF(m.utf8().get_data()); + jclass singletonClass = (jclass)env->CallObjectMethod(cls, findClass, strClassName); + + if (!singletonClass) { + + ERR_EXPLAIN("Couldn't find singleton for class: "+m); + ERR_CONTINUE(!singletonClass); + } + + __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class data %x",singletonClass); + jmethodID initialize = env->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lcom/android/godot/Godot$SingletonBase;"); + + jobject obj = env->CallStaticObjectMethod(singletonClass,initialize,_godot_instance); + __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class instance %x",obj); + jobject gob = env->NewGlobalRef(obj); + + + } + + } + +} + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_step(JNIEnv * env, jobject obj) +{ + + + ThreadAndroid::setup_thread(); + + //__android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_ID()); + + + suspend_mutex->lock(); + input_mutex->lock(); + //first time step happens, initialize + if (step == 0) { + // ugly hack to initialize the rest of the engine + // because of the way android forces you to do everything with threads + + _initialize_java_modules(); + + Main::setup2(); + ++step; + suspend_mutex->unlock(); + input_mutex->unlock(); + return; + }; + if (step == 1) { + if (!Main::start()) { + + input_mutex->unlock(); + suspend_mutex->lock(); + return; //should exit instead and print the error + } + + os_android->main_loop_begin(); + ++step; + } + + while(pointer_events.size()) { + + JAndroidPointerEvent jpe=pointer_events.front()->get(); + os_android->process_touch(jpe.what,jpe.pointer,jpe.points); + + pointer_events.pop_front(); + } + + while (key_events.size()) { + + InputEvent event = key_events.front()->get(); + os_android->process_event(event); + + key_events.pop_front(); + }; + + if (quit_request) { + + os_android->main_loop_request_quit(); + quit_request=false; + } + + + input_mutex->unlock(); + + os_android->process_accelerometer(accelerometer); + + if (os_android->main_loop_iterate()==true) { + + jclass cls = env->FindClass("com/android/godot/Godot"); + jmethodID _finish = env->GetMethodID(cls, "forceQuit", "()V"); + env->CallVoidMethod(_godot_instance, _finish); + __android_log_print(ANDROID_LOG_INFO,"godot","**FINISH REQUEST!!! - %p-%i\n",env,Thread::get_caller_ID()); + + } + + suspend_mutex->unlock(); + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_touch(JNIEnv * env, jobject obj, jint ev,jint pointer, jint count, jintArray positions) { + + + + //__android_log_print(ANDROID_LOG_INFO,"godot","**TOUCH EVENT! - %p-%i\n",env,Thread::get_caller_ID()); + + + Vector<OS_Android::TouchPos> points; + for(int i=0;i<count;i++) { + + jint p[3]; + env->GetIntArrayRegion(positions,i*3,3,p); + OS_Android::TouchPos tp; + tp.pos=Point2(p[1],p[2]); + tp.id=p[0]; + points.push_back(tp); + } + + JAndroidPointerEvent jpe; + jpe.pointer=pointer; + jpe.points=points; + jpe.what=ev; + + input_mutex->lock(); + + pointer_events.push_back(jpe); + + input_mutex->unlock(); + //if (os_android) +// os_android->process_touch(ev,pointer,points); + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_key(JNIEnv * env, jobject obj, jint ev, jint p_unicode_char, jboolean p_pressed) { + + InputEvent ievent; + ievent.type = InputEvent::KEY; + ievent.device = 0; + int val = p_unicode_char; + ievent.key.scancode = val; + ievent.key.unicode = val; + if (val == 61448) { + ievent.key.scancode = KEY_BACKSPACE; + ievent.key.unicode = KEY_BACKSPACE; + }; + if (val == 61453) { + ievent.key.scancode = KEY_ENTER; + ievent.key.unicode = KEY_ENTER; + }; + + input_mutex->lock(); + key_events.push_back(ievent); + input_mutex->unlock(); +}; + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_accelerometer(JNIEnv * env, jobject obj, jfloat x, jfloat y, jfloat z) { + + input_mutex->lock(); + accelerometer=Vector3(x,y,z); + input_mutex->unlock(); + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_focusin(JNIEnv * env, jobject obj){ + + if (!suspend_mutex) + return; + suspend_mutex->lock(); + + if (os_android && step > 0) + os_android->main_loop_focusin(); + + suspend_mutex->unlock(); +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_focusout(JNIEnv * env, jobject obj){ + + if (!suspend_mutex) + return; + suspend_mutex->lock(); + + if (os_android && step > 0) + os_android->main_loop_focusout(); + + suspend_mutex->unlock(); + +} + + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_audio(JNIEnv * env, jobject obj) { + + ThreadAndroid::setup_thread(); + AudioDriverAndroid::thread_func(env); + + +} + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_singleton(JNIEnv * env, jobject obj, jstring name,jobject p_object){ + + String singname = env->GetStringUTFChars( name, NULL ); + JNISingleton *s = memnew( JNISingleton ); + s->set_instance(env->NewGlobalRef(p_object)); + jni_singletons[singname]=s; + + Globals::get_singleton()->add_singleton(Globals::Singleton(singname,s)); + Globals::get_singleton()->set(singname,s); + +} + + +static 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::INT_ARRAY}, + {"[F",Variant::REAL_ARRAY}, + {"[java.lang.String",Variant::STRING_ARRAY}, + {"com.android.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; +} + + +static const char* get_jni_sig(const String& p_type) { + + print_line("getting sig for " + 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;"}, + {"com.android.godot.Dictionary", "Lcom/android/godot/Dictionary;"}, + {"[I","[I"}, + {"[F","[F"}, + {"[java.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;"; +} + +JNIEXPORT jstring JNICALL Java_com_android_godot_GodotLib_getGlobal(JNIEnv * env, jobject obj, jstring path) { + + String js = env->GetStringUTFChars( path, NULL ); + + return env->NewStringUTF(Globals::get_singleton()->get(js).operator String().utf8().get_data()); + + +} + + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_method(JNIEnv * env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args){ + + String singname = env->GetStringUTFChars( sname, NULL ); + + ERR_FAIL_COND(!jni_singletons.has(singname)); + + JNISingleton *s = jni_singletons.get(singname); + + + String mname = env->GetStringUTFChars( name, NULL ); + String retval = env->GetStringUTFChars( ret, NULL ); + Vector<Variant::Type> types; + String cs="("; + + + int stringCount = env->GetArrayLength(args); + + print_line("Singl: "+singname+" Method: "+mname+" RetVal: "+retval); + for (int i=0; i<stringCount; i++) { + + jstring string = (jstring) env->GetObjectArrayElement(args, i); + const char *rawString = env->GetStringUTFChars(string, 0); + types.push_back(get_jni_type(String(rawString))); + cs+=get_jni_sig(String(rawString)); + } + + cs+=")"; + cs+=get_jni_sig(retval); + jclass cls = env->GetObjectClass(s->get_instance()); + print_line("METHOD: "+mname+" sig: "+cs); + jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data()); + if (!mid) { + + print_line("FAILED GETTING METHOID "+mname); + } + + s->add_method(mname,mid,types,get_jni_type(retval)); + + +} + +JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_callobject(JNIEnv * env, jobject p_obj, jint ID, jstring method, jobjectArray params) { + + String str_method = env->GetStringUTFChars( method, NULL ); + + Object* obj = ObjectDB::get_instance(ID); + ERR_FAIL_COND(!obj); + + int count = env->GetArrayLength(params); + Variant* vlist = (Variant*)alloca(sizeof(Variant) * count); + Variant** vptr = (Variant**)alloca(sizeof(Variant*) * count); + for (int i=0; i<count; i++) { + + jobject obj = env->GetObjectArrayElement(params, i); + Variant v = _jobject_to_variant(env, obj); + memnew_placement(&vlist[i], Variant); + vlist[i] = v; + vptr[i] = &vlist[i]; + }; + + Variant::CallError err; + obj->call(str_method, (const Variant**)vptr, count, err); + // something +}; + + +//Main::cleanup(); + +//return os.get_exit_code(); +#endif diff --git a/platform/android/java_glue.h b/platform/android/java_glue.h new file mode 100644 index 0000000000..7a0666f63d --- /dev/null +++ b/platform/android/java_glue.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* java_glue.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 ANDROID_NATIVE_ACTIVITY + +#ifndef JAVA_GLUE_H +#define JAVA_GLUE_H + +#include <jni.h> +#include <android/log.h> + + +extern "C" { + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_initialize(JNIEnv * env, jobject obj, jobject activity,jboolean p_need_reload_hook); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_resize(JNIEnv * env, jobject obj, jint width, jint height, jboolean reload); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_newcontext(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_step(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_quit(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_touch(JNIEnv * env, jobject obj, jint ev,jint pointer, jint count, jintArray positions); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_key(JNIEnv * env, jobject obj, jint ev, jint p_unicode_char, jboolean p_pressed); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_audio(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_accelerometer(JNIEnv * env, jobject obj, jfloat x, jfloat y, jfloat z); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_focusin(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_focusout(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_singleton(JNIEnv * env, jobject obj, jstring name,jobject p_object); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_method(JNIEnv * env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args); + JNIEXPORT jstring JNICALL Java_com_android_godot_GodotLib_getGlobal(JNIEnv * env, jobject obj, jstring path); + JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_callobject(JNIEnv * env, jobject obj, jint ID, jstring method, jobjectArray params); +}; + + +#endif +#endif // JAVA_GLUE_H diff --git a/platform/android/logo.png b/platform/android/logo.png Binary files differnew file mode 100644 index 0000000000..a7e2c6f130 --- /dev/null +++ b/platform/android/logo.png diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp new file mode 100644 index 0000000000..9d9da3750e --- /dev/null +++ b/platform/android/os_android.cpp @@ -0,0 +1,709 @@ +/*************************************************************************/ +/* os_android.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "os_android.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles1/rasterizer_gles1.h" +#include "core/io/file_access_buffered_fa.h" +#include "drivers/unix/file_access_unix.h" +#include "drivers/unix/dir_access_unix.h" + +#include "servers/visual/visual_server_raster.h" + +#include "main/main.h" + +#include "core/globals.h" + +#ifdef ANDROID_NATIVE_ACTIVITY +#include "file_access_android.h" +#include "dir_access_android.h" +#else +#include "file_access_jandroid.h" +#include "dir_access_jandroid.h" +#endif + +int OS_Android::get_video_driver_count() const { + + return 2; +} +const char * OS_Android::get_video_driver_name(int p_driver) const { + + return p_driver==0?"GLES2":"GLES1"; +} + +OS::VideoMode OS_Android::get_default_video_mode() const { + + return OS::VideoMode(); +} + +int OS_Android::get_audio_driver_count() const { + + return 1; +} + +const char * OS_Android::get_audio_driver_name(int p_driver) const { + + return "Android"; +} + +void OS_Android::initialize_core() { + + OS_Unix::initialize_core(); + +#ifdef ANDROID_NATIVE_ACTIVITY + + FileAccess::make_default<FileAccessAndroid>(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA); + FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM); + //FileAccessBufferedFA<FileAccessUnix>::make_default(); + DirAccess::make_default<DirAccessAndroid>(DirAccess::ACCESS_RESOURCES); + DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA); + DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); + +#else + + FileAccess::make_default<FileAccessBufferedFA<FileAccessJAndroid> >(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA); + FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM); + //FileAccessBufferedFA<FileAccessUnix>::make_default(); + DirAccess::make_default<DirAccessJAndroid>(DirAccess::ACCESS_RESOURCES); + DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA); + DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); + +#endif + +} + +void OS_Android::set_opengl_extensions(const char* p_gl_extensions) { + + ERR_FAIL_COND(!p_gl_extensions); + gl_extensions=p_gl_extensions; +} + +void OS_Android::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + + use_gl2=p_video_driver!=1; + + + if (gfx_init_func) + gfx_init_func(gfx_init_ud,use_gl2); + + AudioDriverManagerSW::add_driver(&audio_driver_android); + + + if (use_gl2) { + RasterizerGLES2 *rasterizer_gles22=memnew( RasterizerGLES2(false,use_reload_hooks,false,use_reload_hooks ) ); + if (gl_extensions) + rasterizer_gles22->set_extensions(gl_extensions); + rasterizer = rasterizer_gles22; + } else { + rasterizer = memnew( RasterizerGLES1(use_reload_hooks, use_reload_hooks) ); + + } + + visual_server = memnew( VisualServerRaster(rasterizer) ); + visual_server->init(); + visual_server->cursor_set_visible(false, 0); + + AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton(); + + if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) { + + ERR_PRINT("Initializing audio failed."); + } + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + + audio_server->set_mixer_params(AudioMixerSW::INTERPOLATION_LINEAR,false); + audio_server->init(); + + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + +} + +void OS_Android::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); +#if 0 + + print_line("preGS"); + FileAccess *f=memnew( FileAccessAndroid ); + print("made f %p\n",f); + Error err = f->open("AndroidManifest.xml",FileAccess::READ); + if (err) { + + print("************NO FILE!!\n"); + } else { + print("************YES FILE!!\n"); + } + f->close(); + print_line("end"); + + + AAssetDir* aad = AAssetManager_openDir(FileAccessAndroid::asset_manager,"."); + + if (aad) { + + print_line("DIR OPEN OK"); + + const char *fn= AAssetDir_getNextFileName(aad); + + while(fn) { + + print_line("FNAME: "+String(fn)); + fn= AAssetDir_getNextFileName(aad); + } + + AAssetDir_close(aad); + } else { + + print_line("DIR NO OPEN"); + } + +#endif + +} + +void OS_Android::delete_main_loop() { + + memdelete( main_loop ); +} + +void OS_Android::finalize() { + + memdelete(input); +} + + +void OS_Android::vprint(const char* p_format, va_list p_list, bool p_stderr) { + + __android_log_vprint(p_stderr?ANDROID_LOG_ERROR:ANDROID_LOG_INFO,"godot",p_format,p_list); +} + +void OS_Android::print(const char *p_format, ... ) { + + va_list argp; + va_start(argp, p_format); + __android_log_vprint(ANDROID_LOG_INFO,"godot",p_format,argp); + va_end(argp); + +} + +void OS_Android::alert(const String& p_alert) { + + print("ALERT: %s\n",p_alert.utf8().get_data()); +} + + +void OS_Android::set_mouse_show(bool p_show) { + + //android has no mouse... +} + +void OS_Android::set_mouse_grab(bool p_grab) { + + //it really has no mouse...! +} + +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 +//void set_clipboard(const String& p_text); +//String get_clipboard() const; + +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); +} + +String OS_Android::get_name() { + + return "Android"; +} + +MainLoop *OS_Android::get_main_loop() const { + + return main_loop; +} + +bool OS_Android::can_draw() const { + + return true; //always? +} + +void OS_Android::set_cursor_shape(CursorShape p_shape) { + + //android really really really has no mouse.. how amazing.. +} + +void OS_Android::main_loop_begin() { + + if (main_loop) + main_loop->init(); +} +bool OS_Android::main_loop_iterate() { + + if (!main_loop) + return false; + return Main::iteration(); +} + +void OS_Android::main_loop_end() { + + if (main_loop) + main_loop->finish(); + +} + +void OS_Android::main_loop_focusout() { + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + audio_driver_android.set_pause(true); + +} + +void OS_Android::main_loop_focusin(){ + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + audio_driver_android.set_pause(false); + +} + + +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) { + +// print_line("ev: "+itos(p_what)+" pnt: "+itos(p_pointer)+" pointc: "+itos(p_points.size())); + + switch(p_what) { + case 0: { //gesture begin + + if (touch.size()) { + //end all if exist + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.ID=last_id++; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.button_mask=BUTTON_MASK_LEFT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=touch[0].pos.x; + ev.mouse_button.y=touch[0].pos.y; + ev.mouse_button.global_x=touch[0].pos.x; + ev.mouse_button.global_y=touch[0].pos.y; + input->parse_input_event(ev); + + + for(int i=0;i<touch.size();i++) { + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=last_id++; + ev.screen_touch.index=touch[i].id; + ev.screen_touch.pressed=false; + ev.screen_touch.x=touch[i].pos.x; + ev.screen_touch.y=touch[i].pos.y; + input->parse_input_event(ev); + + } + } + + touch.resize(p_points.size()); + for(int i=0;i<p_points.size();i++) { + touch[i].id=p_points[i].id; + touch[i].pos=p_points[i].pos; + } + + { + //send mouse + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.ID=last_id++; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.button_mask=BUTTON_MASK_LEFT; + ev.mouse_button.pressed=true; + ev.mouse_button.x=touch[0].pos.x; + ev.mouse_button.y=touch[0].pos.y; + ev.mouse_button.global_x=touch[0].pos.x; + ev.mouse_button.global_y=touch[0].pos.y; + last_mouse=touch[0].pos; + input->parse_input_event(ev); + } + + + //send touch + for(int i=0;i<touch.size();i++) { + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=last_id++; + ev.screen_touch.index=touch[i].id; + ev.screen_touch.pressed=true; + ev.screen_touch.x=touch[i].pos.x; + ev.screen_touch.y=touch[i].pos.y; + input->parse_input_event(ev); + } + + } break; + case 1: { //motion + + + if (p_points.size()) { + //send mouse, should look for point 0? + InputEvent ev; + ev.type=InputEvent::MOUSE_MOTION; + ev.ID=last_id++; + ev.mouse_motion.button_mask=BUTTON_MASK_LEFT; + ev.mouse_motion.x=p_points[0].pos.x; + ev.mouse_motion.y=p_points[0].pos.y; + input->set_mouse_pos(Point2(ev.mouse_motion.x,ev.mouse_motion.y)); + ev.mouse_motion.speed_x=input->get_mouse_speed().x; + ev.mouse_motion.speed_y=input->get_mouse_speed().y; + ev.mouse_motion.relative_x=p_points[0].pos.x-last_mouse.x; + ev.mouse_motion.relative_y=p_points[0].pos.y-last_mouse.y; + last_mouse=p_points[0].pos; + input->parse_input_event(ev); + } + + ERR_FAIL_COND(touch.size()!=p_points.size()); + + for(int i=0;i<touch.size();i++) { + + int idx=-1; + for(int j=0;j<p_points.size();j++) { + + if (touch[i].id==p_points[j].id) { + idx=j; + break; + } + + } + + ERR_CONTINUE(idx==-1); + + if (touch[i].pos==p_points[idx].pos) + continue; //no move unncesearily + + InputEvent ev; + ev.type=InputEvent::SCREEN_DRAG; + ev.ID=last_id++; + ev.screen_drag.index=touch[i].id; + ev.screen_drag.x=p_points[idx].pos.x; + ev.screen_drag.y=p_points[idx].pos.y; + ev.screen_drag.relative_x=p_points[idx].pos.x - touch[i].pos.x; + ev.screen_drag.relative_y=p_points[idx].pos.y - touch[i].pos.y; + input->parse_input_event(ev); + touch[i].pos=p_points[idx].pos; + } + + + } break; + case 2: { //release + + + + if (touch.size()) { + //end all if exist + InputEvent ev; + ev.type=InputEvent::MOUSE_BUTTON; + ev.ID=last_id++; + ev.mouse_button.button_index=BUTTON_LEFT; + ev.mouse_button.button_mask=BUTTON_MASK_LEFT; + ev.mouse_button.pressed=false; + ev.mouse_button.x=touch[0].pos.x; + ev.mouse_button.y=touch[0].pos.y; + ev.mouse_button.global_x=touch[0].pos.x; + ev.mouse_button.global_y=touch[0].pos.y; + input->parse_input_event(ev); + + + for(int i=0;i<touch.size();i++) { + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=last_id++; + ev.screen_touch.index=touch[i].id; + ev.screen_touch.pressed=false; + ev.screen_touch.x=touch[i].pos.x; + ev.screen_touch.y=touch[i].pos.y; + input->parse_input_event(ev); + + } + touch.clear(); + } + + } break; + case 3: { // add tuchi + + + + + + ERR_FAIL_INDEX(p_pointer,p_points.size()); + + TouchPos tp=p_points[p_pointer]; + touch.push_back(tp); + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=last_id++; + ev.screen_touch.index=tp.id; + ev.screen_touch.pressed=true; + ev.screen_touch.x=tp.pos.x; + ev.screen_touch.y=tp.pos.y; + input->parse_input_event(ev); + + } break; + case 4: { + + + for(int i=0;i<touch.size();i++) { + if (touch[i].id==p_pointer) { + + InputEvent ev; + ev.type=InputEvent::SCREEN_TOUCH; + ev.ID=last_id++; + ev.screen_touch.index=touch[i].id; + ev.screen_touch.pressed=false; + ev.screen_touch.x=touch[i].pos.x; + ev.screen_touch.y=touch[i].pos.y; + input->parse_input_event(ev); + touch.remove(i); + i--; + } + } + + } break; + + } + +} + +void OS_Android::process_accelerometer(const Vector3& p_accelerometer) { + + input->set_accelerometer(p_accelerometer); +} + +bool OS_Android::has_touchscreen_ui_hint() const { + + return true; +} + +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) { + + if (show_virtual_keyboard_func) { + show_virtual_keyboard_func(p_existing_text); + } else { + + ERR_PRINT("Virtual keyboard not available"); + }; +}; + +void OS_Android::hide_virtual_keyboard() { + + if (hide_virtual_keyboard_func) { + + hide_virtual_keyboard_func(); + } else { + + ERR_PRINT("Virtual keyboard not available"); + }; +}; + + +void OS_Android::init_video_mode(int p_video_width,int p_video_height) { + + default_videomode.width=p_video_width; + default_videomode.height=p_video_height; + default_videomode.fullscreen=true; + default_videomode.resizable=false; +} + +void OS_Android::main_loop_request_quit() { + + if (main_loop) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); +} + +void OS_Android::set_display_size(Size2 p_size) { + + default_videomode.width=p_size.x; + default_videomode.height=p_size.y; +} + +void OS_Android::reload_gfx() { + + if (gfx_init_func) + gfx_init_func(gfx_init_ud,use_gl2); + if (rasterizer) + rasterizer->reload_vram(); +} + +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 { + + return "/"; //android has it's own filesystem for resources inside the APK +} + +String OS_Android::get_locale() const { + + if (get_locale_func) + return get_locale_func(); + return OS_Unix::get_locale(); +} + +String OS_Android::get_model_name() const { + + if (get_model_func) + return get_model_func(); + return OS_Unix::get_model_name(); +} + + +void OS_Android::set_need_reload_hooks(bool p_needs_them) { + + use_reload_hooks=p_needs_them; +} + +String OS_Android::get_data_dir() const { + + if (get_data_dir_func) + return get_data_dir_func(); + 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); +} + + +String OS_Android::get_unique_ID() const { + + if (get_unique_id_func) + return get_unique_id_func(); + return OS::get_unique_ID(); +} + + +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) { + + + default_videomode.width=800; + default_videomode.height=600; + default_videomode.fullscreen=true; + default_videomode.resizable=false; + + gfx_init_func=p_gfx_init_func; + gfx_init_ud=p_gfx_init_ud; + main_loop=NULL; + last_id=1; + gl_extensions=NULL; + rasterizer=NULL; + use_gl2=false; + + open_uri_func=p_open_uri_func; + get_data_dir_func=p_get_data_dir_func; + get_locale_func=p_get_locale_func; + get_model_func=p_get_model_func; + get_unique_id_func=p_get_unique_id; + + show_virtual_keyboard_func = p_show_vk; + hide_virtual_keyboard_func = p_hide_vk; + + set_screen_orientation_func=p_screen_orient; + use_reload_hooks=false; + +} + +OS_Android::~OS_Android() { + + +} diff --git a/platform/android/os_android.h b/platform/android/os_android.h new file mode 100644 index 0000000000..93c672927e --- /dev/null +++ b/platform/android/os_android.h @@ -0,0 +1,197 @@ +/*************************************************************************/ +/* os_android.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 OS_ANDROID_H +#define OS_ANDROID_H + +#include "os/input.h" +#include "drivers/unix/os_unix.h" +#include "os/main_loop.h" +#include "servers/physics/physics_server_sw.h" +#include "servers/spatial_sound/spatial_sound_server_sw.h" +#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" +#include "servers/audio/audio_server_sw.h" +#include "servers/physics_2d/physics_2d_server_sw.h" +#include "servers/visual/rasterizer.h" + + +#ifdef ANDROID_NATIVE_ACTIVITY +#include "audio_driver_android.h" +#include <android/sensor.h> +#include <android/log.h> +#include <android_native_app_glue.h> + +#else +#include "audio_driver_jandroid.h" + +#endif + +typedef void (*GFXInitFunc)(void *ud,bool gl2); +typedef int (*OpenURIFunc)(const String&); +typedef String (*GetDataDirFunc)(); +typedef String (*GetLocaleFunc)(); +typedef String (*GetModelFunc)(); +typedef String (*GetUniqueIDFunc)(); +typedef void (*ShowVirtualKeyboardFunc)(const String&); +typedef void (*HideVirtualKeyboardFunc)(); +typedef void (*SetScreenOrientationFunc)(int); + +class OS_Android : public OS_Unix { +public: + + struct TouchPos { + int id; + Point2 pos; + }; + +private: + + Vector<TouchPos> touch; + + Point2 last_mouse; + unsigned int last_id; + GFXInitFunc gfx_init_func; + void*gfx_init_ud; + + bool use_gl2; + bool use_reload_hooks; + + Rasterizer *rasterizer; + VisualServer *visual_server; + AudioServerSW *audio_server; + SampleManagerMallocSW *sample_manager; + SpatialSoundServerSW *spatial_sound_server; + SpatialSound2DServerSW *spatial_sound_2d_server; + PhysicsServer *physics_server; + Physics2DServer *physics_2d_server; + AudioDriverAndroid audio_driver_android; + const char* gl_extensions; + + InputDefault *input; + VideoMode default_videomode; + MainLoop * main_loop; + + OpenURIFunc open_uri_func; + GetDataDirFunc get_data_dir_func; + GetLocaleFunc get_locale_func; + GetModelFunc get_model_func; + ShowVirtualKeyboardFunc show_virtual_keyboard_func; + HideVirtualKeyboardFunc hide_virtual_keyboard_func; + SetScreenOrientationFunc set_screen_orientation_func; + GetUniqueIDFunc get_unique_id_func; + +public: + + // functions used by main to initialize/deintialize the OS + virtual int get_video_driver_count() const; + virtual const char * get_video_driver_name(int p_driver) const; + + virtual VideoMode get_default_video_mode() const; + + virtual int get_audio_driver_count() const; + virtual const char * get_audio_driver_name(int p_driver) const; + + virtual void initialize_core(); + virtual void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver); + + virtual void set_main_loop( MainLoop * p_main_loop ); + virtual void delete_main_loop(); + + virtual void finalize(); + + + typedef int64_t ProcessID; + + static OS* get_singleton(); + + virtual void vprint(const char* p_format, va_list p_list, bool p_stderr=false); + virtual void print(const char *p_format, ... ); + virtual void alert(const String& p_alert); + + + virtual void set_mouse_show(bool p_show); + virtual void set_mouse_grab(bool p_grab); + virtual bool is_mouse_grab_enabled() const; + virtual Point2 get_mouse_pos() const; + virtual int get_mouse_button_state() const; + virtual void set_window_title(const String& p_title); + + //virtual void set_clipboard(const String& p_text); + //virtual String get_clipboard() const; + + virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen=0); + virtual VideoMode get_video_mode(int p_screen=0) const; + virtual void get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen=0) const; + + virtual String get_name(); + virtual MainLoop *get_main_loop() const; + + virtual bool can_draw() const; + + virtual void set_cursor_shape(CursorShape p_shape); + + void main_loop_begin(); + bool main_loop_iterate(); + void main_loop_request_quit(); + void main_loop_end(); + void main_loop_focusout(); + void main_loop_focusin(); + + virtual bool has_touchscreen_ui_hint() const; + + virtual bool has_virtual_keyboard() const; + virtual void show_virtual_keyboard(const String& p_existing_text,const Rect2& p_screen_rect=Rect2()); + virtual void hide_virtual_keyboard(); + + void set_opengl_extensions(const char* p_gl_extensions); + void set_display_size(Size2 p_size); + + void reload_gfx(); + + void set_need_reload_hooks(bool p_needs_them); + virtual void set_screen_orientation(ScreenOrientation p_orientation); + + virtual Error shell_open(String p_uri); + virtual String get_data_dir() const; + virtual String get_resource_dir() const; + virtual String get_locale() const; + virtual String get_model_name() const; + + virtual String get_unique_ID() const; + + + void process_accelerometer(const Vector3& p_accelerometer); + void process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points); + void process_event(InputEvent p_event); + void init_video_mode(int p_video_width,int p_video_height); + 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); + ~OS_Android(); + +}; + +#endif diff --git a/platform/android/platform_config.h b/platform/android/platform_config.h new file mode 100644 index 0000000000..38fc934ae4 --- /dev/null +++ b/platform/android/platform_config.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* platform_config.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 <alloca.h> diff --git a/platform/android/project.properties.template b/platform/android/project.properties.template new file mode 100644 index 0000000000..72bea9659c --- /dev/null +++ b/platform/android/project.properties.template @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +#android.library=true +target=android-13 diff --git a/platform/android/sign.sh b/platform/android/sign.sh new file mode 100755 index 0000000000..830da05a37 --- /dev/null +++ b/platform/android/sign.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore my-release-key.keystore "$1" reduz + +echo "" +echo "" +echo "Checking if APK is verified..." +jarsigner -verify "$1" -verbose -certs + diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp new file mode 100644 index 0000000000..ec6bef89a0 --- /dev/null +++ b/platform/android/thread_jandroid.cpp @@ -0,0 +1,135 @@ +/*************************************************************************/ +/* thread_jandroid.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "thread_jandroid.h" + +#include "os/memory.h" + +Thread::ID ThreadAndroid::get_ID() const { + + return id; +} + +Thread* ThreadAndroid::create_thread_jandroid() { + + return memnew( ThreadAndroid ); +} + +void *ThreadAndroid::thread_callback(void *userdata) { + + ThreadAndroid *t=reinterpret_cast<ThreadAndroid*>(userdata); + setup_thread(); + t->id=(ID)pthread_self(); + t->callback(t->user); + return NULL; +} + +Thread* ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback,void *p_user,const Settings&) { + + ThreadAndroid *tr= memnew(ThreadAndroid); + tr->callback=p_callback; + tr->user=p_user; + pthread_attr_init(&tr->pthread_attr); + pthread_attr_setdetachstate(&tr->pthread_attr, PTHREAD_CREATE_JOINABLE); + + pthread_create(&tr->pthread, &tr->pthread_attr, thread_callback, tr); + + 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); + ERR_FAIL_COND(!tp); + ERR_FAIL_COND(tp->pthread==0); + + pthread_join(tp->pthread,NULL); + tp->pthread=0; +} + +void ThreadAndroid::_thread_destroyed(void* value) { + + /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */ + JNIEnv *env = (JNIEnv*) value; + if (env != NULL) { + java_vm->DetachCurrentThread(); + pthread_setspecific(jvm_key, NULL); + } + +} + +pthread_key_t ThreadAndroid::jvm_key; +JavaVM* ThreadAndroid::java_vm=NULL; + +void ThreadAndroid::setup_thread() { + + if (pthread_getspecific(jvm_key)) + return; //already setup + JNIEnv *env; + java_vm->AttachCurrentThread(&env, NULL); + pthread_setspecific(jvm_key, (void*) env); + +} + +void ThreadAndroid::make_default(JavaVM* p_java_vm) { + + java_vm=p_java_vm; + create_func=create_func_jandroid; + get_thread_ID_func=get_thread_ID_func_jandroid; + wait_to_finish_func=wait_to_finish_func_jandroid; + pthread_key_create(&jvm_key, _thread_destroyed); + setup_thread(); + +} + +JNIEnv *ThreadAndroid::get_env() { + + if (!pthread_getspecific(jvm_key)) { + setup_thread(); + } + + JNIEnv *env=NULL; + int status = java_vm->AttachCurrentThread(&env, NULL); + return env; +} + + +ThreadAndroid::ThreadAndroid() { + + pthread=0; +} + + +ThreadAndroid::~ThreadAndroid() { + +} + + diff --git a/platform/android/thread_jandroid.h b/platform/android/thread_jandroid.h new file mode 100644 index 0000000000..38b4be01ec --- /dev/null +++ b/platform/android/thread_jandroid.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* thread_jandroid.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 THREAD_POSIX_H +#define THREAD_POSIX_H + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + + +#include <sys/types.h> +#include <pthread.h> +#include "os/thread.h" +#include <jni.h> + +class ThreadAndroid : public Thread { + + pthread_t pthread; + pthread_attr_t pthread_attr; + ThreadCreateCallback callback; + void *user; + ID id; + + static Thread* create_thread_jandroid(); + + + static void *thread_callback(void *userdata); + + static Thread* create_func_jandroid(ThreadCreateCallback p_callback,void *,const Settings&); + static ID get_thread_ID_func_jandroid(); + static void wait_to_finish_func_jandroid(Thread* p_thread); + + static void _thread_destroyed(void* value); + ThreadAndroid(); + + static pthread_key_t jvm_key; + static JavaVM* java_vm; +public: + + + + + virtual ID get_ID() const; + + static void make_default(JavaVM* p_java_vm); + static void setup_thread(); + static JNIEnv *get_env(); + + + ~ThreadAndroid(); + +}; + + + +#endif |