Merge branch 'master' into hotfix-android-unicode-ime-input
This commit is contained in:
commit
77a840e350
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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 );
|
||||
|
@ -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
55
core/func_ref.cpp
Normal 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
23
core/func_ref.h
Normal 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
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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: {
|
||||
|
@ -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);
|
||||
|
@ -120,7 +120,7 @@ class ResourceFormatSaverBinaryInstance {
|
||||
|
||||
String local_path;
|
||||
|
||||
bool no_extensions;
|
||||
|
||||
bool relative_paths;
|
||||
bool bundle_resources;
|
||||
bool skip_editor;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -74,9 +74,6 @@ public:
|
||||
FLAG_OMIT_EDITOR_PROPERTIES=8,
|
||||
FLAG_SAVE_BIG_ENDIAN=16,
|
||||
FLAG_COMPRESS=32,
|
||||
FLAG_NO_EXTENSION=64,
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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_
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
10
core/os/os.h
10
core/os/os.h
@ -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);
|
||||
|
||||
|
@ -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>();
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -56,7 +56,7 @@ String Variant::get_type_name(Variant::Type p_type) {
|
||||
} break;
|
||||
case REAL: {
|
||||
|
||||
return "real";
|
||||
return "float";
|
||||
|
||||
} break;
|
||||
case STRING: {
|
||||
|
@ -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());
|
||||
|
@ -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;\
|
||||
|
@ -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
@ -26402,7 +26402,7 @@
|
||||
<description>
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="release">
|
||||
<signal name="released">
|
||||
<description>
|
||||
</description>
|
||||
</signal>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
111
drivers/openssl/stream_peer_ssl.cpp
Normal file
111
drivers/openssl/stream_peer_ssl.cpp
Normal 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() {
|
||||
}
|
26
drivers/openssl/stream_peer_ssl.h
Normal file
26
drivers/openssl/stream_peer_ssl.h
Normal 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
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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: {
|
||||
|
||||
|
@ -77,6 +77,7 @@ public:
|
||||
LOGIC_CLAMP,
|
||||
LOGIC_NEAREST_PO2,
|
||||
OBJ_WEAKREF,
|
||||
FUNC_FUNCREF,
|
||||
TYPE_CONVERT,
|
||||
TYPE_OF,
|
||||
TEXT_STR,
|
||||
|
@ -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="";
|
||||
|
||||
|
@ -356,6 +356,7 @@ private:
|
||||
template<class T>
|
||||
T* alloc_node();
|
||||
|
||||
int parenthesis;
|
||||
bool error_set;
|
||||
String error;
|
||||
int error_line;
|
||||
|
@ -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;
|
||||
|
@ -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 ' ':
|
||||
|
@ -1,7 +0,0 @@
|
||||
Import('env')
|
||||
|
||||
env.add_source_files(env.modules_sources,"*.cpp")
|
||||
|
||||
Export('env')
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
|
||||
|
||||
def can_build(platform):
|
||||
return True
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -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;
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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'])
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
|
||||
}
|
39
platform/android/java/src/com/android/godot/utils/Crypt.java
Normal file
39
platform/android/java/src/com/android/godot/utils/Crypt.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
|
9
platform/android/libs/apk_expansion/AndroidManifest.xml
Normal file
9
platform/android/libs/apk_expansion/AndroidManifest.xml
Normal 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>
|
92
platform/android/libs/apk_expansion/build.xml
Normal file
92
platform/android/libs/apk_expansion/build.xml
Normal 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>
|
20
platform/android/libs/apk_expansion/proguard-project.txt
Normal file
20
platform/android/libs/apk_expansion/proguard-project.txt
Normal 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 *;
|
||||
#}
|
13
platform/android/libs/apk_expansion/project.properties
Normal file
13
platform/android/libs/apk_expansion/project.properties
Normal 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.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -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>
|
@ -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>
|
@ -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>
|
41
platform/android/libs/apk_expansion/res/values/strings.xml
Normal file
41
platform/android/libs/apk_expansion/res/values/strings.xml
Normal 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>
|
25
platform/android/libs/apk_expansion/res/values/styles.xml
Normal file
25
platform/android/libs/apk_expansion/res/values/styles.xml
Normal 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>
|
@ -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;
|
||||
|
||||
}
|
@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user