Merge branch 'master' into hotfix-android-unicode-ime-input

This commit is contained in:
sanikoyes 2014-04-06 21:52:47 +08:00
commit 77a840e350
200 changed files with 15278 additions and 106197 deletions

1
.gitignore vendored
View file

@ -14,6 +14,7 @@ core/version.h
core/method_bind.inc
core/global_defaults.cpp
tools/editor/register_exporters.cpp
tools/editor/doc_data_compressed.h
-fpic
# Android specific

View file

@ -11,7 +11,7 @@ import multiprocessing
# Enable aggresive compile mode if building on a multi core box
# only is we have not set the number of jobs already or we do
# not want it
if ARGUMENTS.get('spawn_jobs', 'yes') == 'yes' and \
if ARGUMENTS.get('spawn_jobs', 'no') == 'yes' and \
int(GetOption('num_jobs')) <= 1:
NUM_JOBS = multiprocessing.cpu_count()
if NUM_JOBS > 1:

View file

@ -199,6 +199,14 @@ int _OS::get_iterations_per_second() const {
}
void _OS::set_target_fps(int p_fps) {
OS::get_singleton()->set_target_fps(p_fps);
}
float _OS::get_target_fps() const {
return OS::get_singleton()->get_target_fps();
}
void _OS::set_low_processor_usage_mode(bool p_enabled) {
OS::get_singleton()->set_low_processor_usage_mode(p_enabled);
@ -238,6 +246,12 @@ Error _OS::kill(int p_pid) {
return OS::get_singleton()->kill(p_pid);
}
int _OS::get_process_ID() const {
return OS::get_singleton()->get_process_ID();
};
bool _OS::has_environment(const String& p_var) const {
return OS::get_singleton()->has_environment(p_var);
@ -387,6 +401,12 @@ uint32_t _OS::get_ticks_msec() const {
return OS::get_singleton()->get_ticks_msec();
}
bool _OS::can_use_threads() const {
return OS::get_singleton()->can_use_threads();
}
bool _OS::can_draw() const {
return OS::get_singleton()->can_draw();
@ -488,6 +508,27 @@ float _OS::get_frames_per_second() const {
return OS::get_singleton()->get_frames_per_second();
}
Error _OS::native_video_play(String p_path) {
return OS::get_singleton()->native_video_play(p_path);
};
bool _OS::native_video_is_playing() {
return OS::get_singleton()->native_video_is_playing();
};
void _OS::native_video_pause() {
OS::get_singleton()->native_video_pause();
};
void _OS::native_video_stop() {
OS::get_singleton()->native_video_stop();
};
String _OS::get_custom_level() const {
return OS::get_singleton()->get_custom_level();
@ -496,7 +537,7 @@ _OS *_OS::singleton=NULL;
void _OS::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_mouse_pos"),&_OS::get_mouse_pos);
//ObjectTypeDB::bind_method(_MD("get_mouse_pos"),&_OS::get_mouse_pos);
//ObjectTypeDB::bind_method(_MD("is_mouse_grab_enabled"),&_OS::is_mouse_grab_enabled);
ObjectTypeDB::bind_method(_MD("set_clipboard","clipboard"),&_OS::set_clipboard);
@ -510,6 +551,8 @@ void _OS::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_iterations_per_second","iterations_per_second"),&_OS::set_iterations_per_second);
ObjectTypeDB::bind_method(_MD("get_iterations_per_second"),&_OS::get_iterations_per_second);
ObjectTypeDB::bind_method(_MD("set_target_fps","target_fps"),&_OS::set_target_fps);
ObjectTypeDB::bind_method(_MD("get_target_fps"),&_OS::get_target_fps);
ObjectTypeDB::bind_method(_MD("has_touchscreen_ui_hint"),&_OS::has_touchscreen_ui_hint);
@ -524,6 +567,7 @@ void _OS::_bind_methods() {
ObjectTypeDB::bind_method(_MD("execute","path","arguments","blocking"),&_OS::execute);
ObjectTypeDB::bind_method(_MD("kill","pid"),&_OS::kill);
ObjectTypeDB::bind_method(_MD("shell_open","uri"),&_OS::shell_open);
ObjectTypeDB::bind_method(_MD("get_process_ID"),&_OS::get_process_ID);
ObjectTypeDB::bind_method(_MD("get_environment","environment"),&_OS::get_environment);
ObjectTypeDB::bind_method(_MD("has_environment","environment"),&_OS::has_environment);
@ -550,7 +594,9 @@ void _OS::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_frames_drawn"),&_OS::get_frames_drawn);
ObjectTypeDB::bind_method(_MD("is_stdout_verbose"),&_OS::is_stdout_verbose);
ObjectTypeDB::bind_method(_MD("get_mouse_button_state"),&_OS::get_mouse_button_state);
ObjectTypeDB::bind_method(_MD("can_use_threads"),&_OS::can_use_threads);
//ObjectTypeDB::bind_method(_MD("get_mouse_button_state"),&_OS::get_mouse_button_state);
ObjectTypeDB::bind_method(_MD("dump_memory_to_file","file"),&_OS::dump_memory_to_file);
ObjectTypeDB::bind_method(_MD("dump_resources_to_file","file"),&_OS::dump_resources_to_file);
@ -568,6 +614,12 @@ void _OS::_bind_methods() {
ObjectTypeDB::bind_method(_MD("print_all_textures_by_size"),&_OS::print_all_textures_by_size);
ObjectTypeDB::bind_method(_MD("native_video_play"),&_OS::native_video_play);
ObjectTypeDB::bind_method(_MD("native_video_is_playing"),&_OS::native_video_is_playing);
ObjectTypeDB::bind_method(_MD("native_video_stop"),&_OS::native_video_stop);
ObjectTypeDB::bind_method(_MD("native_video_pause"),&_OS::native_video_pause);
BIND_CONSTANT( DAY_SUNDAY );
BIND_CONSTANT( DAY_MONDAY );
BIND_CONSTANT( DAY_TUESDAY );
@ -983,8 +1035,22 @@ void _File::store_string(const String& p_string){
f->store_string(p_string);
}
void _File::store_line(const String& p_string){
void _File::store_pascal_string(const String& p_string) {
ERR_FAIL_COND(!f);
f->store_pascal_string(p_string);
};
String _File::get_pascal_string() {
ERR_FAIL_COND_V(!f, "");
return f->get_pascal_string();
};
void _File::store_line(const String& p_string){
ERR_FAIL_COND(!f);
f->store_line(p_string);
@ -1083,6 +1149,9 @@ void _File::_bind_methods() {
ObjectTypeDB::bind_method(_MD("store_string","string"),&_File::store_string);
ObjectTypeDB::bind_method(_MD("store_var","value"),&_File::store_var);
ObjectTypeDB::bind_method(_MD("store_pascal_string","string"),&_File::store_pascal_string);
ObjectTypeDB::bind_method(_MD("get_pascal_string"),&_File::get_pascal_string);
ObjectTypeDB::bind_method(_MD("file_exists","path"),&_File::file_exists);
BIND_CONSTANT( READ );

View file

@ -98,9 +98,17 @@ public:
bool is_video_mode_resizable(int p_screen=0) const;
Array get_fullscreen_mode_list(int p_screen=0) const;
Error native_video_play(String p_path);
bool native_video_is_playing();
void native_video_pause();
void native_video_stop();
void set_iterations_per_second(int p_ips);
int get_iterations_per_second() const;
void set_target_fps(int p_fps);
float get_target_fps() const;
void set_low_processor_usage_mode(bool p_enabled);
bool is_in_low_processor_usage_mode() const;
@ -109,6 +117,8 @@ public:
Error kill(int p_pid);
Error shell_open(String p_uri);
int get_process_ID() const;
bool has_environment(const String& p_var) const;
String get_environment(const String& p_var) const;
@ -166,6 +176,7 @@ public:
void delay_msec(uint32_t p_msec) const;
uint32_t get_ticks_msec() const;
bool can_use_threads() const;
bool can_draw() const;
@ -280,6 +291,9 @@ public:
void store_string(const String& p_string);
void store_line(const String& p_string);
virtual void store_pascal_string(const String& p_string);
virtual String get_pascal_string();
Vector<String> get_csv_line() const;

55
core/func_ref.cpp Normal file
View file

@ -0,0 +1,55 @@
#include "func_ref.h"
Variant FuncRef::call_func(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
if (id==0) {
r_error.error=Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return Variant();
}
Object* obj = ObjectDB::get_instance(id);
if (!obj) {
r_error.error=Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return Variant();
}
return obj->call(function,p_args,p_argcount,r_error);
}
void FuncRef::set_instance(Object *p_obj){
ERR_FAIL_NULL(p_obj);
id=p_obj->get_instance_ID();
}
void FuncRef::set_function(const StringName& p_func){
function=p_func;
}
void FuncRef::_bind_methods() {
{
MethodInfo mi;
mi.name="call";
mi.arguments.push_back( PropertyInfo( Variant::STRING, "method"));
Vector<Variant> defargs;
for(int i=0;i<10;i++) {
mi.arguments.push_back( PropertyInfo( Variant::NIL, "arg"+itos(i)));
defargs.push_back(Variant());
}
ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"call_func",&FuncRef::call_func,mi,defargs);
}
ObjectTypeDB::bind_method(_MD("set_instance","instance"),&FuncRef::set_instance);
ObjectTypeDB::bind_method(_MD("set_function","name"),&FuncRef::set_function);
}
FuncRef::FuncRef(){
id=0;
}

23
core/func_ref.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef FUNC_REF_H
#define FUNC_REF_H
#include "reference.h"
class FuncRef : public Reference{
OBJ_TYPE(FuncRef,Reference);
ObjectID id;
StringName function;
protected:
static void _bind_methods();
public:
Variant call_func(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
void set_instance(Object *p_obj);
void set_function(const StringName& p_func);
FuncRef();
};
#endif // FUNC_REF_H

View file

@ -166,10 +166,9 @@ bool Globals::_get(const StringName& p_name,Variant &r_ret) const {
_THREAD_SAFE_METHOD_
const VariantContainer *v=props.getptr(p_name);
if (!v)
if (!props.has(p_name))
return false;
r_ret=v->variant;
r_ret=props[p_name].variant;
return true;
}
@ -188,18 +187,17 @@ void Globals::_get_property_list(List<PropertyInfo> *p_list) const {
_THREAD_SAFE_METHOD_
const String *k=NULL;
Set<_VCSort> vclist;
while ((k=props.next(k))) {
for(Map<StringName,VariantContainer>::Element *E=props.front();E;E=E->next()) {
const VariantContainer *v=props.getptr(*k);
const VariantContainer *v=&E->get();
if (v->hide_from_editor)
continue;
_VCSort vc;
vc.name=*k;
vc.name=E->key();
vc.order=v->order;
vc.type=v->variant.get_type();
if (vc.name.begins_with("input/") || vc.name.begins_with("import/") || vc.name.begins_with("export/") || vc.name.begins_with("/remap") || vc.name.begins_with("/locale") || vc.name.begins_with("/autoload"))
@ -1138,24 +1136,23 @@ Error Globals::save_custom(const String& p_path,const CustomMap& p_custom,const
ERR_FAIL_COND_V(p_path=="",ERR_INVALID_PARAMETER);
const String *k=NULL;
Set<_VCSort> vclist;
while ((k=props.next(k))) {
for(Map<StringName,VariantContainer>::Element *G=props.front();G;G=G->next()) {
const VariantContainer *v=props.getptr(*k);
const VariantContainer *v=&G->get();
if (v->hide_from_editor)
continue;
if (p_custom.has(*k))
if (p_custom.has(G->key()))
continue;
bool discard=false;
for(const Set<String>::Element *E=p_ignore_masks.front();E;E=E->next()) {
if ( (*k).match(E->get())) {
if ( String(G->key()).match(E->get())) {
discard=true;
break;
}
@ -1165,7 +1162,7 @@ Error Globals::save_custom(const String& p_path,const CustomMap& p_custom,const
continue;
_VCSort vc;
vc.name=*k;
vc.name=G->key();//*k;
vc.order=v->order;
vc.type=v->variant.get_type();
vc.flags=PROPERTY_USAGE_CHECKABLE|PROPERTY_USAGE_EDITOR|PROPERTY_USAGE_STORAGE;

View file

@ -65,9 +65,9 @@ protected:
};
int last_order;
HashMap<String,VariantContainer> props;
Map<StringName,VariantContainer> props;
String resource_path;
HashMap<String,PropertyInfo> custom_prop_info;
Map<StringName,PropertyInfo> custom_prop_info;
bool disable_platform_override;
bool using_datapack;

View file

@ -172,7 +172,6 @@ bool PackedSourcePCK::try_open_pack(const String& p_path) {
uint64_t size = f->get_64();
uint8_t md5[16];
f->get_buffer(md5,16);
PackedData::get_singleton()->add_path(p_path, path, ofs, size, md5,this);
};

View file

@ -97,8 +97,16 @@ Error HTTPClient::request( Method p_method, const String& p_url, const Vector<St
String request=String(_methods[p_method])+" "+p_url+" HTTP/1.1\r\n";
request+="Host: "+conn_host+":"+itos(conn_port)+"\r\n";
bool add_clen=p_body.length()>0;
for(int i=0;i<p_headers.size();i++) {
request+=p_headers[i]+"\r\n";
if (add_clen && p_headers[i].find("Content-Length:")==0) {
add_clen=false;
}
}
if (add_clen) {
request+="Content-Length: "+itos(p_body.utf8().length())+"\r\n";
//should it add utf8 encoding? not sure
}
request+="\r\n";
request+=p_body;

View file

@ -264,26 +264,94 @@ Error decode_variant(Variant& r_variant,const uint8_t *p_buffer, int p_len,int *
}
r_variant=img;
if (r_len)
if (r_len) {
if (datalen%4)
(*r_len)+=4-datalen%4;
(*r_len)+=4*5+datalen;
}
} break;
case Variant::NODE_PATH: {
ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA);
ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA);
uint32_t strlen = decode_uint32(buf);
buf+=4;
len-=4;
ERR_FAIL_COND_V((int)strlen>len,ERR_INVALID_DATA);
if (strlen&0x80000000) {
//new format
ERR_FAIL_COND_V(len<12,ERR_INVALID_DATA);
Vector<StringName> names;
Vector<StringName> subnames;
bool absolute;
StringName prop;
int i=0;
uint32_t namecount=strlen&=0x7FFFFFFF;
uint32_t subnamecount = decode_uint32(buf+4);
uint32_t flags = decode_uint32(buf+8);
len-=12;
buf+=12;
int total=namecount+subnamecount;
if (flags&2)
total++;
if (r_len)
(*r_len)+=12;
String str;
str.parse_utf8((const char*)buf,strlen);
for(int i=0;i<total;i++) {
r_variant=NodePath(str);
ERR_FAIL_COND_V((int)len<4,ERR_INVALID_DATA);
strlen = decode_uint32(buf);
if (r_len)
(*r_len)+=4+strlen;
int pad=0;
if (strlen%4)
pad+=4-strlen%4;
buf+=4;
len-=4;
ERR_FAIL_COND_V((int)strlen+pad>len,ERR_INVALID_DATA);
String str;
str.parse_utf8((const char*)buf,strlen);
if (i<namecount)
names.push_back(str);
else if (i<namecount+subnamecount)
subnames.push_back(str);
else
prop=str;
buf+=strlen+pad;
len-=strlen+pad;
if (r_len)
(*r_len)+=4+strlen+pad;
}
r_variant=NodePath(names,subnames,flags&1,prop);
} else {
//old format, just a string
buf+=4;
len-=4;
ERR_FAIL_COND_V((int)strlen>len,ERR_INVALID_DATA);
String str;
str.parse_utf8((const char*)buf,strlen);
r_variant=NodePath(str);
if (r_len)
(*r_len)+=4+strlen;
}
} break;
/*case Variant::RESOURCE: {
@ -713,7 +781,59 @@ Error encode_variant(const Variant& p_variant, uint8_t *r_buffer, int &r_len) {
r_len+=4;
} break;
case Variant::NODE_PATH:
case Variant::NODE_PATH: {
NodePath np=p_variant;
if (buf) {
encode_uint32(uint32_t(np.get_name_count())|0x80000000,buf); //for compatibility with the old format
encode_uint32(np.get_subname_count(),buf+4);
uint32_t flags=0;
if (np.is_absolute())
flags|=1;
if (np.get_property()!=StringName())
flags|=2;
encode_uint32(flags,buf+8);
buf+=12;
}
r_len+=12;
int total = np.get_name_count()+np.get_subname_count();
if (np.get_property()!=StringName())
total++;
for(int i=0;i<total;i++) {
String str;
if (i<np.get_name_count())
str=np.get_name(i);
else if (i<np.get_name_count()+np.get_subname_count())
str=np.get_subname(i-np.get_subname_count());
else
str=np.get_property();
CharString utf8 = str.utf8();
int pad = 0;
if (utf8.length()%4)
pad=4-utf8.length()%4;
if (buf) {
encode_uint32(utf8.length(),buf);
buf+=4;
copymem(buf,utf8.get_data(),utf8.length());
buf+=pad+utf8.length();
}
r_len+=4+utf8.length()+pad;
}
} break;
case Variant::STRING: {
@ -879,7 +999,11 @@ Error encode_variant(const Variant& p_variant, uint8_t *r_buffer, int &r_len) {
copymem(&buf[20],&r[0],ds);
}
r_len+=data.size()+5*4;
int pad=0;
if (data.size()%4)
pad=4-data.size()%4;
r_len+=data.size()+5*4+pad;
} break;
/*case Variant::RESOURCE: {

View file

@ -647,7 +647,7 @@ Error ResourceInteractiveLoaderBinary::poll(){
}
stage++;
return OK;
return error;
}
s-=external_resources.size();
@ -804,7 +804,12 @@ void ResourceInteractiveLoaderBinary::get_dependencies(FileAccess *p_f,List<Stri
for(int i=0;i<external_resources.size();i++) {
p_dependencies->push_back(external_resources[i].path);
String dep=external_resources[i].path;
if (dep.ends_with("*")) {
dep=ResourceLoader::guess_full_filename(dep,external_resources[i].type);
}
p_dependencies->push_back(dep);
}
}
@ -892,6 +897,19 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) {
}
//see if the exporter has different set of external resources for more efficient loading
String preload_depts = "deps/"+res_path.md5_text();
if (Globals::get_singleton()->has(preload_depts)) {
external_resources.clear();
//ignore external resources and use these
NodePath depts=Globals::get_singleton()->get(preload_depts);
external_resources.resize(depts.get_name_count());
for(int i=0;i<depts.get_name_count();i++) {
external_resources[i].path=depts.get_name(i);
}
print_line(res_path+" - EXTERNAL RESOURCES: "+itos(external_resources.size()));
}
print_bl("ext resources: "+itos(ext_resources_size));
uint32_t int_resources_size=f->get_32();
@ -931,6 +949,7 @@ String ResourceInteractiveLoaderBinary::recognize(FileAccess *p_f) {
} else if (header[0]!='R' || header[1]!='S' || header[2]!='R' || header[3]!='C') {
//not normal
error=ERR_FILE_UNRECOGNIZED;
return "";
}
@ -1412,8 +1431,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(const Variant& p_property,
f->store_32(OBJECT_EXTERNAL_RESOURCE);
save_unicode_string(res->get_save_type());
String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path();
if (no_extensions)
path=path.basename()+".*";
save_unicode_string(path);
} else {
@ -1439,7 +1456,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(const Variant& p_property,
f->store_32(VARIANT_DICTIONARY);
Dictionary d = p_property;
f->store_32(uint32_t(d.size())|(d.is_shared()?0x80000000:0));
f->store_32(uint32_t(d.size())|(d.is_shared()?0x80000000:0));
List<Variant> keys;
d.get_key_list(&keys);
@ -1734,7 +1751,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path,const RES& p_
skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES;
bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES;
big_endian=p_flags&ResourceSaver::FLAG_SAVE_BIG_ENDIAN;
no_extensions=p_flags&ResourceSaver::FLAG_NO_EXTENSION;
local_path=p_path.get_base_dir();
//bin_meta_idx = get_string_index("__bin_meta__"); //is often used, so create
@ -1816,8 +1833,6 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path,const RES& p_
save_unicode_string(E->get()->get_save_type());
String path = E->get()->get_path();
if (no_extensions)
path=path.basename()+".*";
save_unicode_string(path);
}
// save internal resource table
@ -1861,6 +1876,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path,const RES& p_
}
f->seek_end();
print_line("SAVING: "+p_path);
if (p_resource->get_import_metadata().is_valid()) {
uint64_t md_pos = f->get_pos();
Ref<ResourceImportMetadata> imd=p_resource->get_import_metadata();
@ -1869,6 +1885,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path,const RES& p_
for(int i=0;i<imd->get_source_count();i++) {
save_unicode_string(imd->get_source_path(i));
save_unicode_string(imd->get_source_md5(i));
print_line("SAVE PATH: "+imd->get_source_path(i));
print_line("SAVE MD5: "+imd->get_source_md5(i));
}
List<String> options;
imd->get_options(&options);

View file

@ -120,7 +120,7 @@ class ResourceFormatSaverBinaryInstance {
String local_path;
bool no_extensions;
bool relative_paths;
bool bundle_resources;
bool skip_editor;

View file

@ -1357,6 +1357,31 @@ Error ResourceInteractiveLoaderXML::poll() {
if (error!=OK)
return error;
if (ext_resources.size()) {
error=ERR_FILE_CORRUPT;
String path=ext_resources.front()->get();
RES res = ResourceLoader::load(path);
if (res.is_null()) {
if (ResourceLoader::get_abort_on_missing_resources()) {
ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": editor exported unexisting resource at: "+path);
ERR_FAIL_V(error);
} else {
ResourceLoader::notify_load_error("Resource Not Found: "+path);
}
} else {
resource_cache.push_back(res);
}
error=OK;
ext_resources.pop_front();
resource_current++;
return error;
}
bool exit;
Tag *tag = parse_tag(&exit);
@ -1528,7 +1553,7 @@ int ResourceInteractiveLoaderXML::get_stage() const {
}
int ResourceInteractiveLoaderXML::get_stage_count() const {
return resources_total;
return resources_total+ext_resources.size();
}
ResourceInteractiveLoaderXML::~ResourceInteractiveLoaderXML() {
@ -1573,6 +1598,12 @@ void ResourceInteractiveLoaderXML::get_dependencies(FileAccess *f,List<String> *
path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path);
}
if (path.ends_with("*")) {
ERR_FAIL_COND(!tag->args.has("type"));
String type = tag->args["type"];
path = ResourceLoader::guess_full_filename(path,type);
}
p_dependencies->push_back(path);
Error err = close_tag("ext_resource");
@ -1642,6 +1673,19 @@ void ResourceInteractiveLoaderXML::open(FileAccess *p_f) {
}
String preload_depts = "deps/"+local_path.md5_text();
if (Globals::get_singleton()->has(preload_depts)) {
ext_resources.clear();
//ignore external resources and use these
NodePath depts=Globals::get_singleton()->get(preload_depts);
for(int i=0;i<depts.get_name_count();i++) {
ext_resources.push_back(depts.get_name(i));
}
print_line(local_path+" - EXTERNAL RESOURCES: "+itos(ext_resources.size()));
}
}
String ResourceInteractiveLoaderXML::recognize(FileAccess *p_f) {
@ -1969,8 +2013,6 @@ void ResourceFormatSaverXMLInstance::write_property(const String& p_name,const V
if (res->get_path().length() && res->get_path().find("::")==-1) {
//external resource
String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path();
if (no_extension)
path=path.basename()+".*";
escape(path);
params+=" path=\""+path+"\"";
} else {
@ -2458,7 +2500,6 @@ Error ResourceFormatSaverXMLInstance::save(const String &p_path,const RES& p_res
relative_paths=p_flags&ResourceSaver::FLAG_RELATIVE_PATHS;
skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES;
bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES;
no_extension=p_flags&ResourceSaver::FLAG_NO_EXTENSION;
depth=0;
// save resources
@ -2475,8 +2516,6 @@ Error ResourceFormatSaverXMLInstance::save(const String &p_path,const RES& p_res
write_tabs();
String p = E->get()->get_path();
if (no_extension)
p=p.basename()+".*";
enter_tag("ext_resource","path=\""+p+"\" type=\""+E->get()->get_save_type()+"\""); //bundled
exit_tag("ext_resource"); //bundled

View file

@ -50,6 +50,10 @@ class ResourceInteractiveLoaderXML : public ResourceInteractiveLoader {
_FORCE_INLINE_ Error _parse_array_element(Vector<char> &buff,bool p_number_only,FileAccess *f,bool *end);
List<StringName> ext_resources;
int resources_total;
int resource_current;
String resource_type;
@ -113,7 +117,6 @@ class ResourceFormatSaverXMLInstance {
bool no_extension;
bool relative_paths;
bool bundle_resources;
bool skip_editor;

View file

@ -166,7 +166,7 @@ RES ResourceLoader::load(const String &p_path,const String& p_type_hint,bool p_n
String remapped_path = PathRemap::get_singleton()->get_remap(local_path);
if (OS::get_singleton()->is_stdout_verbose())
print_line("load resource: ");
print_line("load resource: "+remapped_path);
String extension=remapped_path.extension();
bool found=false;
@ -233,6 +233,10 @@ Ref<ResourceImportMetadata> ResourceLoader::load_import_metadata(const String &p
String ResourceLoader::find_complete_path(const String& p_path,const String& p_type) {
//this is an old vestige when the engine saved files without extension.
//remains here for compatibility with old projects and only because it
//can be sometimes nice to open files using .* from a script and have it guess
//the right extension.
String local_path = p_path;
if (local_path.ends_with("*")) {
@ -353,6 +357,13 @@ void ResourceLoader::get_dependencies(const String& p_path,List<String> *p_depen
}
}
String ResourceLoader::guess_full_filename(const String &p_path,const String& p_type) {
String local_path = Globals::get_singleton()->localize_path(p_path);
return find_complete_path(local_path,p_type);
}
String ResourceLoader::get_resource_type(const String &p_path) {

View file

@ -102,6 +102,7 @@ public:
static String get_resource_type(const String &p_path);
static void get_dependencies(const String& p_path,List<String> *p_dependencies);
static String guess_full_filename(const String &p_path,const String& p_type);
static void set_timestamp_on_load(bool p_timestamp) { timestamp_on_load=p_timestamp; }

View file

@ -74,9 +74,6 @@ public:
FLAG_OMIT_EDITOR_PROPERTIES=8,
FLAG_SAVE_BIG_ENDIAN=16,
FLAG_COMPRESS=32,
FLAG_NO_EXTENSION=64,
};

View file

@ -220,9 +220,16 @@ int Math::decimals(double p_step) {
double Math::ease(double p_x, double p_c) {
if (p_x<0)
p_x=0;
else if (p_x>1.0)
p_x=1.0;
if (p_c>0) {
return Math::pow(p_x,p_c);
if (p_c<1.0) {
return 1.0-Math::pow(1.0-p_x,1.0/p_c);
} else {
return Math::pow(p_x,p_c);
}
} else if (p_c<0) {
//inout ease

View file

@ -378,11 +378,12 @@ void MessageQueue::flush() {
}
}
message->~Message();
read_pos+=sizeof(Message);
if (message->type!=TYPE_NOTIFICATION)
read_pos+=sizeof(Variant)*message->args;
message->~Message();
_THREAD_SAFE_UNLOCK_
}

View file

@ -33,6 +33,30 @@
#include "message_queue.h"
#include "core_string_names.h"
#include "translation.h"
#ifdef DEBUG_ENABLED
struct _ObjectDebugLock {
Object *obj;
_ObjectDebugLock(Object *p_obj) {
obj=p_obj;
obj->_lock_index.ref();
}
~_ObjectDebugLock() {
obj->_lock_index.unref();
}
};
#define OBJ_DEBUG_LOCK _ObjectDebugLock _debug_lock(this);
#else
#define OBJ_DEBUG_LOCK
#endif
Array convert_property_list(const List<PropertyInfo> * p_list) {
Array va;
@ -562,13 +586,22 @@ void Object::call_multilevel(const StringName& p_method,const Variant** p_args,i
ERR_FAIL();
return;
}
if (_lock_index.get()>1) {
ERR_EXPLAIN("Object is locked and can't be freed.");
ERR_FAIL();
return;
}
#endif
//must be here, must be before everything,
memdelete(this);
return;
}
//Variant ret;
OBJ_DEBUG_LOCK
Variant::CallError error;
@ -594,6 +627,7 @@ void Object::call_multilevel_reversed(const StringName& p_method,const Variant**
MethodBind *method=ObjectTypeDB::get_method(get_type_name(),p_method);
Variant::CallError error;
OBJ_DEBUG_LOCK
if (method) {
@ -813,6 +847,15 @@ Variant Object::call(const StringName& p_method,const Variant** p_args,int p_arg
ERR_EXPLAIN("Can't 'free' a reference.");
ERR_FAIL_V(Variant());
}
if (_lock_index.get()>1) {
r_error.argument=0;
r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
ERR_EXPLAIN("Object is locked and can't be freed.");
ERR_FAIL_V(Variant());
}
#endif
//must be here, must be before everything,
memdelete(this);
@ -821,7 +864,7 @@ Variant Object::call(const StringName& p_method,const Variant** p_args,int p_arg
}
Variant ret;
OBJ_DEBUG_LOCK
if (script_instance) {
ret = script_instance->call(p_method,p_args,p_argcount,r_error);
//force jumptable
@ -902,7 +945,7 @@ void Object::set_script(const RefPtr& p_script) {
Ref<Script> s(script);
if (!s.is_null() && s->can_instance() ) {
OBJ_DEBUG_LOCK
script_instance = s->instance_create(this);
}
@ -1066,6 +1109,8 @@ void Object::emit_signal(const StringName& p_name,VARIANT_ARG_DECLARE) {
int ssize = slot_map.size();
OBJ_DEBUG_LOCK
for(int i=0;i<ssize;i++) {
const Connection &c = slot_map.getv(i).conn;
@ -1536,6 +1581,11 @@ Object::Object() {
_edited=false;
#endif
#ifdef DEBUG_ENABLED
_lock_index.init(1);
#endif
}

View file

@ -331,7 +331,9 @@ public:
Connection(const Variant& p_variant);
};
private:
#ifdef DEBUG_ENABLED
friend class _ObjectDebugLock;
#endif
friend bool predelete_handler(Object*);
friend void postinitialize_handler(Object*);
@ -365,7 +367,9 @@ friend void postinitialize_handler(Object*);
HashMap< StringName, Signal, StringNameHasher> signal_map;
List<Connection> connections;
#ifdef DEBUG_ENABLED
SafeRefCount _lock_index;
#endif
bool _block_signals;
int _predelete_ok;
Set<Object*> change_receptors;

View file

@ -428,8 +428,30 @@ void FileAccess::store_string(const String& p_string) {
CharString cs=p_string.utf8();
store_buffer((uint8_t*)&cs[0],cs.length());
}
void FileAccess::store_pascal_string(const String& p_string) {
CharString cs = p_string.utf8();
store_32(cs.length());
store_buffer((uint8_t*)&cs[0], cs.length());
};
String FileAccess::get_pascal_string() {
uint32_t sl = get_32();
CharString cs;
cs.resize(sl+1);
get_buffer((uint8_t*)cs.ptr(),sl);
cs[sl]=0;
String ret;
ret.parse_utf8(cs.ptr());
return ret;
};
void FileAccess::store_line(const String& p_line) {
store_string(p_line);

View file

@ -125,6 +125,9 @@ public:
virtual void store_string(const String& p_string);
virtual void store_line(const String& p_string);
virtual void store_pascal_string(const String& p_string);
virtual String get_pascal_string();
virtual void store_buffer(const uint8_t *p_src,int p_length); ///< store an array of bytes
virtual bool file_exists(const String& p_name)=0; ///< return true if a file exists

View file

@ -56,6 +56,7 @@ void Input::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_accelerometer"),&Input::get_accelerometer);
ObjectTypeDB::bind_method(_MD("get_mouse_pos"),&Input::get_mouse_pos);
ObjectTypeDB::bind_method(_MD("get_mouse_speed"),&Input::get_mouse_speed);
ObjectTypeDB::bind_method(_MD("get_mouse_button_mask"),&Input::get_mouse_button_mask);
ObjectTypeDB::bind_method(_MD("set_mouse_mode","mode"),&Input::set_mouse_mode);
ObjectTypeDB::bind_method(_MD("get_mouse_mode"),&Input::get_mouse_mode);
@ -280,6 +281,12 @@ Point2 InputDefault::get_mouse_speed() const {
return mouse_speed_track.speed;
}
int InputDefault::get_mouse_button_mask() const {
return OS::get_singleton()->get_mouse_button_state();
}
void InputDefault::iteration(float p_step) {

View file

@ -64,6 +64,7 @@ public:
virtual Point2 get_mouse_pos() const=0;
virtual Point2 get_mouse_speed() const=0;
virtual int get_mouse_button_mask() const=0;
virtual Vector3 get_accelerometer()=0;
@ -120,6 +121,7 @@ public:
virtual Point2 get_mouse_pos() const;
virtual Point2 get_mouse_speed() const;
virtual int get_mouse_button_mask() const;
void parse_input_event(const InputEvent& p_event);
void set_accelerometer(const Vector3& p_accel);

View file

@ -50,7 +50,7 @@ public:
virtual void lock()=0; ///< Lock the mutex, block if locked by someone else
virtual void unlock()=0; ///< Unlock the mutex, let other threads continue
virtual Error try_lock()=0; ///< Attempt to lock the mutex, true on success, false means it can't lock.
virtual Error try_lock()=0; ///< Attempt to lock the mutex, OK on success, ERROR means it can't lock.
static Mutex * create(bool p_recursive=true); ///< Create a mutex

View file

@ -92,6 +92,14 @@ int OS::get_iterations_per_second() const {
return ips;
}
void OS::set_target_fps(int p_fps) {
_target_fps=p_fps>0? p_fps : 0;
}
float OS::get_target_fps() const {
return _target_fps;
}
void OS::set_low_processor_usage_mode(bool p_enabled) {
low_processor_usage_mode=p_enabled;
@ -116,6 +124,11 @@ String OS::get_executable_path() const {
return _execpath;
}
int OS::get_process_ID() const {
return -1;
};
uint64_t OS::get_frames_drawn() {
return frames_drawn;
@ -430,7 +443,7 @@ Error OS::native_video_play(String p_path) {
return FAILED;
};
bool OS::native_video_is_playing() {
bool OS::native_video_is_playing() const {
return false;
};
@ -447,6 +460,15 @@ void OS::set_mouse_mode(MouseMode p_mode) {
}
bool OS::can_use_threads() const {
#ifdef NO_THREADS
return false;
#else
return true;
#endif
}
OS::MouseMode OS::get_mouse_mode() const{
return MOUSE_MODE_VISIBLE;
@ -465,6 +487,7 @@ OS::OS() {
_exit_code=0;
_orientation=SCREEN_LANDSCAPE;
_fps=1;
_target_fps=0;
_render_thread_mode=RENDER_THREAD_SAFE;
Math::seed(1234567);
}

View file

@ -54,6 +54,7 @@ class OS {
int _exit_code;
int _orientation;
float _fps;
int _target_fps;
char *last_error;
@ -149,14 +150,19 @@ public:
virtual void set_iterations_per_second(int p_ips);
virtual int get_iterations_per_second() const;
virtual void set_target_fps(int p_fps);
virtual float get_target_fps() const;
virtual float get_frames_per_second() const { return _fps; };
virtual void set_low_processor_usage_mode(bool p_enabled);
virtual bool is_in_low_processor_usage_mode() const;
virtual String get_executable_path() const;
virtual Error execute(const String& p_path, const List<String>& p_arguments,bool p_blocking,ProcessID *r_child_id=NULL,String* r_pipe=NULL,int *r_exitcode=NULL)=0;
virtual Error kill(const ProcessID& p_pid)=0;
virtual int get_process_ID() const;
virtual Error shell_open(String p_uri);
virtual Error set_cwd(const String& p_cwd);
@ -316,10 +322,12 @@ public:
virtual String get_unique_ID() const;
virtual Error native_video_play(String p_path);
virtual bool native_video_is_playing();
virtual bool native_video_is_playing() const;
virtual void native_video_pause();
virtual void native_video_stop();
virtual bool can_use_threads() const;
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, Object* p_obj, String p_callback);
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, Object* p_obj, String p_callback);

View file

@ -49,6 +49,7 @@
#include "core/io/xml_parser.h"
#include "io/http_client.h"
#include "packed_data_container.h"
#include "func_ref.h"
#ifdef XML_ENABLED
static ResourceFormatSaverXML *resource_saver_xml=NULL;
@ -135,6 +136,7 @@ void register_core_types() {
ObjectTypeDB::register_type<Reference>();
ObjectTypeDB::register_type<ResourceImportMetadata>();
ObjectTypeDB::register_type<Resource>();
ObjectTypeDB::register_type<FuncRef>();
ObjectTypeDB::register_virtual_type<StreamPeer>();
ObjectTypeDB::register_create_type<StreamPeerTCP>();
ObjectTypeDB::register_create_type<TCP_Server>();

View file

@ -31,6 +31,7 @@
#include "os/memory.h"
#include "print_string.h"
#include "math_funcs.h"
#include "io/md5.h"
#include "ucaps.h"
#include "color.h"
#define MAX_DIGITS 6
@ -2264,6 +2265,15 @@ uint64_t String::hash64() const {
}
String String::md5_text() const {
CharString cs=utf8();
MD5_CTX ctx;
MD5Init(&ctx);
MD5Update(&ctx,(unsigned char*)cs.ptr(),cs.length());
MD5Final(&ctx);
return String::md5(ctx.digest);
}
String String::insert(int p_at_pos,String p_string) const {

View file

@ -181,7 +181,8 @@ public:
static uint32_t hash(const char* p_cstr,int p_len); /* hash the string */
static uint32_t hash(const char* p_cstr); /* hash the string */
uint32_t hash() const; /* hash the string */
uint64_t hash64() const; /* hash the string */
uint64_t hash64() const; /* hash the string */
String md5_text() const;
inline bool empty() const { return length() == 0; }

View file

@ -56,7 +56,7 @@ String Variant::get_type_name(Variant::Type p_type) {
} break;
case REAL: {
return "real";
return "float";
} break;
case STRING: {

View file

@ -600,6 +600,7 @@ static void _call_##m_type##_##m_method(Variant& r_ret,Variant& p_self,const Var
VCALL_PTR0R( Matrix3, determinant );
VCALL_PTR2R( Matrix3, rotated );
VCALL_PTR1R( Matrix3, scaled );
VCALL_PTR0R( Matrix3, get_scale );
VCALL_PTR0R( Matrix3, get_euler );
VCALL_PTR1R( Matrix3, tdotx );
VCALL_PTR1R( Matrix3, tdoty );
@ -1390,6 +1391,7 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl
ADDFUNC0(MATRIX3,REAL,Matrix3,determinant,varray());
ADDFUNC2(MATRIX3,MATRIX3,Matrix3,rotated,VECTOR3,"axis",REAL,"phi",varray());
ADDFUNC1(MATRIX3,MATRIX3,Matrix3,scaled,VECTOR3,"scale",varray());
ADDFUNC0(MATRIX3,VECTOR3,Matrix3,get_scale,varray());
ADDFUNC0(MATRIX3,VECTOR3,Matrix3,get_euler,varray());
ADDFUNC1(MATRIX3,REAL,Matrix3,tdotx,VECTOR3,"with",varray());
ADDFUNC1(MATRIX3,REAL,Matrix3,tdoty,VECTOR3,"with",varray());

View file

@ -103,8 +103,8 @@ case m_name: {\
case BOOL: _RETURN( p_a._data.m_type m_op p_b._data._bool);\
case INT: _RETURN( p_a._data.m_type m_op p_b._data._int);\
case REAL: _RETURN( p_a._data.m_type m_op p_b._data._real);\
case VECTOR2: _RETURN( p_a._data.m_type m_op *reinterpret_cast<const Vector2*>(p_a._data._mem));\
case VECTOR3: _RETURN( p_a._data.m_type m_op *reinterpret_cast<const Vector3*>(p_a._data._mem));\
case VECTOR2: _RETURN( p_a._data.m_type m_op *reinterpret_cast<const Vector2*>(p_b._data._mem));\
case VECTOR3: _RETURN( p_a._data.m_type m_op *reinterpret_cast<const Vector3*>(p_b._data._mem));\
default: {}\
}\
r_valid=false;\

View file

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<resource_file type="PackedScene" subresource_count="24" version="1.0" version_name="Godot Engine v1.0.3917-beta1">
<ext_resource path="res://robot_demo.*" type="Texture"></ext_resource>
<ext_resource path="res://osb_jump.*" type="Texture"></ext_resource>
<ext_resource path="res://player.*" type="Script"></ext_resource>
<ext_resource path="res://osb_right.*" type="Texture"></ext_resource>
<ext_resource path="res://sound_coin.*" type="Sample"></ext_resource>
<ext_resource path="res://sound_jump.*" type="Sample"></ext_resource>
<ext_resource path="res://sound_shoot.*" type="Sample"></ext_resource>
<ext_resource path="res://bullet.*" type="Texture"></ext_resource>
<ext_resource path="res://osb_fire.*" type="Texture"></ext_resource>
<ext_resource path="res://osb_left.*" type="Texture"></ext_resource>
<ext_resource path="res://osb_jump.png" type="Texture"></ext_resource>
<ext_resource path="res://bullet.png" type="Texture"></ext_resource>
<ext_resource path="res://osb_right.png" type="Texture"></ext_resource>
<ext_resource path="res://sound_coin.wav" type="Sample"></ext_resource>
<ext_resource path="res://sound_shoot.wav" type="Sample"></ext_resource>
<ext_resource path="res://osb_fire.png" type="Texture"></ext_resource>
<ext_resource path="res://robot_demo.png" type="Texture"></ext_resource>
<ext_resource path="res://osb_left.png" type="Texture"></ext_resource>
<ext_resource path="res://player.gd" type="Script"></ext_resource>
<ext_resource path="res://sound_jump.wav" type="Sample"></ext_resource>
<resource type="RayShape2D" path="local://1">
<real name="custom_solver_bias"> 0.5 </real>
<real name="length"> 20 </real>
@ -50,30 +50,6 @@
</resource>
<resource type="Animation" path="local://4">
<string name="resource/name"> "jumping" </string>
<real name="length"> 0.5 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="3"> 1, 1, 1 </real_array>
<string> "values" </string>
<array len="3" shared="false">
<int> 23 </int>
<int> 24 </int>
<int> 23 </int>
</array>
<string> "times" </string>
<real_array len="3"> 0, 0.25, 0.5 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://5">
<string name="resource/name"> "run" </string>
<real name="length"> 1.25 </real>
<bool name="loop"> True </bool>
@ -100,144 +76,7 @@
</dictionary>
</resource>
<resource type="Animation" path="local://6">
<string name="resource/name"> "run_weapon" </string>
<real name="length"> 1.25 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="6"> 1, 1, 1, 1, 1, 1 </real_array>
<string> "values" </string>
<array len="6" shared="false">
<int> 5 </int>
<int> 6 </int>
<int> 7 </int>
<int> 8 </int>
<int> 9 </int>
<int> 5 </int>
</array>
<string> "times" </string>
<real_array len="6"> 0, 0.25, 0.5, 0.75, 1, 1.25 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://7">
<string name="resource/name"> "falling" </string>
<real name="length"> 0.01 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 21 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://8">
<string name="resource/name"> "falling_weapon" </string>
<real name="length"> 0.5 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 26 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://9">
<string name="resource/name"> "idle_weapon" </string>
<real name="length"> 0.5 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 25 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://10">
<string name="resource/name"> "jumping_weapon" </string>
<real name="length"> 0.5 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 26 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://11">
<string name="resource/name"> "crouch" </string>
<real name="length"> 0.01 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 22 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://12">
<resource type="Animation" path="local://5">
<string name="resource/name"> "run_gun_fire" </string>
<real name="length"> 1.25 </real>
<bool name="loop"> True </bool>
@ -263,6 +102,167 @@
<real_array len="6"> 0, 0.25, 0.5, 0.75, 1, 1.25 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://6">
<string name="resource/name"> "jumping_weapon" </string>
<real name="length"> 0.5 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 26 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://7">
<string name="resource/name"> "crouch" </string>
<real name="length"> 0.01 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 22 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://8">
<string name="resource/name"> "jumping" </string>
<real name="length"> 0.5 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="3"> 1, 1, 1 </real_array>
<string> "values" </string>
<array len="3" shared="false">
<int> 23 </int>
<int> 24 </int>
<int> 23 </int>
</array>
<string> "times" </string>
<real_array len="3"> 0, 0.25, 0.5 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://9">
<string name="resource/name"> "run_weapon" </string>
<real name="length"> 1.25 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="6"> 1, 1, 1, 1, 1, 1 </real_array>
<string> "values" </string>
<array len="6" shared="false">
<int> 5 </int>
<int> 6 </int>
<int> 7 </int>
<int> 8 </int>
<int> 9 </int>
<int> 5 </int>
</array>
<string> "times" </string>
<real_array len="6"> 0, 0.25, 0.5, 0.75, 1, 1.25 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://10">
<string name="resource/name"> "idle_weapon" </string>
<real name="length"> 0.5 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 25 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://11">
<string name="resource/name"> "falling_weapon" </string>
<real name="length"> 0.5 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 26 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="Animation" path="local://12">
<string name="resource/name"> "falling" </string>
<real name="length"> 0.01 </real>
<bool name="loop"> True </bool>
<real name="step"> 0.25 </real>
<string name="tracks/0/type"> "value" </string>
<node_path name="tracks/0/path"> "sprite:frame" </node_path>
<int name="tracks/0/interp"> 1 </int>
<dictionary name="tracks/0/keys" shared="false">
<string> "cont" </string>
<bool> False </bool>
<string> "transitions" </string>
<real_array len="1"> 1 </real_array>
<string> "values" </string>
<array len="1" shared="false">
<int> 21 </int>
</array>
<string> "times" </string>
<real_array len="1"> 0 </real_array>
</dictionary>
</resource>
<resource type="SampleLibrary" path="local://13">
<dictionary name="samples/jump" shared="false">
@ -271,7 +271,7 @@
<string> "pitch" </string>
<real> 1 </real>
<string> "sample" </string>
<resource resource_type="Sample" path="res://sound_jump.*"> </resource>
<resource resource_type="Sample" path="res://sound_jump.wav"> </resource>
</dictionary>
<dictionary name="samples/shoot" shared="false">
<string> "db" </string>
@ -279,7 +279,7 @@
<string> "pitch" </string>
<real> 1 </real>
<string> "sample" </string>
<resource resource_type="Sample" path="res://sound_shoot.*"> </resource>
<resource resource_type="Sample" path="res://sound_shoot.wav"> </resource>
</dictionary>
<dictionary name="samples/coin" shared="false">
<string> "db" </string>
@ -287,7 +287,7 @@
<string> "pitch" </string>
<real> 1 </real>
<string> "sample" </string>
<resource resource_type="Sample" path="res://sound_coin.*"> </resource>
<resource resource_type="Sample" path="res://sound_coin.wav"> </resource>
</dictionary>
</resource>
@ -300,7 +300,7 @@
<string> "visibility/visible" </string>
<string> "visibility/opacity" </string>
<string> "visibility/self_opacity" </string>
<string> "visibility/on_top" </string>
<string> "visibility/behind_parent" </string>
<string> "transform/pos" </string>
<string> "transform/rot" </string>
<string> "transform/scale" </string>
@ -396,15 +396,15 @@
<string> "playback/default_blend_time" </string>
<string> "root/root" </string>
<string> "anims/idle" </string>
<string> "anims/jumping" </string>
<string> "anims/run" </string>
<string> "anims/run_weapon" </string>
<string> "anims/falling" </string>
<string> "anims/falling_weapon" </string>
<string> "anims/idle_weapon" </string>
<string> "anims/standing_weapon_ready" </string>
<string> "anims/jumping_weapon" </string>
<string> "anims/crouch" </string>
<string> "anims/standing_weapon_ready" </string>
<string> "anims/jumping" </string>
<string> "anims/run_weapon" </string>
<string> "anims/idle_weapon" </string>
<string> "anims/falling_weapon" </string>
<string> "anims/falling" </string>
<string> "playback/active" </string>
<string> "playback/speed" </string>
<string> "blend_times" </string>
@ -476,18 +476,19 @@
<array len="71" shared="false">
<bool> True </bool>
<real> 1 </real>
<bool> False </bool>
<vector2> 0, 0 </vector2>
<real> 0 </real>
<vector2> 1, 1 </vector2>
<int> 2 </int>
<resource resource_type="Shape2D" path="local://1"> </resource>
<matrix32> 1, -0, 0, 1.76469, 0.291992, -12.1587 </matrix32>
<bool> False </bool>
<resource resource_type="Shape2D" path="local://2"> </resource>
<matrix32> 1, -0, 0, 1, 0, 0 </matrix32>
<real> 3 </real>
<int> 0 </int>
<int> 3 </int>
<resource resource_type="Script" path="res://player.*"> </resource>
<resource resource_type="Script" path="res://player.gd"> </resource>
<dictionary shared="false">
<string> "__editor_plugin_states__" </string>
<dictionary shared="false">
@ -507,7 +508,7 @@
<string> "zoom" </string>
<real> 2.272073 </real>
<string> "ofs" </string>
<vector2> -125.17, -137.776 </vector2>
<vector2> -181.946, -86.2812 </vector2>
</dictionary>
<string> "3D" </string>
<dictionary shared="false">
@ -594,20 +595,20 @@
<int> 0 </int>
</dictionary>
<string> "__editor_plugin_screen__" </string>
<string> "2D" </string>
<string> "3D" </string>
</dictionary>
<resource resource_type="Texture" path="res://robot_demo.*"> </resource>
<resource resource_type="Texture" path="res://robot_demo.png"> </resource>
<int> 16 </int>
<int> 1 </int>
<color> 1, 1, 1, 1 </color>
<rect2> 0, 0, 0, 0 </rect2>
<real> 0.363636 </real>
<int> 1 </int>
<vector2> 20.7312, 3.21187 </vector2>
<real> 83.450417 </real>
<int> 4 </int>
<real> 0.3 </real>
<real> 0.1 </real>
<resource resource_type="Texture" path="res://bullet.*"> </resource>
<resource resource_type="Texture" path="res://bullet.png"> </resource>
<real> 180 </real>
<real> 20 </real>
<real> 9.8 </real>
@ -630,7 +631,6 @@
<array len="0" shared="false">
</array>
<string> "" </string>
<int> 0 </int>
<int> 10000000 </int>
<real> 0.2 </real>
<vector2> 31.2428, 4.08784 </vector2>
@ -641,20 +641,20 @@
<vector2_array len="3"> -0.138023, 16.5036, -19.902, -24.8691, 19.3625, -24.6056 </vector2_array>
<vector2> 27.7593, 360.87 </vector2>
<vector2> 1.49157, 1.46265 </vector2>
<resource resource_type="Texture" path="res://osb_left.*"> </resource>
<resource resource_type="Texture" path="res://osb_left.png"> </resource>
<resource name=""></resource> <string> "move_left" </string>
<vector2> 121.542, 361.415 </vector2>
<resource resource_type="Texture" path="res://osb_right.*"> </resource>
<resource resource_type="Texture" path="res://osb_right.png"> </resource>
<string> "move_right" </string>
<vector2> 666.224, 359.02 </vector2>
<resource resource_type="Texture" path="res://osb_jump.*"> </resource>
<resource resource_type="Texture" path="res://osb_jump.png"> </resource>
<string> "jump" </string>
<vector2> 668.073, 262.788 </vector2>
<resource resource_type="Texture" path="res://osb_fire.*"> </resource>
<resource resource_type="Texture" path="res://osb_fire.png"> </resource>
<string> "shoot" </string>
</array>
<string> "nodes" </string>
<int_array len="572"> -1, -1, 1, 0, -1, 28, 2, 0, 3, 1, 4, 1, 5, 0, 6, 2, 7, 3, 8, 4, 9, 5, 10, 6, 11, 7, 12, 8, 13, 9, 14, 10, 15, 8, 16, 5, 17, 11, 18, 3, 19, 3, 20, 0, 21, 8, 22, 12, 23, 8, 24, 0, 25, 0, 26, 2, 27, 3, 28, 13, 29, 14, 0, 0, 0, 31, 30, -1, 18, 2, 0, 3, 1, 4, 1, 5, 0, 6, 2, 7, 3, 8, 4, 32, 15, 33, 0, 34, 2, 35, 8, 36, 8, 37, 5, 38, 16, 39, 17, 40, 18, 41, 8, 42, 19, 0, 1, 0, 44, 43, -1, 57, 2, 0, 3, 1, 4, 20, 5, 0, 45, 17, 6, 21, 7, 22, 8, 4, 46, 23, 47, 24, 48, 1, 49, 3, 50, 24, 51, 8, 52, 2, 53, 2, 54, 8, 55, 25, 56, 8, 57, 8, 58, 26, 59, 3, 60, 27, 61, 28, 62, 1, 63, 3, 64, 3, 65, 29, 66, 3, 67, 3, 68, 3, 69, 30, 70, 30, 71, 3, 72, 3, 73, 3, 74, 3, 75, 30, 76, 3, 77, 3, 78, 3, 79, 3, 80, 3, 81, 3, 82, 3, 83, 3, 84, 3, 85, 5, 86, 3, 87, 18, 88, 1, 89, 31, 90, 1, 91, 32, 92, 1, 93, 33, 94, 34, 0, 0, 0, 96, 95, -1, 17, 97, 17, 98, 3, 99, 35, 100, 36, 101, 37, 102, 38, 103, 39, 104, 40, 105, 41, 106, 42, 107, 43, 108, 44, 109, 45, 110, 0, 111, 30, 112, 46, 113, 47, 0, 0, 0, 115, 114, -1, 22, 2, 0, 3, 1, 4, 1, 5, 0, 6, 2, 7, 3, 8, 4, 33, 0, 116, 8, 117, 0, 118, 3, 119, 4, 120, 48, 121, 48, 122, 49, 123, 49, 124, 0, 125, 0, 126, 50, 127, 50, 128, 50, 129, 50, 0, 0, 0, 131, 130, -1, 7, 2, 0, 3, 1, 4, 1, 5, 0, 6, 51, 7, 3, 8, 4, 0, 0, 0, 132, 132, -1, 9, 2, 0, 3, 1, 4, 1, 5, 0, 6, 52, 7, 3, 8, 53, 133, 6, 134, 8, 0, 0, 0, 136, 135, -1, 14, 137, 12, 138, 54, 139, 3, 140, 1, 141, 3, 142, 3, 143, 3, 144, 55, 145, 55, 146, 55, 147, 55, 148, 5, 149, 3, 150, 3, 0, 0, 0, 151, 151, -1, 9, 2, 0, 3, 1, 4, 1, 5, 0, 6, 2, 7, 3, 8, 4, 152, 48, 153, 56, 0, 0, 0, 155, 154, -1, 4, 156, 48, 34, 2, 157, 3, 158, 4, 0, 9, 0, 160, 159, -1, 13, 2, 0, 3, 1, 4, 1, 5, 0, 6, 57, 7, 3, 8, 58, 161, 59, 162, 60, 163, 60, 164, 0, 165, 61, 166, 17, 0, 9, 0, 160, 167, -1, 13, 2, 0, 3, 1, 4, 1, 5, 0, 6, 62, 7, 3, 8, 58, 161, 63, 162, 60, 163, 60, 164, 0, 165, 64, 166, 17, 0, 9, 0, 160, 168, -1, 13, 2, 0, 3, 1, 4, 1, 5, 0, 6, 65, 7, 3, 8, 58, 161, 66, 162, 60, 163, 60, 164, 8, 165, 67, 166, 17, 0, 9, 0, 160, 169, -1, 13, 2, 0, 3, 1, 4, 1, 5, 0, 6, 68, 7, 3, 8, 58, 161, 69, 162, 60, 163, 60, 164, 8, 165, 70, 166, 17, 0 </int_array>
<int_array len="572"> -1, -1, 1, 0, -1, 28, 2, 0, 3, 1, 4, 1, 5, 2, 6, 3, 7, 4, 8, 5, 9, 6, 10, 7, 11, 8, 12, 2, 13, 9, 14, 10, 15, 2, 16, 6, 17, 11, 18, 4, 19, 4, 20, 0, 21, 12, 22, 13, 23, 2, 24, 0, 25, 0, 26, 3, 27, 4, 28, 14, 29, 15, 0, 0, 0, 31, 30, -1, 18, 2, 0, 3, 1, 4, 1, 5, 2, 6, 3, 7, 4, 8, 5, 32, 16, 33, 0, 34, 3, 35, 2, 36, 2, 37, 6, 38, 17, 39, 12, 40, 18, 41, 2, 42, 19, 0, 1, 0, 44, 43, -1, 57, 2, 0, 3, 1, 4, 20, 5, 2, 45, 21, 6, 22, 7, 23, 8, 5, 46, 24, 47, 25, 48, 1, 49, 4, 50, 25, 51, 2, 52, 3, 53, 3, 54, 2, 55, 26, 56, 2, 57, 2, 58, 27, 59, 4, 60, 28, 61, 29, 62, 1, 63, 4, 64, 4, 65, 30, 66, 4, 67, 4, 68, 4, 69, 31, 70, 31, 71, 4, 72, 4, 73, 4, 74, 4, 75, 31, 76, 4, 77, 4, 78, 4, 79, 4, 80, 4, 81, 4, 82, 4, 83, 4, 84, 4, 85, 6, 86, 4, 87, 18, 88, 1, 89, 32, 90, 1, 91, 33, 92, 1, 93, 34, 94, 35, 0, 0, 0, 96, 95, -1, 17, 97, 21, 98, 4, 99, 36, 100, 37, 101, 38, 102, 39, 103, 40, 104, 41, 105, 42, 106, 43, 107, 44, 108, 45, 109, 46, 110, 0, 111, 31, 112, 47, 113, 48, 0, 0, 0, 115, 114, -1, 22, 2, 0, 3, 1, 4, 1, 5, 2, 6, 3, 7, 4, 8, 5, 33, 0, 116, 2, 117, 0, 118, 4, 119, 5, 120, 12, 121, 12, 122, 49, 123, 49, 124, 0, 125, 0, 126, 50, 127, 50, 128, 50, 129, 50, 0, 0, 0, 131, 130, -1, 7, 2, 0, 3, 1, 4, 1, 5, 2, 6, 51, 7, 4, 8, 5, 0, 0, 0, 132, 132, -1, 9, 2, 0, 3, 1, 4, 1, 5, 2, 6, 52, 7, 4, 8, 53, 133, 7, 134, 2, 0, 0, 0, 136, 135, -1, 14, 137, 13, 138, 54, 139, 4, 140, 1, 141, 4, 142, 4, 143, 4, 144, 55, 145, 55, 146, 55, 147, 55, 148, 6, 149, 4, 150, 4, 0, 0, 0, 151, 151, -1, 9, 2, 0, 3, 1, 4, 1, 5, 2, 6, 3, 7, 4, 8, 5, 152, 12, 153, 56, 0, 0, 0, 155, 154, -1, 4, 156, 12, 34, 3, 157, 4, 158, 5, 0, 9, 0, 160, 159, -1, 13, 2, 0, 3, 1, 4, 1, 5, 2, 6, 57, 7, 4, 8, 58, 161, 59, 162, 60, 163, 60, 164, 0, 165, 61, 166, 21, 0, 9, 0, 160, 167, -1, 13, 2, 0, 3, 1, 4, 1, 5, 2, 6, 62, 7, 4, 8, 58, 161, 63, 162, 60, 163, 60, 164, 0, 165, 64, 166, 21, 0, 9, 0, 160, 168, -1, 13, 2, 0, 3, 1, 4, 1, 5, 2, 6, 65, 7, 4, 8, 58, 161, 66, 162, 60, 163, 60, 164, 2, 165, 67, 166, 21, 0, 9, 0, 160, 169, -1, 13, 2, 0, 3, 1, 4, 1, 5, 2, 6, 68, 7, 4, 8, 58, 161, 69, 162, 60, 163, 60, 164, 2, 165, 70, 166, 21, 0 </int_array>
<string> "conns" </string>
<int_array len="0"> </int_array>
</dictionary>

File diff suppressed because one or more lines are too long

View file

@ -26402,7 +26402,7 @@
<description>
</description>
</signal>
<signal name="release">
<signal name="released">
<description>
</description>
</signal>

View file

@ -140,7 +140,7 @@ mpc_bool_t AudioStreamMPC::_mpc_canseek(mpc_reader *p_reader) {
bool AudioStreamMPC::_can_mix() const {
return active && !paused;
return /*active &&*/ !paused;
}

View file

@ -0,0 +1,111 @@
#include "stream_peer_ssl.h"
int StreamPeerSSL::bio_create( BIO *b ) {
b->init = 1;
b->num = 0;
b->ptr = NULL;
b->flags = 0;
return 1;
}
int StreamPeerSSL::bio_destroy( BIO *b ) {
if ( b == NULL ) return 0;
b->ptr = NULL; /* sb_tls_remove() will free it */
b->init = 0;
b->flags = 0;
return 1;
}
int StreamPeerSSL::bio_read( BIO *b, char *buf, int len ) {
if ( buf == NULL || len <= 0 ) return 0;
StreamPeerSSL * sp = (StreamPeerSSL*)b->ptr;
if (sp->base.is_null())
return 0;
BIO_clear_retry_flags( b );
Error err;
int ret=0;
if (sp->block) {
err = sp->base->get_data((const uint8_t*)buf,len);
if (err==OK)
ret=len;
} else {
err = sp->base->get_partial_data((const uint8_t*)buf,len,ret);
if (err==OK && ret!=len) {
BIO_set_retry_write( b );
}
}
return ret;
}
int StreamPeerSSL::bio_write( BIO *b, const char *buf, int len ) {
if ( buf == NULL || len <= 0 ) return 0;
StreamPeerSSL * sp = (StreamPeerSSL*)b->ptr;
if (sp->base.is_null())
return 0;
BIO_clear_retry_flags( b );
Error err;
int wrote=0;
if (sp->block) {
err = sp->base->put_data((const uint8_t*)buf,len);
if (err==OK)
wrote=len;
} else {
err = sp->base->put_partial_data((const uint8_t*)buf,len,wrote);
if (err==OK && wrote!=len) {
BIO_set_retry_write( b );
}
}
return wrote;
}
long StreamPeerSSL::bio_ctrl( BIO *b, int cmd, long num, void *ptr ) {
if ( cmd == BIO_CTRL_FLUSH ) {
/* The OpenSSL library needs this */
return 1;
}
return 0;
}
int StreamPeerSSL::bio_gets( BIO *b, char *buf, int len ) {
return -1;
}
int StreamPeerSSL::bio_puts( BIO *b, const char *str ) {
return StreamPeerSSL::bio_write( b, str, strlen( str ) );
}
BIO_METHOD StreamPeerSSL::bio_methods =
{
( 100 | 0x400 ), /* it's a source/sink BIO */
"sockbuf glue",
StreamPeerSSL::bio_write,
StreamPeerSSL::bio_read,
StreamPeerSSL::bio_puts,
StreamPeerSSL::bio_gets,
StreamPeerSSL::bio_ctrl,
StreamPeerSSL::bio_create,
StreamPeerSSL::bio_destroy
};
StreamPeerSSL::StreamPeerSSL() {
}

View file

@ -0,0 +1,26 @@
#ifndef STREAM_PEER_SSL_H
#define STREAM_PEER_SSL_H
#include "io/stream_peer.h"
class StreamPeerSSL : public StreamPeer {
OBJ_TYPE(StreamPeerSSL,StreamPeer);
Ref<StreamPeer> base;
bool block;
static BIO_METHOD bio_methods;
static int bio_create( BIO *b );
static int bio_destroy( BIO *b );
static int bio_read( BIO *b, char *buf, int len );
static int bio_write( BIO *b, const char *buf, int len );
static long bio_ctrl( BIO *b, int cmd, long num, void *ptr );
static int bio_gets( BIO *b, char *buf, int len );
static int bio_puts( BIO *b, const char *str );
public:
StreamPeerSSL();
};
#endif // STREAM_PEER_SSL_H

View file

@ -333,6 +333,12 @@ Error OS_Unix::kill(const ProcessID& p_pid) {
return ret?ERR_INVALID_PARAMETER:OK;
}
int OS_Unix::get_process_ID() const {
return getpid();
};
bool OS_Unix::has_environment(const String& p_var) const {
return getenv(p_var.utf8().get_data())!=NULL;

View file

@ -98,6 +98,7 @@ public:
virtual Error execute(const String& p_path, const List<String>& p_arguments,bool p_blocking,ProcessID *r_child_id=NULL,String* r_pipe=NULL,int *r_exitcode=NULL);
virtual Error kill(const ProcessID& p_pid);
virtual int get_process_ID() const;
virtual bool has_environment(const String& p_var) const;
virtual String get_environment(const String& p_var) const;

View file

@ -97,7 +97,7 @@ long AudioStreamOGGVorbis::_ov_tell_func(void *_f) {
bool AudioStreamOGGVorbis::_can_mix() const {
return playing && !paused;
return /*playing &&*/ !paused;
}
@ -125,6 +125,8 @@ void AudioStreamOGGVorbis::update() {
if (ret<0) {
playing = false;
setting_up=false;
ERR_EXPLAIN("Error reading OGG Vorbis File: "+file);
ERR_BREAK(ret<0);
} else if (ret==0) { // end of song, reload?
@ -135,7 +137,8 @@ void AudioStreamOGGVorbis::update() {
if (!has_loop()) {
playing=false;
playing=false;
setting_up=false;
repeats=1;
return;
}
@ -145,6 +148,7 @@ void AudioStreamOGGVorbis::update() {
int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
if (errv!=0) {
playing=false;
setting_up=false;
return; // :(
}
@ -179,6 +183,8 @@ void AudioStreamOGGVorbis::play() {
playing=false;
setting_up=true;
update();
if (!setting_up)
return;
setting_up=false;
playing=true;
}

View file

@ -46,7 +46,7 @@ DWORD ThreadWindows::thread_callback( LPVOID userdata ) {
ThreadWindows *t=reinterpret_cast<ThreadWindows*>(userdata);
t->callback(t->user);
t->id=(ID)0; // must implement
t->id=(ID)GetCurrentThreadId(); // must implement
return 0;
}
@ -67,7 +67,7 @@ Thread* ThreadWindows::create_func_windows(ThreadCreateCallback p_callback,void
}
Thread::ID ThreadWindows::get_thread_ID_func_windows() {
return (ID)0; //must implement
return (ID)GetCurrentThreadId(); //must implement
}
void ThreadWindows::wait_to_finish_func_windows(Thread* p_thread) {

View file

@ -577,12 +577,15 @@ Error Main::setup(const char *execpath,int argc, char *argv[],bool p_second_phas
video_mode.height=globals->get("display/height");
if (use_custom_res && globals->has("display/fullscreen"))
video_mode.fullscreen=globals->get("display/fullscreen");
if (use_custom_res && globals->has("display/resizable"))
video_mode.resizable=globals->get("display/resizable");
GLOBAL_DEF("display/width",video_mode.width);
GLOBAL_DEF("display/height",video_mode.height);
GLOBAL_DEF("display/fullscreen",video_mode.fullscreen);
GLOBAL_DEF("display/resizable",video_mode.resizable);
if (rtm==-1) {
rtm=GLOBAL_DEF("render/thread_model",OS::RENDER_THREAD_SAFE);
}
@ -648,7 +651,8 @@ Error Main::setup(const char *execpath,int argc, char *argv[],bool p_second_phas
OS::get_singleton()->set_screen_orientation(OS::SCREEN_LANDSCAPE);
}
OS::get_singleton()->set_iterations_per_second(GLOBAL_DEF("display/target_fps",60));
OS::get_singleton()->set_iterations_per_second(GLOBAL_DEF("physics/fixed_fps",60));
OS::get_singleton()->set_target_fps(GLOBAL_DEF("application/target_fps",0));
if (!OS::get_singleton()->_verbose_stdout) //overrided
OS::get_singleton()->_verbose_stdout=GLOBAL_DEF("debug/verbose_stdout",false);
@ -1210,6 +1214,7 @@ bool Main::start() {
}
uint64_t Main::last_ticks=0;
uint64_t Main::target_ticks=0;
float Main::time_accum=0;
uint32_t Main::frames=0;
uint32_t Main::frame=0;
@ -1295,7 +1300,6 @@ bool Main::iteration() {
}
} else {
VisualServer::get_singleton()->flush(); // flush visual commands
}
if (AudioServer::get_singleton())
@ -1343,6 +1347,16 @@ bool Main::iteration() {
OS::get_singleton()->delay_usec( OS::get_singleton()->get_frame_delay()*1000 );
}
int taret_fps = OS::get_singleton()->get_target_fps();
if (taret_fps>0) {
uint64_t time_step = 1000000L/taret_fps;
target_ticks += time_step;
uint64_t current_ticks = OS::get_singleton()->get_ticks_usec();
if (current_ticks<target_ticks) OS::get_singleton()->delay_usec(target_ticks-current_ticks);
current_ticks = OS::get_singleton()->get_ticks_usec();
target_ticks = MIN(MAX(target_ticks,current_ticks-time_step),current_ticks+time_step);
}
return exit;
}

View file

@ -40,13 +40,12 @@
class Main {
static void print_help(const char* p_binary);
static uint64_t last_ticks;
static uint64_t target_ticks;
static float time_accum;
static uint32_t frames;
static uint32_t frame;
static bool force_redraw_requested;
public:
static Error setup(const char *execpath,int argc, char *argv[],bool p_second_phase=true);

View file

@ -10,7 +10,6 @@ env.modules_sources=[
#env.add_source_files(env.modules_sources,"*.cpp")
Export('env')
for x in env.module_list:
if (x in env.disabled_modules):
continue

View file

@ -31,6 +31,7 @@
#include "object_type_db.h"
#include "reference.h"
#include "gd_script.h"
#include "func_ref.h"
#include "os/os.h"
const char *GDFunctions::get_func_name(Function p_func) {
@ -80,6 +81,7 @@ const char *GDFunctions::get_func_name(Function p_func) {
"clamp",
"nearest_po2",
"weakref",
"funcref",
"convert",
"typeof",
"str",
@ -451,6 +453,36 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
} break;
case FUNC_FUNCREF: {
VALIDATE_ARG_COUNT(2);
if (p_args[0]->get_type()!=Variant::OBJECT) {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_error.expected=Variant::OBJECT;
r_ret=Variant();
return;
}
if (p_args[1]->get_type()!=Variant::STRING && p_args[1]->get_type()!=Variant::NODE_PATH) {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=1;
r_error.expected=Variant::STRING;
r_ret=Variant();
return;
}
Ref<FuncRef> fr = memnew( FuncRef);
Object *obj = *p_args[0];
fr->set_instance(*p_args[0]);
fr->set_function(*p_args[1]);
r_ret=fr;
} break;
case TYPE_CONVERT: {
VALIDATE_ARG_COUNT(2);
@ -678,7 +710,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
}
r_ret=ResourceLoader::load(*p_args[0]);
}
} break;
case INST2DICT: {
VALIDATE_ARG_COUNT(1);
@ -1063,7 +1095,7 @@ MethodInfo GDFunctions::get_info(Function p_func) {
return mi;
} break;
case MATH_RAND: {
MethodInfo mi("rand");
MethodInfo mi("randi");
mi.return_val.type=Variant::INT;
return mi;
} break;
@ -1129,6 +1161,13 @@ MethodInfo GDFunctions::get_info(Function p_func) {
mi.return_val.type=Variant::OBJECT;
return mi;
} break;
case FUNC_FUNCREF: {
MethodInfo mi("funcref",PropertyInfo(Variant::OBJECT,"instance"),PropertyInfo(Variant::STRING,"funcname"));
mi.return_val.type=Variant::OBJECT;
return mi;
} break;
case TYPE_CONVERT: {

View file

@ -77,6 +77,7 @@ public:
LOGIC_CLAMP,
LOGIC_NEAREST_PO2,
OBJ_WEAKREF,
FUNC_FUNCREF,
TYPE_CONVERT,
TYPE_OF,
TEXT_STR,

View file

@ -174,10 +174,19 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
/* Parse Operand */
/*****************/
if (parenthesis>0) {
//remove empty space (only allowed if inside parenthesis
while(tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
tokenizer->advance();
}
}
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
//subexpression ()
tokenizer->advance();
parenthesis++;
Node* subexpr = _parse_expression(p_parent,p_static);
parenthesis--;
if (!subexpr)
return NULL;
@ -629,6 +638,12 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
/* Parse Operator */
/******************/
if (parenthesis>0) {
//remove empty space (only allowed if inside parenthesis
while(tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
tokenizer->advance();
}
}
Expression e;
e.is_op=false;
@ -2475,6 +2490,7 @@ void GDParser::clear() {
tab_level.push_back(0);
error_line=0;
error_column=0;
parenthesis=0;
current_export.type=Variant::NIL;
error="";

View file

@ -356,6 +356,7 @@ private:
template<class T>
T* alloc_node();
int parenthesis;
bool error_set;
String error;
int error_line;

View file

@ -635,6 +635,19 @@ Variant GDFunction::call(GDInstance *p_instance,const Variant **p_args, int p_ar
err.argument-=1;
}
}
} if (methodstr=="free") {
if (err.error==Variant::CallError::CALL_ERROR_INVALID_METHOD) {
if (base->is_ref()) {
err_text="Attempted to free a reference.";
break;
} else if (base->get_type()==Variant::OBJECT) {
err_text="Attempted to free a locked object (calling or emitting).";
break;
}
}
}
err_text=_get_call_error(err,"function '"+methodstr+"' in base '"+basestr+"'",(const Variant**)argptrs);
break;

View file

@ -242,6 +242,24 @@ void GDTokenizerText::_advance() {
case 0:
_make_token(TK_EOF);
break;
case '\\':
INCPOS(1);
if (GETCHAR(0)=='\r') {
INCPOS(1);
}
if (GETCHAR(0)!='\n') {
_make_error("Expected newline after '\\'.");
return;
}
INCPOS(1);
while(GETCHAR(0)==' ' || GETCHAR(0)=='\t') {
INCPOS(1);
}
continue;
case '\t':
case '\r':
case ' ':

View file

@ -1,7 +0,0 @@
Import('env')
env.add_source_files(env.modules_sources,"*.cpp")
Export('env')

View file

@ -1,11 +0,0 @@
def can_build(platform):
return True
def configure(env):
pass

View file

@ -1,498 +0,0 @@
/*************************************************************************/
/* multi_script.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 "multi_script.h"
bool MultiScriptInstance::set(const StringName& p_name, const Variant& p_value) {
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
bool found = sarr[i]->set(p_name,p_value);
if (found)
return true;
}
if (String(p_name).begins_with("script_")) {
bool valid;
owner->set(p_name,p_value,&valid);
return valid;
}
return false;
}
bool MultiScriptInstance::get(const StringName& p_name, Variant &r_ret) const{
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
bool found = sarr[i]->get(p_name,r_ret);
if (found)
return true;
}
if (String(p_name).begins_with("script_")) {
bool valid;
r_ret=owner->get(p_name,&valid);
return valid;
}
return false;
}
void MultiScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const{
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
Set<String> existing;
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
List<PropertyInfo> pl;
sarr[i]->get_property_list(&pl);
for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
if (existing.has(E->get().name))
continue;
p_properties->push_back(E->get());
existing.insert(E->get().name);
}
}
p_properties->push_back( PropertyInfo(Variant::NIL,"Scripts",PROPERTY_HINT_NONE,String(),PROPERTY_USAGE_CATEGORY) );
for(int i=0;i<owner->scripts.size();i++) {
p_properties->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+i),PROPERTY_HINT_RESOURCE_TYPE,"Script",PROPERTY_USAGE_EDITOR) );
}
if (owner->scripts.size()<25) {
p_properties->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+(owner->scripts.size())),PROPERTY_HINT_RESOURCE_TYPE,"Script",PROPERTY_USAGE_EDITOR) );
}
}
void MultiScriptInstance::get_method_list(List<MethodInfo> *p_list) const{
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
Set<StringName> existing;
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
List<MethodInfo> ml;
sarr[i]->get_method_list(&ml);
for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) {
if (existing.has(E->get().name))
continue;
p_list->push_back(E->get());
existing.insert(E->get().name);
}
}
}
bool MultiScriptInstance::has_method(const StringName& p_method) const{
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
if (sarr[i]->has_method(p_method))
return true;
}
return false;
}
Variant MultiScriptInstance::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) {
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
Variant r = sarr[i]->call(p_method,p_args,p_argcount,r_error);
if (r_error.error==Variant::CallError::CALL_OK)
return r;
else if (r_error.error!=Variant::CallError::CALL_ERROR_INVALID_METHOD)
return r;
}
r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
}
void MultiScriptInstance::call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount){
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
sarr[i]->call_multilevel(p_method,p_args,p_argcount);
}
}
void MultiScriptInstance::notification(int p_notification){
ScriptInstance **sarr = instances.ptr();
int sc = instances.size();
for(int i=0;i<sc;i++) {
if (!sarr[i])
continue;
sarr[i]->notification(p_notification);
}
}
Ref<Script> MultiScriptInstance::get_script() const {
return owner;
}
ScriptLanguage *MultiScriptInstance::get_language() {
return MultiScriptLanguage::get_singleton();
}
MultiScriptInstance::~MultiScriptInstance() {
owner->remove_instance(object);
}
///////////////////
bool MultiScript::is_tool() const {
for(int i=0;i<scripts.size();i++) {
if (scripts[i]->is_tool())
return true;
}
return false;
}
bool MultiScript::_set(const StringName& p_name, const Variant& p_value) {
_THREAD_SAFE_METHOD_
String s = String(p_name);
if (s.begins_with("script_")) {
int idx = s[7];
if (idx==0)
return false;
idx-='a';
ERR_FAIL_COND_V(idx<0,false);
Ref<Script> s = p_value;
if (idx<scripts.size()) {
if (s.is_null())
remove_script(idx);
else
set_script(idx,s);
} else if (idx==scripts.size()) {
if (s.is_null())
return false;
add_script(s);
} else
return false;
return true;
}
return false;
}
bool MultiScript::_get(const StringName& p_name,Variant &r_ret) const{
_THREAD_SAFE_METHOD_
String s = String(p_name);
if (s.begins_with("script_")) {
int idx = s[7];
if (idx==0)
return false;
idx-='a';
ERR_FAIL_COND_V(idx<0,false);
if (idx<scripts.size()) {
r_ret=get_script(idx);
return true;
} else if (idx==scripts.size()) {
r_ret=Ref<Script>();
return true;
}
}
return false;
}
void MultiScript::_get_property_list( List<PropertyInfo> *p_list) const{
_THREAD_SAFE_METHOD_
for(int i=0;i<scripts.size();i++) {
p_list->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+i),PROPERTY_HINT_RESOURCE_TYPE,"Script") );
}
if (scripts.size()<25) {
p_list->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+(scripts.size())),PROPERTY_HINT_RESOURCE_TYPE,"Script") );
}
}
void MultiScript::set_script(int p_idx,const Ref<Script>& p_script ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX(p_idx,scripts.size());
ERR_FAIL_COND( p_script.is_null() );
scripts[p_idx]=p_script;
Ref<Script> s=p_script;
for (Map<Object*,MultiScriptInstance*>::Element *E=instances.front();E;E=E->next()) {
MultiScriptInstance*msi=E->get();
ScriptInstance *si = msi->instances[p_idx];
if (si) {
msi->instances[p_idx]=NULL;
memdelete(si);
}
if (p_script->can_instance())
msi->instances[p_idx]=s->instance_create(msi->object);
}
}
Ref<Script> MultiScript::get_script(int p_idx) const{
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX_V(p_idx,scripts.size(),Ref<Script>());
return scripts[p_idx];
}
void MultiScript::add_script(const Ref<Script>& p_script){
_THREAD_SAFE_METHOD_
ERR_FAIL_COND( p_script.is_null() );
scripts.push_back(p_script);
Ref<Script> s=p_script;
for (Map<Object*,MultiScriptInstance*>::Element *E=instances.front();E;E=E->next()) {
MultiScriptInstance*msi=E->get();
if (p_script->can_instance())
msi->instances.push_back( s->instance_create(msi->object) );
else
msi->instances.push_back(NULL);
msi->object->_change_notify();
}
_change_notify();
}
void MultiScript::remove_script(int p_idx) {
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX(p_idx,scripts.size());
scripts.remove(p_idx);
for (Map<Object*,MultiScriptInstance*>::Element *E=instances.front();E;E=E->next()) {
MultiScriptInstance*msi=E->get();
ScriptInstance *si = msi->instances[p_idx];
msi->instances.remove(p_idx);
if (si) {
memdelete(si);
}
msi->object->_change_notify();
}
}
void MultiScript::remove_instance(Object *p_object) {
_THREAD_SAFE_METHOD_
instances.erase(p_object);
}
bool MultiScript::can_instance() const {
return true;
}
StringName MultiScript::get_instance_base_type() const {
return StringName();
}
ScriptInstance* MultiScript::instance_create(Object *p_this) {
_THREAD_SAFE_METHOD_
MultiScriptInstance *msi = memnew( MultiScriptInstance );
msi->object=p_this;
msi->owner=this;
for(int i=0;i<scripts.size();i++) {
ScriptInstance *si;
if (scripts[i]->can_instance())
si = scripts[i]->instance_create(p_this);
else
si=NULL;
msi->instances.push_back(si);
}
instances[p_this]=msi;
p_this->_change_notify();
return msi;
}
bool MultiScript::instance_has(const Object *p_this) const {
_THREAD_SAFE_METHOD_
return instances.has((Object*)p_this);
}
bool MultiScript::has_source_code() const {
return false;
}
String MultiScript::get_source_code() const {
return "";
}
void MultiScript::set_source_code(const String& p_code) {
}
Error MultiScript::reload() {
for(int i=0;i<scripts.size();i++)
scripts[i]->reload();
return OK;
}
String MultiScript::get_node_type() const {
return "";
}
void MultiScript::_bind_methods() {
}
ScriptLanguage *MultiScript::get_language() const {
return MultiScriptLanguage::get_singleton();
}
///////////////
MultiScript::MultiScript() {
}
MultiScriptLanguage *MultiScriptLanguage::singleton=NULL;

View file

@ -1,158 +0,0 @@
/*************************************************************************/
/* multi_script.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 MULTI_SCRIPT_H
#define MULTI_SCRIPT_H
#include "script_language.h"
#include "os/thread_safe.h"
class MultiScript;
class MultiScriptInstance : public ScriptInstance {
friend class MultiScript;
mutable Vector<ScriptInstance*> instances;
Object *object;
mutable MultiScript *owner;
public:
virtual bool set(const StringName& p_name, const Variant& p_value);
virtual bool get(const StringName& p_name, Variant &r_ret) const;
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName& p_method) const;
virtual Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error);
virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
virtual void notification(int p_notification);
virtual Ref<Script> get_script() const;
virtual ScriptLanguage *get_language();
virtual ~MultiScriptInstance();
};
class MultiScript : public Script {
_THREAD_SAFE_CLASS_
friend class MultiScriptInstance;
OBJ_TYPE( MultiScript,Script);
Vector<Ref<Script> > scripts;
Map<Object*,MultiScriptInstance*> instances;
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;
static void _bind_methods();
public:
void remove_instance(Object *p_object);
virtual bool can_instance() const;
virtual StringName get_instance_base_type() const;
virtual ScriptInstance* instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
virtual bool has_source_code() const;
virtual String get_source_code() const;
virtual void set_source_code(const String& p_code);
virtual Error reload();
virtual bool is_tool() const;
virtual String get_node_type() const;
void set_script(int p_idx,const Ref<Script>& p_script );
Ref<Script> get_script(int p_idx) const;
void remove_script(int p_idx);
void add_script(const Ref<Script>& p_script);
virtual ScriptLanguage *get_language() const;
MultiScript();
};
class MultiScriptLanguage : public ScriptLanguage {
static MultiScriptLanguage *singleton;
public:
static _FORCE_INLINE_ MultiScriptLanguage *get_singleton() { return singleton; }
virtual String get_name() const { return "MultiScript"; }
/* LANGUAGE FUNCTIONS */
virtual void init() {}
virtual String get_type() const { return "MultiScript"; }
virtual String get_extension() const { return ""; }
virtual Error execute_file(const String& p_path) { return OK; }
virtual void finish() {}
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const {}
virtual void get_comment_delimiters(List<String> *p_delimiters) const {}
virtual void get_string_delimiters(List<String> *p_delimiters) const {}
virtual String get_template(const String& p_class_name, const String& p_base_class_name) const { return ""; }
virtual bool validate(const String& p_script, int &r_line_error,int &r_col_error,String& r_test_error,const String& p_path="",List<String>* r_fn=NULL) const { return true; }
virtual Script *create_script() const { return memnew( MultiScript ); }
virtual bool has_named_classes() const { return false; }
virtual int find_function(const String& p_function,const String& p_code) const { return -1; }
virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const { return ""; }
/* DEBUGGER FUNCTIONS */
virtual String debug_get_error() const { return ""; }
virtual int debug_get_stack_level_count() const { return 0; }
virtual int debug_get_stack_level_line(int p_level) const { return 0; }
virtual String debug_get_stack_level_function(int p_level) const { return ""; }
virtual String debug_get_stack_level_source(int p_level) const { return ""; }
virtual void debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1) {}
virtual void debug_get_stack_level_members(int p_level,List<String> *p_members, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1) {}
virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1) {}
virtual String debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems=-1,int p_max_depth=-1) { return ""; }
/* LOADER FUNCTIONS */
virtual void get_recognized_extensions(List<String> *p_extensions) const {}
virtual void get_public_functions(List<MethodInfo> *p_functions) const {}
virtual void get_public_constants(List<Pair<String,Variant> > *p_constants) const {}
MultiScriptLanguage() { singleton=this; }
virtual ~MultiScriptLanguage() {};
};
#endif // MULTI_SCRIPT_H

View file

@ -1,33 +0,0 @@
/*************************************************/
/* register_script_types.cpp */
/*************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/*************************************************/
/* Source code within this file is: */
/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */
/* All Rights Reserved. */
/*************************************************/
#include "register_types.h"
#include "multi_script.h"
#include "io/resource_loader.h"
static MultiScriptLanguage *script_multi_script=NULL;
void register_multiscript_types() {
script_multi_script = memnew( MultiScriptLanguage );
ScriptServer::register_language(script_multi_script);
ObjectTypeDB::register_type<MultiScript>();
}
void unregister_multiscript_types() {
if (script_multi_script) {
memdelete(script_multi_script);
}
}

View file

@ -1,30 +0,0 @@
/*************************************************************************/
/* register_types.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. */
/*************************************************************************/
void register_multiscript_types();
void unregister_multiscript_types();

View file

@ -11,7 +11,7 @@
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden">
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -35,6 +35,6 @@ $$ADD_APPLICATION_CHUNKS$$
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="11"/>
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="15"/>
</manifest>

View file

@ -8,7 +8,7 @@ android_files = [
'godot_android.cpp',
'file_access_android.cpp',
'dir_access_android.cpp',
'audio_driver_android.cpp',
'audio_driver_opensl.cpp',
'file_access_jandroid.cpp',
'dir_access_jandroid.cpp',
'thread_jandroid.cpp',
@ -37,7 +37,9 @@ 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

View file

@ -1,5 +1,5 @@
/*************************************************************************/
/* audio_driver_android.cpp */
/* audio_driver_opensl.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -26,9 +26,8 @@
/* 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 "audio_driver_opensl.h"
#include <string.h>
#ifdef ANDROID_NATIVE_ACTIVITY
@ -40,21 +39,32 @@
/* Structure for passing information to callback function */
void AudioDriverAndroid::_buffer_callback(
void AudioDriverOpenSL::_buffer_callback(
SLAndroidSimpleBufferQueueItf queueItf
/* SLuint32 eventFlags,
const void * pBuffer,
SLuint32 bufferSize,
SLuint32 dataUsed*/) {
bool mix=true;
if (pause) {
mix=false;
} else if (mutex) {
mix = mutex->try_lock()==OK;
}
if (mutex)
mutex->lock();
if (mix) {
audio_server_process(buffer_size,mixdown_buffer);
} else {
audio_server_process(buffer_size,mixdown_buffer);
int32_t* src_buff=mixdown_buffer;
for(int i=0;i<buffer_size*2;i++) {
src_buff[i]=0;
}
}
if (mutex)
if (mutex && mix)
mutex->unlock();
@ -87,7 +97,7 @@ void AudioDriverAndroid::_buffer_callback(
#endif
}
void AudioDriverAndroid::_buffer_callbacks(
void AudioDriverOpenSL::_buffer_callbacks(
SLAndroidSimpleBufferQueueItf queueItf,
/*SLuint32 eventFlags,
const void * pBuffer,
@ -96,7 +106,7 @@ void AudioDriverAndroid::_buffer_callbacks(
void *pContext) {
AudioDriverAndroid *ad = (AudioDriverAndroid*)pContext;
AudioDriverOpenSL *ad = (AudioDriverOpenSL*)pContext;
// ad->_buffer_callback(queueItf,eventFlags,pBuffer,bufferSize,dataUsed);
ad->_buffer_callback(queueItf);
@ -104,17 +114,17 @@ void AudioDriverAndroid::_buffer_callbacks(
}
AudioDriverAndroid* AudioDriverAndroid::s_ad=NULL;
AudioDriverOpenSL* AudioDriverOpenSL::s_ad=NULL;
const char* AudioDriverAndroid::get_name() const {
const char* AudioDriverOpenSL::get_name() const {
return "Android";
}
#if 0
int AudioDriverAndroid::thread_func(SceSize args, void *argp) {
int AudioDriverOpenSL::thread_func(SceSize args, void *argp) {
AudioDriverAndroid* ad = s_ad;
AudioDriverOpenSL* ad = s_ad;
sceAudioOutput2Reserve(AUDIO_OUTPUT_SAMPLE);
int half=0;
@ -170,7 +180,7 @@ int AudioDriverAndroid::thread_func(SceSize args, void *argp) {
}
#endif
Error AudioDriverAndroid::init(){
Error AudioDriverOpenSL::init(){
SLresult
res;
@ -197,7 +207,7 @@ Error AudioDriverAndroid::init(){
return OK;
}
void AudioDriverAndroid::start(){
void AudioDriverOpenSL::start(){
mutex = Mutex::create();
@ -357,37 +367,44 @@ void AudioDriverAndroid::start(){
active=true;
}
int AudioDriverAndroid::get_mix_rate() const {
int AudioDriverOpenSL::get_mix_rate() const {
return 44100;
}
AudioDriverSW::OutputFormat AudioDriverAndroid::get_output_format() const{
AudioDriverSW::OutputFormat AudioDriverOpenSL::get_output_format() const{
return OUTPUT_STEREO;
}
void AudioDriverAndroid::lock(){
void AudioDriverOpenSL::lock(){
//if (active && mutex)
// mutex->lock();
if (active && mutex)
mutex->lock();
}
void AudioDriverAndroid::unlock() {
void AudioDriverOpenSL::unlock() {
//if (active && mutex)
// mutex->unlock();
if (active && mutex)
mutex->unlock();
}
void AudioDriverAndroid::finish(){
void AudioDriverOpenSL::finish(){
(*sl)->Destroy(sl);
}
void AudioDriverOpenSL::set_pause(bool p_pause) {
AudioDriverAndroid::AudioDriverAndroid()
{
s_ad=this;
mutex=NULL;
pause=p_pause;
}
#endif
AudioDriverOpenSL::AudioDriverOpenSL()
{
s_ad=this;
mutex=Mutex::create();//NULL;
pause=false;
}

View file

@ -1,5 +1,5 @@
/*************************************************************************/
/* audio_driver_android.h */
/* audio_driver_opensl.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -26,16 +26,18 @@
/* 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
#ifndef AUDIO_DRIVER_OPENSL_H
#define AUDIO_DRIVER_OPENSL_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 {
class AudioDriverOpenSL : public AudioDriverSW {
bool active;
Mutex *mutex;
@ -45,7 +47,7 @@ class AudioDriverAndroid : public AudioDriverSW {
BUFFER_COUNT=2
};
bool pause;
uint32_t buffer_size;
@ -67,7 +69,7 @@ class AudioDriverAndroid : public AudioDriverSW {
SLDataLocator_OutputMix locator_outputmix;
SLBufferQueueState state;
static AudioDriverAndroid* s_ad;
static AudioDriverOpenSL* s_ad;
void _buffer_callback(
SLAndroidSimpleBufferQueueItf queueItf
@ -97,9 +99,10 @@ public:
virtual void unlock();
virtual void finish();
virtual void set_pause(bool p_pause);
AudioDriverAndroid();
AudioDriverOpenSL();
};
#endif // AUDIO_DRIVER_ANDROID_H
#endif

View file

@ -14,6 +14,7 @@ def can_build():
import os
if (not os.environ.has_key("ANDROID_NDK_ROOT")):
return False
return True
def get_opts():
@ -23,7 +24,7 @@ def get_opts():
('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"),
('NDK_TARGET', 'toolchain to use for the NDK',"arm-linux-androideabi-4.8"),
('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')
@ -55,13 +56,10 @@ def configure(env):
env.Tool('gcc')
env['SPAWN'] = methods.win32_spawn
env.android_source_modules.append("../libs/apk_expansion")
ndk_platform=""
if (env["ndk_platform"]=="2.2"):
ndk_platform="android-8"
else:
ndk_platform="android-9"
env.Append(CPPFLAGS=["-DANDROID_NATIVE_ACTIVITY"])
ndk_platform="android-15"
print("Godot Android!!!!!")
@ -111,6 +109,7 @@ def configure(env):
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=['OpenSLES'])
# 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'])

View file

@ -15,8 +15,3 @@
# '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

View file

@ -49,17 +49,21 @@ import android.media.*;
import android.hardware.*;
import android.content.*;
import android.net.Uri;
import android.media.MediaPlayer;
import java.lang.reflect.Method;
import java.util.List;
import java.util.ArrayList;
import com.android.godot.payments.PaymentsManager;
import java.io.IOException;
import android.provider.Settings.Secure;
import android.widget.FrameLayout;
import com.android.godot.input.*;
public class Godot extends Activity implements SensorEventListener
{
{
static public class SingletonBase {
protected void registerClass(String p_name, String[] p_methods) {
@ -133,8 +137,12 @@ public class Godot extends Activity implements SensorEventListener
};
public ResultCallback result_callback;
private PaymentsManager mPaymentsManager = null;
@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) {
if (result_callback != null) {
if(requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE){
mPaymentsManager.processPurchaseResponse(resultCode, data);
}else if (result_callback != null) {
result_callback.callback(requestCode, resultCode, data);
result_callback = null;
};
@ -163,13 +171,18 @@ public class Godot extends Activity implements SensorEventListener
io.setEdit(edittext);
}
private static Godot _self;
public static Godot getInstance(){
return Godot._self;
}
@Override protected void onCreate(Bundle icicle) {
System.out.printf("** GODOT ACTIVITY CREATED HERE ***\n");
super.onCreate(icicle);
_self = this;
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@ -184,11 +197,19 @@ public class Godot extends Activity implements SensorEventListener
result_callback = null;
mPaymentsManager = PaymentsManager.createManager(this).initService();
// instanceSingleton( new GodotFacebook(this) );
}
@Override protected void onDestroy(){
if(mPaymentsManager != null ) mPaymentsManager.destroy();
super.onDestroy();
}
@Override protected void onPause() {
super.onPause();
mView.onPause();
@ -333,7 +354,15 @@ public class Godot extends Activity implements SensorEventListener
@Override public boolean onKeyDown(int keyCode, KeyEvent event) {
GodotLib.key(keyCode, event.getUnicodeChar(0), true);
return super.onKeyDown(keyCode, event);
};
}
public PaymentsManager getPaymentsManager() {
return mPaymentsManager;
}
// public void setPaymentsManager(PaymentsManager mPaymentsManager) {
// this.mPaymentsManager = mPaymentsManager;
// };
// Audio

View file

@ -59,6 +59,9 @@ public class GodotIO {
Godot activity;
GodotEditText edit;
Context applicationContext;
MediaPlayer mediaPlayer;
final int SCREEN_LANDSCAPE=0;
final int SCREEN_PORTRAIT=1;
final int SCREEN_REVERSE_LANDSCAPE=2;
@ -328,7 +331,7 @@ public class GodotIO {
activity=p_activity;
streams=new HashMap<Integer,AssetData>();
dirs=new HashMap<Integer,AssetDir>();
applicationContext = activity.getApplicationContext();
}
@ -475,8 +478,13 @@ public class GodotIO {
if(edit != null)
edit.hideKeyboard();
//InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
//inputMgr.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
View v = activity.getCurrentFocus();
if (v != null) {
inputMgr.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
} else {
inputMgr.hideSoftInputFromWindow(new View(activity).getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
};
public void setScreenOrientation(int p_orientation) {
@ -512,6 +520,43 @@ public class GodotIO {
edit = _edit;
}
public void playVideo(String p_path)
{
Uri filePath = Uri.parse(p_path);
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(applicationContext, filePath);
mediaPlayer.prepare();
mediaPlayer.start();
}
catch(IOException e)
{
System.out.println("IOError while playing video");
}
}
public boolean isVideoPlaying() {
if (mediaPlayer != null) {
return mediaPlayer.isPlaying();
}
return false;
}
public void pauseVideo() {
if (mediaPlayer != null) {
mediaPlayer.pause();
}
}
public void stopVideo() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
protected static final String PREFS_FILE = "device_id.xml";
protected static final String PREFS_DEVICE_ID = "device_id";

View file

@ -58,7 +58,7 @@ public class GodotLib {
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);
public static native void calldeferred(int p_ID, String p_method, Object[] p_params);
public static native void callobject(int p_ID, String p_method, Object[] p_params);
public static native void calldeferred(int p_ID, String p_method, Object[] p_params);
}

View file

@ -0,0 +1,83 @@
package com.android.godot;
import android.app.Activity;
public class GodotPaymentV3 extends Godot.SingletonBase {
private Godot activity;
private Integer purchaseCallbackId = 0;
private String accessToken;
private String purchaseValidationUrlPrefix;
public void purchase( String _sku) {
final String sku = _sku;
activity.getPaymentsManager().setBaseSingleton(this);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.getPaymentsManager().requestPurchase(sku);
}
});
};
static public Godot.SingletonBase initialize(Activity p_activity) {
return new GodotPaymentV3(p_activity);
}
public GodotPaymentV3(Activity p_activity) {
registerClass("GodotPayments", new String[] {"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix"});
activity=(Godot) p_activity;
}
public void callbackSuccess(){
GodotLib.callobject(purchaseCallbackId, "purchase_success", new Object[]{});
}
public void callbackFail(){
GodotLib.callobject(purchaseCallbackId, "purchase_fail", new Object[]{});
}
public void callbackCancel(){
GodotLib.callobject(purchaseCallbackId, "purchase_cancel", new Object[]{});
}
public int getPurchaseCallbackId() {
return purchaseCallbackId;
}
public void setPurchaseCallbackId(int purchaseCallbackId) {
this.purchaseCallbackId = purchaseCallbackId;
}
public String getPurchaseValidationUrlPrefix(){
return this.purchaseValidationUrlPrefix ;
}
public void setPurchaseValidationUrlPrefix(String url){
this.purchaseValidationUrlPrefix = url;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
}

View file

@ -0,0 +1,71 @@
package com.android.godot.payments;
import com.android.vending.billing.IInAppBillingService;
import android.content.Context;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.util.Log;
abstract public class ConsumeTask {
private Context context;
private IInAppBillingService mService;
public ConsumeTask(IInAppBillingService mService, Context context ){
this.context = context;
this.mService = mService;
}
public void consume(final String sku){
// Log.d("XXX", "Consuming product " + sku);
PaymentsCache pc = new PaymentsCache(context);
Boolean isBlocked = pc.getConsumableFlag("block", sku);
String _token = pc.getConsumableValue("token", sku);
// Log.d("XXX", "token " + _token);
if(!isBlocked && _token == null){
// _token = "inapp:"+context.getPackageName()+":android.test.purchased";
// Log.d("XXX", "Consuming product " + sku + " with token " + _token);
}else if(!isBlocked){
// Log.d("XXX", "It is not blocked ¿?");
return;
}else if(_token == null){
// Log.d("XXX", "No token available");
this.error("No token for sku:" + sku);
return;
}
final String token = _token;
new AsyncTask<String, String, String>(){
@Override
protected String doInBackground(String... params) {
try {
// Log.d("XXX", "Requesting to release item.");
int response = mService.consumePurchase(3, context.getPackageName(), token);
// Log.d("XXX", "release response code: " + response);
if(response == 0 || response == 8){
return null;
}
} catch (RemoteException e) {
return e.getMessage();
}
return "Some error";
}
protected void onPostExecute(String param){
if(param == null){
success();
}else{
error(param);
}
}
}.execute();
}
abstract protected void success();
abstract protected void error(String message);
}

View file

@ -0,0 +1,79 @@
package com.android.godot.payments;
import org.json.JSONException;
import org.json.JSONObject;
import com.android.godot.GodotLib;
import com.android.godot.utils.Crypt;
import com.android.vending.billing.IInAppBillingService;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
abstract public class HandlePurchaseTask {
private Activity context;
public HandlePurchaseTask(Activity context ){
this.context = context;
}
public void handlePurchaseRequest(int resultCode, Intent data){
// Log.d("XXX", "Handling purchase response");
// int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
PaymentsCache pc = new PaymentsCache(context);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
// String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == Activity.RESULT_OK) {
try {
Log.d("SARLANGA", purchaseData);
JSONObject jo = new JSONObject(purchaseData);
// String sku = jo.getString("productId");
// alert("You have bought the " + sku + ". Excellent choice, aventurer!");
// String orderId = jo.getString("orderId");
// String packageName = jo.getString("packageName");
String productId = jo.getString("productId");
// Long purchaseTime = jo.getLong("purchaseTime");
// Integer state = jo.getInt("purchaseState");
String developerPayload = jo.getString("developerPayload");
String purchaseToken = jo.getString("purchaseToken");
if(! pc.getConsumableValue("validation_hash", productId).equals(developerPayload) ) {
error("Untrusted callback");
return;
}
pc.setConsumableValue("ticket", productId, purchaseData);
pc.setConsumableFlag("block", productId, true);
pc.setConsumableValue("token", productId, purchaseToken);
success(purchaseToken, productId);
return;
} catch (JSONException e) {
error(e.getMessage());
}
}else if( resultCode == Activity.RESULT_CANCELED){
canceled();
}
}
abstract protected void success(String purchaseToken, String sku);
abstract protected void error(String message);
abstract protected void canceled();
}

View file

@ -0,0 +1,42 @@
package com.android.godot.payments;
import android.content.Context;
import android.content.SharedPreferences;
public class PaymentsCache {
public Context context;
public PaymentsCache(Context context){
this.context = context;
}
public void setConsumableFlag(String set, String sku, Boolean flag){
SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putBoolean(sku, flag);
editor.commit();
}
public boolean getConsumableFlag(String set, String sku){
SharedPreferences sharedPref = context.getSharedPreferences(
"consumables_" + set, Context.MODE_PRIVATE);
return sharedPref.getBoolean(sku, false);
}
public void setConsumableValue(String set, String sku, String value){
SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(sku, value);
editor.commit();
}
public String getConsumableValue(String set, String sku){
SharedPreferences sharedPref = context.getSharedPreferences(
"consumables_" + set, Context.MODE_PRIVATE);
return sharedPref.getString(sku, null);
}
}

View file

@ -0,0 +1,151 @@
package com.android.godot.payments;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import com.android.godot.Godot;
import com.android.godot.GodotPaymentV3;
import com.android.vending.billing.IInAppBillingService;
public class PaymentsManager {
public static final int BILLING_RESPONSE_RESULT_OK = 0;
public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001;
private Activity activity;
IInAppBillingService mService;
public void setActivity(Activity activity){
this.activity = activity;
}
public static PaymentsManager createManager(Activity activity){
PaymentsManager manager = new PaymentsManager(activity);
return manager;
}
private PaymentsManager(Activity activity){
this.activity = activity;
}
public PaymentsManager initService(){
activity.bindService(
new Intent("com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn,
Context.BIND_AUTO_CREATE);
return this;
}
public void destroy(){
if (mService != null) {
activity.unbindService(mServiceConn);
}
}
ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
@Override
public void onServiceConnected(ComponentName name,
IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
}
};
public void requestPurchase(String sku){
new PurchaseTask(mService, Godot.getInstance()) {
@Override
protected void error(String message) {
godotPaymentV3.callbackFail();
}
@Override
protected void canceled() {
godotPaymentV3.callbackCancel();
}
}.purchase(sku);
}
public void processPurchaseResponse(int resultCode, Intent data) {
new HandlePurchaseTask(activity){
@Override
protected void success(String purchaseToken, String sku) {
validatePurchase(purchaseToken, sku);
}
@Override
protected void error(String message) {
godotPaymentV3.callbackFail();
}
@Override
protected void canceled() {
godotPaymentV3.callbackCancel();
}}.handlePurchaseRequest(resultCode, data);
}
public void validatePurchase(String purchaseToken, final String sku){
new ValidateTask(activity, godotPaymentV3){
@Override
protected void success() {
new ConsumeTask(mService, activity) {
@Override
protected void success() {
godotPaymentV3.callbackSuccess();
}
@Override
protected void error(String message) {
godotPaymentV3.callbackFail();
}
}.consume(sku);
}
@Override
protected void error(String message) {
godotPaymentV3.callbackFail();
}
@Override
protected void canceled() {
godotPaymentV3.callbackCancel();
}
}.validatePurchase(sku);
}
private GodotPaymentV3 godotPaymentV3;
public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) {
this.godotPaymentV3 = godotPaymentV3;
}
}

View file

@ -0,0 +1,121 @@
package com.android.godot.payments;
import org.json.JSONException;
import org.json.JSONObject;
import com.android.godot.GodotLib;
import com.android.godot.utils.Crypt;
import com.android.vending.billing.IInAppBillingService;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
abstract public class PurchaseTask {
private Activity context;
private IInAppBillingService mService;
public PurchaseTask(IInAppBillingService mService, Activity context ){
this.context = context;
this.mService = mService;
}
private boolean isLooping = false;
public void purchase(final String sku){
// Log.d("XXX", "Starting purchase");
PaymentsCache pc = new PaymentsCache(context);
Boolean isBlocked = pc.getConsumableFlag("block", sku);
// if(isBlocked){
// Log.d("XXX", "Is awaiting payment confirmation");
// error("Awaiting payment confirmation");
// return;
// }
final String hash = Crypt.createRandomHash() + Crypt.createRandomHash();
Bundle buyIntentBundle;
try {
buyIntentBundle = mService.getBuyIntent(3, context.getApplicationContext().getPackageName(), sku, "inapp", hash );
} catch (RemoteException e) {
// Log.d("XXX", "Error: " + e.getMessage());
error(e.getMessage());
return;
}
Object rc = buyIntentBundle.get("RESPONSE_CODE");
int responseCode = 0;
if(rc == null){
responseCode = PaymentsManager.BILLING_RESPONSE_RESULT_OK;
}else if( rc instanceof Integer){
responseCode = ((Integer)rc).intValue();
}else if( rc instanceof Long){
responseCode = (int)((Long)rc).longValue();
}
// Log.d("XXX", "Buy intent response code: " + responseCode);
if(responseCode == 1 || responseCode == 3 || responseCode == 4){
canceled();
return ;
}
if(responseCode == 7){
new ConsumeTask(mService, context) {
@Override
protected void success() {
// Log.d("XXX", "Product was erroniously purchased!");
if(isLooping){
// Log.d("XXX", "It is looping");
error("Error while purchasing product");
return;
}
isLooping=true;
PurchaseTask.this.purchase(sku);
}
@Override
protected void error(String message) {
PurchaseTask.this.error(message);
}
}.consume(sku);
return;
}
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
pc.setConsumableValue("validation_hash", sku, hash);
try {
if(context == null){
// Log.d("XXX", "No context!");
}
if(pendingIntent == null){
// Log.d("XXX", "No pending intent");
}
// Log.d("XXX", "Starting activity for purchase!");
context.startIntentSenderForResult(
pendingIntent.getIntentSender(),
PaymentsManager.REQUEST_CODE_FOR_PURCHASE,
new Intent(),
Integer.valueOf(0), Integer.valueOf(0),
Integer.valueOf(0));
} catch (SendIntentException e) {
error(e.getMessage());
}
}
abstract protected void error(String message);
abstract protected void canceled();
}

View file

@ -0,0 +1,97 @@
package com.android.godot.payments;
import org.json.JSONException;
import org.json.JSONObject;
import com.android.godot.Godot;
import com.android.godot.GodotLib;
import com.android.godot.GodotPaymentV3;
import com.android.godot.utils.Crypt;
import com.android.godot.utils.HttpRequester;
import com.android.godot.utils.RequestParams;
import com.android.vending.billing.IInAppBillingService;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
abstract public class ValidateTask {
private Activity context;
private GodotPaymentV3 godotPaymentsV3;
public ValidateTask(Activity context, GodotPaymentV3 godotPaymentsV3){
this.context = context;
this.godotPaymentsV3 = godotPaymentsV3;
}
public void validatePurchase(final String sku){
new AsyncTask<String, String, String>(){
private ProgressDialog dialog;
@Override
protected void onPreExecute(){
dialog = ProgressDialog.show(context, null, "Please wait...");
}
@Override
protected String doInBackground(String... params) {
PaymentsCache pc = new PaymentsCache(context);
String url = godotPaymentsV3.getPurchaseValidationUrlPrefix();
RequestParams param = new RequestParams();
param.setUrl(url);
param.put("ticket", pc.getConsumableValue("ticket", sku));
param.put("purchaseToken", pc.getConsumableValue("token", sku));
param.put("sku", sku);
// Log.d("XXX", "Haciendo request a " + url);
// Log.d("XXX", "ticket: " + pc.getConsumableValue("ticket", sku));
// Log.d("XXX", "purchaseToken: " + pc.getConsumableValue("token", sku));
// Log.d("XXX", "sku: " + sku);
param.put("package", context.getApplicationContext().getPackageName());
HttpRequester requester = new HttpRequester();
String jsonResponse = requester.post(param);
// Log.d("XXX", "Validation response:\n"+jsonResponse);
return jsonResponse;
}
@Override
protected void onPostExecute(String response){
if(dialog != null){
dialog.dismiss();
}
JSONObject j;
try {
j = new JSONObject(response);
if(j.getString("status").equals("OK")){
success();
return;
}else if(j.getString("status") != null){
error(j.getString("message"));
}else{
error("Connection error");
}
} catch (JSONException e) {
error(e.getMessage());
}catch (Exception e){
error(e.getMessage());
}
}
}.execute();
}
abstract protected void success();
abstract protected void error(String message);
abstract protected void canceled();
}

View file

@ -0,0 +1,39 @@
package com.android.godot.utils;
import java.security.MessageDigest;
import java.util.Random;
public class Crypt {
public static String md5(String input){
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(input.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
for (int i=0; i<messageDigest.length; i++)
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
return hexString.toString();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public static String createRandomHash(){
return md5(Long.toString(createRandomLong()));
}
public static long createAbsRandomLong(){
return Math.abs(createRandomLong());
}
public static long createRandomLong(){
Random r = new Random();
return r.nextLong();
}
}

View file

@ -0,0 +1,54 @@
package com.android.godot.utils;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ssl.SSLSocketFactory;
/**
*
* @author Luis Linietsky <luis.linietsky@gmail.com>
*/
public class CustomSSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
}

View file

@ -0,0 +1,206 @@
package com.android.godot.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
/**
*
* @author Luis Linietsky <luis.linietsky@gmail.com>
*/
public class HttpRequester {
private Context context;
private static final int TTL = 600000; // 10 minutos
private long cttl=0;
public HttpRequester(){
// Log.d("XXX", "Creando http request sin contexto");
}
public HttpRequester(Context context){
this.context=context;
// Log.d("XXX", "Creando http request con contexto");
}
public String post(RequestParams params){
HttpPost httppost = new HttpPost(params.getUrl());
try {
httppost.setEntity(new UrlEncodedFormEntity(params.toPairsList()));
return request(httppost);
} catch (UnsupportedEncodingException e) {
return null;
}
}
public String get(RequestParams params){
String response = getResponseFromCache(params.getUrl());
if(response == null){
// Log.d("XXX", "Cache miss!");
HttpGet httpget = new HttpGet(params.getUrl());
long timeInit = new Date().getTime();
response = request(httpget);
long delay = new Date().getTime() - timeInit;
Log.d("com.app11tt.android.utils.HttpRequest::get(url)", "Url: " + params.getUrl() + " downloaded in " + String.format("%.03f", delay/1000.0f) + " seconds");
if(response == null || response.length() == 0){
response = "";
}else{
saveResponseIntoCache(params.getUrl(), response);
}
}
Log.d("XXX", "Req: " + params.getUrl());
Log.d("XXX", "Resp: " + response);
return response;
}
private String request(HttpUriRequest request){
// Log.d("XXX", "Haciendo request a: " + request.getURI() );
Log.d("PPP", "Haciendo request a: " + request.getURI() );
long init = new Date().getTime();
HttpClient httpclient = getNewHttpClient();
HttpParams httpParameters = httpclient.getParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, 0);
HttpConnectionParams.setSoTimeout(httpParameters, 0);
HttpConnectionParams.setTcpNoDelay(httpParameters, true);
try {
HttpResponse response = httpclient.execute(request);
Log.d("PPP", "Fin de request (" + (new Date().getTime() - init) + ") a: " + request.getURI() );
// Log.d("XXX1", "Status:" + response.getStatusLine().toString());
if(response.getStatusLine().getStatusCode() == 200){
String strResponse = EntityUtils.toString(response.getEntity());
// Log.d("XXX2", strResponse);
return strResponse;
}else{
Log.d("XXX3", "Response status code:" + response.getStatusLine().getStatusCode() + "\n" + EntityUtils.toString(response.getEntity()));
return null;
}
} catch (ClientProtocolException e) {
Log.d("XXX3", e.getMessage());
} catch (IOException e) {
Log.d("XXX4", e.getMessage());
}
return null;
}
private HttpClient getNewHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
private static String convertStreamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append((line + "\n"));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
public void saveResponseIntoCache(String request, String response){
if(context == null){
// Log.d("XXX", "No context, cache failed!");
return;
}
SharedPreferences sharedPref = context.getSharedPreferences("http_get_cache", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString("request_" + Crypt.md5(request), response);
editor.putLong("request_" + Crypt.md5(request) + "_ttl", new Date().getTime() + getTtl());
editor.commit();
}
public String getResponseFromCache(String request){
if(context == null){
Log.d("XXX", "No context, cache miss");
return null;
}
SharedPreferences sharedPref = context.getSharedPreferences( "http_get_cache", Context.MODE_PRIVATE);
long ttl = getResponseTtl(request);
if(ttl == 0l || (new Date().getTime() - ttl) > 0l){
Log.d("XXX", "Cache invalid ttl:" + ttl + " vs now:" + new Date().getTime());
return null;
}
return sharedPref.getString("request_" + Crypt.md5(request), null);
}
public long getResponseTtl(String request){
SharedPreferences sharedPref = context.getSharedPreferences(
"http_get_cache", Context.MODE_PRIVATE);
return sharedPref.getLong("request_" + Crypt.md5(request) + "_ttl", 0l);
}
public long getTtl() {
return cttl > 0 ? cttl : TTL;
}
public void setTtl(long ttl) {
this.cttl = (ttl*1000) + new Date().getTime();
}
}

View file

@ -0,0 +1,58 @@
package com.android.godot.utils;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
/**
*
* @author Luis Linietsky <luis.linietsky@gmail.com>
*/
public class RequestParams {
private HashMap<String,String> params;
private String url;
public RequestParams(){
params = new HashMap<String,String>();
}
public void put(String key, String value){
params.put(key, value);
}
public String get(String key){
return params.get(key);
}
public void remove(Object key){
params.remove(key);
}
public boolean has(String key){
return params.containsKey(key);
}
public List<NameValuePair> toPairsList(){
List<NameValuePair> fields = new ArrayList<NameValuePair>();
for(String key : params.keySet()){
fields.add(new BasicNameValuePair(key, this.get(key)));
}
return fields;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View file

@ -0,0 +1,144 @@
/*
* Copyright (C) 2012 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.
*/
package com.android.vending.billing;
import android.os.Bundle;
/**
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
* This service provides the following features:
* 1. Provides a new API to get details of in-app items published for the app including
* price, type, title and description.
* 2. The purchase flow is synchronous and purchase information is available immediately
* after it completes.
* 3. Purchase information of in-app purchases is maintained within the Google Play system
* till the purchase is consumed.
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
* in-app items are consumable and thereafter can be purchased again.
* 5. An API to get current purchases of the user immediately. This will not contain any
* consumed purchases.
*
* All calls will give a response code with the following possible values
* RESULT_OK = 0 - success
* RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
* RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
* RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
* RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
* RESULT_ERROR = 6 - Fatal error during the API action
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
*/
interface IInAppBillingService {
/**
* Checks support for the requested billing API version, package and in-app type.
* Minimum API version supported by this interface is 3.
* @param apiVersion the billing version which the app is using
* @param packageName the package name of the calling app
* @param type type of the in-app item being purchased "inapp" for one-time purchases
* and "subs" for subscription.
* @return RESULT_OK(0) on success, corresponding result code on failures
*/
int isBillingSupported(int apiVersion, String packageName, String type);
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the Third-party is using
* @param packageName the package name of the calling app
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
* "title : "Example Title", "description" : "This is an example description" }'
*/
Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type the type of the in-app item ("inapp" for one-time purchases
* and "subs" for subscription).
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
* TODO: change this to app-specific keys.
*/
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
String developerPayload);
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type the type of the in-app items being requested
* ("inapp" for one-time purchases and "subs" for subscription).
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
/**
* Consume the last purchase of the given SKU. This will result in this item being removed
* from all subsequent responses to getPurchases() and allow re-purchase of this item.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param purchaseToken token in the purchase information JSON that identifies the purchase
* to be consumed
* @return 0 if consumption succeeded. Appropriate error values for failures.
*/
int consumePurchase(int apiVersion, String packageName, String purchaseToken);
}

View file

@ -440,7 +440,8 @@ public:
case Variant::STRING: {
jobject o = env->CallObjectMethodA(instance,E->get().method,v);
String singname = env->GetStringUTFChars((jstring)o, NULL );
String str = env->GetStringUTFChars((jstring)o, NULL );
ret=str;
} break;
case Variant::STRING_ARRAY: {
@ -569,6 +570,11 @@ static jmethodID _hideKeyboard=0;
static jmethodID _setScreenOrientation=0;
static jmethodID _getUniqueID=0;
static jmethodID _playVideo=0;
static jmethodID _isVideoPlaying=0;
static jmethodID _pauseVideo=0;
static jmethodID _stopVideo=0;
static void _gfx_init_func(void* ud, bool gl2) {
@ -629,17 +635,43 @@ static void _hide_vk() {
env->CallVoidMethod(godot_io, _hideKeyboard);
};
// virtual Error native_video_play(String p_path);
// virtual bool native_video_is_playing();
// virtual void native_video_pause();
// virtual void native_video_stop();
static void _play_video(const String& p_path) {
}
static bool _is_video_playing() {
JNIEnv* env = ThreadAndroid::get_env();
return env->CallBooleanMethod(godot_io, _isVideoPlaying);
//return false;
}
static void _pause_video() {
JNIEnv* env = ThreadAndroid::get_env();
env->CallVoidMethod(godot_io, _pauseVideo);
}
static void _stop_video() {
JNIEnv* env = ThreadAndroid::get_env();
env->CallVoidMethod(godot_io, _stopVideo);
}
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);
_godot_instance=env->NewGlobalRef(activity);
// _godot_instance=activity;
__android_log_print(ANDROID_LOG_INFO,"godot","***************** HELLO FROM JNI!!!!!!!!");
@ -676,6 +708,11 @@ JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_initialize(JNIEnv * env,
_showKeyboard = env->GetMethodID(c,"showKeyboard","(Ljava/lang/String;)V");
_hideKeyboard = env->GetMethodID(c,"hideKeyboard","()V");
_setScreenOrientation = env->GetMethodID(c,"setScreenOrientation","(I)V");
_playVideo = env->GetMethodID(c,"playVideo","(Ljava/lang/String;)V");
_isVideoPlaying = env->GetMethodID(c,"isVideoPlaying","()Z");
_pauseVideo = env->GetMethodID(c,"pauseVideo","()V");
_stopVideo = env->GetMethodID(c,"stopVideo","()V");
}
ThreadAndroid::make_default(jvm);
@ -686,7 +723,7 @@ JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_initialize(JNIEnv * env,
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 = 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, _play_video, _is_video_playing, _pause_video, _stop_video);
os_android->set_need_reload_hooks(p_need_reload_hook);
char wd[500];
@ -781,8 +818,10 @@ static void _initialize_java_modules() {
jmethodID getClassLoader = env->GetMethodID(activityClass,"getClassLoader", "()Ljava/lang/ClassLoader;");
jobject cls = env->CallObjectMethod(_godot_instance, getClassLoader);
//cls=env->NewGlobalRef(cls);
jclass classLoader = env->FindClass("java/lang/ClassLoader");
//classLoader=(jclass)env->NewGlobalRef(classLoader);
jmethodID findClass = env->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
@ -800,10 +839,17 @@ static void _initialize_java_modules() {
ERR_EXPLAIN("Couldn't find singleton for class: "+m);
ERR_CONTINUE(!singletonClass);
}
//singletonClass=(jclass)env->NewGlobalRef(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;");
if (!initialize) {
ERR_EXPLAIN("Couldn't find proper initialize function 'public static Godot.SingletonBase Class::initialize(Activity p_activity)' initializer for singleton class: "+m);
ERR_CONTINUE(!initialize);
}
jobject obj = env->CallStaticObjectMethod(singletonClass,initialize,_godot_instance);
__android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class instance %x",obj);
jobject gob = env->NewGlobalRef(obj);

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.vending.expansion.downloader"
android:versionCode="2"
android:versionName="1.1" >
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15"/>
</manifest>

View file

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="apk_expansion" 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>

View file

@ -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 *;
#}

View file

@ -0,0 +1,13 @@
# 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,
# "ant.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-15
android.library=true
android.library.reference.1=../play_licensing

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2008, 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.
*/
-->
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:orientation="horizontal" android:id="@+id/notificationLayout" xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
android:layout_width="35dp"
android:layout_height="fill_parent"
android:paddingTop="10dp"
android:paddingBottom="8dp" >
<ImageView
android:id="@+id/appIcon"
android:layout_width="fill_parent"
android:layout_height="25dp"
android:scaleType="centerInside"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="@android:drawable/stat_sys_download" />
<TextView
android:id="@+id/progress_text"
style="@style/NotificationText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:layout_gravity="center_horizontal"
android:singleLine="true"
android:gravity="center" />
</RelativeLayout>
<RelativeLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1.0"
android:clickable="true"
android:focusable="true"
android:paddingTop="10dp"
android:paddingRight="8dp"
android:paddingBottom="8dp" >
<TextView
android:id="@+id/title"
style="@style/NotificationTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:singleLine="true"/>
<TextView
android:id="@+id/time_remaining"
style="@style/NotificationText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:singleLine="true"/>
<!-- Only one of progress_bar and paused_text will be visible. -->
<FrameLayout
android:id="@+id/progress_bar_frame"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" >
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingRight="25dp" />
<TextView
android:id="@+id/description"
style="@style/NotificationTextShadow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingRight="25dp"
android:singleLine="true" />
</FrameLayout>
</RelativeLayout>
</LinearLayout>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NotificationTextSecondary" parent="NotificationText">
<item name="android:textSize">12sp</item>
</style>
</resources>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
</resources>

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- When a download completes, a notification is displayed, and this
string is used to indicate that the download successfully completed.
Note that such a download could have been initiated by a variety of
applications, including (but not limited to) the browser, an email
application, a content marketplace. -->
<string name="notification_download_complete">Download complete</string>
<!-- When a download completes, a notification is displayed, and this
string is used to indicate that the download failed.
Note that such a download could have been initiated by a variety of
applications, including (but not limited to) the browser, an email
application, a content marketplace. -->
<string name="notification_download_failed">Download unsuccessful</string>
<string name="state_unknown">Starting..."</string>
<string name="state_idle">Waiting for download to start</string>
<string name="state_fetching_url">Looking for resources to download</string>
<string name="state_connecting">Connecting to the download server</string>
<string name="state_downloading">Downloading resources</string>
<string name="state_completed">Download finished</string>
<string name="state_paused_network_unavailable">Download paused because no network is available</string>
<string name="state_paused_network_setup_failure">Download paused. Test a website in browser</string>
<string name="state_paused_by_request">Download paused</string>
<string name="state_paused_wifi_unavailable">Download paused because wifi is unavailable</string>
<string name="state_paused_wifi_disabled">Download paused because wifi is disabled</string>
<string name="state_paused_roaming">Download paused because you are roaming</string>
<string name="state_paused_sdcard_unavailable">Download paused because the external storage is unavailable</string>
<string name="state_failed_unlicensed">Download failed because you may not have purchased this app</string>
<string name="state_failed_fetching_url">Download failed because the resources could not be found</string>
<string name="state_failed_sdcard_full">Download failed because the external storage is full</string>
<string name="state_failed_cancelled">Download cancelled</string>
<string name="state_failed">Download failed</string>
<string name="kilobytes_per_second">%1$s KB/s</string>
<string name="time_remaining">Time remaining: %1$s</string>
<string name="time_remaining_notification">%1$s left</string>
</resources>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NotificationText">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NotificationTextShadow" parent="NotificationText">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:shadowColor">@android:color/background_dark</item>
<item name="android:shadowDx">1.0</item>
<item name="android:shadowDy">1.0</item>
<item name="android:shadowRadius">1</item>
</style>
<style name="NotificationTitle">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textStyle">bold</item>
</style>
<style name="ButtonBackground">
<item name="android:background">@android:color/background_dark</item>
</style>
</resources>

View file

@ -0,0 +1,236 @@
/*
* Copyright (C) 2012 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.
*/
package com.google.android.vending.expansion.downloader;
import java.io.File;
/**
* Contains the internal constants that are used in the download manager.
* As a general rule, modifying these constants should be done with care.
*/
public class Constants {
/** Tag used for debugging/logging */
public static final String TAG = "LVLDL";
/**
* Expansion path where we store obb files
*/
public static final String EXP_PATH = File.separator + "Android"
+ File.separator + "obb" + File.separator;
/** The intent that gets sent when the service must wake up for a retry */
public static final String ACTION_RETRY = "android.intent.action.DOWNLOAD_WAKEUP";
/** the intent that gets sent when clicking a successful download */
public static final String ACTION_OPEN = "android.intent.action.DOWNLOAD_OPEN";
/** the intent that gets sent when clicking an incomplete/failed download */
public static final String ACTION_LIST = "android.intent.action.DOWNLOAD_LIST";
/** the intent that gets sent when deleting the notification of a completed download */
public static final String ACTION_HIDE = "android.intent.action.DOWNLOAD_HIDE";
/**
* When a number has to be appended to the filename, this string is used to separate the
* base filename from the sequence number
*/
public static final String FILENAME_SEQUENCE_SEPARATOR = "-";
/** The default user agent used for downloads */
public static final String DEFAULT_USER_AGENT = "Android.LVLDM";
/** The buffer size used to stream the data */
public static final int BUFFER_SIZE = 4096;
/** The minimum amount of progress that has to be done before the progress bar gets updated */
public static final int MIN_PROGRESS_STEP = 4096;
/** The minimum amount of time that has to elapse before the progress bar gets updated, in ms */
public static final long MIN_PROGRESS_TIME = 1000;
/** The maximum number of rows in the database (FIFO) */
public static final int MAX_DOWNLOADS = 1000;
/**
* The number of times that the download manager will retry its network
* operations when no progress is happening before it gives up.
*/
public static final int MAX_RETRIES = 5;
/**
* The minimum amount of time that the download manager accepts for
* a Retry-After response header with a parameter in delta-seconds.
*/
public static final int MIN_RETRY_AFTER = 30; // 30s
/**
* The maximum amount of time that the download manager accepts for
* a Retry-After response header with a parameter in delta-seconds.
*/
public static final int MAX_RETRY_AFTER = 24 * 60 * 60; // 24h
/**
* The maximum number of redirects.
*/
public static final int MAX_REDIRECTS = 5; // can't be more than 7.
/**
* The time between a failure and the first retry after an IOException.
* Each subsequent retry grows exponentially, doubling each time.
* The time is in seconds.
*/
public static final int RETRY_FIRST_DELAY = 30;
/** Enable separate connectivity logging */
public static final boolean LOGX = true;
/** Enable verbose logging */
public static final boolean LOGV = false;
/** Enable super-verbose logging */
private static final boolean LOCAL_LOGVV = false;
public static final boolean LOGVV = LOCAL_LOGVV && LOGV;
/**
* This download has successfully completed.
* Warning: there might be other status values that indicate success
* in the future.
* Use isSucccess() to capture the entire category.
*/
public static final int STATUS_SUCCESS = 200;
/**
* This request couldn't be parsed. This is also used when processing
* requests with unknown/unsupported URI schemes.
*/
public static final int STATUS_BAD_REQUEST = 400;
/**
* This download can't be performed because the content type cannot be
* handled.
*/
public static final int STATUS_NOT_ACCEPTABLE = 406;
/**
* This download cannot be performed because the length cannot be
* determined accurately. This is the code for the HTTP error "Length
* Required", which is typically used when making requests that require
* a content length but don't have one, and it is also used in the
* client when a response is received whose length cannot be determined
* accurately (therefore making it impossible to know when a download
* completes).
*/
public static final int STATUS_LENGTH_REQUIRED = 411;
/**
* This download was interrupted and cannot be resumed.
* This is the code for the HTTP error "Precondition Failed", and it is
* also used in situations where the client doesn't have an ETag at all.
*/
public static final int STATUS_PRECONDITION_FAILED = 412;
/**
* The lowest-valued error status that is not an actual HTTP status code.
*/
public static final int MIN_ARTIFICIAL_ERROR_STATUS = 488;
/**
* The requested destination file already exists.
*/
public static final int STATUS_FILE_ALREADY_EXISTS_ERROR = 488;
/**
* Some possibly transient error occurred, but we can't resume the download.
*/
public static final int STATUS_CANNOT_RESUME = 489;
/**
* This download was canceled
*/
public static final int STATUS_CANCELED = 490;
/**
* This download has completed with an error.
* Warning: there will be other status values that indicate errors in
* the future. Use isStatusError() to capture the entire category.
*/
public static final int STATUS_UNKNOWN_ERROR = 491;
/**
* This download couldn't be completed because of a storage issue.
* Typically, that's because the filesystem is missing or full.
* Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR}
* and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.
*/
public static final int STATUS_FILE_ERROR = 492;
/**
* This download couldn't be completed because of an HTTP
* redirect response that the download manager couldn't
* handle.
*/
public static final int STATUS_UNHANDLED_REDIRECT = 493;
/**
* This download couldn't be completed because of an
* unspecified unhandled HTTP code.
*/
public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
/**
* This download couldn't be completed because of an
* error receiving or processing data at the HTTP level.
*/
public static final int STATUS_HTTP_DATA_ERROR = 495;
/**
* This download couldn't be completed because of an
* HttpException while setting up the request.
*/
public static final int STATUS_HTTP_EXCEPTION = 496;
/**
* This download couldn't be completed because there were
* too many redirects.
*/
public static final int STATUS_TOO_MANY_REDIRECTS = 497;
/**
* This download couldn't be completed due to insufficient storage
* space. Typically, this is because the SD card is full.
*/
public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
/**
* This download couldn't be completed because no external storage
* device was found. Typically, this is because the SD card is not
* mounted.
*/
public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
/**
* The wake duration to check to see if a download is possible.
*/
public static final long WATCHDOG_WAKE_TIMER = 60*1000;
/**
* The wake duration to check to see if the process was killed.
*/
public static final long ACTIVE_THREAD_WATCHDOG = 5*1000;
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (C) 2012 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.
*/
package com.google.android.vending.expansion.downloader;
import android.os.Parcel;
import android.os.Parcelable;
/**
* This class contains progress information about the active download(s).
*
* When you build the Activity that initiates a download and tracks the
* progress by implementing the {@link IDownloaderClient} interface, you'll
* receive a DownloadProgressInfo object in each call to the {@link
* IDownloaderClient#onDownloadProgress} method. This allows you to update
* your activity's UI with information about the download progress, such
* as the progress so far, time remaining and current speed.
*/
public class DownloadProgressInfo implements Parcelable {
public long mOverallTotal;
public long mOverallProgress;
public long mTimeRemaining; // time remaining
public float mCurrentSpeed; // speed in KB/S
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel p, int i) {
p.writeLong(mOverallTotal);
p.writeLong(mOverallProgress);
p.writeLong(mTimeRemaining);
p.writeFloat(mCurrentSpeed);
}
public DownloadProgressInfo(Parcel p) {
mOverallTotal = p.readLong();
mOverallProgress = p.readLong();
mTimeRemaining = p.readLong();
mCurrentSpeed = p.readFloat();
}
public DownloadProgressInfo(long overallTotal, long overallProgress,
long timeRemaining,
float currentSpeed) {
this.mOverallTotal = overallTotal;
this.mOverallProgress = overallProgress;
this.mTimeRemaining = timeRemaining;
this.mCurrentSpeed = currentSpeed;
}
public static final Creator<DownloadProgressInfo> CREATOR = new Creator<DownloadProgressInfo>() {
@Override
public DownloadProgressInfo createFromParcel(Parcel parcel) {
return new DownloadProgressInfo(parcel);
}
@Override
public DownloadProgressInfo[] newArray(int i) {
return new DownloadProgressInfo[i];
}
};
}

View file

@ -0,0 +1,277 @@
/*
* Copyright (C) 2012 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.
*/
package com.google.android.vending.expansion.downloader;
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
/**
* This class binds the service API to your application client. It contains the IDownloaderClient proxy,
* which is used to call functions in your client as well as the Stub, which is used to call functions
* in the client implementation of IDownloaderClient.
*
* <p>The IPC is implemented using an Android Messenger and a service Binder. The connect method
* should be called whenever the client wants to bind to the service. It opens up a service connection
* that ends up calling the onServiceConnected client API that passes the service messenger
* in. If the client wants to be notified by the service, it is responsible for then passing its
* messenger to the service in a separate call.
*
* <p>Critical methods are {@link #startDownloadServiceIfRequired} and {@link #CreateStub}.
*
* <p>When your application first starts, you should first check whether your app's expansion files are
* already on the device. If not, you should then call {@link #startDownloadServiceIfRequired}, which
* starts your {@link impl.DownloaderService} to download the expansion files if necessary. The method
* returns a value indicating whether download is required or not.
*
* <p>If a download is required, {@link #startDownloadServiceIfRequired} begins the download through
* the specified service and you should then call {@link #CreateStub} to instantiate a member {@link
* IStub} object that you need in order to receive calls through your {@link IDownloaderClient}
* interface.
*/
public class DownloaderClientMarshaller {
public static final int MSG_ONDOWNLOADSTATE_CHANGED = 10;
public static final int MSG_ONDOWNLOADPROGRESS = 11;
public static final int MSG_ONSERVICECONNECTED = 12;
public static final String PARAM_NEW_STATE = "newState";
public static final String PARAM_PROGRESS = "progress";
public static final String PARAM_MESSENGER = DownloaderService.EXTRA_MESSAGE_HANDLER;
public static final int NO_DOWNLOAD_REQUIRED = DownloaderService.NO_DOWNLOAD_REQUIRED;
public static final int LVL_CHECK_REQUIRED = DownloaderService.LVL_CHECK_REQUIRED;
public static final int DOWNLOAD_REQUIRED = DownloaderService.DOWNLOAD_REQUIRED;
private static class Proxy implements IDownloaderClient {
private Messenger mServiceMessenger;
@Override
public void onDownloadStateChanged(int newState) {
Bundle params = new Bundle(1);
params.putInt(PARAM_NEW_STATE, newState);
send(MSG_ONDOWNLOADSTATE_CHANGED, params);
}
@Override
public void onDownloadProgress(DownloadProgressInfo progress) {
Bundle params = new Bundle(1);
params.putParcelable(PARAM_PROGRESS, progress);
send(MSG_ONDOWNLOADPROGRESS, params);
}
private void send(int method, Bundle params) {
Message m = Message.obtain(null, method);
m.setData(params);
try {
mServiceMessenger.send(m);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public Proxy(Messenger msg) {
mServiceMessenger = msg;
}
@Override
public void onServiceConnected(Messenger m) {
/**
* This is never called through the proxy.
*/
}
}
private static class Stub implements IStub {
private IDownloaderClient mItf = null;
private Class<?> mDownloaderServiceClass;
private boolean mBound;
private Messenger mServiceMessenger;
private Context mContext;
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ONDOWNLOADPROGRESS:
Bundle bun = msg.getData();
if ( null != mContext ) {
bun.setClassLoader(mContext.getClassLoader());
DownloadProgressInfo dpi = (DownloadProgressInfo) msg.getData()
.getParcelable(PARAM_PROGRESS);
mItf.onDownloadProgress(dpi);
}
break;
case MSG_ONDOWNLOADSTATE_CHANGED:
mItf.onDownloadStateChanged(msg.getData().getInt(PARAM_NEW_STATE));
break;
case MSG_ONSERVICECONNECTED:
mItf.onServiceConnected(
(Messenger) msg.getData().getParcelable(PARAM_MESSENGER));
break;
}
}
});
public Stub(IDownloaderClient itf, Class<?> downloaderService) {
mItf = itf;
mDownloaderServiceClass = downloaderService;
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mServiceMessenger = new Messenger(service);
mItf.onServiceConnected(
mServiceMessenger);
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mServiceMessenger = null;
}
};
@Override
public void connect(Context c) {
mContext = c;
Intent bindIntent = new Intent(c, mDownloaderServiceClass);
bindIntent.putExtra(PARAM_MESSENGER, mMessenger);
if ( !c.bindService(bindIntent, mConnection, Context.BIND_DEBUG_UNBIND) ) {
if ( Constants.LOGVV ) {
Log.d(Constants.TAG, "Service Unbound");
}
} else {
mBound = true;
}
}
@Override
public void disconnect(Context c) {
if (mBound) {
c.unbindService(mConnection);
mBound = false;
}
mContext = null;
}
@Override
public Messenger getMessenger() {
return mMessenger;
}
}
/**
* Returns a proxy that will marshal calls to IDownloaderClient methods
*
* @param msg
* @return
*/
public static IDownloaderClient CreateProxy(Messenger msg) {
return new Proxy(msg);
}
/**
* Returns a stub object that, when connected, will listen for marshaled
* {@link IDownloaderClient} methods and translate them into calls to the supplied
* interface.
*
* @param itf An implementation of IDownloaderClient that will be called
* when remote method calls are unmarshaled.
* @param downloaderService The class for your implementation of {@link
* impl.DownloaderService}.
* @return The {@link IStub} that allows you to connect to the service such that
* your {@link IDownloaderClient} receives status updates.
*/
public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) {
return new Stub(itf, downloaderService);
}
/**
* Starts the download if necessary. This function starts a flow that does `
* many things. 1) Checks to see if the APK version has been checked and
* the metadata database updated 2) If the APK version does not match,
* checks the new LVL status to see if a new download is required 3) If the
* APK version does match, then checks to see if the download(s) have been
* completed 4) If the downloads have been completed, returns
* NO_DOWNLOAD_REQUIRED The idea is that this can be called during the
* startup of an application to quickly ascertain if the application needs
* to wait to hear about any updated APK expansion files. Note that this does
* mean that the application MUST be run for the first time with a network
* connection, even if Market delivers all of the files.
*
* @param context Your application Context.
* @param notificationClient A PendingIntent to start the Activity in your application
* that shows the download progress and which will also start the application when download
* completes.
* @param serviceClass the class of your {@link imp.DownloaderService} implementation
* @return whether the service was started and the reason for starting the service.
* Either {@link #NO_DOWNLOAD_REQUIRED}, {@link #LVL_CHECK_REQUIRED}, or {@link
* #DOWNLOAD_REQUIRED}.
* @throws NameNotFoundException
*/
public static int startDownloadServiceIfRequired(Context context, PendingIntent notificationClient,
Class<?> serviceClass)
throws NameNotFoundException {
return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
serviceClass);
}
/**
* This version assumes that the intent contains the pending intent as a parameter. This
* is used for responding to alarms.
* <p>The pending intent must be in an extra with the key {@link
* impl.DownloaderService#EXTRA_PENDING_INTENT}.
*
* @param context
* @param notificationClient
* @param serviceClass the class of the service to start
* @return
* @throws NameNotFoundException
*/
public static int startDownloadServiceIfRequired(Context context, Intent notificationClient,
Class<?> serviceClass)
throws NameNotFoundException {
return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
serviceClass);
}
}

View file

@ -0,0 +1,181 @@
/*
* Copyright (C) 2012 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.
*/
package com.google.android.vending.expansion.downloader;
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
/**
* This class is used by the client activity to proxy requests to the Downloader
* Service.
*
* Most importantly, you must call {@link #CreateProxy} during the {@link
* IDownloaderClient#onServiceConnected} callback in your activity in order to instantiate
* an {@link IDownloaderService} object that you can then use to issue commands to the {@link
* DownloaderService} (such as to pause and resume downloads).
*/
public class DownloaderServiceMarshaller {
public static final int MSG_REQUEST_ABORT_DOWNLOAD =
1;
public static final int MSG_REQUEST_PAUSE_DOWNLOAD =
2;
public static final int MSG_SET_DOWNLOAD_FLAGS =
3;
public static final int MSG_REQUEST_CONTINUE_DOWNLOAD =
4;
public static final int MSG_REQUEST_DOWNLOAD_STATE =
5;
public static final int MSG_REQUEST_CLIENT_UPDATE =
6;
public static final String PARAMS_FLAGS = "flags";
public static final String PARAM_MESSENGER = DownloaderService.EXTRA_MESSAGE_HANDLER;
private static class Proxy implements IDownloaderService {
private Messenger mMsg;
private void send(int method, Bundle params) {
Message m = Message.obtain(null, method);
m.setData(params);
try {
mMsg.send(m);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public Proxy(Messenger msg) {
mMsg = msg;
}
@Override
public void requestAbortDownload() {
send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
}
@Override
public void requestPauseDownload() {
send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
}
@Override
public void setDownloadFlags(int flags) {
Bundle params = new Bundle();
params.putInt(PARAMS_FLAGS, flags);
send(MSG_SET_DOWNLOAD_FLAGS, params);
}
@Override
public void requestContinueDownload() {
send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
}
@Override
public void requestDownloadStatus() {
send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
}
@Override
public void onClientUpdated(Messenger clientMessenger) {
Bundle bundle = new Bundle(1);
bundle.putParcelable(PARAM_MESSENGER, clientMessenger);
send(MSG_REQUEST_CLIENT_UPDATE, bundle);
}
}
private static class Stub implements IStub {
private IDownloaderService mItf = null;
final Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REQUEST_ABORT_DOWNLOAD:
mItf.requestAbortDownload();
break;
case MSG_REQUEST_CONTINUE_DOWNLOAD:
mItf.requestContinueDownload();
break;
case MSG_REQUEST_PAUSE_DOWNLOAD:
mItf.requestPauseDownload();
break;
case MSG_SET_DOWNLOAD_FLAGS:
mItf.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS));
break;
case MSG_REQUEST_DOWNLOAD_STATE:
mItf.requestDownloadStatus();
break;
case MSG_REQUEST_CLIENT_UPDATE:
mItf.onClientUpdated((Messenger) msg.getData().getParcelable(
PARAM_MESSENGER));
break;
}
}
});
public Stub(IDownloaderService itf) {
mItf = itf;
}
@Override
public Messenger getMessenger() {
return mMessenger;
}
@Override
public void connect(Context c) {
}
@Override
public void disconnect(Context c) {
}
}
/**
* Returns a proxy that will marshall calls to IDownloaderService methods
*
* @param ctx
* @return
*/
public static IDownloaderService CreateProxy(Messenger msg) {
return new Proxy(msg);
}
/**
* Returns a stub object that, when connected, will listen for marshalled
* IDownloaderService methods and translate them into calls to the supplied
* interface.
*
* @param itf An implementation of IDownloaderService that will be called
* when remote method calls are unmarshalled.
* @return
*/
public static IStub CreateStub(IDownloaderService itf) {
return new Stub(itf);
}
}

View file

@ -0,0 +1,306 @@
/*
* Copyright (C) 2012 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.
*/
package com.google.android.vending.expansion.downloader;
import com.android.vending.expansion.downloader.R;
import android.content.Context;
import android.os.Environment;
import android.os.StatFs;
import android.os.SystemClock;
import android.util.Log;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Some helper functions for the download manager
*/
public class Helpers {
public static Random sRandom = new Random(SystemClock.uptimeMillis());
/** Regex used to parse content-disposition headers */
private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern
.compile("attachment;\\s*filename\\s*=\\s*\"([^\"]*)\"");
private Helpers() {
}
/*
* Parse the Content-Disposition HTTP Header. The format of the header is
* defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This
* header provides a filename for content that is going to be downloaded to
* the file system. We only support the attachment type.
*/
static String parseContentDisposition(String contentDisposition) {
try {
Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
if (m.find()) {
return m.group(1);
}
} catch (IllegalStateException ex) {
// This function is defined as returning null when it can't parse
// the header
}
return null;
}
/**
* @return the root of the filesystem containing the given path
*/
public static File getFilesystemRoot(String path) {
File cache = Environment.getDownloadCacheDirectory();
if (path.startsWith(cache.getPath())) {
return cache;
}
File external = Environment.getExternalStorageDirectory();
if (path.startsWith(external.getPath())) {
return external;
}
throw new IllegalArgumentException(
"Cannot determine filesystem root for " + path);
}
public static boolean isExternalMediaMounted() {
if (!Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
// No SD card found.
if ( Constants.LOGVV ) {
Log.d(Constants.TAG, "no external storage");
}
return false;
}
return true;
}
/**
* @return the number of bytes available on the filesystem rooted at the
* given File
*/
public static long getAvailableBytes(File root) {
StatFs stat = new StatFs(root.getPath());
// put a bit of margin (in case creating the file grows the system by a
// few blocks)
long availableBlocks = (long) stat.getAvailableBlocks() - 4;
return stat.getBlockSize() * availableBlocks;
}
/**
* Checks whether the filename looks legitimate
*/
public static boolean isFilenameValid(String filename) {
filename = filename.replaceFirst("/+", "/"); // normalize leading
// slashes
return filename.startsWith(Environment.getDownloadCacheDirectory().toString())
|| filename.startsWith(Environment.getExternalStorageDirectory().toString());
}
/*
* Delete the given file from device
*/
/* package */static void deleteFile(String path) {
try {
File file = new File(path);
file.delete();
} catch (Exception e) {
Log.w(Constants.TAG, "file: '" + path + "' couldn't be deleted", e);
}
}
/**
* Showing progress in MB here. It would be nice to choose the unit (KB, MB,
* GB) based on total file size, but given what we know about the expected
* ranges of file sizes for APK expansion files, it's probably not necessary.
*
* @param overallProgress
* @param overallTotal
* @return
*/
static public String getDownloadProgressString(long overallProgress, long overallTotal) {
if (overallTotal == 0) {
if ( Constants.LOGVV ) {
Log.e(Constants.TAG, "Notification called when total is zero");
}
return "";
}
return String.format("%.2f",
(float) overallProgress / (1024.0f * 1024.0f))
+ "MB /" +
String.format("%.2f", (float) overallTotal /
(1024.0f * 1024.0f)) + "MB";
}
/**
* Adds a percentile to getDownloadProgressString.
*
* @param overallProgress
* @param overallTotal
* @return
*/
static public String getDownloadProgressStringNotification(long overallProgress,
long overallTotal) {
if (overallTotal == 0) {
if ( Constants.LOGVV ) {
Log.e(Constants.TAG, "Notification called when total is zero");
}
return "";
}
return getDownloadProgressString(overallProgress, overallTotal) + " (" +
getDownloadProgressPercent(overallProgress, overallTotal) + ")";
}
public static String getDownloadProgressPercent(long overallProgress, long overallTotal) {
if (overallTotal == 0) {
if ( Constants.LOGVV ) {
Log.e(Constants.TAG, "Notification called when total is zero");
}
return "";
}
return Long.toString(overallProgress * 100 / overallTotal) + "%";
}
public static String getSpeedString(float bytesPerMillisecond) {
return String.format("%.2f", bytesPerMillisecond * 1000 / 1024);
}
public static String getTimeRemaining(long durationInMilliseconds) {
SimpleDateFormat sdf;
if (durationInMilliseconds > 1000 * 60 * 60) {
sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());
} else {
sdf = new SimpleDateFormat("mm:ss", Locale.getDefault());
}
return sdf.format(new Date(durationInMilliseconds - TimeZone.getDefault().getRawOffset()));
}
/**
* Returns the file name (without full path) for an Expansion APK file from
* the given context.
*
* @param c the context
* @param mainFile true for main file, false for patch file
* @param versionCode the version of the file
* @return String the file name of the expansion file
*/
public static String getExpansionAPKFileName(Context c, boolean mainFile, int versionCode) {
return (mainFile ? "main." : "patch.") + versionCode + "." + c.getPackageName() + ".obb";
}
/**
* Returns the filename (where the file should be saved) from info about a
* download
*/
static public String generateSaveFileName(Context c, String fileName) {
String path = getSaveFilePath(c)
+ File.separator + fileName;
return path;
}
static public String getSaveFilePath(Context c) {
File root = Environment.getExternalStorageDirectory();
String path = root.toString() + Constants.EXP_PATH + c.getPackageName();
return path;
}
/**
* Helper function to ascertain the existence of a file and return
* true/false appropriately
*
* @param c the app/activity/service context
* @param fileName the name (sans path) of the file to query
* @param fileSize the size that the file must match
* @param deleteFileOnMismatch if the file sizes do not match, delete the
* file
* @return true if it does exist, false otherwise
*/
static public boolean doesFileExist(Context c, String fileName, long fileSize,
boolean deleteFileOnMismatch) {
// the file may have been delivered by Market --- let's make sure
// it's the size we expect
File fileForNewFile = new File(Helpers.generateSaveFileName(c, fileName));
if (fileForNewFile.exists()) {
if (fileForNewFile.length() == fileSize) {
return true;
}
if (deleteFileOnMismatch) {
// delete the file --- we won't be able to resume
// because we cannot confirm the integrity of the file
fileForNewFile.delete();
}
}
return false;
}
/**
* Converts download states that are returned by the {@link
* IDownloaderClient#onDownloadStateChanged} callback into usable strings.
* This is useful if using the state strings built into the library to display user messages.
* @param state One of the STATE_* constants from {@link IDownloaderClient}.
* @return string resource ID for the corresponding string.
*/
static public int getDownloaderStringResourceIDFromState(int state) {
switch (state) {
case IDownloaderClient.STATE_IDLE:
return R.string.state_idle;
case IDownloaderClient.STATE_FETCHING_URL:
return R.string.state_fetching_url;
case IDownloaderClient.STATE_CONNECTING:
return R.string.state_connecting;
case IDownloaderClient.STATE_DOWNLOADING:
return R.string.state_downloading;
case IDownloaderClient.STATE_COMPLETED:
return R.string.state_completed;
case IDownloaderClient.STATE_PAUSED_NETWORK_UNAVAILABLE:
return R.string.state_paused_network_unavailable;
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
return R.string.state_paused_by_request;
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
return R.string.state_paused_wifi_disabled;
case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
return R.string.state_paused_wifi_unavailable;
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED:
return R.string.state_paused_wifi_disabled;
case IDownloaderClient.STATE_PAUSED_NEED_WIFI:
return R.string.state_paused_wifi_unavailable;
case IDownloaderClient.STATE_PAUSED_ROAMING:
return R.string.state_paused_roaming;
case IDownloaderClient.STATE_PAUSED_NETWORK_SETUP_FAILURE:
return R.string.state_paused_network_setup_failure;
case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
return R.string.state_paused_sdcard_unavailable;
case IDownloaderClient.STATE_FAILED_UNLICENSED:
return R.string.state_failed_unlicensed;
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
return R.string.state_failed_fetching_url;
case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
return R.string.state_failed_sdcard_full;
case IDownloaderClient.STATE_FAILED_CANCELED:
return R.string.state_failed_cancelled;
default:
return R.string.state_unknown;
}
}
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (C) 2012 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.
*/
package com.google.android.vending.expansion.downloader;
import android.os.Messenger;
/**
* This interface should be implemented by the client activity for the
* downloader. It is used to pass status from the service to the client.
*/
public interface IDownloaderClient {
static final int STATE_IDLE = 1;
static final int STATE_FETCHING_URL = 2;
static final int STATE_CONNECTING = 3;
static final int STATE_DOWNLOADING = 4;
static final int STATE_COMPLETED = 5;
static final int STATE_PAUSED_NETWORK_UNAVAILABLE = 6;
static final int STATE_PAUSED_BY_REQUEST = 7;
/**
* Both STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION and
* STATE_PAUSED_NEED_CELLULAR_PERMISSION imply that Wi-Fi is unavailable and
* cellular permission will restart the service. Wi-Fi disabled means that
* the Wi-Fi manager is returning that Wi-Fi is not enabled, while in the
* other case Wi-Fi is enabled but not available.
*/
static final int STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION = 8;
static final int STATE_PAUSED_NEED_CELLULAR_PERMISSION = 9;
/**
* Both STATE_PAUSED_WIFI_DISABLED and STATE_PAUSED_NEED_WIFI imply that
* Wi-Fi is unavailable and cellular permission will NOT restart the
* service. Wi-Fi disabled means that the Wi-Fi manager is returning that
* Wi-Fi is not enabled, while in the other case Wi-Fi is enabled but not
* available.
* <p>
* The service does not return these values. We recommend that app
* developers with very large payloads do not allow these payloads to be
* downloaded over cellular connections.
*/
static final int STATE_PAUSED_WIFI_DISABLED = 10;
static final int STATE_PAUSED_NEED_WIFI = 11;
static final int STATE_PAUSED_ROAMING = 12;
/**
* Scary case. We were on a network that redirected us to another website
* that delivered us the wrong file.
*/
static final int STATE_PAUSED_NETWORK_SETUP_FAILURE = 13;
static final int STATE_PAUSED_SDCARD_UNAVAILABLE = 14;
static final int STATE_FAILED_UNLICENSED = 15;
static final int STATE_FAILED_FETCHING_URL = 16;
static final int STATE_FAILED_SDCARD_FULL = 17;
static final int STATE_FAILED_CANCELED = 18;
static final int STATE_FAILED = 19;
/**
* Called internally by the stub when the service is bound to the client.
* <p>
* Critical implementation detail. In onServiceConnected we create the
* remote service and marshaler. This is how we pass the client information
* back to the service so the client can be properly notified of changes. We
* must do this every time we reconnect to the service.
* <p>
* That is, when you receive this callback, you should call
* {@link DownloaderServiceMarshaller#CreateProxy} to instantiate a member
* instance of {@link IDownloaderService}, then call
* {@link IDownloaderService#onClientUpdated} with the Messenger retrieved
* from your {@link IStub} proxy object.
*
* @param m the service Messenger. This Messenger is used to call the
* service API from the client.
*/
void onServiceConnected(Messenger m);
/**
* Called when the download state changes. Depending on the state, there may
* be user requests. The service is free to change the download state in the
* middle of a user request, so the client should be able to handle this.
* <p>
* The Downloader Library includes a collection of string resources that
* correspond to each of the states, which you can use to provide users a
* useful message based on the state provided in this callback. To fetch the
* appropriate string for a state, call
* {@link Helpers#getDownloaderStringResourceIDFromState}.
* <p>
* What this means to the developer: The application has gotten a message
* that the download has paused due to lack of WiFi. The developer should
* then show UI asking the user if they want to enable downloading over
* cellular connections with appropriate warnings. If the application
* suddenly starts downloading, the application should revert to showing the
* progress again, rather than leaving up the download over cellular UI up.
*
* @param newState one of the STATE_* values defined in IDownloaderClient
*/
void onDownloadStateChanged(int newState);
/**
* Shows the download progress. This is intended to be used to fill out a
* client UI. This progress should only be shown in a few states such as
* STATE_DOWNLOADING.
*
* @param progress the DownloadProgressInfo object containing the current
* progress of all downloads.
*/
void onDownloadProgress(DownloadProgressInfo progress);
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (C) 2012 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.
*/
package com.google.android.vending.expansion.downloader;
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
import android.os.Messenger;
/**
* This interface is implemented by the DownloaderService and by the
* DownloaderServiceMarshaller. It contains functions to control the service.
* When a client binds to the service, it must call the onClientUpdated
* function.
* <p>
* You can acquire a proxy that implements this interface for your service by
* calling {@link DownloaderServiceMarshaller#CreateProxy} during the
* {@link IDownloaderClient#onServiceConnected} callback. At which point, you
* should immediately call {@link #onClientUpdated}.
*/
public interface IDownloaderService {
/**
* Set this flag in response to the
* IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION state and then
* call RequestContinueDownload to resume a download
*/
public static final int FLAGS_DOWNLOAD_OVER_CELLULAR = 1;
/**
* Request that the service abort the current download. The service should
* respond by changing the state to {@link IDownloaderClient.STATE_ABORTED}.
*/
void requestAbortDownload();
/**
* Request that the service pause the current download. The service should
* respond by changing the state to
* {@link IDownloaderClient.STATE_PAUSED_BY_REQUEST}.
*/
void requestPauseDownload();
/**
* Request that the service continue a paused download, when in any paused
* or failed state, including
* {@link IDownloaderClient.STATE_PAUSED_BY_REQUEST}.
*/
void requestContinueDownload();
/**
* Set the flags for this download (e.g.
* {@link DownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR}).
*
* @param flags
*/
void setDownloadFlags(int flags);
/**
* Requests that the download status be sent to the client.
*/
void requestDownloadStatus();
/**
* Call this when you get {@link
* IDownloaderClient.onServiceConnected(Messenger m)} from the
* DownloaderClient to register the client with the service. It will
* automatically send the current status to the client.
*
* @param clientMessenger
*/
void onClientUpdated(Messenger clientMessenger);
}

Some files were not shown because too many files have changed in this diff Show more