Removed the non-desktop platforms.
@ -1,14 +0,0 @@
|
||||
# Android platform port
|
||||
|
||||
This folder contains the Java and C++ (JNI) code for the Android platform port,
|
||||
using [Gradle](https://gradle.org/) as a build system.
|
||||
|
||||
## Artwork license
|
||||
|
||||
[`logo.png`](logo.png) and [`run_icon.png`](run_icon.png) are licensed under
|
||||
[Creative Commons Attribution 3.0 Unported](https://developer.android.com/distribute/marketing-tools/brand-guidelines#android_robot)
|
||||
per the Android logo usage guidelines:
|
||||
|
||||
> The Android robot is reproduced or modified from work created and shared by
|
||||
> Google and used according to terms described in the Creative Commons 3.0
|
||||
> Attribution License.
|
@ -1,70 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
android_files = [
|
||||
"os_android.cpp",
|
||||
"android_input_handler.cpp",
|
||||
"file_access_android.cpp",
|
||||
"file_access_filesystem_jandroid.cpp",
|
||||
"audio_driver_opensl.cpp",
|
||||
"dir_access_jandroid.cpp",
|
||||
"thread_jandroid.cpp",
|
||||
"net_socket_android.cpp",
|
||||
"java_pandemonium_lib_jni.cpp",
|
||||
"java_class_wrapper.cpp",
|
||||
"java_pandemonium_wrapper.cpp",
|
||||
"java_pandemonium_io_wrapper.cpp",
|
||||
"java_pandemonium_view_wrapper.cpp",
|
||||
"jni_utils.cpp",
|
||||
"android_keys_utils.cpp",
|
||||
"plugin/pandemonium_plugin_jni.cpp",
|
||||
]
|
||||
|
||||
env_android = env.Clone()
|
||||
|
||||
android_objects = []
|
||||
for x in android_files:
|
||||
android_objects.append(env_android.SharedObject(x))
|
||||
|
||||
env_thirdparty = env_android.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
thirdparty_obj = env_thirdparty.SharedObject("#thirdparty/misc/ifaddrs-android.cc")
|
||||
android_objects.append(thirdparty_obj)
|
||||
|
||||
lib = env_android.add_shared_library("#bin/libpandemonium", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"])
|
||||
|
||||
# Needed to force rebuilding the platform files when the thirdparty code is updated.
|
||||
env.Depends(lib, thirdparty_obj)
|
||||
|
||||
lib_arch_dir = ""
|
||||
if env["android_arch"] == "armv7":
|
||||
lib_arch_dir = "armeabi-v7a"
|
||||
elif env["android_arch"] == "arm64v8":
|
||||
lib_arch_dir = "arm64-v8a"
|
||||
elif env["android_arch"] == "x86":
|
||||
lib_arch_dir = "x86"
|
||||
elif env["android_arch"] == "x86_64":
|
||||
lib_arch_dir = "x86_64"
|
||||
else:
|
||||
print("WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin")
|
||||
|
||||
if lib_arch_dir != "":
|
||||
if env["target"] == "release":
|
||||
lib_type_dir = "release"
|
||||
elif env["target"] == "release_debug":
|
||||
lib_type_dir = "debug"
|
||||
else: # debug
|
||||
lib_type_dir = "dev"
|
||||
|
||||
lib_tools_dir = ""
|
||||
|
||||
out_dir = "#platform/android/java/lib/libs/" + lib_tools_dir + lib_type_dir + "/" + lib_arch_dir
|
||||
env_android.Command(
|
||||
out_dir + "/libpandemonium_android.so", "#bin/libpandemonium" + env["SHLIBSUFFIX"], Move("$TARGET", "$SOURCE")
|
||||
)
|
||||
|
||||
stl_lib_path = (
|
||||
str(env["ANDROID_NDK_ROOT"]) + "/sources/cxx-stl/llvm-libc++/libs/" + lib_arch_dir + "/libc++_shared.so"
|
||||
)
|
||||
env_android.Command(out_dir + "/libc++_shared.so", stl_lib_path, Copy("$TARGET", "$SOURCE"))
|
@ -1,419 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* android_input_handler.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "android_input_handler.h"
|
||||
|
||||
#include "android_keys_utils.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
void AndroidInputHandler::process_joy_event(const JoypadEvent &p_event) {
|
||||
switch (p_event.type) {
|
||||
case JOY_EVENT_BUTTON:
|
||||
input->joy_button(p_event.device, p_event.index, p_event.pressed);
|
||||
break;
|
||||
case JOY_EVENT_AXIS:
|
||||
input->joy_axis(p_event.device, p_event.index, p_event.value);
|
||||
break;
|
||||
case JOY_EVENT_HAT:
|
||||
input->joy_hat(p_event.device, p_event.hat);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_set_key_modifier_state(Ref<InputEventWithModifiers> ev) const {
|
||||
ev->set_shift(shift_mem);
|
||||
ev->set_alt(alt_mem);
|
||||
ev->set_metakey(meta_mem);
|
||||
ev->set_control(control_mem);
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_key_event(int p_scancode, int p_physical_scancode, int p_unicode, bool p_pressed) {
|
||||
Ref<InputEventKey> ev;
|
||||
ev.instance();
|
||||
|
||||
unsigned int physical_scancode = pandemonium_code_from_android_code(p_physical_scancode);
|
||||
unsigned int scancode = physical_scancode;
|
||||
if (p_scancode != 0) {
|
||||
scancode = pandemonium_code_from_unicode(p_scancode);
|
||||
}
|
||||
|
||||
switch (physical_scancode) {
|
||||
case KEY_SHIFT: {
|
||||
shift_mem = p_pressed;
|
||||
} break;
|
||||
case KEY_ALT: {
|
||||
alt_mem = p_pressed;
|
||||
} break;
|
||||
case KEY_CONTROL: {
|
||||
control_mem = p_pressed;
|
||||
} break;
|
||||
case KEY_META: {
|
||||
meta_mem = p_pressed;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ev->set_scancode(scancode);
|
||||
ev->set_physical_scancode(physical_scancode);
|
||||
ev->set_unicode(p_unicode);
|
||||
ev->set_pressed(p_pressed);
|
||||
|
||||
_set_key_modifier_state(ev);
|
||||
|
||||
if (p_physical_scancode == AKEYCODE_BACK) {
|
||||
if (MainLoop *main_loop = OS::get_singleton()->get_main_loop()) {
|
||||
main_loop->call_deferred("notification", MainLoop::NOTIFICATION_WM_GO_BACK_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
input->parse_input_event(ev);
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_cancel_all_touch() {
|
||||
_parse_all_touch(false, true);
|
||||
touch.clear();
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_parse_all_touch(bool p_pressed, bool p_canceled, bool p_double_tap) {
|
||||
if (touch.size()) {
|
||||
//end all if exist
|
||||
for (int i = 0; i < touch.size(); i++) {
|
||||
Ref<InputEventScreenTouch> ev;
|
||||
ev.instance();
|
||||
ev->set_index(touch[i].id);
|
||||
ev->set_pressed(p_pressed);
|
||||
ev->set_canceled(p_canceled);
|
||||
ev->set_position(touch[i].pos);
|
||||
ev->set_double_tap(p_double_tap);
|
||||
input->parse_input_event(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_release_all_touch() {
|
||||
_parse_all_touch(false);
|
||||
touch.clear();
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap) {
|
||||
switch (p_event) {
|
||||
case AMOTION_EVENT_ACTION_DOWN: { //gesture begin
|
||||
// Release any remaining touches or mouse event
|
||||
_release_mouse_event_info();
|
||||
_release_all_touch();
|
||||
|
||||
touch.resize(p_points.size());
|
||||
for (int i = 0; i < p_points.size(); i++) {
|
||||
touch.write[i].id = p_points[i].id;
|
||||
touch.write[i].pos = p_points[i].pos;
|
||||
}
|
||||
|
||||
//send touch
|
||||
_parse_all_touch(true, false, p_double_tap);
|
||||
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_MOVE: { //motion
|
||||
if (touch.size() != p_points.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < touch.size(); i++) {
|
||||
int idx = -1;
|
||||
for (int j = 0; j < p_points.size(); j++) {
|
||||
if (touch[i].id == p_points[j].id) {
|
||||
idx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_CONTINUE(idx == -1);
|
||||
|
||||
if (touch[i].pos == p_points[idx].pos)
|
||||
continue; //no move unncesearily
|
||||
|
||||
Ref<InputEventScreenDrag> ev;
|
||||
ev.instance();
|
||||
ev->set_index(touch[i].id);
|
||||
ev->set_position(p_points[idx].pos);
|
||||
ev->set_relative(p_points[idx].pos - touch[i].pos);
|
||||
input->parse_input_event(ev);
|
||||
touch.write[i].pos = p_points[idx].pos;
|
||||
}
|
||||
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_CANCEL: {
|
||||
_cancel_all_touch();
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_UP: { //release
|
||||
_release_all_touch();
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_POINTER_DOWN: { // add touch
|
||||
for (int i = 0; i < p_points.size(); i++) {
|
||||
if (p_points[i].id == p_pointer) {
|
||||
TouchPos tp = p_points[i];
|
||||
touch.push_back(tp);
|
||||
|
||||
Ref<InputEventScreenTouch> ev;
|
||||
ev.instance();
|
||||
|
||||
ev->set_index(tp.id);
|
||||
ev->set_pressed(true);
|
||||
ev->set_position(tp.pos);
|
||||
input->parse_input_event(ev);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_POINTER_UP: { // remove touch
|
||||
for (int i = 0; i < touch.size(); i++) {
|
||||
if (touch[i].id == p_pointer) {
|
||||
Ref<InputEventScreenTouch> ev;
|
||||
ev.instance();
|
||||
ev->set_index(touch[i].id);
|
||||
ev->set_pressed(false);
|
||||
ev->set_position(touch[i].pos);
|
||||
input->parse_input_event(ev);
|
||||
touch.remove(i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_cancel_mouse_event_info(bool p_source_mouse_relative) {
|
||||
buttons_state = 0;
|
||||
_parse_mouse_event_info(0, false, true, false, p_source_mouse_relative);
|
||||
mouse_event_info.valid = false;
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_parse_mouse_event_info(int buttons_mask, bool p_pressed, bool p_canceled, bool p_double_click, bool p_source_mouse_relative) {
|
||||
if (!mouse_event_info.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseButton> ev;
|
||||
ev.instance();
|
||||
_set_key_modifier_state(ev);
|
||||
|
||||
if (p_source_mouse_relative) {
|
||||
ev->set_position(hover_prev_pos);
|
||||
ev->set_global_position(hover_prev_pos);
|
||||
} else {
|
||||
ev->set_position(mouse_event_info.pos);
|
||||
ev->set_global_position(mouse_event_info.pos);
|
||||
hover_prev_pos = mouse_event_info.pos;
|
||||
}
|
||||
|
||||
ev->set_pressed(p_pressed);
|
||||
ev->set_canceled(p_canceled);
|
||||
int changed_button_mask = buttons_state ^ buttons_mask;
|
||||
|
||||
buttons_state = buttons_mask;
|
||||
|
||||
ev->set_button_index(_button_index_from_mask(changed_button_mask));
|
||||
ev->set_button_mask(buttons_mask);
|
||||
ev->set_doubleclick(p_double_click);
|
||||
input->parse_input_event(ev);
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
|
||||
_parse_mouse_event_info(0, false, false, false, p_source_mouse_relative);
|
||||
mouse_event_info.valid = false;
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative) {
|
||||
int event_buttons_mask = _android_button_mask_to_pandemonium_button_mask(p_event_android_buttons_mask);
|
||||
switch (p_event_action) {
|
||||
case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
|
||||
case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
|
||||
case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit
|
||||
// https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
|
||||
Ref<InputEventMouseMotion> ev;
|
||||
ev.instance();
|
||||
_set_key_modifier_state(ev);
|
||||
ev->set_position(p_event_pos);
|
||||
ev->set_global_position(p_event_pos);
|
||||
ev->set_relative(p_event_pos - hover_prev_pos);
|
||||
input->parse_input_event(ev);
|
||||
hover_prev_pos = p_event_pos;
|
||||
} break;
|
||||
case AMOTION_EVENT_ACTION_DOWN:
|
||||
case AMOTION_EVENT_ACTION_BUTTON_PRESS: {
|
||||
// Release any remaining touches or mouse event
|
||||
_release_mouse_event_info();
|
||||
_release_all_touch();
|
||||
|
||||
mouse_event_info.valid = true;
|
||||
mouse_event_info.pos = p_event_pos;
|
||||
_parse_mouse_event_info(event_buttons_mask, true, false, p_double_click, p_source_mouse_relative);
|
||||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_CANCEL: {
|
||||
_cancel_mouse_event_info(p_source_mouse_relative);
|
||||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_UP:
|
||||
case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
|
||||
_release_mouse_event_info(p_source_mouse_relative);
|
||||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_MOVE: {
|
||||
if (!mouse_event_info.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> ev;
|
||||
ev.instance();
|
||||
_set_key_modifier_state(ev);
|
||||
|
||||
if (p_source_mouse_relative) {
|
||||
ev->set_position(hover_prev_pos);
|
||||
ev->set_global_position(hover_prev_pos);
|
||||
ev->set_relative(p_event_pos);
|
||||
} else {
|
||||
ev->set_position(p_event_pos);
|
||||
ev->set_global_position(p_event_pos);
|
||||
ev->set_relative(p_event_pos - hover_prev_pos);
|
||||
mouse_event_info.pos = p_event_pos;
|
||||
hover_prev_pos = p_event_pos;
|
||||
}
|
||||
|
||||
ev->set_button_mask(event_buttons_mask);
|
||||
input->parse_input_event(ev);
|
||||
} break;
|
||||
|
||||
case AMOTION_EVENT_ACTION_SCROLL: {
|
||||
Ref<InputEventMouseButton> ev;
|
||||
ev.instance();
|
||||
_set_key_modifier_state(ev);
|
||||
|
||||
if (p_source_mouse_relative) {
|
||||
ev->set_position(hover_prev_pos);
|
||||
ev->set_global_position(hover_prev_pos);
|
||||
} else {
|
||||
ev->set_position(p_event_pos);
|
||||
ev->set_global_position(p_event_pos);
|
||||
}
|
||||
|
||||
ev->set_pressed(true);
|
||||
buttons_state = event_buttons_mask;
|
||||
if (p_delta.y > 0) {
|
||||
_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_UP, p_delta.y);
|
||||
} else if (p_delta.y < 0) {
|
||||
_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_DOWN, -p_delta.y);
|
||||
}
|
||||
|
||||
if (p_delta.x > 0) {
|
||||
_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_RIGHT, p_delta.x);
|
||||
} else if (p_delta.x < 0) {
|
||||
_wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_LEFT, -p_delta.x);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidInputHandler::_wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor) {
|
||||
Ref<InputEventMouseButton> evd = ev->duplicate();
|
||||
evd->set_button_index(wheel_button);
|
||||
evd->set_button_mask(event_buttons_mask ^ (1 << (wheel_button - 1)));
|
||||
evd->set_factor(factor);
|
||||
input->parse_input_event(evd);
|
||||
Ref<InputEventMouseButton> evdd = evd->duplicate();
|
||||
evdd->set_pressed(false);
|
||||
evdd->set_button_mask(event_buttons_mask);
|
||||
input->parse_input_event(evdd);
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_magnify(Point2 p_pos, float p_factor) {
|
||||
Ref<InputEventMagnifyGesture> magnify_event;
|
||||
magnify_event.instance();
|
||||
_set_key_modifier_state(magnify_event);
|
||||
magnify_event->set_position(p_pos);
|
||||
magnify_event->set_factor(p_factor);
|
||||
input->parse_input_event(magnify_event);
|
||||
}
|
||||
|
||||
void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
|
||||
Ref<InputEventPanGesture> pan_event;
|
||||
pan_event.instance();
|
||||
_set_key_modifier_state(pan_event);
|
||||
pan_event->set_position(p_pos);
|
||||
pan_event->set_delta(p_delta);
|
||||
input->parse_input_event(pan_event);
|
||||
}
|
||||
|
||||
int AndroidInputHandler::_button_index_from_mask(int button_mask) {
|
||||
switch (button_mask) {
|
||||
case BUTTON_MASK_LEFT:
|
||||
return BUTTON_LEFT;
|
||||
case BUTTON_MASK_RIGHT:
|
||||
return BUTTON_RIGHT;
|
||||
case BUTTON_MASK_MIDDLE:
|
||||
return BUTTON_MIDDLE;
|
||||
case BUTTON_MASK_XBUTTON1:
|
||||
return BUTTON_XBUTTON1;
|
||||
case BUTTON_MASK_XBUTTON2:
|
||||
return BUTTON_XBUTTON2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int AndroidInputHandler::_android_button_mask_to_pandemonium_button_mask(int android_button_mask) {
|
||||
int pandemonium_button_mask = 0;
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) {
|
||||
pandemonium_button_mask |= BUTTON_MASK_LEFT;
|
||||
}
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
|
||||
pandemonium_button_mask |= BUTTON_MASK_RIGHT;
|
||||
}
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) {
|
||||
pandemonium_button_mask |= BUTTON_MASK_MIDDLE;
|
||||
}
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) {
|
||||
pandemonium_button_mask |= BUTTON_MASK_XBUTTON1;
|
||||
}
|
||||
if (android_button_mask & AMOTION_EVENT_BUTTON_FORWARD) {
|
||||
pandemonium_button_mask |= BUTTON_MASK_XBUTTON2;
|
||||
}
|
||||
|
||||
return pandemonium_button_mask;
|
||||
}
|
||||
|
||||
void AndroidInputHandler::joy_connection_changed(int p_device, bool p_connected, String p_name) {
|
||||
input->joy_connection_changed(p_device, p_connected, p_name, "");
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
#ifndef ANDROID_INPUT_HANDLER_H
|
||||
#define ANDROID_INPUT_HANDLER_H
|
||||
/*************************************************************************/
|
||||
/* android_input_handler.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "main/input_default.h"
|
||||
|
||||
// This class encapsulates all the handling of input events that come from the Android UI thread.
|
||||
// Remarks:
|
||||
// - It's not thread-safe by itself, so its functions must only be called on a single thread, which is the Android UI thread.
|
||||
// - Its functions must only call thread-safe methods.
|
||||
class AndroidInputHandler {
|
||||
public:
|
||||
struct TouchPos {
|
||||
int id;
|
||||
Point2 pos;
|
||||
};
|
||||
|
||||
struct MouseEventInfo {
|
||||
bool valid = false;
|
||||
Point2 pos;
|
||||
};
|
||||
|
||||
enum {
|
||||
JOY_EVENT_BUTTON = 0,
|
||||
JOY_EVENT_AXIS = 1,
|
||||
JOY_EVENT_HAT = 2
|
||||
};
|
||||
|
||||
struct JoypadEvent {
|
||||
int device;
|
||||
int type;
|
||||
int index;
|
||||
bool pressed;
|
||||
float value;
|
||||
int hat;
|
||||
};
|
||||
|
||||
private:
|
||||
Vector<TouchPos> touch;
|
||||
MouseEventInfo mouse_event_info;
|
||||
Point2 hover_prev_pos; // needed to calculate the relative position on hover events
|
||||
|
||||
bool alt_mem = false;
|
||||
bool shift_mem = false;
|
||||
bool control_mem = false;
|
||||
bool meta_mem = false;
|
||||
|
||||
int buttons_state = 0;
|
||||
|
||||
InputDefault *input = static_cast<InputDefault *>(InputDefault::get_singleton());
|
||||
|
||||
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev) const;
|
||||
|
||||
static int _button_index_from_mask(int button_mask);
|
||||
|
||||
static int _android_button_mask_to_pandemonium_button_mask(int android_button_mask);
|
||||
|
||||
void _wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor);
|
||||
|
||||
void _parse_mouse_event_info(int buttons_mask, bool p_pressed, bool p_canceled, bool p_double_click, bool p_source_mouse_relative);
|
||||
|
||||
void _release_mouse_event_info(bool p_source_mouse_relative = false);
|
||||
|
||||
void _cancel_mouse_event_info(bool p_source_mouse_relative = false);
|
||||
|
||||
void _parse_all_touch(bool p_pressed, bool p_canceled = false, bool p_double_tap = false);
|
||||
|
||||
void _release_all_touch();
|
||||
|
||||
void _cancel_all_touch();
|
||||
|
||||
public:
|
||||
void process_joy_event(const JoypadEvent &p_event);
|
||||
void process_key_event(int p_scancode, int p_physical_scancode, int p_unicode, bool p_pressed);
|
||||
void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative);
|
||||
void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap);
|
||||
void process_magnify(Point2 p_pos, float p_factor);
|
||||
void process_pan(Point2 p_pos, Vector2 p_delta);
|
||||
void joy_connection_changed(int p_device, bool p_connected, String p_name);
|
||||
};
|
||||
|
||||
#endif
|
@ -1,79 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* android_keys_utils.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "android_keys_utils.h"
|
||||
|
||||
unsigned int pandemonium_code_from_android_code(unsigned int p_code) {
|
||||
for (int i = 0; android_pandemonium_code_pairs[i].android_code != AKEYCODE_MAX; i++) {
|
||||
if (android_pandemonium_code_pairs[i].android_code == p_code) {
|
||||
return android_pandemonium_code_pairs[i].pandemonium_code;
|
||||
}
|
||||
}
|
||||
|
||||
return KEY_UNKNOWN;
|
||||
}
|
||||
|
||||
unsigned int pandemonium_code_from_unicode(unsigned int p_code) {
|
||||
unsigned int code = p_code;
|
||||
if (code > 0xFF) {
|
||||
return KEY_UNKNOWN;
|
||||
}
|
||||
// Known control codes.
|
||||
if (code == '\b') { // 0x08
|
||||
return KEY_BACKSPACE;
|
||||
}
|
||||
if (code == '\t') { // 0x09
|
||||
return KEY_TAB;
|
||||
}
|
||||
if (code == '\n') { // 0x0A
|
||||
return KEY_ENTER;
|
||||
}
|
||||
if (code == 0x1B) {
|
||||
return KEY_ESCAPE;
|
||||
}
|
||||
if (code == 0x7F) {
|
||||
return KEY_DELETE;
|
||||
}
|
||||
// Unknown control codes.
|
||||
if (code <= 0x1F || (code >= 0x80 && code <= 0x9F)) {
|
||||
return KEY_UNKNOWN;
|
||||
}
|
||||
// Convert to uppercase.
|
||||
if (code >= 'a' && code <= 'z') { // 0x61 - 0x7A
|
||||
code -= ('a' - 'A');
|
||||
}
|
||||
if (code >= u'à' && code <= u'ö') { // 0xE0 - 0xF6
|
||||
code -= (u'à' - u'À'); // 0xE0 - 0xC0
|
||||
}
|
||||
if (code >= u'ø' && code <= u'þ') { // 0xF8 - 0xFF
|
||||
code -= (u'ø' - u'Ø'); // 0xF8 - 0xD8
|
||||
}
|
||||
return code;
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
#ifndef ANDROID_KEYS_UTILS_H
|
||||
#define ANDROID_KEYS_UTILS_H
|
||||
/*************************************************************************/
|
||||
/* android_keys_utils.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include <android/input.h>
|
||||
#include <core/os/keyboard.h>
|
||||
|
||||
#define AKEYCODE_MAX 0xFFFF
|
||||
|
||||
struct AndroidPandemoniumCodePair {
|
||||
unsigned int android_code = 0;
|
||||
unsigned int pandemonium_code = 0;
|
||||
};
|
||||
|
||||
static AndroidPandemoniumCodePair android_pandemonium_code_pairs[] = {
|
||||
{ AKEYCODE_UNKNOWN, KEY_UNKNOWN }, // (0) Unknown key code.
|
||||
{ AKEYCODE_BACK, KEY_BACK }, // (4) Back key.
|
||||
{ AKEYCODE_0, KEY_0 }, // (7) '0' key.
|
||||
{ AKEYCODE_1, KEY_1 }, // (8) '1' key.
|
||||
{ AKEYCODE_2, KEY_2 }, // (9) '2' key.
|
||||
{ AKEYCODE_3, KEY_3 }, // (10) '3' key.
|
||||
{ AKEYCODE_4, KEY_4 }, // (11) '4' key.
|
||||
{ AKEYCODE_5, KEY_5 }, // (12) '5' key.
|
||||
{ AKEYCODE_6, KEY_6 }, // (13) '6' key.
|
||||
{ AKEYCODE_7, KEY_7 }, // (14) '7' key.
|
||||
{ AKEYCODE_8, KEY_8 }, // (15) '8' key.
|
||||
{ AKEYCODE_9, KEY_9 }, // (16) '9' key.
|
||||
{ AKEYCODE_STAR, KEY_ASTERISK }, // (17) '*' key.
|
||||
{ AKEYCODE_POUND, KEY_NUMBERSIGN }, // (18) '#' key.
|
||||
{ AKEYCODE_DPAD_UP, KEY_UP }, // (19) Directional Pad Up key.
|
||||
{ AKEYCODE_DPAD_DOWN, KEY_DOWN }, // (20) Directional Pad Down key.
|
||||
{ AKEYCODE_DPAD_LEFT, KEY_LEFT }, // (21) Directional Pad Left key.
|
||||
{ AKEYCODE_DPAD_RIGHT, KEY_RIGHT }, // (22) Directional Pad Right key.
|
||||
{ AKEYCODE_DPAD_CENTER, KEY_ENTER }, // (23) Directional Pad Center key.
|
||||
{ AKEYCODE_VOLUME_UP, KEY_VOLUMEUP }, // (24) Volume Up key.
|
||||
{ AKEYCODE_VOLUME_DOWN, KEY_VOLUMEDOWN }, // (25) Volume Down key.
|
||||
{ AKEYCODE_POWER, KEY_STANDBY }, // (26) Power key.
|
||||
{ AKEYCODE_CLEAR, KEY_CLEAR }, // (28) Clear key.
|
||||
{ AKEYCODE_A, KEY_A }, // (29) 'A' key.
|
||||
{ AKEYCODE_B, KEY_B }, // (30) 'B' key.
|
||||
{ AKEYCODE_C, KEY_C }, // (31) 'C' key.
|
||||
{ AKEYCODE_D, KEY_D }, // (32) 'D' key.
|
||||
{ AKEYCODE_E, KEY_E }, // (33) 'E' key.
|
||||
{ AKEYCODE_F, KEY_F }, // (34) 'F' key.
|
||||
{ AKEYCODE_G, KEY_G }, // (35) 'G' key.
|
||||
{ AKEYCODE_H, KEY_H }, // (36) 'H' key.
|
||||
{ AKEYCODE_I, KEY_I }, // (37) 'I' key.
|
||||
{ AKEYCODE_J, KEY_J }, // (38) 'J' key.
|
||||
{ AKEYCODE_K, KEY_K }, // (39) 'K' key.
|
||||
{ AKEYCODE_L, KEY_L }, // (40) 'L' key.
|
||||
{ AKEYCODE_M, KEY_M }, // (41) 'M' key.
|
||||
{ AKEYCODE_N, KEY_N }, // (42) 'N' key.
|
||||
{ AKEYCODE_O, KEY_O }, // (43) 'O' key.
|
||||
{ AKEYCODE_P, KEY_P }, // (44) 'P' key.
|
||||
{ AKEYCODE_Q, KEY_Q }, // (45) 'Q' key.
|
||||
{ AKEYCODE_R, KEY_R }, // (46) 'R' key.
|
||||
{ AKEYCODE_S, KEY_S }, // (47) 'S' key.
|
||||
{ AKEYCODE_T, KEY_T }, // (48) 'T' key.
|
||||
{ AKEYCODE_U, KEY_U }, // (49) 'U' key.
|
||||
{ AKEYCODE_V, KEY_V }, // (50) 'V' key.
|
||||
{ AKEYCODE_W, KEY_W }, // (51) 'W' key.
|
||||
{ AKEYCODE_X, KEY_X }, // (52) 'X' key.
|
||||
{ AKEYCODE_Y, KEY_Y }, // (53) 'Y' key.
|
||||
{ AKEYCODE_Z, KEY_Z }, // (54) 'Z' key.
|
||||
{ AKEYCODE_COMMA, KEY_COMMA }, // (55) ',’ key.
|
||||
{ AKEYCODE_PERIOD, KEY_PERIOD }, // (56) '.' key.
|
||||
{ AKEYCODE_ALT_LEFT, KEY_ALT }, // (57) Left Alt modifier key.
|
||||
{ AKEYCODE_ALT_RIGHT, KEY_ALT }, // (58) Right Alt modifier key.
|
||||
{ AKEYCODE_SHIFT_LEFT, KEY_SHIFT }, // (59) Left Shift modifier key.
|
||||
{ AKEYCODE_SHIFT_RIGHT, KEY_SHIFT }, // (60) Right Shift modifier key.
|
||||
{ AKEYCODE_TAB, KEY_TAB }, // (61) Tab key.
|
||||
{ AKEYCODE_SPACE, KEY_SPACE }, // (62) Space key.
|
||||
{ AKEYCODE_ENVELOPE, KEY_LAUNCHMAIL }, // (65) Envelope special function key.
|
||||
{ AKEYCODE_ENTER, KEY_ENTER }, // (66) Enter key.
|
||||
{ AKEYCODE_DEL, KEY_BACKSPACE }, // (67) Backspace key.
|
||||
{ AKEYCODE_GRAVE, KEY_QUOTELEFT }, // (68) '`' (backtick) key.
|
||||
{ AKEYCODE_MINUS, KEY_MINUS }, // (69) '-'.
|
||||
{ AKEYCODE_EQUALS, KEY_EQUAL }, // (70) '=' key.
|
||||
{ AKEYCODE_LEFT_BRACKET, KEY_BRACKETLEFT }, // (71) '[' key.
|
||||
{ AKEYCODE_RIGHT_BRACKET, KEY_BRACKETRIGHT }, // (72) ']' key.
|
||||
{ AKEYCODE_BACKSLASH, KEY_BACKSLASH }, // (73) '\' key.
|
||||
{ AKEYCODE_SEMICOLON, KEY_SEMICOLON }, // (74) ';' key.
|
||||
{ AKEYCODE_APOSTROPHE, KEY_APOSTROPHE }, // (75) ''' (apostrophe) key.
|
||||
{ AKEYCODE_SLASH, KEY_SLASH }, // (76) '/' key.
|
||||
{ AKEYCODE_AT, KEY_AT }, // (77) '@' key.
|
||||
{ AKEYCODE_PLUS, KEY_PLUS }, // (81) '+' key.
|
||||
{ AKEYCODE_MENU, KEY_MENU }, // (82) Menu key.
|
||||
{ AKEYCODE_SEARCH, KEY_SEARCH }, // (84) Search key.
|
||||
{ AKEYCODE_MEDIA_STOP, KEY_MEDIASTOP }, // (86) Stop media key.
|
||||
{ AKEYCODE_MEDIA_NEXT, KEY_MEDIANEXT }, // (87) Play Next media key.
|
||||
{ AKEYCODE_MEDIA_PREVIOUS, KEY_MEDIAPREVIOUS }, // (88) Play Previous media key.
|
||||
{ AKEYCODE_PAGE_UP, KEY_PAGEUP }, // (92) Page Up key.
|
||||
{ AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN }, // (93) Page Down key.
|
||||
{ AKEYCODE_ESCAPE, KEY_ESCAPE }, // (111) Escape key.
|
||||
{ AKEYCODE_FORWARD_DEL, KEY_DELETE }, // (112) Forward Delete key.
|
||||
{ AKEYCODE_CTRL_LEFT, KEY_CONTROL }, // (113) Left Control modifier key.
|
||||
{ AKEYCODE_CTRL_RIGHT, KEY_CONTROL }, // (114) Right Control modifier key.
|
||||
{ AKEYCODE_CAPS_LOCK, KEY_CAPSLOCK }, // (115) Caps Lock key.
|
||||
{ AKEYCODE_SCROLL_LOCK, KEY_SCROLLLOCK }, // (116) Scroll Lock key.
|
||||
{ AKEYCODE_META_LEFT, KEY_META }, // (117) Left Meta modifier key.
|
||||
{ AKEYCODE_META_RIGHT, KEY_META }, // (118) Right Meta modifier key.
|
||||
{ AKEYCODE_SYSRQ, KEY_PRINT }, // (120) System Request / Print Screen key.
|
||||
{ AKEYCODE_BREAK, KEY_PAUSE }, // (121) Break / Pause key.
|
||||
{ AKEYCODE_MOVE_HOME, KEY_HOME }, // (122) Home Movement key.
|
||||
{ AKEYCODE_MOVE_END, KEY_END }, // (123) End Movement key.
|
||||
{ AKEYCODE_INSERT, KEY_INSERT }, // (124) Insert key.
|
||||
{ AKEYCODE_FORWARD, KEY_FORWARD }, // (125) Forward key.
|
||||
{ AKEYCODE_MEDIA_PLAY, KEY_MEDIAPLAY }, // (126) Play media key.
|
||||
{ AKEYCODE_MEDIA_RECORD, KEY_MEDIARECORD }, // (130) Record media key.
|
||||
{ AKEYCODE_F1, KEY_F1 }, // (131) F1 key.
|
||||
{ AKEYCODE_F2, KEY_F2 }, // (132) F2 key.
|
||||
{ AKEYCODE_F3, KEY_F3 }, // (133) F3 key.
|
||||
{ AKEYCODE_F4, KEY_F4 }, // (134) F4 key.
|
||||
{ AKEYCODE_F5, KEY_F5 }, // (135) F5 key.
|
||||
{ AKEYCODE_F6, KEY_F6 }, // (136) F6 key.
|
||||
{ AKEYCODE_F7, KEY_F7 }, // (137) F7 key.
|
||||
{ AKEYCODE_F8, KEY_F8 }, // (138) F8 key.
|
||||
{ AKEYCODE_F9, KEY_F9 }, // (139) F9 key.
|
||||
{ AKEYCODE_F10, KEY_F10 }, // (140) F10 key.
|
||||
{ AKEYCODE_F11, KEY_F11 }, // (141) F11 key.
|
||||
{ AKEYCODE_F12, KEY_F12 }, // (142) F12 key.
|
||||
{ AKEYCODE_NUM_LOCK, KEY_NUMLOCK }, // (143) Num Lock key.
|
||||
{ AKEYCODE_NUMPAD_0, KEY_KP_0 }, // (144) Numeric keypad '0' key.
|
||||
{ AKEYCODE_NUMPAD_1, KEY_KP_1 }, // (145) Numeric keypad '1' key.
|
||||
{ AKEYCODE_NUMPAD_2, KEY_KP_2 }, // (146) Numeric keypad '2' key.
|
||||
{ AKEYCODE_NUMPAD_3, KEY_KP_3 }, // (147) Numeric keypad '3' key.
|
||||
{ AKEYCODE_NUMPAD_4, KEY_KP_4 }, // (148) Numeric keypad '4' key.
|
||||
{ AKEYCODE_NUMPAD_5, KEY_KP_5 }, // (149) Numeric keypad '5' key.
|
||||
{ AKEYCODE_NUMPAD_6, KEY_KP_6 }, // (150) Numeric keypad '6' key.
|
||||
{ AKEYCODE_NUMPAD_7, KEY_KP_7 }, // (151) Numeric keypad '7' key.
|
||||
{ AKEYCODE_NUMPAD_8, KEY_KP_8 }, // (152) Numeric keypad '8' key.
|
||||
{ AKEYCODE_NUMPAD_9, KEY_KP_9 }, // (153) Numeric keypad '9' key.
|
||||
{ AKEYCODE_NUMPAD_DIVIDE, KEY_KP_DIVIDE }, // (154) Numeric keypad '/' key (for division).
|
||||
{ AKEYCODE_NUMPAD_MULTIPLY, KEY_KP_MULTIPLY }, // (155) Numeric keypad '*' key (for multiplication).
|
||||
{ AKEYCODE_NUMPAD_SUBTRACT, KEY_KP_SUBTRACT }, // (156) Numeric keypad '-' key (for subtraction).
|
||||
{ AKEYCODE_NUMPAD_ADD, KEY_KP_ADD }, // (157) Numeric keypad '+' key (for addition).
|
||||
{ AKEYCODE_NUMPAD_DOT, KEY_KP_PERIOD }, // (158) Numeric keypad '.' key (for decimals or digit grouping).
|
||||
{ AKEYCODE_NUMPAD_ENTER, KEY_KP_ENTER }, // (160) Numeric keypad Enter key.
|
||||
{ AKEYCODE_VOLUME_MUTE, KEY_VOLUMEMUTE }, // (164) Volume Mute key.
|
||||
{ AKEYCODE_YEN, KEY_YEN }, // (216) Japanese Yen key.
|
||||
{ AKEYCODE_HELP, KEY_HELP }, // (259) Help key.
|
||||
{ AKEYCODE_REFRESH, KEY_REFRESH }, // (285) Refresh key.
|
||||
{ AKEYCODE_MAX, KEY_UNKNOWN }
|
||||
};
|
||||
|
||||
unsigned int pandemonium_code_from_android_code(unsigned int p_code);
|
||||
unsigned int pandemonium_code_from_unicode(unsigned int p_code);
|
||||
|
||||
#endif // ANDROID_KEYS_UTILS_H
|
@ -1,88 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* api.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "api.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "java_class_wrapper.h"
|
||||
#include "jni_singleton.h"
|
||||
|
||||
#if !defined(ANDROID_ENABLED)
|
||||
static JavaClassWrapper *java_class_wrapper = nullptr;
|
||||
#endif
|
||||
|
||||
void register_android_api() {
|
||||
#if !defined(ANDROID_ENABLED)
|
||||
// On Android platforms, the `java_class_wrapper` instantiation and the
|
||||
// `JNISingleton` registration occurs in
|
||||
// `platform/android/java_pandemonium_lib_jni.cpp#Java_org_pandemoniumengine_pandemonium_PandemoniumLib_setup`
|
||||
java_class_wrapper = memnew(JavaClassWrapper); // Dummy
|
||||
ClassDB::register_class<JNISingleton>();
|
||||
#endif
|
||||
|
||||
ClassDB::register_class<JavaClass>();
|
||||
ClassDB::register_class<JavaClassWrapper>();
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("JavaClassWrapper", JavaClassWrapper::get_singleton()));
|
||||
}
|
||||
|
||||
void unregister_android_api() {
|
||||
#if !defined(ANDROID_ENABLED)
|
||||
memdelete(java_class_wrapper);
|
||||
#endif
|
||||
}
|
||||
|
||||
void JavaClassWrapper::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("wrap", "name"), &JavaClassWrapper::wrap);
|
||||
}
|
||||
|
||||
#if !defined(ANDROID_ENABLED)
|
||||
|
||||
Variant JavaClass::call(const StringName &, const Variant **, int, Variant::CallError &) {
|
||||
return Variant();
|
||||
}
|
||||
|
||||
JavaClass::JavaClass() {
|
||||
}
|
||||
|
||||
Variant JavaObject::call(const StringName &, const Variant **, int, Variant::CallError &) {
|
||||
return Variant();
|
||||
}
|
||||
|
||||
JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
|
||||
|
||||
Ref<JavaClass> JavaClassWrapper::wrap(const String &) {
|
||||
return Ref<JavaClass>();
|
||||
}
|
||||
|
||||
JavaClassWrapper::JavaClassWrapper() {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,36 +0,0 @@
|
||||
#ifndef ANDROID_API_H
|
||||
#define ANDROID_API_H
|
||||
/*************************************************************************/
|
||||
/* api.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
void register_android_api();
|
||||
void unregister_android_api();
|
||||
|
||||
#endif // ANDROID_API_H
|
@ -1,254 +0,0 @@
|
||||
#ifndef JAVA_CLASS_WRAPPER_H
|
||||
#define JAVA_CLASS_WRAPPER_H
|
||||
/*************************************************************************/
|
||||
/* java_class_wrapper.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "core/object/reference.h"
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
class JavaObject;
|
||||
#endif
|
||||
|
||||
class JavaClass : public Reference {
|
||||
GDCLASS(JavaClass, Reference);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
enum ArgumentType{
|
||||
|
||||
ARG_TYPE_VOID,
|
||||
ARG_TYPE_BOOLEAN,
|
||||
ARG_TYPE_BYTE,
|
||||
ARG_TYPE_CHAR,
|
||||
ARG_TYPE_SHORT,
|
||||
ARG_TYPE_INT,
|
||||
ARG_TYPE_LONG,
|
||||
ARG_TYPE_FLOAT,
|
||||
ARG_TYPE_DOUBLE,
|
||||
ARG_TYPE_STRING, //special case
|
||||
ARG_TYPE_CLASS,
|
||||
ARG_ARRAY_BIT = 1 << 16,
|
||||
ARG_NUMBER_CLASS_BIT = 1 << 17,
|
||||
ARG_TYPE_MASK = (1 << 16) - 1
|
||||
};
|
||||
|
||||
RBMap<StringName, Variant> constant_map;
|
||||
|
||||
struct MethodInfo {
|
||||
bool _static;
|
||||
Vector<uint32_t> param_types;
|
||||
Vector<StringName> param_sigs;
|
||||
uint32_t return_type;
|
||||
jmethodID method;
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ static void _convert_to_variant_type(int p_sig, Variant::Type &r_type, float &likelihood) {
|
||||
likelihood = 1.0;
|
||||
r_type = Variant::NIL;
|
||||
|
||||
switch (p_sig) {
|
||||
case ARG_TYPE_VOID:
|
||||
r_type = Variant::NIL;
|
||||
break;
|
||||
case ARG_TYPE_BOOLEAN | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_BOOLEAN:
|
||||
r_type = Variant::BOOL;
|
||||
break;
|
||||
case ARG_TYPE_BYTE | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_BYTE:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 0.1;
|
||||
break;
|
||||
case ARG_TYPE_CHAR | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_CHAR:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 0.2;
|
||||
break;
|
||||
case ARG_TYPE_SHORT | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_SHORT:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 0.3;
|
||||
break;
|
||||
case ARG_TYPE_INT | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_INT:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_TYPE_LONG | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_LONG:
|
||||
r_type = Variant::INT;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_TYPE_FLOAT | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_FLOAT:
|
||||
r_type = Variant::REAL;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_TYPE_DOUBLE | ARG_NUMBER_CLASS_BIT:
|
||||
case ARG_TYPE_DOUBLE:
|
||||
r_type = Variant::REAL;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_TYPE_STRING:
|
||||
r_type = Variant::STRING;
|
||||
break;
|
||||
case ARG_TYPE_CLASS:
|
||||
r_type = Variant::OBJECT;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_VOID:
|
||||
r_type = Variant::NIL;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN:
|
||||
r_type = Variant::ARRAY;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_BYTE:
|
||||
r_type = Variant::POOL_BYTE_ARRAY;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CHAR:
|
||||
r_type = Variant::POOL_BYTE_ARRAY;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_SHORT:
|
||||
r_type = Variant::POOL_INT_ARRAY;
|
||||
likelihood = 0.3;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_INT:
|
||||
r_type = Variant::POOL_INT_ARRAY;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_LONG:
|
||||
r_type = Variant::POOL_INT_ARRAY;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_FLOAT:
|
||||
r_type = Variant::POOL_REAL_ARRAY;
|
||||
likelihood = 1.0;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE:
|
||||
r_type = Variant::POOL_REAL_ARRAY;
|
||||
likelihood = 0.5;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_STRING:
|
||||
r_type = Variant::POOL_STRING_ARRAY;
|
||||
break;
|
||||
case ARG_ARRAY_BIT | ARG_TYPE_CLASS:
|
||||
r_type = Variant::ARRAY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ static bool _convert_object_to_variant(JNIEnv *env, jobject obj, Variant &var, uint32_t p_sig);
|
||||
|
||||
bool _call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error, Variant &ret);
|
||||
|
||||
friend class JavaClassWrapper;
|
||||
RBMap<StringName, List<MethodInfo>> methods;
|
||||
jclass _class;
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
|
||||
|
||||
JavaClass();
|
||||
};
|
||||
|
||||
class JavaObject : public Reference {
|
||||
GDCLASS(JavaObject, Reference);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
Ref<JavaClass> base_class;
|
||||
friend class JavaClass;
|
||||
|
||||
jobject instance;
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
JavaObject(const Ref<JavaClass> &p_base, jobject *p_instance);
|
||||
~JavaObject();
|
||||
#endif
|
||||
};
|
||||
|
||||
class JavaClassWrapper : public Object {
|
||||
GDCLASS(JavaClassWrapper, Object);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
RBMap<String, Ref<JavaClass>> class_cache;
|
||||
friend class JavaClass;
|
||||
jclass activityClass;
|
||||
jmethodID findClass;
|
||||
jmethodID getDeclaredMethods;
|
||||
jmethodID getFields;
|
||||
jmethodID getParameterTypes;
|
||||
jmethodID getReturnType;
|
||||
jmethodID getModifiers;
|
||||
jmethodID getName;
|
||||
jmethodID Class_getName;
|
||||
jmethodID Field_getName;
|
||||
jmethodID Field_getModifiers;
|
||||
jmethodID Field_get;
|
||||
jmethodID Boolean_booleanValue;
|
||||
jmethodID Byte_byteValue;
|
||||
jmethodID Character_characterValue;
|
||||
jmethodID Short_shortValue;
|
||||
jmethodID Integer_integerValue;
|
||||
jmethodID Long_longValue;
|
||||
jmethodID Float_floatValue;
|
||||
jmethodID Double_doubleValue;
|
||||
jobject classLoader;
|
||||
|
||||
bool _get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, String &strsig);
|
||||
#endif
|
||||
|
||||
static JavaClassWrapper *singleton;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static JavaClassWrapper *get_singleton() { return singleton; }
|
||||
|
||||
Ref<JavaClass> wrap(const String &p_class);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
JavaClassWrapper(jobject p_activity = NULL);
|
||||
#else
|
||||
JavaClassWrapper();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // JAVA_CLASS_WRAPPER_H
|
@ -1,221 +0,0 @@
|
||||
#ifndef JNI_SINGLETON_H
|
||||
#define JNI_SINGLETON_H
|
||||
/*************************************************************************/
|
||||
/* jni_singleton.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include <core/config/engine.h>
|
||||
#include <core/variant/variant.h>
|
||||
#ifdef ANDROID_ENABLED
|
||||
#include <platform/android/jni_utils.h>
|
||||
#endif
|
||||
|
||||
class JNISingleton : public Object {
|
||||
GDCLASS(JNISingleton, Object);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
struct MethodData {
|
||||
jmethodID method;
|
||||
Variant::Type ret_type;
|
||||
Vector<Variant::Type> argtypes;
|
||||
};
|
||||
|
||||
jobject instance;
|
||||
RBMap<StringName, MethodData> method_map;
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
#ifdef ANDROID_ENABLED
|
||||
RBMap<StringName, MethodData>::Element *E = method_map.find(p_method);
|
||||
|
||||
// Check the method we're looking for is in the JNISingleton map and that
|
||||
// the arguments match.
|
||||
bool call_error = !E || E->get().argtypes.size() != p_argcount;
|
||||
if (!call_error) {
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
|
||||
call_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (call_error) {
|
||||
// The method is not in this map, defaulting to the regular instance calls.
|
||||
return Object::call(p_method, p_args, p_argcount, r_error);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(!instance, Variant());
|
||||
|
||||
r_error.error = Variant::CallError::CALL_OK;
|
||||
|
||||
jvalue *v = NULL;
|
||||
|
||||
if (p_argcount) {
|
||||
v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
|
||||
}
|
||||
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
||||
int res = env->PushLocalFrame(16);
|
||||
|
||||
ERR_FAIL_COND_V(res != 0, Variant());
|
||||
|
||||
List<jobject> to_erase;
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]);
|
||||
v[i] = vr.val;
|
||||
if (vr.obj)
|
||||
to_erase.push_back(vr.obj);
|
||||
}
|
||||
|
||||
Variant ret;
|
||||
|
||||
switch (E->get().ret_type) {
|
||||
case Variant::NIL: {
|
||||
env->CallVoidMethodA(instance, E->get().method, v);
|
||||
} break;
|
||||
case Variant::BOOL: {
|
||||
ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE;
|
||||
} break;
|
||||
case Variant::INT: {
|
||||
ret = env->CallIntMethodA(instance, E->get().method, v);
|
||||
} break;
|
||||
case Variant::REAL: {
|
||||
ret = env->CallFloatMethodA(instance, E->get().method, v);
|
||||
} break;
|
||||
case Variant::STRING: {
|
||||
jobject o = env->CallObjectMethodA(instance, E->get().method, v);
|
||||
ret = jstring_to_string((jstring)o, env);
|
||||
env->DeleteLocalRef(o);
|
||||
} break;
|
||||
case Variant::POOL_STRING_ARRAY: {
|
||||
jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v);
|
||||
|
||||
ret = _jobject_to_variant(env, arr);
|
||||
|
||||
env->DeleteLocalRef(arr);
|
||||
} break;
|
||||
case Variant::POOL_INT_ARRAY: {
|
||||
jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v);
|
||||
|
||||
int fCount = env->GetArrayLength(arr);
|
||||
PoolVector<int> sarr;
|
||||
sarr.resize(fCount);
|
||||
|
||||
PoolVector<int>::Write w = sarr.write();
|
||||
env->GetIntArrayRegion(arr, 0, fCount, w.ptr());
|
||||
w.release();
|
||||
ret = sarr;
|
||||
env->DeleteLocalRef(arr);
|
||||
} break;
|
||||
case Variant::POOL_REAL_ARRAY: {
|
||||
jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v);
|
||||
|
||||
int fCount = env->GetArrayLength(arr);
|
||||
PoolVector<float> sarr;
|
||||
sarr.resize(fCount);
|
||||
|
||||
PoolVector<float>::Write w = sarr.write();
|
||||
env->GetFloatArrayRegion(arr, 0, fCount, w.ptr());
|
||||
w.release();
|
||||
ret = sarr;
|
||||
env->DeleteLocalRef(arr);
|
||||
} break;
|
||||
|
||||
case Variant::DICTIONARY: {
|
||||
jobject obj = env->CallObjectMethodA(instance, E->get().method, v);
|
||||
ret = _jobject_to_variant(env, obj);
|
||||
env->DeleteLocalRef(obj);
|
||||
|
||||
} break;
|
||||
default: {
|
||||
env->PopLocalFrame(NULL);
|
||||
ERR_FAIL_V(Variant());
|
||||
} break;
|
||||
}
|
||||
|
||||
while (to_erase.size()) {
|
||||
env->DeleteLocalRef(to_erase.front()->get());
|
||||
to_erase.pop_front();
|
||||
}
|
||||
|
||||
env->PopLocalFrame(NULL);
|
||||
|
||||
return ret;
|
||||
#else // ANDROID_ENABLED
|
||||
|
||||
// Defaulting to the regular instance calls.
|
||||
return Object::call(p_method, p_args, p_argcount, r_error);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
jobject get_instance() const {
|
||||
return instance;
|
||||
}
|
||||
|
||||
void set_instance(jobject p_instance) {
|
||||
instance = p_instance;
|
||||
}
|
||||
|
||||
void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
|
||||
MethodData md;
|
||||
md.method = p_method;
|
||||
md.argtypes = p_args;
|
||||
md.ret_type = p_ret_type;
|
||||
method_map[p_name] = md;
|
||||
}
|
||||
|
||||
void add_signal(const StringName &p_name, const Vector<Variant::Type> &p_args) {
|
||||
if (p_args.size() == 0)
|
||||
ADD_SIGNAL(MethodInfo(p_name));
|
||||
else if (p_args.size() == 1)
|
||||
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1")));
|
||||
else if (p_args.size() == 2)
|
||||
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2")));
|
||||
else if (p_args.size() == 3)
|
||||
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3")));
|
||||
else if (p_args.size() == 4)
|
||||
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4")));
|
||||
else if (p_args.size() == 5)
|
||||
ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4"), PropertyInfo(p_args[4], "arg5")));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
JNISingleton() {
|
||||
#ifdef ANDROID_ENABLED
|
||||
instance = NULL;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif // JNI_SINGLETON_H
|
@ -1,345 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* audio_driver_opensl.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "audio_driver_opensl.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_NUMBER_INTERFACES 3
|
||||
#define MAX_NUMBER_OUTPUT_DEVICES 6
|
||||
|
||||
/* Structure for passing information to callback function */
|
||||
|
||||
void AudioDriverOpenSL::_buffer_callback(
|
||||
SLAndroidSimpleBufferQueueItf queueItf) {
|
||||
bool mix = true;
|
||||
|
||||
if (pause) {
|
||||
mix = false;
|
||||
} else {
|
||||
mix = mutex.try_lock() == OK;
|
||||
}
|
||||
|
||||
if (mix) {
|
||||
audio_server_process(buffer_size, mixdown_buffer);
|
||||
} else {
|
||||
int32_t *src_buff = mixdown_buffer;
|
||||
for (unsigned int i = 0; i < buffer_size * 2; i++) {
|
||||
src_buff[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mix)
|
||||
mutex.unlock();
|
||||
|
||||
const int32_t *src_buff = mixdown_buffer;
|
||||
|
||||
int16_t *ptr = (int16_t *)buffers[last_free];
|
||||
last_free = (last_free + 1) % BUFFER_COUNT;
|
||||
|
||||
for (unsigned int i = 0; i < buffer_size * 2; i++) {
|
||||
ptr[i] = src_buff[i] >> 16;
|
||||
}
|
||||
|
||||
(*queueItf)->Enqueue(queueItf, ptr, 4 * buffer_size);
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::_buffer_callbacks(
|
||||
SLAndroidSimpleBufferQueueItf queueItf,
|
||||
void *pContext) {
|
||||
AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext;
|
||||
|
||||
ad->_buffer_callback(queueItf);
|
||||
}
|
||||
|
||||
AudioDriverOpenSL *AudioDriverOpenSL::s_ad = NULL;
|
||||
|
||||
const char *AudioDriverOpenSL::get_name() const {
|
||||
return "Android";
|
||||
}
|
||||
|
||||
Error AudioDriverOpenSL::init() {
|
||||
SLresult res;
|
||||
SLEngineOption EngineOption[] = {
|
||||
{ (SLuint32)SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE }
|
||||
};
|
||||
res = slCreateEngine(&sl, 1, EngineOption, 0, NULL, NULL);
|
||||
ERR_FAIL_COND_V_MSG(res != SL_RESULT_SUCCESS, ERR_INVALID_PARAMETER, "Could not initialize OpenSL.");
|
||||
|
||||
res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
|
||||
ERR_FAIL_COND_V_MSG(res != SL_RESULT_SUCCESS, ERR_INVALID_PARAMETER, "Could not realize OpenSL.");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::start() {
|
||||
active = false;
|
||||
|
||||
SLresult res;
|
||||
|
||||
buffer_size = 1024;
|
||||
|
||||
for (int i = 0; i < BUFFER_COUNT; i++) {
|
||||
buffers[i] = memnew_arr(int16_t, buffer_size * 2);
|
||||
memset(buffers[i], 0, buffer_size * 4);
|
||||
}
|
||||
|
||||
mixdown_buffer = memnew_arr(int32_t, buffer_size * 2);
|
||||
|
||||
/* Callback context for the buffer queue callback function */
|
||||
|
||||
/* Get the SL Engine Interface which is implicit */
|
||||
res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void *)&EngineItf);
|
||||
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
|
||||
{
|
||||
const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB };
|
||||
const SLboolean req[1] = { SL_BOOLEAN_FALSE };
|
||||
res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0, ids, req);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
// Realizing the Output Mix object in synchronous mode.
|
||||
res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, BUFFER_COUNT };
|
||||
//bufferQueue.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
|
||||
//bufferQueue.numBuffers = BUFFER_COUNT; /* Four buffers in our buffer queue */
|
||||
/* Setup the format of the content in the buffer queue */
|
||||
pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
pcm.numChannels = 2;
|
||||
pcm.samplesPerSec = SL_SAMPLINGRATE_44_1;
|
||||
pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||
#ifdef BIG_ENDIAN_ENABLED
|
||||
pcm.endianness = SL_BYTEORDER_BIGENDIAN;
|
||||
#else
|
||||
pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
#endif
|
||||
audioSource.pFormat = (void *)&pcm;
|
||||
audioSource.pLocator = (void *)&loc_bufq;
|
||||
|
||||
/* Setup the data sink structure */
|
||||
locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
locator_outputmix.outputMix = OutputMix;
|
||||
audioSink.pLocator = (void *)&locator_outputmix;
|
||||
audioSink.pFormat = NULL;
|
||||
/* Initialize the context for Buffer queue callbacks */
|
||||
//cntxt.pDataBase = (void*)&pcmData;
|
||||
//cntxt.pData = cntxt.pDataBase;
|
||||
//cntxt.size = sizeof(pcmData);
|
||||
|
||||
/* Create the music player */
|
||||
|
||||
{
|
||||
const SLInterfaceID ids[2] = { SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND };
|
||||
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
||||
|
||||
res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &audioSource, &audioSink, 1, ids, req);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
}
|
||||
/* Realizing the player in synchronous mode. */
|
||||
res = (*player)->Realize(player, SL_BOOLEAN_FALSE);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
/* Get seek and play interfaces */
|
||||
res = (*player)->GetInterface(player, SL_IID_PLAY, (void *)&playItf);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE,
|
||||
(void *)&bufferQueueItf);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
/* Setup to receive buffer queue event callbacks */
|
||||
res = (*bufferQueueItf)->RegisterCallback(bufferQueueItf, _buffer_callbacks, this);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
|
||||
last_free = 0;
|
||||
|
||||
//fill up buffers
|
||||
for (int i = 0; i < BUFFER_COUNT; i++) {
|
||||
/* Enqueue a few buffers to get the ball rolling */
|
||||
res = (*bufferQueueItf)->Enqueue(bufferQueueItf, buffers[i], 4 * buffer_size); /* Size given in */
|
||||
}
|
||||
|
||||
res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
|
||||
active = true;
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::_record_buffer_callback(SLAndroidSimpleBufferQueueItf queueItf) {
|
||||
for (int i = 0; i < rec_buffer.size(); i++) {
|
||||
int32_t sample = rec_buffer[i] << 16;
|
||||
input_buffer_write(sample);
|
||||
input_buffer_write(sample); // call twice to convert to Stereo
|
||||
}
|
||||
|
||||
SLresult res = (*recordBufferQueueItf)->Enqueue(recordBufferQueueItf, rec_buffer.ptrw(), rec_buffer.size() * sizeof(int16_t));
|
||||
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::_record_buffer_callbacks(SLAndroidSimpleBufferQueueItf queueItf, void *pContext) {
|
||||
AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext;
|
||||
|
||||
ad->_record_buffer_callback(queueItf);
|
||||
}
|
||||
|
||||
Error AudioDriverOpenSL::capture_init_device() {
|
||||
SLDataLocator_IODevice loc_dev = {
|
||||
SL_DATALOCATOR_IODEVICE,
|
||||
SL_IODEVICE_AUDIOINPUT,
|
||||
SL_DEFAULTDEVICEID_AUDIOINPUT,
|
||||
NULL
|
||||
};
|
||||
SLDataSource recSource = { &loc_dev, NULL };
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {
|
||||
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
2
|
||||
};
|
||||
SLDataFormat_PCM format_pcm = {
|
||||
SL_DATAFORMAT_PCM,
|
||||
1,
|
||||
SL_SAMPLINGRATE_44_1,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_SPEAKER_FRONT_CENTER,
|
||||
SL_BYTEORDER_LITTLEENDIAN
|
||||
};
|
||||
SLDataSink recSnk = { &loc_bq, &format_pcm };
|
||||
|
||||
const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
|
||||
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
||||
|
||||
SLresult res = (*EngineItf)->CreateAudioRecorder(EngineItf, &recorder, &recSource, &recSnk, 2, ids, req);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recorder)->Realize(recorder, SL_BOOLEAN_FALSE);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recorder)->GetInterface(recorder, SL_IID_RECORD, (void *)&recordItf);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recorder)->GetInterface(recorder, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, (void *)&recordBufferQueueItf);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recordBufferQueueItf)->RegisterCallback(recordBufferQueueItf, _record_buffer_callbacks, this);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
SLuint32 state;
|
||||
res = (*recordItf)->GetRecordState(recordItf, &state);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
if (state != SL_RECORDSTATE_STOPPED) {
|
||||
res = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recordBufferQueueItf)->Clear(recordBufferQueueItf);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
}
|
||||
|
||||
const int rec_buffer_frames = 2048;
|
||||
rec_buffer.resize(rec_buffer_frames);
|
||||
input_buffer_init(rec_buffer_frames);
|
||||
|
||||
res = (*recordBufferQueueItf)->Enqueue(recordBufferQueueItf, rec_buffer.ptrw(), rec_buffer.size() * sizeof(int16_t));
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverOpenSL::capture_start() {
|
||||
if (OS::get_singleton()->request_permission("RECORD_AUDIO")) {
|
||||
return capture_init_device();
|
||||
}
|
||||
|
||||
WARN_PRINT("Unable to start audio capture - No RECORD_AUDIO permission");
|
||||
return ERR_UNAUTHORIZED;
|
||||
}
|
||||
|
||||
Error AudioDriverOpenSL::capture_stop() {
|
||||
SLuint32 state;
|
||||
SLresult res = (*recordItf)->GetRecordState(recordItf, &state);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
if (state != SL_RECORDSTATE_STOPPED) {
|
||||
res = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
|
||||
res = (*recordBufferQueueItf)->Clear(recordBufferQueueItf);
|
||||
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int AudioDriverOpenSL::get_mix_rate() const {
|
||||
return 44100; // hardcoded for Android, as selected by SL_SAMPLINGRATE_44_1
|
||||
}
|
||||
|
||||
AudioDriver::SpeakerMode AudioDriverOpenSL::get_speaker_mode() const {
|
||||
return SPEAKER_MODE_STEREO;
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::lock() {
|
||||
if (active)
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::unlock() {
|
||||
if (active)
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::finish() {
|
||||
(*sl)->Destroy(sl);
|
||||
}
|
||||
|
||||
void AudioDriverOpenSL::set_pause(bool p_pause) {
|
||||
pause = p_pause;
|
||||
|
||||
if (active) {
|
||||
if (pause) {
|
||||
(*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED);
|
||||
} else {
|
||||
(*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioDriverOpenSL::AudioDriverOpenSL() {
|
||||
s_ad = this;
|
||||
pause = false;
|
||||
active = false;
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
#ifndef AUDIO_DRIVER_OPENSL_H
|
||||
#define AUDIO_DRIVER_OPENSL_H
|
||||
/*************************************************************************/
|
||||
/* audio_driver_opensl.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "servers/audio_server.h"
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
class AudioDriverOpenSL : public AudioDriver {
|
||||
bool active;
|
||||
Mutex mutex;
|
||||
|
||||
enum {
|
||||
|
||||
BUFFER_COUNT = 2
|
||||
};
|
||||
|
||||
bool pause;
|
||||
|
||||
uint32_t buffer_size;
|
||||
int16_t *buffers[BUFFER_COUNT];
|
||||
int32_t *mixdown_buffer;
|
||||
int last_free;
|
||||
|
||||
Vector<int16_t> rec_buffer;
|
||||
|
||||
SLPlayItf playItf;
|
||||
SLRecordItf recordItf;
|
||||
SLObjectItf sl;
|
||||
SLEngineItf EngineItf;
|
||||
SLObjectItf OutputMix;
|
||||
SLObjectItf player;
|
||||
SLObjectItf recorder;
|
||||
SLAndroidSimpleBufferQueueItf bufferQueueItf;
|
||||
SLAndroidSimpleBufferQueueItf recordBufferQueueItf;
|
||||
SLDataSource audioSource;
|
||||
SLDataFormat_PCM pcm;
|
||||
SLDataSink audioSink;
|
||||
SLDataLocator_OutputMix locator_outputmix;
|
||||
|
||||
static AudioDriverOpenSL *s_ad;
|
||||
|
||||
void _buffer_callback(
|
||||
SLAndroidSimpleBufferQueueItf queueItf);
|
||||
|
||||
static void _buffer_callbacks(
|
||||
SLAndroidSimpleBufferQueueItf queueItf,
|
||||
void *pContext);
|
||||
|
||||
void _record_buffer_callback(
|
||||
SLAndroidSimpleBufferQueueItf queueItf);
|
||||
|
||||
static void _record_buffer_callbacks(
|
||||
SLAndroidSimpleBufferQueueItf queueItf,
|
||||
void *pContext);
|
||||
|
||||
virtual Error capture_init_device();
|
||||
|
||||
public:
|
||||
virtual const char *get_name() const;
|
||||
|
||||
virtual Error init();
|
||||
virtual void start();
|
||||
virtual int get_mix_rate() const;
|
||||
virtual SpeakerMode get_speaker_mode() const;
|
||||
virtual void lock();
|
||||
virtual void unlock();
|
||||
virtual void finish();
|
||||
|
||||
virtual void set_pause(bool p_pause);
|
||||
|
||||
virtual Error capture_start();
|
||||
virtual Error capture_stop();
|
||||
|
||||
AudioDriverOpenSL();
|
||||
};
|
||||
|
||||
#endif // AUDIO_DRIVER_ANDROID_H
|
@ -1,197 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import subprocess
|
||||
|
||||
def is_active():
|
||||
return True
|
||||
|
||||
def get_name():
|
||||
return "Android"
|
||||
|
||||
def can_build():
|
||||
return os.path.exists(get_env_android_sdk_root())
|
||||
|
||||
def get_opts():
|
||||
from SCons.Variables import BoolVariable, EnumVariable
|
||||
|
||||
return [
|
||||
("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()),
|
||||
("ndk_platform", 'Target platform (android-<api>, e.g. "android-19")', "android-19"),
|
||||
EnumVariable("android_arch", "Target architecture", "armv7", ("armv7", "arm64v8", "x86", "x86_64")),
|
||||
BoolVariable("android_neon", "Enable NEON support (armv7 only)", True),
|
||||
]
|
||||
|
||||
# Return the ANDROID_SDK_ROOT environment variable.
|
||||
def get_env_android_sdk_root():
|
||||
return os.environ.get("ANDROID_SDK_ROOT", -1)
|
||||
|
||||
def get_min_sdk_version(platform):
|
||||
return int(platform.split("-")[1])
|
||||
|
||||
def get_android_ndk_root(env):
|
||||
return env["ANDROID_SDK_ROOT"] + "/ndk/" + get_ndk_version()
|
||||
|
||||
# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
|
||||
def get_ndk_version():
|
||||
return "23.2.8568313"
|
||||
|
||||
def get_flags():
|
||||
return [
|
||||
("tools", False),
|
||||
]
|
||||
|
||||
# Check if Android NDK version is installed
|
||||
# If not, install it.
|
||||
def install_ndk_if_needed(env):
|
||||
print("Checking for Android NDK...")
|
||||
sdk_root = env["ANDROID_SDK_ROOT"]
|
||||
if not os.path.exists(get_android_ndk_root(env)):
|
||||
extension = ".bat" if os.name == "nt" else ""
|
||||
|
||||
sdkmanager = sdk_root + "/cmdline-tools/latest/bin/sdkmanager" + extension
|
||||
if os.path.exists(sdkmanager):
|
||||
# Install the Android NDK
|
||||
print("Installing Android NDK...")
|
||||
ndk_download_args = "ndk;" + get_ndk_version()
|
||||
subprocess.check_call([sdkmanager, ndk_download_args])
|
||||
else:
|
||||
print("Cannot find " + sdkmanager)
|
||||
print(
|
||||
"Please ensure ANDROID_SDK_ROOT is correct and cmdline-tools are installed, or install NDK version "
|
||||
+ get_ndk_version()
|
||||
+ " manually."
|
||||
)
|
||||
sys.exit()
|
||||
|
||||
env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env)
|
||||
|
||||
def configure(env):
|
||||
install_ndk_if_needed(env)
|
||||
ndk_root = env["ANDROID_NDK_ROOT"]
|
||||
|
||||
# Architecture
|
||||
|
||||
if env["android_arch"] not in ["armv7", "arm64v8", "x86", "x86_64"]:
|
||||
env["android_arch"] = "arm64v8"
|
||||
|
||||
neon_text = ""
|
||||
if env["android_arch"] == "armv7" and env["android_neon"]:
|
||||
neon_text = " (with NEON)"
|
||||
print("Building for Android (" + env["android_arch"] + ")" + neon_text)
|
||||
|
||||
if get_min_sdk_version(env["ndk_platform"]) < 21:
|
||||
if env["android_arch"] == "x86_64" or env["android_arch"] == "arm64v8":
|
||||
print(
|
||||
"WARNING: android_arch="
|
||||
+ env["android_arch"]
|
||||
+ " is not supported by ndk_platform lower than android-21; setting ndk_platform=android-21"
|
||||
)
|
||||
env["ndk_platform"] = "android-21"
|
||||
|
||||
if env["android_arch"] == "armv7":
|
||||
target_triple = "armv7a-linux-androideabi"
|
||||
if env["android_neon"]:
|
||||
env.extra_suffix = ".armv7.neon" + env.extra_suffix
|
||||
else:
|
||||
env.extra_suffix = ".armv7" + env.extra_suffix
|
||||
elif env["android_arch"] == "arm64v8":
|
||||
target_triple = "aarch64-linux-android"
|
||||
env.extra_suffix = ".armv8" + env.extra_suffix
|
||||
elif env["android_arch"] == "x86":
|
||||
target_triple = "i686-linux-android"
|
||||
env.extra_suffix = ".x86" + env.extra_suffix
|
||||
elif env["android_arch"] == "x86_64":
|
||||
target_triple = "x86_64-linux-android"
|
||||
env.extra_suffix = ".x86_64" + env.extra_suffix
|
||||
|
||||
target_option = ["-target", target_triple + str(get_min_sdk_version(env["ndk_platform"]))]
|
||||
env.Append(ASFLAGS=[target_option, "-c"])
|
||||
env.Append(CCFLAGS=target_option)
|
||||
env.Append(LINKFLAGS=target_option)
|
||||
|
||||
# Build type
|
||||
|
||||
if env["target"].startswith("release"):
|
||||
if env["optimize"] == "speed": # optimize for speed (default)
|
||||
# `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces
|
||||
# when using `target=release_debug`.
|
||||
opt = "-O3" if env["target"] == "release" else "-O2"
|
||||
env.Append(CCFLAGS=[opt])
|
||||
elif env["optimize"] == "size": # optimize for size
|
||||
env.Append(CCFLAGS=["-Oz"])
|
||||
elif env["target"] == "debug":
|
||||
env.Append(LINKFLAGS=["-O0"])
|
||||
env.Append(CCFLAGS=["-O0", "-g"])
|
||||
|
||||
# LTO
|
||||
|
||||
if env["lto"] == "auto": # LTO benefits for Android (size, performance) haven't been clearly established yet.
|
||||
env["lto"] = "none"
|
||||
|
||||
if env["lto"] != "none":
|
||||
if env["lto"] == "thin":
|
||||
env.Append(CCFLAGS=["-flto=thin"])
|
||||
env.Append(LINKFLAGS=["-flto=thin"])
|
||||
else:
|
||||
env.Append(CCFLAGS=["-flto"])
|
||||
env.Append(LINKFLAGS=["-flto"])
|
||||
|
||||
# Compiler configuration
|
||||
|
||||
env["SHLIBSUFFIX"] = ".so"
|
||||
|
||||
if env["PLATFORM"] == "win32":
|
||||
env.use_windows_spawn_fix()
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
host_subpath = "linux-x86_64"
|
||||
elif sys.platform.startswith("darwin"):
|
||||
host_subpath = "darwin-x86_64"
|
||||
elif sys.platform.startswith("win"):
|
||||
if platform.machine().endswith("64"):
|
||||
host_subpath = "windows-x86_64"
|
||||
else:
|
||||
host_subpath = "windows"
|
||||
|
||||
toolchain_path = ndk_root + "/toolchains/llvm/prebuilt/" + host_subpath
|
||||
compiler_path = toolchain_path + "/bin"
|
||||
|
||||
env["CC"] = compiler_path + "/clang"
|
||||
env["CXX"] = compiler_path + "/clang++"
|
||||
env["AR"] = compiler_path + "/llvm-ar"
|
||||
env["RANLIB"] = compiler_path + "/llvm-ranlib"
|
||||
env["AS"] = compiler_path + "/clang"
|
||||
|
||||
env.Append(
|
||||
CCFLAGS="-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
|
||||
)
|
||||
env.Append(CPPDEFINES=["NO_STATVFS", "GLES_ENABLED"])
|
||||
|
||||
if get_min_sdk_version(env["ndk_platform"]) >= 24:
|
||||
env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
|
||||
|
||||
env["neon_enabled"] = False
|
||||
if env["android_arch"] == "x86":
|
||||
env.Append(CCFLAGS=["-mstackrealign"])
|
||||
elif env["android_arch"] == "armv7":
|
||||
env.Append(CCFLAGS="-march=armv7-a -mfloat-abi=softfp".split())
|
||||
env.Append(CPPDEFINES=["__ARM_ARCH_7__", "__ARM_ARCH_7A__"])
|
||||
if env["android_neon"]:
|
||||
env["neon_enabled"] = True
|
||||
env.Append(CPPDEFINES=["__ARM_NEON__"])
|
||||
else:
|
||||
env.Append(CCFLAGS=["-mfpu=vfpv3-d16"])
|
||||
elif env["android_arch"] == "arm64v8":
|
||||
env.Append(CCFLAGS=["-mfix-cortex-a53-835769"])
|
||||
env.Append(CPPDEFINES=["__ARM_ARCH_8A__"])
|
||||
|
||||
# Link flags
|
||||
env.Append(LINKFLAGS="-Wl,--gc-sections -Wl,--no-undefined -Wl,-z,now".split())
|
||||
env.Append(LINKFLAGS="-Wl,-soname,libgodot_android.so")
|
||||
|
||||
env.Prepend(CPPPATH=["#platform/android"])
|
||||
env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED", "NO_FCNTL"])
|
||||
env.Append(LIBS=["OpenSLES", "EGL", "GLESv3", "GLESv2", "android", "log", "z", "dl"])
|
||||
|
||||
|
@ -1,356 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* dir_access_jandroid.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "dir_access_jandroid.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "file_access_android.h"
|
||||
#include "string_android.h"
|
||||
#include "thread_jandroid.h"
|
||||
|
||||
jobject DirAccessJAndroid::dir_access_handler = nullptr;
|
||||
jclass DirAccessJAndroid::cls = NULL;
|
||||
jmethodID DirAccessJAndroid::_dir_open = NULL;
|
||||
jmethodID DirAccessJAndroid::_dir_next = NULL;
|
||||
jmethodID DirAccessJAndroid::_dir_close = NULL;
|
||||
jmethodID DirAccessJAndroid::_dir_is_dir = NULL;
|
||||
jmethodID DirAccessJAndroid::_dir_exists = nullptr;
|
||||
jmethodID DirAccessJAndroid::_file_exists = nullptr;
|
||||
jmethodID DirAccessJAndroid::_get_drive_count = nullptr;
|
||||
jmethodID DirAccessJAndroid::_get_drive = nullptr;
|
||||
jmethodID DirAccessJAndroid::_make_dir = nullptr;
|
||||
jmethodID DirAccessJAndroid::_get_space_left = nullptr;
|
||||
jmethodID DirAccessJAndroid::_rename = nullptr;
|
||||
jmethodID DirAccessJAndroid::_remove = nullptr;
|
||||
jmethodID DirAccessJAndroid::_current_is_hidden = nullptr;
|
||||
|
||||
Error DirAccessJAndroid::list_dir_begin() {
|
||||
list_dir_end();
|
||||
|
||||
int res = dir_open(current_dir);
|
||||
if (res <= 0) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
id = res;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::get_next() {
|
||||
ERR_FAIL_COND_V(id == 0, "");
|
||||
|
||||
if (_dir_next) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, "");
|
||||
jstring str = (jstring)env->CallObjectMethod(dir_access_handler, _dir_next, get_access_type(), id);
|
||||
|
||||
if (!str) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String ret = jstring_to_string((jstring)str, env);
|
||||
env->DeleteLocalRef((jobject)str);
|
||||
return ret;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool DirAccessJAndroid::current_is_dir() const {
|
||||
if (_dir_is_dir) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, false);
|
||||
return env->CallBooleanMethod(dir_access_handler, _dir_is_dir, get_access_type(), id);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DirAccessJAndroid::current_is_hidden() const {
|
||||
if (_current_is_hidden) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, false);
|
||||
return env->CallBooleanMethod(dir_access_handler, _current_is_hidden, get_access_type(), id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DirAccessJAndroid::list_dir_end() {
|
||||
if (id == 0)
|
||||
return;
|
||||
|
||||
dir_close(id);
|
||||
id = 0;
|
||||
}
|
||||
|
||||
int DirAccessJAndroid::get_drive_count() {
|
||||
if (_get_drive_count) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, 0);
|
||||
return env->CallIntMethod(dir_access_handler, _get_drive_count, get_access_type());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::get_drive(int p_drive) {
|
||||
if (_get_drive) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, "");
|
||||
jstring j_drive = (jstring)env->CallObjectMethod(dir_access_handler, _get_drive, get_access_type(), p_drive);
|
||||
|
||||
if (!j_drive) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String drive = jstring_to_string(j_drive, env);
|
||||
env->DeleteLocalRef(j_drive);
|
||||
return drive;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::_get_root_string() const {
|
||||
if (get_access_type() == ACCESS_FILESYSTEM) {
|
||||
return "/";
|
||||
}
|
||||
return DirAccessUnix::_get_root_string();
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::get_current_dir() {
|
||||
String base = _get_root_path();
|
||||
String bd = current_dir;
|
||||
if (!base.empty()) {
|
||||
bd = current_dir.replace_first(base, "");
|
||||
}
|
||||
|
||||
String root_string = _get_root_string();
|
||||
if (bd.begins_with(root_string)) {
|
||||
return bd;
|
||||
} else if (bd.begins_with("/")) {
|
||||
return root_string + bd.substr(1, bd.length());
|
||||
} else {
|
||||
return root_string + bd;
|
||||
}
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::change_dir(String p_dir) {
|
||||
String new_dir = get_absolute_path(p_dir);
|
||||
if (new_dir == current_dir) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (!dir_exists(new_dir)) {
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
current_dir = new_dir;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
String DirAccessJAndroid::get_absolute_path(String p_path) {
|
||||
if (current_dir != "" && p_path == current_dir) {
|
||||
return current_dir;
|
||||
}
|
||||
|
||||
if (p_path.is_rel_path()) {
|
||||
p_path = get_current_dir().plus_file(p_path);
|
||||
}
|
||||
|
||||
p_path = fix_path(p_path);
|
||||
p_path = p_path.simplify_path();
|
||||
return p_path;
|
||||
}
|
||||
|
||||
bool DirAccessJAndroid::file_exists(String p_file) {
|
||||
if (_file_exists) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, false);
|
||||
|
||||
String path = get_absolute_path(p_file);
|
||||
jstring j_path = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _file_exists, get_access_type(), j_path);
|
||||
env->DeleteLocalRef(j_path);
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DirAccessJAndroid::dir_exists(String p_dir) {
|
||||
if (_dir_exists) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, false);
|
||||
|
||||
String path = get_absolute_path(p_dir);
|
||||
jstring j_path = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _dir_exists, get_access_type(), j_path);
|
||||
env->DeleteLocalRef(j_path);
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::make_dir_recursive(String p_dir) {
|
||||
// Check if the directory exists already
|
||||
if (dir_exists(p_dir)) {
|
||||
return ERR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
if (_make_dir) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, ERR_UNCONFIGURED);
|
||||
|
||||
String path = get_absolute_path(p_dir);
|
||||
jstring j_dir = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _make_dir, get_access_type(), j_dir);
|
||||
env->DeleteLocalRef(j_dir);
|
||||
if (result) {
|
||||
return OK;
|
||||
} else {
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::make_dir(String p_dir) {
|
||||
return make_dir_recursive(p_dir);
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::rename(String p_from, String p_to) {
|
||||
if (_rename) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, ERR_UNCONFIGURED);
|
||||
|
||||
String from_path = get_absolute_path(p_from);
|
||||
jstring j_from = env->NewStringUTF(from_path.utf8().get_data());
|
||||
|
||||
String to_path = get_absolute_path(p_to);
|
||||
jstring j_to = env->NewStringUTF(to_path.utf8().get_data());
|
||||
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _rename, get_access_type(), j_from, j_to);
|
||||
env->DeleteLocalRef(j_from);
|
||||
env->DeleteLocalRef(j_to);
|
||||
if (result) {
|
||||
return OK;
|
||||
} else {
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
}
|
||||
|
||||
Error DirAccessJAndroid::remove(String p_name) {
|
||||
if (_remove) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, ERR_UNCONFIGURED);
|
||||
|
||||
String path = get_absolute_path(p_name);
|
||||
jstring j_name = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(dir_access_handler, _remove, get_access_type(), j_name);
|
||||
env->DeleteLocalRef(j_name);
|
||||
if (result) {
|
||||
return OK;
|
||||
} else {
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DirAccessJAndroid::get_space_left() {
|
||||
if (_get_space_left) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, 0);
|
||||
return env->CallLongMethod(dir_access_handler, _get_space_left, get_access_type());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DirAccessJAndroid::setup(jobject p_dir_access_handler) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
dir_access_handler = env->NewGlobalRef(p_dir_access_handler);
|
||||
|
||||
jclass c = env->GetObjectClass(dir_access_handler);
|
||||
cls = (jclass)env->NewGlobalRef(c);
|
||||
|
||||
_dir_open = env->GetMethodID(cls, "dirOpen", "(ILjava/lang/String;)I");
|
||||
_dir_next = env->GetMethodID(cls, "dirNext", "(II)Ljava/lang/String;");
|
||||
_dir_close = env->GetMethodID(cls, "dirClose", "(II)V");
|
||||
_dir_is_dir = env->GetMethodID(cls, "dirIsDir", "(II)Z");
|
||||
_dir_exists = env->GetMethodID(cls, "dirExists", "(ILjava/lang/String;)Z");
|
||||
_file_exists = env->GetMethodID(cls, "fileExists", "(ILjava/lang/String;)Z");
|
||||
_get_drive_count = env->GetMethodID(cls, "getDriveCount", "(I)I");
|
||||
_get_drive = env->GetMethodID(cls, "getDrive", "(II)Ljava/lang/String;");
|
||||
_make_dir = env->GetMethodID(cls, "makeDir", "(ILjava/lang/String;)Z");
|
||||
_get_space_left = env->GetMethodID(cls, "getSpaceLeft", "(I)J");
|
||||
_rename = env->GetMethodID(cls, "rename", "(ILjava/lang/String;Ljava/lang/String;)Z");
|
||||
_remove = env->GetMethodID(cls, "remove", "(ILjava/lang/String;)Z");
|
||||
_current_is_hidden = env->GetMethodID(cls, "isCurrentHidden", "(II)Z");
|
||||
}
|
||||
|
||||
DirAccessJAndroid::DirAccessJAndroid() {
|
||||
id = 0;
|
||||
}
|
||||
|
||||
DirAccessJAndroid::~DirAccessJAndroid() {
|
||||
list_dir_end();
|
||||
}
|
||||
|
||||
int DirAccessJAndroid::dir_open(String p_path) {
|
||||
if (_dir_open) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, 0);
|
||||
|
||||
String path = get_absolute_path(p_path);
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
int dirId = env->CallIntMethod(dir_access_handler, _dir_open, get_access_type(), js);
|
||||
env->DeleteLocalRef(js);
|
||||
return dirId;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DirAccessJAndroid::dir_close(int p_id) {
|
||||
if (_dir_close) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND(env == nullptr);
|
||||
env->CallVoidMethod(dir_access_handler, _dir_close, get_access_type(), p_id);
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
#ifndef DIR_ACCESS_JANDROID_H
|
||||
#define DIR_ACCESS_JANDROID_H
|
||||
/*************************************************************************/
|
||||
/* dir_access_jandroid.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "core/os/dir_access.h"
|
||||
#include "drivers/unix/dir_access_unix.h"
|
||||
#include "java_pandemonium_lib_jni.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/// Android implementation of the DirAccess interface used to provide access to
|
||||
/// ACCESS_FILESYSTEM and ACCESS_RESOURCES directory resources.
|
||||
/// The implementation use jni in order to comply with Android filesystem
|
||||
/// access restriction.
|
||||
class DirAccessJAndroid : public DirAccessUnix {
|
||||
static jobject dir_access_handler;
|
||||
|
||||
static jclass cls;
|
||||
|
||||
static jmethodID _dir_open;
|
||||
static jmethodID _dir_next;
|
||||
static jmethodID _dir_close;
|
||||
static jmethodID _dir_is_dir;
|
||||
|
||||
static jmethodID _dir_exists;
|
||||
static jmethodID _file_exists;
|
||||
static jmethodID _get_drive_count;
|
||||
static jmethodID _get_drive;
|
||||
static jmethodID _make_dir;
|
||||
static jmethodID _get_space_left;
|
||||
static jmethodID _rename;
|
||||
static jmethodID _remove;
|
||||
static jmethodID _current_is_hidden;
|
||||
|
||||
public:
|
||||
virtual Error list_dir_begin(); ///< This starts dir listing
|
||||
virtual String get_next();
|
||||
virtual bool current_is_dir() const;
|
||||
virtual bool current_is_hidden() const;
|
||||
virtual void list_dir_end(); ///<
|
||||
|
||||
virtual int get_drive_count();
|
||||
virtual String get_drive(int p_drive);
|
||||
|
||||
virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
|
||||
virtual String get_current_dir(); ///< return current dir location
|
||||
|
||||
virtual bool file_exists(String p_file);
|
||||
virtual bool dir_exists(String p_dir);
|
||||
|
||||
virtual Error make_dir(String p_dir);
|
||||
virtual Error make_dir_recursive(String p_dir);
|
||||
|
||||
virtual Error rename(String p_from, String p_to);
|
||||
virtual Error remove(String p_name);
|
||||
|
||||
virtual bool is_link(String p_file) { return false; }
|
||||
virtual String read_link(String p_file) { return p_file; }
|
||||
virtual Error create_link(String p_source, String p_target) { return FAILED; }
|
||||
|
||||
virtual uint64_t get_space_left();
|
||||
|
||||
static void setup(jobject p_dir_access_handler);
|
||||
|
||||
DirAccessJAndroid();
|
||||
~DirAccessJAndroid();
|
||||
|
||||
protected:
|
||||
String _get_root_string() const;
|
||||
|
||||
private:
|
||||
int id;
|
||||
|
||||
int dir_open(String p_path);
|
||||
void dir_close(int p_id);
|
||||
String get_absolute_path(String p_path);
|
||||
};
|
||||
|
||||
#endif // DIR_ACCESS_JANDROID_H
|
@ -1,57 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* export.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#include "export_plugin.h"
|
||||
|
||||
#include "editor/editor_settings.h"
|
||||
|
||||
void register_android_exporter() {
|
||||
#ifndef ANDROID_ENABLED
|
||||
String exe_ext;
|
||||
if (OS::get_singleton()->get_name() == "Windows") {
|
||||
exe_ext = "*.exe";
|
||||
}
|
||||
|
||||
EDITOR_DEF("export/android/android_sdk_path", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/android_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
|
||||
EDITOR_DEF("export/android/debug_keystore", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"));
|
||||
EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey");
|
||||
EDITOR_DEF("export/android/debug_keystore_pass", "android");
|
||||
EDITOR_DEF("export/android/force_system_user", false);
|
||||
|
||||
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
|
||||
#endif
|
||||
|
||||
Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid));
|
||||
EditorExport::get_singleton()->add_export_platform(exporter);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* export.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef ANDROID_EXPORT_H
|
||||
#define ANDROID_EXPORT_H
|
||||
|
||||
void register_android_exporter();
|
||||
|
||||
#endif // ANDROID_EXPORT_H
|
@ -1,261 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* export_plugin.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/json.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/dir_access.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/os/safe_refcount.h"
|
||||
#include "core/version.h"
|
||||
#include "drivers/png/png_driver_common.h"
|
||||
#include "editor/editor_export.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "platform/android/logo.gen.h"
|
||||
#include "platform/android/run_icon.gen.h"
|
||||
|
||||
#include "gradle_export_util.h"
|
||||
#include "pandemonium_plugin_config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/splash_bg_color" />
|
||||
<item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:filter="%s"
|
||||
android:src="@drawable/splash" />
|
||||
</item>
|
||||
</layer-list>
|
||||
)SPLASH";
|
||||
|
||||
struct LauncherIcon {
|
||||
const char *export_path;
|
||||
int dimensions;
|
||||
};
|
||||
|
||||
class EditorExportPlatformAndroid : public EditorExportPlatform {
|
||||
GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform);
|
||||
|
||||
Ref<ImageTexture> logo;
|
||||
Ref<ImageTexture> run_icon;
|
||||
|
||||
struct Device {
|
||||
String id;
|
||||
String name;
|
||||
String description;
|
||||
int api_level;
|
||||
};
|
||||
|
||||
struct APKExportData {
|
||||
zipFile apk;
|
||||
EditorProgress *ep;
|
||||
};
|
||||
|
||||
Vector<PluginConfigAndroid> plugins;
|
||||
String last_plugin_names;
|
||||
uint64_t last_custom_build_time = 0;
|
||||
SafeFlag plugins_changed;
|
||||
Mutex plugins_lock;
|
||||
Vector<Device> devices;
|
||||
SafeFlag devices_changed;
|
||||
Mutex device_lock;
|
||||
|
||||
#ifndef ANDROID_ENABLED
|
||||
Thread check_for_changes_thread;
|
||||
SafeFlag quit_request;
|
||||
|
||||
static void _check_for_changes_poll_thread(void *ud);
|
||||
#endif
|
||||
|
||||
String get_project_name(const String &p_name) const;
|
||||
|
||||
String get_package_name(const String &p_package) const;
|
||||
|
||||
String get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const;
|
||||
|
||||
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
|
||||
|
||||
static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data);
|
||||
|
||||
static zip_fileinfo get_zip_fileinfo();
|
||||
|
||||
static Vector<String> get_abis();
|
||||
|
||||
/// List the gdap files in the directory specified by the p_path parameter.
|
||||
static Vector<String> list_gdap_files(const String &p_path);
|
||||
|
||||
static Vector<PluginConfigAndroid> get_plugins();
|
||||
|
||||
static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets);
|
||||
|
||||
static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED);
|
||||
|
||||
static Error save_apk_so(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total);
|
||||
|
||||
static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total);
|
||||
|
||||
static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so);
|
||||
|
||||
bool _has_read_write_storage_permission(const Vector<String> &p_permissions);
|
||||
|
||||
bool _has_manage_external_storage_permission(const Vector<String> &p_permissions);
|
||||
|
||||
void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions);
|
||||
|
||||
void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug);
|
||||
|
||||
void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet);
|
||||
|
||||
static String _parse_string(const uint8_t *p_bytes, bool p_utf8);
|
||||
|
||||
void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest);
|
||||
|
||||
void _load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data);
|
||||
|
||||
void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data);
|
||||
|
||||
String load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image);
|
||||
|
||||
void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background);
|
||||
|
||||
void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data);
|
||||
|
||||
void store_image(const String &export_path, const Vector<uint8_t> &data);
|
||||
|
||||
void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
|
||||
const String &processed_splash_config_xml,
|
||||
const Ref<Image> &splash_image,
|
||||
const Ref<Image> &splash_bg_color_image,
|
||||
const Ref<Image> &main_image,
|
||||
const Ref<Image> &foreground,
|
||||
const Ref<Image> &background);
|
||||
|
||||
static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
public:
|
||||
typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total);
|
||||
|
||||
public:
|
||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
|
||||
|
||||
virtual void get_export_options(List<ExportOption> *r_options);
|
||||
|
||||
virtual String get_name() const;
|
||||
|
||||
virtual String get_os_name() const;
|
||||
|
||||
virtual Ref<Texture> get_logo() const;
|
||||
|
||||
virtual bool should_update_export_options();
|
||||
|
||||
virtual bool poll_export();
|
||||
|
||||
virtual int get_options_count() const;
|
||||
|
||||
virtual String get_options_tooltip() const;
|
||||
|
||||
virtual String get_option_label(int p_index) const;
|
||||
|
||||
virtual String get_option_tooltip(int p_index) const;
|
||||
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags);
|
||||
|
||||
virtual Ref<Texture> get_run_icon() const;
|
||||
|
||||
static String get_adb_path();
|
||||
|
||||
static String get_apksigner_path();
|
||||
|
||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
|
||||
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const;
|
||||
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
void _update_custom_build_project();
|
||||
|
||||
inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) {
|
||||
String plugin_names = PluginConfigAndroid::get_plugins_names(enabled_plugins);
|
||||
bool first_build = last_custom_build_time == 0;
|
||||
bool have_plugins_changed = false;
|
||||
|
||||
if (!first_build) {
|
||||
have_plugins_changed = plugin_names != last_plugin_names;
|
||||
if (!have_plugins_changed) {
|
||||
for (int i = 0; i < enabled_plugins.size(); i++) {
|
||||
if (enabled_plugins.get(i).last_updated > last_custom_build_time) {
|
||||
have_plugins_changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_custom_build_time = OS::get_singleton()->get_unix_time();
|
||||
last_plugin_names = plugin_names;
|
||||
|
||||
return have_plugins_changed || first_build;
|
||||
}
|
||||
|
||||
String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
||||
|
||||
Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
||||
|
||||
void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags);
|
||||
|
||||
Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep);
|
||||
|
||||
void _clear_assets_directory();
|
||||
|
||||
void _remove_copied_libs();
|
||||
|
||||
String join_list(List<String> parts, const String &separator) const;
|
||||
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
|
||||
|
||||
Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags);
|
||||
|
||||
virtual void get_platform_features(List<String> *r_features);
|
||||
|
||||
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, RBSet<String> &p_features);
|
||||
|
||||
EditorExportPlatformAndroid();
|
||||
|
||||
~EditorExportPlatformAndroid();
|
||||
};
|
@ -1,250 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* gradle_export_util.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "gradle_export_util.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
int _get_android_orientation_value(OS::ScreenOrientation screen_orientation) {
|
||||
switch (screen_orientation) {
|
||||
case OS::SCREEN_PORTRAIT:
|
||||
return 1;
|
||||
case OS::SCREEN_REVERSE_LANDSCAPE:
|
||||
return 8;
|
||||
case OS::SCREEN_REVERSE_PORTRAIT:
|
||||
return 9;
|
||||
case OS::SCREEN_SENSOR_LANDSCAPE:
|
||||
return 11;
|
||||
case OS::SCREEN_SENSOR_PORTRAIT:
|
||||
return 12;
|
||||
case OS::SCREEN_SENSOR:
|
||||
return 13;
|
||||
case OS::SCREEN_LANDSCAPE:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
String _get_android_orientation_label(OS::ScreenOrientation screen_orientation) {
|
||||
switch (screen_orientation) {
|
||||
case OS::SCREEN_PORTRAIT:
|
||||
return "portrait";
|
||||
case OS::SCREEN_REVERSE_LANDSCAPE:
|
||||
return "reverseLandscape";
|
||||
case OS::SCREEN_REVERSE_PORTRAIT:
|
||||
return "reversePortrait";
|
||||
case OS::SCREEN_SENSOR_LANDSCAPE:
|
||||
return "userLandscape";
|
||||
case OS::SCREEN_SENSOR_PORTRAIT:
|
||||
return "userPortrait";
|
||||
case OS::SCREEN_SENSOR:
|
||||
return "fullUser";
|
||||
case OS::SCREEN_LANDSCAPE:
|
||||
default:
|
||||
return "landscape";
|
||||
}
|
||||
}
|
||||
|
||||
// Utility method used to create a directory.
|
||||
Error create_directory(const String &p_dir) {
|
||||
if (!DirAccess::exists(p_dir)) {
|
||||
DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
|
||||
Error err = filesystem_da->make_dir_recursive(p_dir);
|
||||
ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
|
||||
memdelete(filesystem_da);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Writes p_data into a file at p_path, creating directories if necessary.
|
||||
// Note: this will overwrite the file at p_path if it already exists.
|
||||
Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) {
|
||||
String dir = p_path.get_base_dir();
|
||||
Error err = create_directory(dir);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
|
||||
fa->store_buffer(p_data.ptr(), p_data.size());
|
||||
memdelete(fa);
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Writes string p_data into a file at p_path, creating directories if necessary.
|
||||
// Note: this will overwrite the file at p_path if it already exists.
|
||||
Error store_string_at_path(const String &p_path, const String &p_data) {
|
||||
String dir = p_path.get_base_dir();
|
||||
Error err = create_directory(dir);
|
||||
if (err != OK) {
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
print_error("Unable to write data into " + p_path);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
|
||||
fa->store_string(p_data);
|
||||
memdelete(fa);
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Implementation of EditorExportSaveFunction.
|
||||
// This method will only be called as an input to export_project_files.
|
||||
// It is used by the export_project_files method to save all the asset files into the gradle project.
|
||||
// It's functionality mirrors that of the method save_apk_file.
|
||||
// This method will be called ONLY when custom build is enabled.
|
||||
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
|
||||
CustomExportData *export_data = (CustomExportData *)p_userdata;
|
||||
String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/");
|
||||
print_verbose("Saving project files from " + p_path + " into " + dst_path);
|
||||
Error err = store_file_at_path(dst_path, p_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
String _android_xml_escape(const String &p_string) {
|
||||
// Android XML requires strings to be both valid XML (`xml_escape()`) but also
|
||||
// to escape characters which are valid XML but have special meaning in Android XML.
|
||||
// https://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling
|
||||
// Note: Didn't handle U+XXXX unicode chars, could be done if needed.
|
||||
return p_string
|
||||
.replace("@", "\\@")
|
||||
.replace("?", "\\?")
|
||||
.replace("'", "\\'")
|
||||
.replace("\"", "\\\"")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\t", "\\t")
|
||||
.xml_escape(false);
|
||||
}
|
||||
|
||||
// Creates strings.xml files inside the gradle project for different locales.
|
||||
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) {
|
||||
print_verbose("Creating strings resources for supported locales for project " + project_name);
|
||||
// Stores the string into the default values directory.
|
||||
String processed_default_xml_string = vformat(pandemonium_project_name_xml_string, _android_xml_escape(project_name));
|
||||
store_string_at_path("res://android/build/res/values/pandemonium_project_name_string.xml", processed_default_xml_string);
|
||||
|
||||
// Searches the Gradle project res/ directory to find all supported locales
|
||||
DirAccessRef da = DirAccess::open("res://android/build/res");
|
||||
if (!da) {
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
print_error("Unable to open Android resources directory.");
|
||||
}
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
da->list_dir_begin();
|
||||
while (true) {
|
||||
String file = da->get_next();
|
||||
if (file == "") {
|
||||
break;
|
||||
}
|
||||
if (!file.begins_with("values-")) {
|
||||
// NOTE: This assumes all directories that start with "values-" are for localization.
|
||||
continue;
|
||||
}
|
||||
String locale = file.replace("values-", "").replace("-r", "_");
|
||||
String property_name = "application/config/name_" + locale;
|
||||
String locale_directory = "res://android/build/res/" + file + "/pandemonium_project_name_string.xml";
|
||||
if (ProjectSettings::get_singleton()->has_setting(property_name)) {
|
||||
String locale_project_name = ProjectSettings::get_singleton()->get(property_name);
|
||||
String processed_xml_string = vformat(pandemonium_project_name_xml_string, _android_xml_escape(locale_project_name));
|
||||
print_verbose("Storing project name for locale " + locale + " under " + locale_directory);
|
||||
store_string_at_path(locale_directory, processed_xml_string);
|
||||
} else {
|
||||
// TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch
|
||||
store_string_at_path(locale_directory, processed_default_xml_string);
|
||||
}
|
||||
}
|
||||
da->list_dir_end();
|
||||
return OK;
|
||||
}
|
||||
|
||||
String bool_to_string(bool v) {
|
||||
return v ? "true" : "false";
|
||||
}
|
||||
|
||||
String _get_gles_tag() {
|
||||
bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name") == "GLES3" &&
|
||||
!ProjectSettings::get_singleton()->get("rendering/quality/driver/fallback_to_gles2");
|
||||
return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : "";
|
||||
}
|
||||
|
||||
String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) {
|
||||
String manifest_screen_sizes = " <supports-screens \n tools:node=\"replace\"";
|
||||
String sizes[] = { "small", "normal", "large", "xlarge" };
|
||||
size_t num_sizes = sizeof(sizes) / sizeof(sizes[0]);
|
||||
for (size_t i = 0; i < num_sizes; i++) {
|
||||
String feature_name = vformat("screen/support_%s", sizes[i]);
|
||||
String feature_support = bool_to_string(p_preset->get(feature_name));
|
||||
String xml_entry = vformat("\n android:%sScreens=\"%s\"", sizes[i], feature_support);
|
||||
manifest_screen_sizes += xml_entry;
|
||||
}
|
||||
manifest_screen_sizes += " />\n";
|
||||
return manifest_screen_sizes;
|
||||
}
|
||||
|
||||
String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
|
||||
String orientation = _get_android_orientation_label(
|
||||
OS::get_singleton()->get_screen_orientation_from_string(GLOBAL_GET("display/window/handheld/orientation")));
|
||||
String manifest_activity_text = vformat(
|
||||
" <activity android:name=\"com.pandemonium.game.PandemoniumApp\" "
|
||||
"tools:replace=\"android:screenOrientation,android:excludeFromRecents,android:resizeableActivity\" "
|
||||
"android:excludeFromRecents=\"%s\" "
|
||||
"android:screenOrientation=\"%s\" "
|
||||
"android:resizeableActivity=\"%s\">\n",
|
||||
bool_to_string(p_preset->get("package/exclude_from_recents")),
|
||||
orientation,
|
||||
bool_to_string(bool(GLOBAL_GET("display/window/size/resizable"))));
|
||||
manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n";
|
||||
manifest_activity_text += " </activity>\n";
|
||||
return manifest_activity_text;
|
||||
}
|
||||
|
||||
String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission) {
|
||||
String manifest_application_text = vformat(
|
||||
" <application android:label=\"@string/pandemonium_project_name_string\"\n"
|
||||
" android:allowBackup=\"%s\"\n"
|
||||
" android:isGame=\"%s\"\n"
|
||||
" android:hasFragileUserData=\"%s\"\n"
|
||||
" android:requestLegacyExternalStorage=\"%s\"\n"
|
||||
" tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n"
|
||||
" tools:ignore=\"GoogleAppIndexingWarning\"\n"
|
||||
" android:icon=\"@mipmap/icon\" >\n\n",
|
||||
bool_to_string(p_preset->get("user_data_backup/allow")),
|
||||
bool_to_string(p_preset->get("package/classify_as_game")),
|
||||
bool_to_string(p_preset->get("package/retain_data_on_uninstall")),
|
||||
bool_to_string(p_has_read_write_storage_permission));
|
||||
|
||||
manifest_application_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.supportedDevices\" />\n";
|
||||
manifest_application_text += _get_activity_tag(p_preset);
|
||||
manifest_application_text += " </application>\n";
|
||||
return manifest_application_text;
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* gradle_export_util.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* PANDEMONIUM ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef PANDEMONIUM_GRADLE_EXPORT_UTIL_H
|
||||
#define PANDEMONIUM_GRADLE_EXPORT_UTIL_H
|
||||
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/dir_access.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/editor_export.h"
|
||||
|
||||
const String pandemonium_project_name_xml_string = R"(<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="pandemonium_project_name_string">%s</string>
|
||||
</resources>
|
||||
)";
|
||||
|
||||
struct CustomExportData {
|
||||
String assets_directory;
|
||||
bool debug;
|
||||
Vector<String> libs;
|
||||
};
|
||||
|
||||
int _get_android_orientation_value(OS::ScreenOrientation screen_orientation);
|
||||
|
||||
String _get_android_orientation_label(OS::ScreenOrientation screen_orientation);
|
||||
|
||||
// Utility method used to create a directory.
|
||||
Error create_directory(const String &p_dir);
|
||||
|
||||
// Writes p_data into a file at p_path, creating directories if necessary.
|
||||
// Note: this will overwrite the file at p_path if it already exists.
|
||||
Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data);
|
||||
|
||||
// Writes string p_data into a file at p_path, creating directories if necessary.
|
||||
// Note: this will overwrite the file at p_path if it already exists.
|
||||
Error store_string_at_path(const String &p_path, const String &p_data);
|
||||
|
||||
// Implementation of EditorExportSaveFunction.
|
||||
// This method will only be called as an input to export_project_files.
|
||||
// It is used by the export_project_files method to save all the asset files into the gradle project.
|
||||
// It's functionality mirrors that of the method save_apk_file.
|
||||
// This method will be called ONLY when custom build is enabled.
|
||||
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total);
|
||||
|
||||
// Creates strings.xml files inside the gradle project for different locales.
|
||||
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name);
|
||||
|
||||
String bool_to_string(bool v);
|
||||
|
||||
String _get_gles_tag();
|
||||
|
||||
String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
String _get_activity_tag(const Ref<EditorExportPreset> &p_preset);
|
||||
|
||||
String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission);
|
||||
|
||||
#endif //PANDEMONIUM_GRADLE_EXPORT_UTIL_H
|
@ -1,229 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* pandemonium_plugin_config.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "pandemonium_plugin_config.h"
|
||||
|
||||
const char *PluginConfigAndroid::PLUGIN_CONFIG_EXT = ".gdap";
|
||||
|
||||
const char *PluginConfigAndroid::CONFIG_SECTION = "config";
|
||||
const char *PluginConfigAndroid::CONFIG_NAME_KEY = "name";
|
||||
const char *PluginConfigAndroid::CONFIG_BINARY_TYPE_KEY = "binary_type";
|
||||
const char *PluginConfigAndroid::CONFIG_BINARY_KEY = "binary";
|
||||
|
||||
const char *PluginConfigAndroid::DEPENDENCIES_SECTION = "dependencies";
|
||||
const char *PluginConfigAndroid::DEPENDENCIES_LOCAL_KEY = "local";
|
||||
const char *PluginConfigAndroid::DEPENDENCIES_REMOTE_KEY = "remote";
|
||||
const char *PluginConfigAndroid::DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos";
|
||||
|
||||
const char *PluginConfigAndroid::BINARY_TYPE_LOCAL = "local";
|
||||
const char *PluginConfigAndroid::BINARY_TYPE_REMOTE = "remote";
|
||||
|
||||
const char *PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR = "|";
|
||||
|
||||
/*
|
||||
* Set of prebuilt plugins.
|
||||
* Currently unused, this is just for future reference:
|
||||
*/
|
||||
// static const PluginConfigAndroid MY_PREBUILT_PLUGIN = {
|
||||
// /*.valid_config =*/true,
|
||||
// /*.last_updated =*/0,
|
||||
// /*.name =*/"PandemoniumPayment",
|
||||
// /*.binary_type =*/"local",
|
||||
// /*.binary =*/"res://android/build/libs/plugins/PandemoniumPayment.release.aar",
|
||||
// /*.local_dependencies =*/{},
|
||||
// /*.remote_dependencies =*/String("com.android.billingclient:billing:2.2.1").split("|"),
|
||||
// /*.custom_maven_repos =*/{}
|
||||
// };
|
||||
|
||||
String PluginConfigAndroid::resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
|
||||
String absolute_path;
|
||||
if (!dependency_path.empty()) {
|
||||
if (dependency_path.is_abs_path()) {
|
||||
absolute_path = ProjectSettings::get_singleton()->globalize_path(dependency_path);
|
||||
} else {
|
||||
absolute_path = plugin_config_dir.plus_file(dependency_path);
|
||||
}
|
||||
}
|
||||
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
PluginConfigAndroid PluginConfigAndroid::resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir) {
|
||||
PluginConfigAndroid resolved = prebuilt_plugin;
|
||||
resolved.binary = resolved.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ? resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.binary) : prebuilt_plugin.binary;
|
||||
if (!prebuilt_plugin.local_dependencies.empty()) {
|
||||
resolved.local_dependencies.clear();
|
||||
for (int i = 0; i < prebuilt_plugin.local_dependencies.size(); i++) {
|
||||
resolved.local_dependencies.push_back(resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.local_dependencies[i]));
|
||||
}
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
Vector<PluginConfigAndroid> PluginConfigAndroid::get_prebuilt_plugins(String plugins_base_dir) {
|
||||
Vector<PluginConfigAndroid> prebuilt_plugins;
|
||||
// prebuilt_plugins.push_back(resolve_prebuilt_plugin(MY_PREBUILT_PLUGIN, plugins_base_dir));
|
||||
return prebuilt_plugins;
|
||||
}
|
||||
|
||||
bool PluginConfigAndroid::is_plugin_config_valid(PluginConfigAndroid plugin_config) {
|
||||
bool valid_name = !plugin_config.name.empty();
|
||||
bool valid_binary_type = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ||
|
||||
plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE;
|
||||
|
||||
bool valid_binary = false;
|
||||
if (valid_binary_type) {
|
||||
valid_binary = !plugin_config.binary.empty() &&
|
||||
(plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE ||
|
||||
FileAccess::exists(plugin_config.binary));
|
||||
}
|
||||
|
||||
bool valid_local_dependencies = true;
|
||||
if (!plugin_config.local_dependencies.empty()) {
|
||||
for (int i = 0; i < plugin_config.local_dependencies.size(); i++) {
|
||||
if (!FileAccess::exists(plugin_config.local_dependencies[i])) {
|
||||
valid_local_dependencies = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid_name && valid_binary && valid_binary_type && valid_local_dependencies;
|
||||
}
|
||||
|
||||
uint64_t PluginConfigAndroid::get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path) {
|
||||
uint64_t last_modified = FileAccess::get_modified_time(config_path);
|
||||
last_modified = MAX(last_modified, FileAccess::get_modified_time(plugin_config.binary));
|
||||
|
||||
for (int i = 0; i < plugin_config.local_dependencies.size(); i++) {
|
||||
String local_dependency = plugin_config.local_dependencies.get(i);
|
||||
last_modified = MAX(last_modified, FileAccess::get_modified_time(local_dependency));
|
||||
}
|
||||
|
||||
return last_modified;
|
||||
}
|
||||
|
||||
PluginConfigAndroid PluginConfigAndroid::load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
|
||||
PluginConfigAndroid plugin_config = {};
|
||||
|
||||
if (config_file.is_valid()) {
|
||||
Error err = config_file->load(path);
|
||||
if (err == OK) {
|
||||
String config_base_dir = path.get_base_dir();
|
||||
|
||||
plugin_config.name = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_NAME_KEY, String());
|
||||
plugin_config.binary_type = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_BINARY_TYPE_KEY, String());
|
||||
|
||||
String binary_path = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_BINARY_KEY, String());
|
||||
plugin_config.binary = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ? resolve_local_dependency_path(config_base_dir, binary_path) : binary_path;
|
||||
|
||||
if (config_file->has_section(PluginConfigAndroid::DEPENDENCIES_SECTION)) {
|
||||
Vector<String> local_dependencies_paths = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_LOCAL_KEY, Vector<String>());
|
||||
if (!local_dependencies_paths.empty()) {
|
||||
for (int i = 0; i < local_dependencies_paths.size(); i++) {
|
||||
plugin_config.local_dependencies.push_back(resolve_local_dependency_path(config_base_dir, local_dependencies_paths[i]));
|
||||
}
|
||||
}
|
||||
|
||||
plugin_config.remote_dependencies = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_REMOTE_KEY, Vector<String>());
|
||||
plugin_config.custom_maven_repos = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY, Vector<String>());
|
||||
}
|
||||
|
||||
plugin_config.valid_config = is_plugin_config_valid(plugin_config);
|
||||
plugin_config.last_updated = get_plugin_modification_time(plugin_config, path);
|
||||
}
|
||||
}
|
||||
|
||||
return plugin_config;
|
||||
}
|
||||
|
||||
String PluginConfigAndroid::get_plugins_binaries(String type, Vector<PluginConfigAndroid> plugins_configs) {
|
||||
String plugins_binaries;
|
||||
if (!plugins_configs.empty()) {
|
||||
Vector<String> binaries;
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
PluginConfigAndroid config = plugins_configs[i];
|
||||
if (!config.valid_config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.binary_type == type) {
|
||||
binaries.push_back(config.binary);
|
||||
}
|
||||
|
||||
if (type == PluginConfigAndroid::BINARY_TYPE_LOCAL) {
|
||||
binaries.append_array(config.local_dependencies);
|
||||
}
|
||||
|
||||
if (type == PluginConfigAndroid::BINARY_TYPE_REMOTE) {
|
||||
binaries.append_array(config.remote_dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
plugins_binaries = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(binaries);
|
||||
}
|
||||
|
||||
return plugins_binaries;
|
||||
}
|
||||
|
||||
String PluginConfigAndroid::get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs) {
|
||||
String maven_repos;
|
||||
if (!plugins_configs.empty()) {
|
||||
Vector<String> repos_urls;
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
PluginConfigAndroid config = plugins_configs[i];
|
||||
if (!config.valid_config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
repos_urls.append_array(config.custom_maven_repos);
|
||||
}
|
||||
|
||||
maven_repos = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(repos_urls);
|
||||
}
|
||||
return maven_repos;
|
||||
}
|
||||
|
||||
String PluginConfigAndroid::get_plugins_names(Vector<PluginConfigAndroid> plugins_configs) {
|
||||
String plugins_names;
|
||||
if (!plugins_configs.empty()) {
|
||||
Vector<String> names;
|
||||
for (int i = 0; i < plugins_configs.size(); i++) {
|
||||
PluginConfigAndroid config = plugins_configs[i];
|
||||
if (!config.valid_config) {
|
||||
continue;
|
||||
}
|
||||
|
||||
names.push_back(config.name);
|
||||
}
|
||||
plugins_names = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(names);
|
||||
}
|
||||
|
||||
return plugins_names;
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* pandemonium_plugin_config.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* PANDEMONIUM ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef ANDROID_PANDEMONIUM_PLUGIN_CONFIG_H
|
||||
#define ANDROID_PANDEMONIUM_PLUGIN_CONFIG_H
|
||||
|
||||
#include "core/error/error_list.h"
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
/*
|
||||
The `config` section and fields are required and defined as follow:
|
||||
- **name**: name of the plugin.
|
||||
- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field.
|
||||
- **binary**:
|
||||
- if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`).
|
||||
- if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.pandemonium.example:my-plugin:0.0.0").
|
||||
|
||||
The `dependencies` section and fields are optional and defined as follow:
|
||||
- **local**: contains a list of local `.aar` binary files the plugin depends on. The local binary dependencies must also be located in the `res://android/plugins` directory.
|
||||
- **remote**: contains a list of remote binary gradle dependencies for the plugin.
|
||||
- **custom_maven_repos**: contains a list of urls specifying custom maven repos required for the plugin's dependencies.
|
||||
|
||||
See https://github.com/pandemoniumengine/pandemonium/issues/38157#issuecomment-618773871
|
||||
*/
|
||||
struct PluginConfigAndroid {
|
||||
static const char *PLUGIN_CONFIG_EXT;
|
||||
|
||||
static const char *CONFIG_SECTION;
|
||||
static const char *CONFIG_NAME_KEY;
|
||||
static const char *CONFIG_BINARY_TYPE_KEY;
|
||||
static const char *CONFIG_BINARY_KEY;
|
||||
|
||||
static const char *DEPENDENCIES_SECTION;
|
||||
static const char *DEPENDENCIES_LOCAL_KEY;
|
||||
static const char *DEPENDENCIES_REMOTE_KEY;
|
||||
static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY;
|
||||
|
||||
static const char *BINARY_TYPE_LOCAL;
|
||||
static const char *BINARY_TYPE_REMOTE;
|
||||
|
||||
static const char *PLUGIN_VALUE_SEPARATOR;
|
||||
|
||||
// Set to true when the config file is properly loaded.
|
||||
bool valid_config = false;
|
||||
// Unix timestamp of last change to this plugin.
|
||||
uint64_t last_updated = 0;
|
||||
|
||||
// Required config section
|
||||
String name;
|
||||
String binary_type;
|
||||
String binary;
|
||||
|
||||
// Optional dependencies section
|
||||
Vector<String> local_dependencies;
|
||||
Vector<String> remote_dependencies;
|
||||
Vector<String> custom_maven_repos;
|
||||
|
||||
static String resolve_local_dependency_path(String plugin_config_dir, String dependency_path);
|
||||
|
||||
static PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir);
|
||||
|
||||
static Vector<PluginConfigAndroid> get_prebuilt_plugins(String plugins_base_dir);
|
||||
|
||||
static bool is_plugin_config_valid(PluginConfigAndroid plugin_config);
|
||||
|
||||
static uint64_t get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path);
|
||||
|
||||
static PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path);
|
||||
|
||||
static String get_plugins_binaries(String type, Vector<PluginConfigAndroid> plugins_configs);
|
||||
|
||||
static String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs);
|
||||
|
||||
static String get_plugins_names(Vector<PluginConfigAndroid> plugins_configs);
|
||||
};
|
||||
|
||||
#endif // ANDROID_PANDEMONIUM_PLUGIN_CONFIG_H
|
@ -1,180 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* file_access_android.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "file_access_android.h"
|
||||
#include "core/string/print_string.h"
|
||||
|
||||
AAssetManager *FileAccessAndroid::asset_manager = NULL;
|
||||
|
||||
String FileAccessAndroid::get_path() const {
|
||||
return path_src;
|
||||
}
|
||||
|
||||
String FileAccessAndroid::get_path_absolute() const {
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
Error FileAccessAndroid::_open(const String &p_path, int p_mode_flags) {
|
||||
path_src = p_path;
|
||||
|
||||
String path = fix_path(p_path).simplify_path();
|
||||
absolute_path = path;
|
||||
|
||||
if (path.begins_with("/")) {
|
||||
path = path.substr(1, path.length());
|
||||
} else if (path.begins_with("res://")) {
|
||||
path = path.substr(6, path.length());
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, ERR_UNAVAILABLE); //can't write on android..
|
||||
a = AAssetManager_open(asset_manager, path.utf8().get_data(), AASSET_MODE_STREAMING);
|
||||
|
||||
if (!a) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
//ERR_FAIL_COND_V(!a,ERR_FILE_NOT_FOUND);
|
||||
len = AAsset_getLength(a);
|
||||
pos = 0;
|
||||
eof = false;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void FileAccessAndroid::close() {
|
||||
if (!a) {
|
||||
return;
|
||||
}
|
||||
|
||||
AAsset_close(a);
|
||||
a = NULL;
|
||||
}
|
||||
|
||||
bool FileAccessAndroid::is_open() const {
|
||||
return a != NULL;
|
||||
}
|
||||
|
||||
void FileAccessAndroid::seek(uint64_t p_position) {
|
||||
ERR_FAIL_COND(!a);
|
||||
|
||||
AAsset_seek(a, p_position, SEEK_SET);
|
||||
pos = p_position;
|
||||
if (pos > len) {
|
||||
pos = len;
|
||||
eof = true;
|
||||
} else {
|
||||
eof = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessAndroid::seek_end(int64_t p_position) {
|
||||
ERR_FAIL_COND(!a);
|
||||
AAsset_seek(a, p_position, SEEK_END);
|
||||
pos = len + p_position;
|
||||
}
|
||||
|
||||
uint64_t FileAccessAndroid::get_position() const {
|
||||
return pos;
|
||||
}
|
||||
|
||||
uint64_t FileAccessAndroid::get_len() const {
|
||||
return len;
|
||||
}
|
||||
|
||||
bool FileAccessAndroid::eof_reached() const {
|
||||
return eof;
|
||||
}
|
||||
|
||||
uint8_t FileAccessAndroid::get_8() const {
|
||||
if (pos >= len) {
|
||||
eof = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t byte;
|
||||
AAsset_read(a, &byte, 1);
|
||||
pos++;
|
||||
return byte;
|
||||
}
|
||||
|
||||
uint64_t FileAccessAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||
|
||||
int r = AAsset_read(a, p_dst, p_length);
|
||||
|
||||
if (pos + p_length > len) {
|
||||
eof = true;
|
||||
}
|
||||
|
||||
if (r >= 0) {
|
||||
pos += r;
|
||||
if (pos > len) {
|
||||
pos = len;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
Error FileAccessAndroid::get_error() const {
|
||||
return eof ? ERR_FILE_EOF : OK; // not sure what else it may happen
|
||||
}
|
||||
|
||||
void FileAccessAndroid::flush() {
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
void FileAccessAndroid::store_8(uint8_t p_dest) {
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
bool FileAccessAndroid::file_exists(const String &p_path) {
|
||||
String path = fix_path(p_path).simplify_path();
|
||||
if (path.begins_with("/"))
|
||||
path = path.substr(1, path.length());
|
||||
else if (path.begins_with("res://"))
|
||||
path = path.substr(6, path.length());
|
||||
|
||||
AAsset *at = AAssetManager_open(asset_manager, path.utf8().get_data(), AASSET_MODE_STREAMING);
|
||||
|
||||
if (!at)
|
||||
return false;
|
||||
|
||||
AAsset_close(at);
|
||||
return true;
|
||||
}
|
||||
|
||||
FileAccessAndroid::FileAccessAndroid() {
|
||||
a = NULL;
|
||||
eof = false;
|
||||
}
|
||||
|
||||
FileAccessAndroid::~FileAccessAndroid() {
|
||||
close();
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
#ifndef FILE_ACCESS_ANDROID_H
|
||||
#define FILE_ACCESS_ANDROID_H
|
||||
/*************************************************************************/
|
||||
/* file_access_android.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "core/os/file_access.h"
|
||||
#include <android/asset_manager.h>
|
||||
#include <android/log.h>
|
||||
#include <stdio.h>
|
||||
//#include <android_native_app_glue.h>
|
||||
|
||||
class FileAccessAndroid : public FileAccess {
|
||||
mutable AAsset *a;
|
||||
mutable uint64_t len;
|
||||
mutable uint64_t pos;
|
||||
mutable bool eof;
|
||||
|
||||
String absolute_path;
|
||||
String path_src;
|
||||
|
||||
public:
|
||||
static AAssetManager *asset_manager;
|
||||
|
||||
virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file
|
||||
virtual void close(); ///< close a file
|
||||
virtual bool is_open() const; ///< true when file is open
|
||||
|
||||
/// returns the path for the current open file
|
||||
virtual String get_path() const;
|
||||
/// returns the absolute path for the current open file
|
||||
virtual String get_path_absolute() const;
|
||||
|
||||
virtual void seek(uint64_t p_position); ///< seek to a given position
|
||||
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
|
||||
virtual uint64_t get_position() const; ///< get position in the file
|
||||
virtual uint64_t get_len() const; ///< get size of the file
|
||||
|
||||
virtual bool eof_reached() const; ///< reading passed EOF
|
||||
|
||||
virtual uint8_t get_8() const; ///< get a byte
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const;
|
||||
|
||||
virtual Error get_error() const; ///< get last error
|
||||
|
||||
virtual void flush();
|
||||
virtual void store_8(uint8_t p_dest); ///< store a byte
|
||||
|
||||
virtual bool file_exists(const String &p_path); ///< return true if a file exists
|
||||
|
||||
virtual uint64_t _get_modified_time(const String &p_file) { return 0; }
|
||||
virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; }
|
||||
virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; }
|
||||
|
||||
//static void make_default();
|
||||
|
||||
FileAccessAndroid();
|
||||
~FileAccessAndroid();
|
||||
};
|
||||
|
||||
#endif // FILE_ACCESS_ANDROID_H
|
@ -1,343 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* file_access_filesystem_jandroid.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "file_access_filesystem_jandroid.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "thread_jandroid.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
jobject FileAccessFilesystemJAndroid::file_access_handler = nullptr;
|
||||
jclass FileAccessFilesystemJAndroid::cls;
|
||||
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_open = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_get_size = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_seek = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_seek_end = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_read = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_tell = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_eof = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_set_eof = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_close = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_write = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_flush = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_exists = nullptr;
|
||||
jmethodID FileAccessFilesystemJAndroid::_file_last_modified = nullptr;
|
||||
|
||||
String FileAccessFilesystemJAndroid::get_path() const {
|
||||
return path_src;
|
||||
}
|
||||
|
||||
String FileAccessFilesystemJAndroid::get_path_absolute() const {
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
Error FileAccessFilesystemJAndroid::_open(const String &p_path, int p_mode_flags) {
|
||||
if (is_open()) {
|
||||
close();
|
||||
}
|
||||
|
||||
if (_file_open) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, ERR_UNCONFIGURED);
|
||||
|
||||
String path = fix_path(p_path).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
int res = env->CallIntMethod(file_access_handler, _file_open, js, p_mode_flags);
|
||||
env->DeleteLocalRef(js);
|
||||
|
||||
if (res <= 0) {
|
||||
switch (res) {
|
||||
case 0:
|
||||
default:
|
||||
return ERR_FILE_CANT_OPEN;
|
||||
|
||||
case -1:
|
||||
return ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
id = res;
|
||||
path_src = p_path;
|
||||
absolute_path = path;
|
||||
return OK;
|
||||
} else {
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::close() {
|
||||
if (!is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_file_close) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND(env == nullptr);
|
||||
env->CallVoidMethod(file_access_handler, _file_close, id);
|
||||
}
|
||||
id = 0;
|
||||
}
|
||||
|
||||
bool FileAccessFilesystemJAndroid::is_open() const {
|
||||
return id != 0;
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::seek(uint64_t p_position) {
|
||||
if (_file_seek) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND(env == nullptr);
|
||||
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||
env->CallVoidMethod(file_access_handler, _file_seek, id, p_position);
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::seek_end(int64_t p_position) {
|
||||
if (_file_seek_end) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND(env == nullptr);
|
||||
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||
env->CallVoidMethod(file_access_handler, _file_seek_end, id, p_position);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::get_position() const {
|
||||
if (_file_tell) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, 0);
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
|
||||
return env->CallLongMethod(file_access_handler, _file_tell, id);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::get_len() const {
|
||||
if (_file_get_size) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, 0);
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
|
||||
return env->CallLongMethod(file_access_handler, _file_get_size, id);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileAccessFilesystemJAndroid::eof_reached() const {
|
||||
if (_file_eof) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, false);
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), false, "File must be opened before use.");
|
||||
return env->CallBooleanMethod(file_access_handler, _file_eof, id);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::_set_eof(bool eof) {
|
||||
if (_file_set_eof) {
|
||||
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND(env == nullptr);
|
||||
env->CallVoidMethod(file_access_handler, _file_set_eof, id, eof);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t FileAccessFilesystemJAndroid::get_8() const {
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
|
||||
uint8_t byte;
|
||||
get_buffer(&byte, 1);
|
||||
return byte;
|
||||
}
|
||||
|
||||
String FileAccessFilesystemJAndroid::get_line() const {
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), String(), "File must be opened before use.");
|
||||
|
||||
const size_t buffer_size_limit = 2048;
|
||||
const uint64_t file_size = get_len();
|
||||
const uint64_t start_position = get_position();
|
||||
|
||||
String result;
|
||||
LocalVector<uint8_t> line_buffer;
|
||||
size_t current_buffer_size = 0;
|
||||
uint64_t line_buffer_position = 0;
|
||||
|
||||
while (true) {
|
||||
size_t line_buffer_size = MIN(buffer_size_limit, file_size - get_position());
|
||||
if (line_buffer_size <= 0) {
|
||||
const_cast<FileAccessFilesystemJAndroid *>(this)->_set_eof(true);
|
||||
break;
|
||||
}
|
||||
|
||||
current_buffer_size += line_buffer_size;
|
||||
line_buffer.resize(current_buffer_size);
|
||||
|
||||
uint64_t bytes_read = get_buffer(&line_buffer[line_buffer_position], current_buffer_size - line_buffer_position);
|
||||
if (bytes_read <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (; bytes_read > 0; line_buffer_position++, bytes_read--) {
|
||||
uint8_t elem = line_buffer[line_buffer_position];
|
||||
if (elem == '\n' || elem == '\0') {
|
||||
// Found the end of the line
|
||||
const_cast<FileAccessFilesystemJAndroid *>(this)->seek(start_position + line_buffer_position + 1);
|
||||
if (result.parse_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
|
||||
return String();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.parse_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
|
||||
return String();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||
if (_file_read) {
|
||||
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
|
||||
if (p_length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, 0);
|
||||
|
||||
jobject j_buffer = env->NewDirectByteBuffer(p_dst, p_length);
|
||||
int length = env->CallIntMethod(file_access_handler, _file_read, id, j_buffer);
|
||||
env->DeleteLocalRef(j_buffer);
|
||||
return length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::store_8(uint8_t p_dest) {
|
||||
store_buffer(&p_dest, 1);
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
if (_file_write) {
|
||||
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||
if (p_length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND(env == nullptr);
|
||||
|
||||
jobject j_buffer = env->NewDirectByteBuffer((void *)p_src, p_length);
|
||||
env->CallVoidMethod(file_access_handler, _file_write, id, j_buffer);
|
||||
env->DeleteLocalRef(j_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
Error FileAccessFilesystemJAndroid::get_error() const {
|
||||
if (eof_reached()) {
|
||||
return ERR_FILE_EOF;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::flush() {
|
||||
if (_file_flush) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND(env == nullptr);
|
||||
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||
env->CallVoidMethod(file_access_handler, _file_flush, id);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileAccessFilesystemJAndroid::file_exists(const String &p_path) {
|
||||
if (_file_exists) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, false);
|
||||
|
||||
String path = fix_path(p_path).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
bool result = env->CallBooleanMethod(file_access_handler, _file_exists, js);
|
||||
env->DeleteLocalRef(js);
|
||||
return result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t FileAccessFilesystemJAndroid::_get_modified_time(const String &p_file) {
|
||||
if (_file_last_modified) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_COND_V(env == nullptr, false);
|
||||
|
||||
String path = fix_path(p_file).simplify_path();
|
||||
jstring js = env->NewStringUTF(path.utf8().get_data());
|
||||
uint64_t result = env->CallLongMethod(file_access_handler, _file_last_modified, js);
|
||||
env->DeleteLocalRef(js);
|
||||
return result;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
file_access_handler = env->NewGlobalRef(p_file_access_handler);
|
||||
|
||||
jclass c = env->GetObjectClass(file_access_handler);
|
||||
cls = (jclass)env->NewGlobalRef(c);
|
||||
|
||||
_file_open = env->GetMethodID(cls, "fileOpen", "(Ljava/lang/String;I)I");
|
||||
_file_get_size = env->GetMethodID(cls, "fileGetSize", "(I)J");
|
||||
_file_tell = env->GetMethodID(cls, "fileGetPosition", "(I)J");
|
||||
_file_eof = env->GetMethodID(cls, "isFileEof", "(I)Z");
|
||||
_file_set_eof = env->GetMethodID(cls, "setFileEof", "(IZ)V");
|
||||
_file_seek = env->GetMethodID(cls, "fileSeek", "(IJ)V");
|
||||
_file_seek_end = env->GetMethodID(cls, "fileSeekFromEnd", "(IJ)V");
|
||||
_file_read = env->GetMethodID(cls, "fileRead", "(ILjava/nio/ByteBuffer;)I");
|
||||
_file_close = env->GetMethodID(cls, "fileClose", "(I)V");
|
||||
_file_write = env->GetMethodID(cls, "fileWrite", "(ILjava/nio/ByteBuffer;)V");
|
||||
_file_flush = env->GetMethodID(cls, "fileFlush", "(I)V");
|
||||
_file_exists = env->GetMethodID(cls, "fileExists", "(Ljava/lang/String;)Z");
|
||||
_file_last_modified = env->GetMethodID(cls, "fileLastModified", "(Ljava/lang/String;)J");
|
||||
}
|
||||
|
||||
FileAccessFilesystemJAndroid::FileAccessFilesystemJAndroid() {
|
||||
id = 0;
|
||||
}
|
||||
|
||||
FileAccessFilesystemJAndroid::~FileAccessFilesystemJAndroid() {
|
||||
if (is_open()) {
|
||||
close();
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* file_access_filesystem_jandroid.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef FILE_ACCESS_FILESYSTEM_JANDROID_H
|
||||
#define FILE_ACCESS_FILESYSTEM_JANDROID_H
|
||||
|
||||
#include "core/os/file_access.h"
|
||||
#include "java_pandemonium_lib_jni.h"
|
||||
|
||||
class FileAccessFilesystemJAndroid : public FileAccess {
|
||||
static jobject file_access_handler;
|
||||
static jclass cls;
|
||||
|
||||
static jmethodID _file_open;
|
||||
static jmethodID _file_get_size;
|
||||
static jmethodID _file_seek;
|
||||
static jmethodID _file_seek_end;
|
||||
static jmethodID _file_tell;
|
||||
static jmethodID _file_eof;
|
||||
static jmethodID _file_set_eof;
|
||||
static jmethodID _file_read;
|
||||
static jmethodID _file_write;
|
||||
static jmethodID _file_flush;
|
||||
static jmethodID _file_close;
|
||||
static jmethodID _file_exists;
|
||||
static jmethodID _file_last_modified;
|
||||
|
||||
int id;
|
||||
String absolute_path;
|
||||
String path_src;
|
||||
|
||||
void _set_eof(bool eof);
|
||||
|
||||
public:
|
||||
virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file
|
||||
virtual void close(); ///< close a file
|
||||
virtual bool is_open() const; ///< true when file is open
|
||||
|
||||
/// returns the path for the current open file
|
||||
virtual String get_path() const;
|
||||
/// returns the absolute path for the current open file
|
||||
virtual String get_path_absolute() const;
|
||||
|
||||
virtual void seek(uint64_t p_position); ///< seek to a given position
|
||||
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
|
||||
virtual uint64_t get_position() const; ///< get position in the file
|
||||
virtual uint64_t get_len() const; ///< get size of the file
|
||||
|
||||
virtual bool eof_reached() const; ///< reading passed EOF
|
||||
|
||||
virtual uint8_t get_8() const; ///< get a byte
|
||||
virtual String get_line() const; ///< get a line
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const;
|
||||
|
||||
virtual Error get_error() const; ///< get last error
|
||||
|
||||
virtual void flush();
|
||||
virtual void store_8(uint8_t p_dest); ///< store a byte
|
||||
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length);
|
||||
|
||||
virtual bool file_exists(const String &p_path); ///< return true if a file exists
|
||||
|
||||
static void setup(jobject p_file_access_handler);
|
||||
|
||||
virtual uint64_t _get_modified_time(const String &p_file);
|
||||
virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; }
|
||||
virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; }
|
||||
|
||||
FileAccessFilesystemJAndroid();
|
||||
~FileAccessFilesystemJAndroid();
|
||||
};
|
||||
|
||||
#endif // FILE_ACCESS_FILESYSTEM_JANDROID_H
|
@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.pandemonium.game"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0"
|
||||
android:installLocation="auto" >
|
||||
|
||||
<!-- Adding custom text to the manifest is fine, but do it outside the custom USER and APPLICATION BEGIN/END comments, -->
|
||||
<!-- as that gets rewritten. -->
|
||||
|
||||
<supports-screens
|
||||
android:smallScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:largeScreens="true"
|
||||
android:xlargeScreens="true" />
|
||||
|
||||
<!-- glEsVersion is modified by the exporter, changing this value here has no effect. -->
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
android:required="true" />
|
||||
|
||||
<!-- Custom user permissions XML added by add-ons. It's recommended to add them from the export preset, though. -->
|
||||
<!--CHUNK_USER_PERMISSIONS_BEGIN-->
|
||||
<!--CHUNK_USER_PERMISSIONS_END-->
|
||||
|
||||
<!-- Any tag in this line after android:icon will be erased when doing custom builds. -->
|
||||
<!-- If you want to add tags manually, do before it. -->
|
||||
<!-- WARNING: This should stay on a single line until the parsing code is improved. See GH-32414. -->
|
||||
<application android:label="@string/pandemonium_project_name_string" android:allowBackup="false" android:isGame="true" android:hasFragileUserData="false" android:requestLegacyExternalStorage="false" tools:ignore="GoogleAppIndexingWarning" android:icon="@mipmap/icon" >
|
||||
|
||||
<!-- Records the version of the Pandemonium editor used for building -->
|
||||
<meta-data
|
||||
android:name="org.pandemoniumengine.editor.version"
|
||||
android:value="${pandemoniumEditorVersion}" />
|
||||
|
||||
<!-- The following metadata values are replaced when Pandemonium exports, modifying them here has no effect. -->
|
||||
<!-- Do these changes in the export preset. Adding new ones is fine. -->
|
||||
|
||||
<activity
|
||||
android:name=".PandemoniumApp"
|
||||
android:label="@string/pandemonium_project_name_string"
|
||||
android:theme="@style/PandemoniumAppSplashTheme"
|
||||
android:launchMode="singleInstance"
|
||||
android:excludeFromRecents="false"
|
||||
android:exported="true"
|
||||
android:screenOrientation="landscape"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:resizeableActivity="false"
|
||||
tools:ignore="UnusedAttribute" >
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
||||
<!-- Enable access to OpenXR on Oculus mobile devices, no-op on other Android
|
||||
platforms. -->
|
||||
<category android:name="com.oculus.intent.category.VR" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Custom application XML added by add-ons. -->
|
||||
<!--CHUNK_APPLICATION_BEGIN-->
|
||||
<!--CHUNK_APPLICATION_END-->
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -1,8 +0,0 @@
|
||||
apply plugin: 'com.android.asset-pack'
|
||||
|
||||
assetPack {
|
||||
packName = "installTime" // Directory name for the asset pack
|
||||
dynamicDelivery {
|
||||
deliveryType = "install-time" // Delivery mode
|
||||
}
|
||||
}
|
2
platform/android/java/app/assets/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
@ -1,288 +0,0 @@
|
||||
// Gradle build config for Pandemonium Engine's Android port.
|
||||
//
|
||||
// Do not remove/modify comments ending with BEGIN/END, they are used to inject
|
||||
// addon-specific configuration.
|
||||
buildscript {
|
||||
apply from: 'config.gradle'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
//CHUNK_BUILDSCRIPT_REPOSITORIES_BEGIN
|
||||
//CHUNK_BUILDSCRIPT_REPOSITORIES_END
|
||||
}
|
||||
dependencies {
|
||||
classpath libraries.androidGradlePlugin
|
||||
classpath libraries.kotlinGradlePlugin
|
||||
//CHUNK_BUILDSCRIPT_DEPENDENCIES_BEGIN
|
||||
//CHUNK_BUILDSCRIPT_DEPENDENCIES_END
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
apply from: 'config.gradle'
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
//CHUNK_ALLPROJECTS_REPOSITORIES_BEGIN
|
||||
//CHUNK_ALLPROJECTS_REPOSITORIES_END
|
||||
|
||||
// Pandemonium user plugins custom maven repos
|
||||
String[] mavenRepos = getPandemoniumPluginsMavenRepos()
|
||||
if (mavenRepos != null && mavenRepos.size() > 0) {
|
||||
for (String repoUrl : mavenRepos) {
|
||||
maven {
|
||||
url repoUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
// Initializes a placeholder for the devImplementation dependency configuration.
|
||||
devImplementation {}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation libraries.kotlinStdLib
|
||||
implementation libraries.androidxFragment
|
||||
|
||||
if (rootProject.findProject(":lib")) {
|
||||
implementation project(":lib")
|
||||
} else if (rootProject.findProject(":pandemonium:lib")) {
|
||||
implementation project(":pandemonium:lib")
|
||||
} else {
|
||||
// Custom build mode. In this scenario this project is the only one around and the Pandemonium
|
||||
// library is available through the pre-generated pandemonium-lib.*.aar android archive files.
|
||||
debugImplementation fileTree(dir: 'libs/debug', include: ['*.jar', '*.aar'])
|
||||
devImplementation fileTree(dir: 'libs/dev', include: ['*.jar', '*.aar'])
|
||||
releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar'])
|
||||
}
|
||||
|
||||
// Pandemonium user plugins remote dependencies
|
||||
String[] remoteDeps = getPandemoniumPluginsRemoteBinaries()
|
||||
if (remoteDeps != null && remoteDeps.size() > 0) {
|
||||
for (String dep : remoteDeps) {
|
||||
implementation dep
|
||||
}
|
||||
}
|
||||
|
||||
// Pandemonium user plugins local dependencies
|
||||
String[] pluginsBinaries = getPandemoniumPluginsLocalBinaries()
|
||||
if (pluginsBinaries != null && pluginsBinaries.size() > 0) {
|
||||
implementation files(pluginsBinaries)
|
||||
}
|
||||
|
||||
//CHUNK_DEPENDENCIES_BEGIN
|
||||
//CHUNK_DEPENDENCIES_END
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
buildToolsVersion versions.buildTools
|
||||
ndkVersion versions.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility versions.javaVersion
|
||||
targetCompatibility versions.javaVersion
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = versions.javaVersion
|
||||
}
|
||||
|
||||
assetPacks = [":assetPacks:installTime"]
|
||||
|
||||
defaultConfig {
|
||||
// The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Pandemonium projects.
|
||||
aaptOptions {
|
||||
ignoreAssetsPattern "!.svn:!.git:!.gitignore:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
|
||||
}
|
||||
|
||||
ndk {
|
||||
String[] export_abi_list = getExportEnabledABIs()
|
||||
abiFilters export_abi_list
|
||||
}
|
||||
|
||||
manifestPlaceholders = [pandemoniumEditorVersion: getPandemoniumEditorVersion()]
|
||||
|
||||
// Feel free to modify the application id to your own.
|
||||
applicationId getExportPackageName()
|
||||
versionCode getExportVersionCode()
|
||||
versionName getExportVersionName()
|
||||
minSdkVersion getExportMinSdkVersion()
|
||||
targetSdkVersion getExportTargetSdkVersion()
|
||||
|
||||
missingDimensionStrategy 'products', 'template'
|
||||
//CHUNK_ANDROID_DEFAULTCONFIG_BEGIN
|
||||
//CHUNK_ANDROID_DEFAULTCONFIG_END
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
disable 'MissingTranslation', 'UnusedResources'
|
||||
}
|
||||
|
||||
ndkVersion versions.ndkVersion
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LICENSE'
|
||||
exclude 'META-INF/NOTICE'
|
||||
|
||||
// 'doNotStrip' is enabled for development within Android Studio
|
||||
if (shouldNotStrip()) {
|
||||
doNotStrip '**/*.so'
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
if (hasCustomDebugKeystore()) {
|
||||
storeFile new File(getDebugKeystoreFile())
|
||||
storePassword getDebugKeystorePassword()
|
||||
keyAlias getDebugKeyAlias()
|
||||
keyPassword getDebugKeystorePassword()
|
||||
}
|
||||
}
|
||||
|
||||
release {
|
||||
File keystoreFile = new File(getReleaseKeystoreFile())
|
||||
if (keystoreFile.isFile()) {
|
||||
storeFile keystoreFile
|
||||
storePassword getReleaseKeystorePassword()
|
||||
keyAlias getReleaseKeyAlias()
|
||||
keyPassword getReleaseKeystorePassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
debug {
|
||||
// Signing and zip-aligning are skipped for prebuilt builds, but
|
||||
// performed for custom builds.
|
||||
zipAlignEnabled shouldZipAlign()
|
||||
if (shouldSign()) {
|
||||
signingConfig signingConfigs.debug
|
||||
} else {
|
||||
signingConfig null
|
||||
}
|
||||
}
|
||||
|
||||
dev {
|
||||
initWith debug
|
||||
// Signing and zip-aligning are skipped for prebuilt builds, but
|
||||
// performed for custom builds.
|
||||
zipAlignEnabled shouldZipAlign()
|
||||
if (shouldSign()) {
|
||||
signingConfig signingConfigs.debug
|
||||
} else {
|
||||
signingConfig null
|
||||
}
|
||||
}
|
||||
|
||||
release {
|
||||
// Signing and zip-aligning are skipped for prebuilt builds, but
|
||||
// performed for custom builds.
|
||||
zipAlignEnabled shouldZipAlign()
|
||||
if (shouldSign()) {
|
||||
signingConfig signingConfigs.release
|
||||
} else {
|
||||
signingConfig null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = [
|
||||
'src'
|
||||
//DIR_SRC_BEGIN
|
||||
//DIR_SRC_END
|
||||
]
|
||||
res.srcDirs = [
|
||||
'res'
|
||||
//DIR_RES_BEGIN
|
||||
//DIR_RES_END
|
||||
]
|
||||
aidl.srcDirs = [
|
||||
'aidl'
|
||||
//DIR_AIDL_BEGIN
|
||||
//DIR_AIDL_END
|
||||
]
|
||||
assets.srcDirs = [
|
||||
'assets'
|
||||
//DIR_ASSETS_BEGIN
|
||||
//DIR_ASSETS_END
|
||||
]
|
||||
}
|
||||
debug.jniLibs.srcDirs = [
|
||||
'libs/debug'
|
||||
//DIR_JNI_DEBUG_BEGIN
|
||||
//DIR_JNI_DEBUG_END
|
||||
]
|
||||
dev.jniLibs.srcDirs = [
|
||||
'libs/dev'
|
||||
//DIR_JNI_DEV_BEGIN
|
||||
//DIR_JNI_DEV_END
|
||||
]
|
||||
release.jniLibs.srcDirs = [
|
||||
'libs/release'
|
||||
//DIR_JNI_RELEASE_BEGIN
|
||||
//DIR_JNI_RELEASE_END
|
||||
]
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all { output ->
|
||||
output.outputFileName = "android_${variant.name}.apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task copyAndRenameDebugApk(type: Copy) {
|
||||
from "$buildDir/outputs/apk/debug/android_debug.apk"
|
||||
into getExportPath()
|
||||
rename "android_debug.apk", getExportFilename()
|
||||
}
|
||||
|
||||
task copyAndRenameDevApk(type: Copy) {
|
||||
from "$buildDir/outputs/apk/dev/android_dev.apk"
|
||||
into getExportPath()
|
||||
rename "android_dev.apk", getExportFilename()
|
||||
}
|
||||
|
||||
task copyAndRenameReleaseApk(type: Copy) {
|
||||
from "$buildDir/outputs/apk/release/android_release.apk"
|
||||
into getExportPath()
|
||||
rename "android_release.apk", getExportFilename()
|
||||
}
|
||||
|
||||
task copyAndRenameDebugAab(type: Copy) {
|
||||
from "$buildDir/outputs/bundle/debug/build-debug.aab"
|
||||
into getExportPath()
|
||||
rename "build-debug.aab", getExportFilename()
|
||||
}
|
||||
|
||||
task copyAndRenameDevAab(type: Copy) {
|
||||
from "$buildDir/outputs/bundle/dev/build-dev.aab"
|
||||
into getExportPath()
|
||||
rename "build-dev.aab", getExportFilename()
|
||||
}
|
||||
|
||||
task copyAndRenameReleaseAab(type: Copy) {
|
||||
from "$buildDir/outputs/bundle/release/build-release.aab"
|
||||
into getExportPath()
|
||||
rename "build-release.aab", getExportFilename()
|
||||
}
|
||||
|
||||
//CHUNK_GLOBAL_BEGIN
|
||||
//CHUNK_GLOBAL_END
|
@ -1,315 +0,0 @@
|
||||
ext.versions = [
|
||||
androidGradlePlugin: '7.2.1',
|
||||
compileSdk : 33,
|
||||
minSdk : 19, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION'
|
||||
targetSdk : 33, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
|
||||
buildTools : '33.0.2',
|
||||
kotlinVersion : '1.7.0',
|
||||
fragmentVersion : '1.3.6',
|
||||
nexusPublishVersion: '1.1.0',
|
||||
javaVersion : 11,
|
||||
ndkVersion : '23.2.8568313' // Also update 'platform/android/detect.py#get_ndk_version' when this is updated.
|
||||
|
||||
]
|
||||
|
||||
ext.libraries = [
|
||||
androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin",
|
||||
kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion",
|
||||
kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlinVersion",
|
||||
androidxFragment : "androidx.fragment:fragment:$versions.fragmentVersion",
|
||||
]
|
||||
|
||||
ext.getExportPackageName = { ->
|
||||
// Retrieve the app id from the project property set by the Pandemonium build command.
|
||||
String appId = project.hasProperty("export_package_name") ? project.property("export_package_name") : ""
|
||||
// Check if the app id is valid, otherwise use the default.
|
||||
if (appId == null || appId.isEmpty()) {
|
||||
appId = "com.pandemonium.game"
|
||||
}
|
||||
return appId
|
||||
}
|
||||
|
||||
ext.getExportVersionCode = { ->
|
||||
String versionCode = project.hasProperty("export_version_code") ? project.property("export_version_code") : ""
|
||||
if (versionCode == null || versionCode.isEmpty()) {
|
||||
versionCode = "1"
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(versionCode)
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
ext.getExportVersionName = { ->
|
||||
String versionName = project.hasProperty("export_version_name") ? project.property("export_version_name") : ""
|
||||
if (versionName == null || versionName.isEmpty()) {
|
||||
versionName = "1.0"
|
||||
}
|
||||
return versionName
|
||||
}
|
||||
|
||||
ext.getExportMinSdkVersion = { ->
|
||||
String minSdkVersion = project.hasProperty("export_version_min_sdk") ? project.property("export_version_min_sdk") : ""
|
||||
if (minSdkVersion == null || minSdkVersion.isEmpty()) {
|
||||
minSdkVersion = "$versions.minSdk"
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(minSdkVersion)
|
||||
} catch (NumberFormatException ignored) {
|
||||
return versions.minSdk
|
||||
}
|
||||
}
|
||||
|
||||
ext.getExportTargetSdkVersion = { ->
|
||||
String targetSdkVersion = project.hasProperty("export_version_target_sdk") ? project.property("export_version_target_sdk") : ""
|
||||
if (targetSdkVersion == null || targetSdkVersion.isEmpty()) {
|
||||
targetSdkVersion = "$versions.targetSdk"
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(targetSdkVersion)
|
||||
} catch (NumberFormatException ignored) {
|
||||
return versions.targetSdk
|
||||
}
|
||||
}
|
||||
|
||||
ext.getPandemoniumEditorVersion = { ->
|
||||
String editorVersion = project.hasProperty("pandemonium_editor_version") ? project.property("pandemonium_editor_version") : ""
|
||||
if (editorVersion == null || editorVersion.isEmpty()) {
|
||||
// Try the library version first
|
||||
editorVersion = getPandemoniumLibraryVersionName()
|
||||
|
||||
if (editorVersion.isEmpty()) {
|
||||
// Fallback value.
|
||||
editorVersion = "custom_build"
|
||||
}
|
||||
}
|
||||
return editorVersion
|
||||
}
|
||||
|
||||
ext.getPandemoniumLibraryVersionCode = { ->
|
||||
String versionName = ""
|
||||
int versionCode = 1
|
||||
(versionName, versionCode) = getPandemoniumLibraryVersion()
|
||||
return versionCode
|
||||
}
|
||||
|
||||
ext.getPandemoniumLibraryVersionName = { ->
|
||||
String versionName = ""
|
||||
int versionCode = 1
|
||||
(versionName, versionCode) = getPandemoniumLibraryVersion()
|
||||
return versionName
|
||||
}
|
||||
|
||||
ext.getPandemoniumLibraryVersion = { ->
|
||||
// Attempt to read the version from the `version.py` file.
|
||||
String libraryVersionName = ""
|
||||
int libraryVersionCode = 1
|
||||
|
||||
File versionFile = new File("../../../version.py")
|
||||
if (versionFile.isFile()) {
|
||||
List<String> requiredKeys = ["major", "minor", "patch", "status", "module_config"]
|
||||
def map = [:]
|
||||
|
||||
List<String> lines = versionFile.readLines()
|
||||
for (String line in lines) {
|
||||
String[] keyValue = line.split("=")
|
||||
String key = keyValue[0].trim()
|
||||
String value = keyValue[1].trim().replaceAll("\"", "")
|
||||
|
||||
if (requiredKeys.contains(key)) {
|
||||
if (!value.isEmpty()) {
|
||||
map[key] = value
|
||||
}
|
||||
requiredKeys.remove(key)
|
||||
}
|
||||
}
|
||||
|
||||
if (requiredKeys.empty) {
|
||||
libraryVersionName = map.values().join(".")
|
||||
try {
|
||||
libraryVersionCode = Integer.parseInt(map["patch"]) +
|
||||
(Integer.parseInt(map["minor"]) * 100) +
|
||||
(Integer.parseInt(map["major"]) * 10000)
|
||||
} catch (NumberFormatException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (libraryVersionName.isEmpty()) {
|
||||
// Fallback value in case we're unable to read the file.
|
||||
libraryVersionName = "custom_build"
|
||||
}
|
||||
return [libraryVersionName, libraryVersionCode]
|
||||
}
|
||||
|
||||
final String VALUE_SEPARATOR_REGEX = "\\|"
|
||||
|
||||
// get the list of ABIs the project should be exported to
|
||||
ext.getExportEnabledABIs = { ->
|
||||
String enabledABIs = project.hasProperty("export_enabled_abis") ? project.property("export_enabled_abis") : "";
|
||||
if (enabledABIs == null || enabledABIs.isEmpty()) {
|
||||
enabledABIs = "armeabi-v7a|arm64-v8a|x86|x86_64|"
|
||||
}
|
||||
Set<String> exportAbiFilter = [];
|
||||
for (String abi_name : enabledABIs.split(VALUE_SEPARATOR_REGEX)) {
|
||||
if (!abi_name.trim().isEmpty()){
|
||||
exportAbiFilter.add(abi_name);
|
||||
}
|
||||
}
|
||||
return exportAbiFilter;
|
||||
}
|
||||
|
||||
ext.getExportPath = {
|
||||
String exportPath = project.hasProperty("export_path") ? project.property("export_path") : ""
|
||||
if (exportPath == null || exportPath.isEmpty()) {
|
||||
exportPath = "."
|
||||
}
|
||||
return exportPath
|
||||
}
|
||||
|
||||
ext.getExportFilename = {
|
||||
String exportFilename = project.hasProperty("export_filename") ? project.property("export_filename") : ""
|
||||
if (exportFilename == null || exportFilename.isEmpty()) {
|
||||
exportFilename = "pandemonium_android"
|
||||
}
|
||||
return exportFilename
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'plugins_maven_repos' property and return the list
|
||||
* of maven repos.
|
||||
*/
|
||||
ext.getPandemoniumPluginsMavenRepos = { ->
|
||||
Set<String> mavenRepos = []
|
||||
|
||||
// Retrieve the list of maven repos.
|
||||
if (project.hasProperty("plugins_maven_repos")) {
|
||||
String mavenReposProperty = project.property("plugins_maven_repos")
|
||||
if (mavenReposProperty != null && !mavenReposProperty.trim().isEmpty()) {
|
||||
for (String mavenRepoUrl : mavenReposProperty.split(VALUE_SEPARATOR_REGEX)) {
|
||||
mavenRepos += mavenRepoUrl.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mavenRepos
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'plugins_remote_binaries' property and return
|
||||
* it for inclusion in the build dependencies.
|
||||
*/
|
||||
ext.getPandemoniumPluginsRemoteBinaries = { ->
|
||||
Set<String> remoteDeps = []
|
||||
|
||||
// Retrieve the list of remote plugins binaries.
|
||||
if (project.hasProperty("plugins_remote_binaries")) {
|
||||
String remoteDepsList = project.property("plugins_remote_binaries")
|
||||
if (remoteDepsList != null && !remoteDepsList.trim().isEmpty()) {
|
||||
for (String dep: remoteDepsList.split(VALUE_SEPARATOR_REGEX)) {
|
||||
remoteDeps += dep.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
return remoteDeps
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the project properties for the 'plugins_local_binaries' property and return
|
||||
* their binaries for inclusion in the build dependencies.
|
||||
*/
|
||||
ext.getPandemoniumPluginsLocalBinaries = { ->
|
||||
Set<String> binDeps = []
|
||||
|
||||
// Retrieve the list of local plugins binaries.
|
||||
if (project.hasProperty("plugins_local_binaries")) {
|
||||
String pluginsList = project.property("plugins_local_binaries")
|
||||
if (pluginsList != null && !pluginsList.trim().isEmpty()) {
|
||||
for (String plugin : pluginsList.split(VALUE_SEPARATOR_REGEX)) {
|
||||
binDeps += plugin.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return binDeps
|
||||
}
|
||||
|
||||
ext.getDebugKeystoreFile = { ->
|
||||
String keystoreFile = project.hasProperty("debug_keystore_file") ? project.property("debug_keystore_file") : ""
|
||||
if (keystoreFile == null || keystoreFile.isEmpty()) {
|
||||
keystoreFile = "."
|
||||
}
|
||||
return keystoreFile
|
||||
}
|
||||
|
||||
ext.hasCustomDebugKeystore = { ->
|
||||
File keystoreFile = new File(getDebugKeystoreFile())
|
||||
return keystoreFile.isFile()
|
||||
}
|
||||
|
||||
ext.getDebugKeystorePassword = { ->
|
||||
String keystorePassword = project.hasProperty("debug_keystore_password") ? project.property("debug_keystore_password") : ""
|
||||
if (keystorePassword == null || keystorePassword.isEmpty()) {
|
||||
keystorePassword = "android"
|
||||
}
|
||||
return keystorePassword
|
||||
}
|
||||
|
||||
ext.getDebugKeyAlias = { ->
|
||||
String keyAlias = project.hasProperty("debug_keystore_alias") ? project.property("debug_keystore_alias") : ""
|
||||
if (keyAlias == null || keyAlias.isEmpty()) {
|
||||
keyAlias = "androiddebugkey"
|
||||
}
|
||||
return keyAlias
|
||||
}
|
||||
|
||||
ext.getReleaseKeystoreFile = { ->
|
||||
String keystoreFile = project.hasProperty("release_keystore_file") ? project.property("release_keystore_file") : ""
|
||||
if (keystoreFile == null || keystoreFile.isEmpty()) {
|
||||
keystoreFile = "."
|
||||
}
|
||||
return keystoreFile
|
||||
}
|
||||
|
||||
ext.getReleaseKeystorePassword = { ->
|
||||
String keystorePassword = project.hasProperty("release_keystore_password") ? project.property("release_keystore_password") : ""
|
||||
return keystorePassword
|
||||
}
|
||||
|
||||
ext.getReleaseKeyAlias = { ->
|
||||
String keyAlias = project.hasProperty("release_keystore_alias") ? project.property("release_keystore_alias") : ""
|
||||
return keyAlias
|
||||
}
|
||||
|
||||
ext.isAndroidStudio = { ->
|
||||
def sysProps = System.getProperties()
|
||||
return sysProps != null && sysProps['idea.platform.prefix'] != null
|
||||
}
|
||||
|
||||
ext.shouldZipAlign = { ->
|
||||
String zipAlignFlag = project.hasProperty("perform_zipalign") ? project.property("perform_zipalign") : ""
|
||||
if (zipAlignFlag == null || zipAlignFlag.isEmpty()) {
|
||||
if (isAndroidStudio()) {
|
||||
zipAlignFlag = "true"
|
||||
} else {
|
||||
zipAlignFlag = "false"
|
||||
}
|
||||
}
|
||||
return Boolean.parseBoolean(zipAlignFlag)
|
||||
}
|
||||
|
||||
ext.shouldSign = { ->
|
||||
String signFlag = project.hasProperty("perform_signing") ? project.property("perform_signing") : ""
|
||||
if (signFlag == null || signFlag.isEmpty()) {
|
||||
if (isAndroidStudio()) {
|
||||
signFlag = "true"
|
||||
} else {
|
||||
signFlag = "false"
|
||||
}
|
||||
}
|
||||
return Boolean.parseBoolean(signFlag)
|
||||
}
|
||||
|
||||
ext.shouldNotStrip = { ->
|
||||
return isAndroidStudio() || project.hasProperty("doNotStrip")
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
# Pandemonium custom build Gradle settings.
|
||||
# These properties apply when running custom build from the Pandemonium editor.
|
||||
# NOTE: This should be kept in sync with 'pandemonium/platform/android/java/gradle.properties' except
|
||||
# where otherwise specified.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx4536m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
org.gradle.warning.mode=all
|
||||
|
||||
# Enable resource optimizations for release build.
|
||||
# NOTE: This is turned off for template release build in order to support the build legacy process.
|
||||
android.enableResourceOptimizations=true
|
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 5.8 KiB |
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:drawable="@drawable/splash_bg_color" />
|
||||
|
||||
<item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:filter="false"
|
||||
android:src="@drawable/splash" />
|
||||
</item>
|
||||
</layer-list>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="pandemonium_project_name_string">pandemonium-project-name-en</string>
|
||||
</resources>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="pandemonium_project_name_string">pandemonium-project-name-hu</string>
|
||||
</resources>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
<resources>
|
||||
<string name="pandemonium_project_name_string">pandemonium-project-name</string>
|
||||
</resources>
|
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="PandemoniumAppMainTheme" parent="@android:style/Theme.Black.NoTitleBar"/>
|
||||
|
||||
<style name="PandemoniumAppSplashTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen">
|
||||
<item name="android:windowBackground">@drawable/splash_drawable</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
</style>
|
||||
</resources>
|
@ -1,15 +0,0 @@
|
||||
// This is the root directory of the Pandemonium custom build.
|
||||
pluginManagement {
|
||||
apply from: 'config.gradle'
|
||||
|
||||
plugins {
|
||||
id 'com.android.application' version versions.androidGradlePlugin
|
||||
id 'org.jetbrains.kotlin.android' version versions.kotlinVersion
|
||||
}
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
include ':assetPacks:installTime'
|
@ -1,47 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* PandemoniumApp.java */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
package com.pandemonium.game;
|
||||
|
||||
import org.pandemoniumengine.pandemonium.FullScreenPandemoniumApp;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Template activity for Pandemonium Android custom builds.
|
||||
* Feel free to extend and modify this class for your custom logic.
|
||||
*/
|
||||
public class PandemoniumApp extends FullScreenPandemoniumApp {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(R.style.PandemoniumAppMainTheme);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
}
|
@ -1,330 +0,0 @@
|
||||
|
||||
buildscript {
|
||||
apply from: 'app/config.gradle'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath libraries.androidGradlePlugin
|
||||
classpath libraries.kotlinGradlePlugin
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'io.github.gradle-nexus.publish-plugin'
|
||||
}
|
||||
|
||||
apply from: 'app/config.gradle'
|
||||
//apply from: 'scripts/publish-root.gradle'
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
|
||||
supportedTargetsMap = [release: "release", dev: "debug", debug: "release_debug"]
|
||||
supportedFlavors = ["editor", "template"]
|
||||
|
||||
// Used by gradle to specify which architecture to build for by default when running
|
||||
// `./gradlew build` (this command is usually used by Android Studio).
|
||||
// If building manually on the command line, it's recommended to use the
|
||||
// `./gradlew generatePandemoniumTemplates` build command instead after running the `scons` command(s).
|
||||
// The {selectedAbis} values must be from the {supportedAbis} values.
|
||||
selectedAbis = ["arm64v8"]
|
||||
}
|
||||
|
||||
def rootDir = "../../.."
|
||||
def binDir = "$rootDir/bin/"
|
||||
|
||||
def getSconsTaskName(String flavor, String buildType, String abi) {
|
||||
return "compilePandemoniumNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the generated 'android_debug.apk' binary template into the Pandemonium bin directory.
|
||||
* Depends on the app build task to ensure the binary is generated prior to copying.
|
||||
*/
|
||||
task copyDebugBinaryToBin(type: Copy) {
|
||||
dependsOn ':app:assembleDebug'
|
||||
from('app/build/outputs/apk/debug')
|
||||
into(binDir)
|
||||
include('android_debug.apk')
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the generated 'android_dev.apk' binary template into the Pandemonium bin directory.
|
||||
* Depends on the app build task to ensure the binary is generated prior to copying.
|
||||
*/
|
||||
task copyDevBinaryToBin(type: Copy) {
|
||||
dependsOn ':app:assembleDev'
|
||||
from('app/build/outputs/apk/dev')
|
||||
into(binDir)
|
||||
include('android_dev.apk')
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the generated 'android_release.apk' binary template into the Pandemonium bin directory.
|
||||
* Depends on the app build task to ensure the binary is generated prior to copying.
|
||||
*/
|
||||
task copyReleaseBinaryToBin(type: Copy) {
|
||||
dependsOn ':app:assembleRelease'
|
||||
from('app/build/outputs/apk/release')
|
||||
into(binDir)
|
||||
include('android_release.apk')
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the Pandemonium android library archive debug file into the app module debug libs directory.
|
||||
* Depends on the library build task to ensure the AAR file is generated prior to copying.
|
||||
*/
|
||||
task copyDebugAARToAppModule(type: Copy) {
|
||||
dependsOn ':lib:assembleTemplateDebug'
|
||||
from('lib/build/outputs/aar')
|
||||
into('app/libs/debug')
|
||||
include('pandemonium-lib.debug.aar')
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the Pandemonium android library archive debug file into the root bin directory.
|
||||
* Depends on the library build task to ensure the AAR file is generated prior to copying.
|
||||
*/
|
||||
task copyDebugAARToBin(type: Copy) {
|
||||
dependsOn ':lib:assembleTemplateDebug'
|
||||
from('lib/build/outputs/aar')
|
||||
into(binDir)
|
||||
include('pandemonium-lib.debug.aar')
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the Pandemonium android library archive dev file into the app module dev libs directory.
|
||||
* Depends on the library build task to ensure the AAR file is generated prior to copying.
|
||||
*/
|
||||
task copyDevAARToAppModule(type: Copy) {
|
||||
dependsOn ':lib:assembleTemplateDev'
|
||||
from('lib/build/outputs/aar')
|
||||
into('app/libs/dev')
|
||||
include('pandemonium-lib.dev.aar')
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the Pandemonium android library archive dev file into the root bin directory.
|
||||
* Depends on the library build task to ensure the AAR file is generated prior to copying.
|
||||
*/
|
||||
task copyDevAARToBin(type: Copy) {
|
||||
dependsOn ':lib:assembleTemplateDev'
|
||||
from('lib/build/outputs/aar')
|
||||
into(binDir)
|
||||
include('pandemonium-lib.dev.aar')
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the Pandemonium android library archive release file into the app module release libs directory.
|
||||
* Depends on the library build task to ensure the AAR file is generated prior to copying.
|
||||
*/
|
||||
task copyReleaseAARToAppModule(type: Copy) {
|
||||
dependsOn ':lib:assembleTemplateRelease'
|
||||
from('lib/build/outputs/aar')
|
||||
into('app/libs/release')
|
||||
include('pandemonium-lib.release.aar')
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the Pandemonium android library archive release file into the root bin directory.
|
||||
* Depends on the library build task to ensure the AAR file is generated prior to copying.
|
||||
*/
|
||||
task copyReleaseAARToBin(type: Copy) {
|
||||
dependsOn ':lib:assembleTemplateRelease'
|
||||
from('lib/build/outputs/aar')
|
||||
into(binDir)
|
||||
include('pandemonium-lib.release.aar')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Pandemonium custom build template by zipping the source files from the app directory, as well
|
||||
* as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'.
|
||||
* The zip file also includes some gradle tools to allow building of the custom build.
|
||||
*/
|
||||
task zipCustomBuild(type: Zip) {
|
||||
onlyIf { generatePandemoniumTemplates.state.executed || generateDevTemplate.state.executed }
|
||||
doFirst {
|
||||
logger.lifecycle("Generating Pandemonium custom build template")
|
||||
}
|
||||
from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradlew', 'gradlew.bat', 'gradle/**']))
|
||||
include '**/*'
|
||||
archiveFileName = 'android_source.zip'
|
||||
destinationDirectory = file(binDir)
|
||||
}
|
||||
|
||||
def templateExcludedBuildTask() {
|
||||
// We exclude these gradle tasks so we can run the scons command manually.
|
||||
def excludedTasks = []
|
||||
if (!isAndroidStudio()) {
|
||||
logger.lifecycle("Excluding Android studio build tasks")
|
||||
for (String flavor : supportedFlavors) {
|
||||
for (String buildType : supportedTargetsMap.keySet()) {
|
||||
if (buildType == "release" && flavor == "editor") {
|
||||
// The editor can't be used with target=release as debugging tools are then not
|
||||
// included, and it would crash on errors instead of reporting them.
|
||||
continue
|
||||
}
|
||||
|
||||
for (String abi : selectedAbis) {
|
||||
excludedTasks += ":lib:" + getSconsTaskName(flavor, buildType, abi)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return excludedTasks
|
||||
}
|
||||
|
||||
def templateBuildTasks() {
|
||||
def tasks = []
|
||||
|
||||
// Only build the apks and aar files for which we have native shared libraries.
|
||||
for (String target : supportedTargetsMap.keySet()) {
|
||||
File targetLibs = new File("lib/libs/" + target)
|
||||
if (targetLibs != null
|
||||
&& targetLibs.isDirectory()
|
||||
&& targetLibs.listFiles() != null
|
||||
&& targetLibs.listFiles().length > 0) {
|
||||
String capitalizedTarget = target.capitalize()
|
||||
// Copy the generated aar library files to the custom build directory.
|
||||
tasks += "copy" + capitalizedTarget + "AARToAppModule"
|
||||
// Copy the generated aar library files to the bin directory.
|
||||
tasks += "copy" + capitalizedTarget + "AARToBin"
|
||||
// Copy the prebuilt binary templates to the bin directory.
|
||||
tasks += "copy" + capitalizedTarget + "BinaryToBin"
|
||||
} else {
|
||||
logger.lifecycle("No native shared libs for target $target. Skipping build.")
|
||||
}
|
||||
}
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
def isAndroidStudio() {
|
||||
def sysProps = System.getProperties()
|
||||
return sysProps != null && sysProps['idea.platform.prefix'] != null
|
||||
}
|
||||
|
||||
task copyEditorDebugBinaryToBin(type: Copy) {
|
||||
dependsOn ':editor:assembleDebug'
|
||||
from('editor/build/outputs/apk/debug')
|
||||
into(binDir)
|
||||
include('android_editor.apk')
|
||||
}
|
||||
|
||||
task copyEditorDevBinaryToBin(type: Copy) {
|
||||
dependsOn ':editor:assembleDev'
|
||||
from('editor/build/outputs/apk/dev')
|
||||
into(binDir)
|
||||
include('android_editor_dev.apk')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the Pandemonium Editor Android apk.
|
||||
*
|
||||
* Note: The Pandemonium 'tools' shared libraries must have been generated (via scons) prior to running
|
||||
* this gradle task. The task will only build the apk(s) for which the shared libraries is
|
||||
* available.
|
||||
*/
|
||||
task generatePandemoniumEditor {
|
||||
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||
|
||||
def tasks = []
|
||||
|
||||
for (String target : supportedTargetsMap.keySet()) {
|
||||
if (target == "release") {
|
||||
// The editor can't be used with target=release as debugging tools are then not
|
||||
// included, and it would crash on errors instead of reporting them.
|
||||
continue
|
||||
}
|
||||
File targetLibs = new File("lib/libs/tools/" + target)
|
||||
if (targetLibs != null
|
||||
&& targetLibs.isDirectory()
|
||||
&& targetLibs.listFiles() != null
|
||||
&& targetLibs.listFiles().length > 0) {
|
||||
tasks += "copyEditor${target.capitalize()}BinaryToBin"
|
||||
}
|
||||
}
|
||||
|
||||
dependsOn = tasks
|
||||
}
|
||||
|
||||
/**
|
||||
* Master task used to coordinate the tasks defined above to generate the set of Pandemonium templates.
|
||||
*/
|
||||
task generatePandemoniumTemplates {
|
||||
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||
dependsOn = templateBuildTasks()
|
||||
|
||||
finalizedBy 'zipCustomBuild'
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
dependsOn 'cleanPandemoniumEditor'
|
||||
dependsOn 'cleanPandemoniumTemplates'
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the same output as generatePandemoniumTemplates but with dev symbols
|
||||
*/
|
||||
task generateDevTemplate {
|
||||
// add parameter to set symbols to true
|
||||
gradle.startParameter.projectProperties += [doNotStrip: true]
|
||||
|
||||
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
|
||||
dependsOn = templateBuildTasks()
|
||||
|
||||
finalizedBy 'zipCustomBuild'
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the generated editor artifacts.
|
||||
*/
|
||||
task cleanPandemoniumEditor(type: Delete) {
|
||||
// Delete the generated native tools libs
|
||||
delete("lib/libs/tools")
|
||||
|
||||
// Delete the library generated AAR files
|
||||
delete("lib/build/outputs/aar")
|
||||
|
||||
// Delete the generated binary apks
|
||||
delete("editor/build/outputs/apk")
|
||||
|
||||
// Delete the Pandemonium editor apks in the Pandemonium bin directory
|
||||
delete("$binDir/android_editor.apk")
|
||||
delete("$binDir/android_editor_dev.apk")
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the generated template artifacts.
|
||||
*/
|
||||
task cleanPandemoniumTemplates(type: Delete) {
|
||||
// Delete the generated native libs
|
||||
delete("lib/libs")
|
||||
|
||||
// Delete the library generated AAR files
|
||||
delete("lib/build/outputs/aar")
|
||||
|
||||
// Delete the app libs directory contents
|
||||
delete("app/libs")
|
||||
|
||||
// Delete the generated binary apks
|
||||
delete("app/build/outputs/apk")
|
||||
|
||||
// Delete the Pandemonium templates in the Pandemonium bin directory
|
||||
delete("$binDir/android_debug.apk")
|
||||
delete("$binDir/android_dev.apk")
|
||||
delete("$binDir/android_release.apk")
|
||||
delete("$binDir/android_source.zip")
|
||||
delete("$binDir/pandemonium-lib.debug.aar")
|
||||
delete("$binDir/pandemonium-lib.dev.aar")
|
||||
delete("$binDir/pandemonium-lib.release.aar")
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
// Gradle build config for Pandemonium Engine's Android port.
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation libraries.kotlinStdLib
|
||||
implementation libraries.androidxFragment
|
||||
implementation project(":lib")
|
||||
|
||||
implementation "androidx.window:window:1.0.0"
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
buildToolsVersion versions.buildTools
|
||||
ndkVersion versions.ndkVersion
|
||||
|
||||
defaultConfig {
|
||||
// The 'applicationId' suffix allows to install Pandemonium 3.x(v3) and 4.x(v4) on the same device
|
||||
applicationId "org.pandemoniumengine.editor.v3"
|
||||
versionCode getPandemoniumLibraryVersionCode()
|
||||
versionName getPandemoniumLibraryVersionName()
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
missingDimensionStrategy 'products', 'editor'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility versions.javaVersion
|
||||
targetCompatibility versions.javaVersion
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = versions.javaVersion
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
dev {
|
||||
initWith debug
|
||||
applicationIdSuffix ".dev"
|
||||
}
|
||||
|
||||
debug {
|
||||
initWith release
|
||||
|
||||
// Need to swap with the release signing config when this is ready for public release.
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
|
||||
release {
|
||||
// This buildtype is disabled below.
|
||||
// The editor can't be used with target=release only, as debugging tools are then not
|
||||
// included, and it would crash on errors instead of reporting them.
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
// 'doNotStrip' is enabled for development within Android Studio
|
||||
if (shouldNotStrip()) {
|
||||
doNotStrip '**/*.so'
|
||||
}
|
||||
}
|
||||
|
||||
// Disable 'release' buildtype.
|
||||
// The editor can't be used with target=release only, as debugging tools are then not
|
||||
// included, and it would crash on errors instead of reporting them.
|
||||
variantFilter { variant ->
|
||||
if (variant.buildType.name == "release") {
|
||||
setIgnore(true)
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all { output ->
|
||||
def suffix = variant.name == "dev" ? "_dev" : ""
|
||||
output.outputFileName = "android_editor${suffix}.apk"
|
||||
}
|
||||
}
|
||||
}
|
1
platform/android/java/editor/src/.gitignore
vendored
@ -1 +0,0 @@
|
||||
!/debug
|
@ -1,79 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.pandemoniumengine.editor"
|
||||
android:installLocation="auto">
|
||||
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"
|
||||
android:xlargeScreens="true" />
|
||||
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
android:required="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/icon"
|
||||
android:label="@string/pandemonium_editor_name_string"
|
||||
tools:ignore="GoogleAppIndexingWarning"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
|
||||
<activity
|
||||
android:name=".PandemoniumProjectManager"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:launchMode="singleInstance"
|
||||
android:screenOrientation="userLandscape"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
|
||||
android:process=":PandemoniumProjectManager">
|
||||
|
||||
<layout android:defaultHeight="@dimen/editor_default_window_height"
|
||||
android:defaultWidth="@dimen/editor_default_window_width" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".PandemoniumEditor"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:process=":PandemoniumEditor"
|
||||
android:launchMode="singleInstance"
|
||||
android:screenOrientation="userLandscape"
|
||||
android:exported="false"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
|
||||
<layout android:defaultHeight="@dimen/editor_default_window_height"
|
||||
android:defaultWidth="@dimen/editor_default_window_width" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".PandemoniumGame"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:label="@string/pandemonium_project_name_string"
|
||||
android:process=":PandemoniumGame"
|
||||
android:launchMode="singleInstance"
|
||||
android:exported="false"
|
||||
android:screenOrientation="userLandscape"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
|
||||
<layout android:defaultHeight="@dimen/editor_default_window_height"
|
||||
android:defaultWidth="@dimen/editor_default_window_width" />
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -1,396 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* PandemoniumEditor.kt */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
package org.pandemoniumengine.editor
|
||||
|
||||
import org.pandemoniumengine.pandemonium.FullScreenPandemoniumApp
|
||||
import org.pandemoniumengine.pandemonium.utils.PermissionsUtil
|
||||
import org.pandemoniumengine.pandemonium.utils.ProcessPhoenix
|
||||
import org.pandemoniumengine.pandemonium.PandemoniumLib
|
||||
|
||||
import android.Manifest
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
|
||||
import androidx.window.layout.WindowMetricsCalculator;
|
||||
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Base class for the Pandemonium Android Editor activities.
|
||||
*
|
||||
* This provides the basic templates for the activities making up this application.
|
||||
* Each derived activity runs in its own process, which enable up to have several instances of
|
||||
* the Pandemonium engine up and running at the same time.
|
||||
*
|
||||
* It also plays the role of the primary editor window.
|
||||
*/
|
||||
open class PandemoniumEditor : FullScreenPandemoniumApp() {
|
||||
companion object {
|
||||
private val TAG = PandemoniumEditor::class.java.simpleName
|
||||
|
||||
private const val WAIT_FOR_DEBUGGER = false
|
||||
|
||||
private const val EXTRA_COMMAND_LINE_PARAMS = "command_line_params"
|
||||
|
||||
private const val EDITOR_ID = 777
|
||||
private const val EDITOR_ARG = "--editor"
|
||||
private const val EDITOR_ARG_SHORT = "-e"
|
||||
private const val EDITOR_PROCESS_NAME_SUFFIX = ":PandemoniumEditor"
|
||||
|
||||
private const val GAME_ID = 667
|
||||
private const val GAME_PROCESS_NAME_SUFFIX = ":PandemoniumGame"
|
||||
|
||||
private const val PROJECT_MANAGER_ID = 555
|
||||
private const val PROJECT_MANAGER_ARG = "--project-manager"
|
||||
private const val PROJECT_MANAGER_ARG_SHORT = "-p"
|
||||
private const val PROJECT_MANAGER_PROCESS_NAME_SUFFIX = ":PandemoniumProjectManager"
|
||||
|
||||
/**
|
||||
* Sets of constants to specify the window to use to run the project.
|
||||
*
|
||||
* Should match the values in 'editor/editor_settings.cpp' for the
|
||||
* 'run/window_placement/android_window' setting.
|
||||
*/
|
||||
private const val ANDROID_WINDOW_AUTO = 0
|
||||
private const val ANDROID_WINDOW_SAME_AS_EDITOR = 1
|
||||
private const val ANDROID_WINDOW_SIDE_BY_SIDE_WITH_EDITOR = 2
|
||||
}
|
||||
|
||||
private val commandLineParams = ArrayList<String>()
|
||||
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
// We exclude certain permissions from the set we request at startup, as they'll be
|
||||
// requested on demand based on use-cases.
|
||||
PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
|
||||
|
||||
val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
|
||||
Log.d(TAG, "Received parameters ${params.contentToString()}")
|
||||
updateCommandLineParams(params)
|
||||
|
||||
if (BuildConfig.BUILD_TYPE == "dev" && WAIT_FOR_DEBUGGER) {
|
||||
Debug.waitForDebugger();
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
override fun onPandemoniumSetupCompleted() {
|
||||
super.onPandemoniumSetupCompleted()
|
||||
val longPressEnabled = enableLongPressGestures()
|
||||
val panScaleEnabled = enablePanAndScaleGestures()
|
||||
|
||||
checkForProjectPermissionsToEnable()
|
||||
|
||||
runOnUiThread {
|
||||
// Enable long press, panning and scaling gestures
|
||||
pandemoniumFragment?.renderView?.inputHandler?.apply {
|
||||
enableLongPress(longPressEnabled)
|
||||
enablePanningAndScalingGestures(panScaleEnabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for project permissions to enable
|
||||
*/
|
||||
protected open fun checkForProjectPermissionsToEnable() {
|
||||
// Check for RECORD_AUDIO permission
|
||||
val audioInputEnabled = java.lang.Boolean.parseBoolean(PandemoniumLib.getGlobal("audio/enable_audio_input"));
|
||||
if (audioInputEnabled) {
|
||||
PermissionsUtil.requestPermission(Manifest.permission.RECORD_AUDIO, this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCommandLineParams(args: Array<String>?) {
|
||||
// Update the list of command line params with the new args
|
||||
commandLineParams.clear()
|
||||
if (!args.isNullOrEmpty()) {
|
||||
commandLineParams.addAll(listOf(*args))
|
||||
}
|
||||
if (BuildConfig.BUILD_TYPE == "dev") {
|
||||
commandLineParams.add("--benchmark")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCommandLine() = commandLineParams
|
||||
|
||||
override fun onNewPandemoniumInstanceRequested(args: Array<String>): Int {
|
||||
// Parse the arguments to figure out which activity to start.
|
||||
var targetClass: Class<*> = PandemoniumGame::class.java
|
||||
var instanceId = GAME_ID
|
||||
|
||||
// Whether we should launch the new pandemonium instance in an adjacent window
|
||||
// https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
|
||||
var launchAdjacent = shouldGameLaunchAdjacent()
|
||||
|
||||
for (arg in args) {
|
||||
if (EDITOR_ARG == arg || EDITOR_ARG_SHORT == arg) {
|
||||
targetClass = PandemoniumEditor::class.java
|
||||
launchAdjacent = false
|
||||
instanceId = EDITOR_ID
|
||||
break
|
||||
}
|
||||
|
||||
if (PROJECT_MANAGER_ARG == arg || PROJECT_MANAGER_ARG_SHORT == arg) {
|
||||
targetClass = PandemoniumProjectManager::class.java
|
||||
launchAdjacent = false
|
||||
instanceId = PROJECT_MANAGER_ID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Launch a new activity
|
||||
val newInstance = Intent(this, targetClass).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).putExtra(EXTRA_COMMAND_LINE_PARAMS, args)
|
||||
|
||||
if (launchAdjacent) {
|
||||
newInstance.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT)
|
||||
}
|
||||
|
||||
if (targetClass == javaClass) {
|
||||
Log.d(TAG, "Restarting $targetClass with parameters ${args.contentToString()}")
|
||||
ProcessPhoenix.triggerRebirth(this, newInstance)
|
||||
} else {
|
||||
Log.d(TAG, "Starting $targetClass with parameters ${args.contentToString()}")
|
||||
newInstance.putExtra(EXTRA_NEW_LAUNCH, true)
|
||||
startActivity(newInstance)
|
||||
}
|
||||
|
||||
return instanceId
|
||||
}
|
||||
|
||||
override fun enableForStealingFocus(processId: Int) {
|
||||
if (shouldGameLaunchAdjacent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var reorder_intent : Intent? = null;
|
||||
|
||||
when (processId) {
|
||||
GAME_ID -> {
|
||||
reorder_intent = Intent(this, PandemoniumGame::class.java);
|
||||
}
|
||||
EDITOR_ID -> {
|
||||
reorder_intent = Intent(this, PandemoniumEditor::class.java);
|
||||
}
|
||||
PROJECT_MANAGER_ID -> {
|
||||
reorder_intent = Intent(this, PandemoniumProjectManager::class.java);
|
||||
}
|
||||
else -> {
|
||||
// An unknown PID means the original editor instance
|
||||
reorder_intent = Intent(this, PandemoniumEditor::class.java);
|
||||
}
|
||||
}
|
||||
|
||||
if (reorder_intent != null) {
|
||||
reorder_intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||
reorder_intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
startActivity(reorder_intent);
|
||||
}
|
||||
}
|
||||
|
||||
override fun moveWindowToForeground() {
|
||||
if (shouldGameLaunchAdjacent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var reorder_intent : Intent = Intent(this, javaClass);
|
||||
reorder_intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||
reorder_intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
startActivity(reorder_intent);
|
||||
}
|
||||
|
||||
override fun onPandemoniumForceQuit(pandemoniumInstanceId: Int): Boolean {
|
||||
val targetClass: Class<*>?
|
||||
val processNameSuffix: String
|
||||
|
||||
when (pandemoniumInstanceId) {
|
||||
GAME_ID -> {
|
||||
processNameSuffix = GAME_PROCESS_NAME_SUFFIX
|
||||
targetClass = PandemoniumGame::class.java
|
||||
}
|
||||
EDITOR_ID -> {
|
||||
processNameSuffix = EDITOR_PROCESS_NAME_SUFFIX
|
||||
targetClass = PandemoniumEditor::class.java
|
||||
}
|
||||
PROJECT_MANAGER_ID -> {
|
||||
processNameSuffix = PROJECT_MANAGER_PROCESS_NAME_SUFFIX
|
||||
targetClass = PandemoniumProjectManager::class.java
|
||||
}
|
||||
else -> {
|
||||
processNameSuffix = ""
|
||||
targetClass = null
|
||||
}
|
||||
}
|
||||
|
||||
if (targetClass == javaClass) {
|
||||
Log.d(TAG, "Force quitting $targetClass")
|
||||
ProcessPhoenix.forceQuit(this)
|
||||
return true
|
||||
}
|
||||
|
||||
if (processNameSuffix.isBlank()) {
|
||||
return false
|
||||
}
|
||||
|
||||
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
val runningProcesses = activityManager.runningAppProcesses
|
||||
for (runningProcess in runningProcesses) {
|
||||
if (runningProcess.processName.endsWith(processNameSuffix)) {
|
||||
if (targetClass == null) {
|
||||
// Killing process directly
|
||||
Log.v(TAG, "Killing Pandemonium process ${runningProcess.processName}")
|
||||
Process.killProcess(runningProcess.pid)
|
||||
} else {
|
||||
// Activity is running; sending a request for self termination.
|
||||
Log.v(TAG, "Sending force quit request to $targetClass running on process ${runningProcess.processName}")
|
||||
val forceQuitIntent = Intent(this, targetClass).putExtra(EXTRA_FORCE_QUIT, true)
|
||||
startActivity(forceQuitIntent)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Get the screen's density scale
|
||||
protected val isLargeScreen: Boolean
|
||||
// Get the minimum window size // Correspond to the EXPANDED window size class.
|
||||
get() {
|
||||
val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
|
||||
|
||||
// Get the screen's density scale
|
||||
val scale = resources.displayMetrics.density
|
||||
|
||||
// Get the minimum window size
|
||||
val minSize = min(metrics.bounds.width(), metrics.bounds.height()).toFloat()
|
||||
val minSizeDp = minSize / scale
|
||||
return minSizeDp >= 840f // Correspond to the EXPANDED window size class.
|
||||
}
|
||||
|
||||
override fun setRequestedOrientation(requestedOrientation: Int) {
|
||||
if (!overrideOrientationRequest()) {
|
||||
super.setRequestedOrientation(requestedOrientation)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Android Editor sets its own orientation via its AndroidManifest
|
||||
*/
|
||||
protected open fun overrideOrientationRequest() = true
|
||||
|
||||
/**
|
||||
* Enable long press gestures for the Pandemonium Android editor.
|
||||
*/
|
||||
protected open fun enableLongPressGestures() =
|
||||
java.lang.Boolean.parseBoolean(PandemoniumLib.getEditorSetting("interface/touchscreen/enable_long_press_as_right_click"))
|
||||
|
||||
/**
|
||||
* Enable pan and scale gestures for the Pandemonium Android editor.
|
||||
*/
|
||||
protected open fun enablePanAndScaleGestures() =
|
||||
java.lang.Boolean.parseBoolean(PandemoniumLib.getEditorSetting("interface/touchscreen/enable_pan_and_scale_gestures"))
|
||||
|
||||
|
||||
|
||||
protected open fun shouldGameLaunchAdjacent(): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
try {
|
||||
when (Integer.parseInt(PandemoniumLib.getEditorSetting("run/window_placement/android_window"))) {
|
||||
ANDROID_WINDOW_SAME_AS_EDITOR -> false
|
||||
ANDROID_WINDOW_SIDE_BY_SIDE_WITH_EDITOR -> true
|
||||
else -> {
|
||||
// ANDROID_WINDOW_AUTO
|
||||
isInMultiWindowMode || isLargeScreen
|
||||
}
|
||||
}
|
||||
} catch (e: NumberFormatException) {
|
||||
// Fall-back to the 'Auto' behavior
|
||||
isInMultiWindowMode || isLargeScreen
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
// Check if we got the MANAGE_EXTERNAL_STORAGE permission
|
||||
if (requestCode == PermissionsUtil.REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
Toast.makeText(
|
||||
this,
|
||||
R.string.denied_storage_permission_error_msg,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String?>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
// Check if we got access to the necessary storage permissions
|
||||
if (requestCode == PermissionsUtil.REQUEST_ALL_PERMISSION_REQ_CODE) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
var hasReadAccess = false
|
||||
var hasWriteAccess = false
|
||||
for (i in permissions.indices) {
|
||||
if (Manifest.permission.READ_EXTERNAL_STORAGE == permissions[i] && grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
||||
hasReadAccess = true
|
||||
}
|
||||
if (Manifest.permission.WRITE_EXTERNAL_STORAGE == permissions[i] && grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
||||
hasWriteAccess = true
|
||||
}
|
||||
}
|
||||
if (!hasReadAccess || !hasWriteAccess) {
|
||||
Toast.makeText(
|
||||
this,
|
||||
R.string.denied_storage_permission_error_msg,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* PandemoniumGame.kt */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
package org.pandemoniumengine.editor
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.*
|
||||
|
||||
/**
|
||||
* Drives the 'run project' window of the Pandemonium Editor.
|
||||
*/
|
||||
class PandemoniumGame : PandemoniumEditor() {
|
||||
private var isAdjacentFlagSet = false
|
||||
|
||||
override fun overrideOrientationRequest() = false
|
||||
|
||||
override fun enableLongPressGestures() = false
|
||||
|
||||
override fun enablePanAndScaleGestures() = false
|
||||
|
||||
override fun checkForProjectPermissionsToEnable() {
|
||||
// Nothing to do.. by the time we get here, the project permissions will have already
|
||||
// been requested by the Editor window.
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
isAdjacentFlagSet = (getIntent().getFlags() and Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0;
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
override fun shouldGameLaunchAdjacent(): Boolean {
|
||||
return isAdjacentFlagSet
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* PandemoniumProjectManager.kt */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
package org.pandemoniumengine.editor
|
||||
|
||||
/**
|
||||
* Launcher activity for the Pandemonium Android Editor.
|
||||
*
|
||||
* It presents the user with the project manager interface.
|
||||
* Upon selection of a project, this activity (via its parent logic) starts the
|
||||
* [PandemoniumEditor] activity.
|
||||
*/
|
||||
|
||||
class PandemoniumProjectManager : PandemoniumEditor() {
|
||||
override fun checkForProjectPermissionsToEnable() {
|
||||
// Nothing to do here.. we have yet to select a project to load.
|
||||
}
|
||||
|
||||
override fun shouldGameLaunchAdjacent(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="editor_default_window_height">600dp</dimen>
|
||||
<dimen name="editor_default_window_width">800dp</dimen>
|
||||
</resources>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pandemonium_editor_name_string">Pandemonium Editor</string>
|
||||
<string name="denied_storage_permission_error_msg">Missing storage access permission!</string>
|
||||
</resources>
|
@ -1,28 +0,0 @@
|
||||
# Project-wide Gradle settings.
|
||||
# NOTE: This should be kept in sync with 'pandemonium/platform/android/java/app/gradle.properties' except
|
||||
# where otherwise specified.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx4536m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
org.gradle.warning.mode=all
|
||||
|
||||
# Disable resource optimizations for template release build.
|
||||
# NOTE: This is turned on for custom build in order to improve the release build.
|
||||
android.enableResourceOptimizations=false
|
@ -1,5 +0,0 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
185
platform/android/java/gradlew
vendored
@ -1,185 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
89
platform/android/java/gradlew.bat
vendored
@ -1,89 +0,0 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.pandemoniumengine.pandemonium"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<application>
|
||||
|
||||
<!-- Records the version of the Pandemonium library -->
|
||||
<meta-data
|
||||
android:name="net.relintai.pandemonium.library.version"
|
||||
android:value="${pandemoniumLibraryVersion}" />
|
||||
|
||||
<service android:name=".PandemoniumDownloaderService" />
|
||||
|
||||
<activity
|
||||
android:name=".utils.ProcessPhoenix"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:process=":phoenix"
|
||||
android:exported="false"
|
||||
/>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/pandemonium_provider_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -1,39 +0,0 @@
|
||||
# Third-party libraries
|
||||
|
||||
This file list third-party libraries used in the Android source folder,
|
||||
with their provenance and, when relevant, modifications made to those files.
|
||||
|
||||
## com.android.vending.billing
|
||||
|
||||
- Upstream: https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive/app/src/main
|
||||
- Version: git (7a94c69, 2019)
|
||||
- License: Apache 2.0
|
||||
|
||||
Overwrite the file `aidl/com/android/vending/billing/IInAppBillingService.aidl`.
|
||||
|
||||
## com.google.android.vending.expansion.downloader
|
||||
|
||||
- Upstream: https://github.com/google/play-apk-expansion/tree/master/apkx_library
|
||||
- Version: git (9ecf54e, 2017)
|
||||
- License: Apache 2.0
|
||||
|
||||
Overwrite all files under:
|
||||
|
||||
- `src/com/google/android/vending/expansion/downloader`
|
||||
|
||||
Some files have been modified for yet unclear reasons.
|
||||
See the `patches/com.google.android.vending.expansion.downloader.patch` file.
|
||||
|
||||
## com.google.android.vending.licensing
|
||||
|
||||
- Upstream: https://github.com/google/play-licensing/tree/master/lvl_library/
|
||||
- Version: git (eb57657, 2018) with modifications
|
||||
- License: Apache 2.0
|
||||
|
||||
Overwrite all files under:
|
||||
|
||||
- `aidl/com/android/vending/licensing`
|
||||
- `src/com/google/android/vending/licensing`
|
||||
|
||||
Some files have been modified to silence linter errors or fix downstream issues.
|
||||
See the `patches/com.google.android.vending.licensing.patch` file.
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.vending.licensing;
|
||||
|
||||
oneway interface ILicenseResultListener {
|
||||
void verifyLicense(int responseCode, String signedData, String signature);
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.vending.licensing;
|
||||
|
||||
import com.android.vending.licensing.ILicenseResultListener;
|
||||
|
||||
oneway interface ILicensingService {
|
||||
void checkLicense(long nonce, String packageName, in ILicenseResultListener listener);
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation libraries.kotlinStdLib
|
||||
implementation libraries.androidxFragment
|
||||
}
|
||||
|
||||
def pathToRootDir = "../../../../"
|
||||
|
||||
android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
buildToolsVersion versions.buildTools
|
||||
ndkVersion versions.ndkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
|
||||
manifestPlaceholders = [pandemoniumLibraryVersion: getPandemoniumLibraryVersionName()]
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility versions.javaVersion
|
||||
targetCompatibility versions.javaVersion
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = versions.javaVersion
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
dev {
|
||||
initWith debug
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "products"
|
||||
productFlavors {
|
||||
editor {}
|
||||
template {}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
disable 'MissingTranslation', 'UnusedResources'
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LICENSE'
|
||||
exclude 'META-INF/NOTICE'
|
||||
|
||||
// 'doNotStrip' is enabled for development within Android Studio
|
||||
if (shouldNotStrip()) {
|
||||
doNotStrip '**/*.so'
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
aidl.srcDirs = ['aidl']
|
||||
assets.srcDirs = ['assets']
|
||||
}
|
||||
|
||||
debug.jniLibs.srcDirs = ['libs/debug']
|
||||
dev.jniLibs.srcDirs = ['libs/dev']
|
||||
release.jniLibs.srcDirs = ['libs/release']
|
||||
|
||||
// Editor jni library
|
||||
editorDebug.jniLibs.srcDirs = ['libs/tools/debug']
|
||||
editorDev.jniLibs.srcDirs = ['libs/tools/dev']
|
||||
}
|
||||
|
||||
// Disable 'editorRelease'.
|
||||
// The editor can't be used with target=release as debugging tools are then not
|
||||
// included, and it would crash on errors instead of reporting them.
|
||||
variantFilter { variant ->
|
||||
if (variant.name == "editorRelease") {
|
||||
setIgnore(true)
|
||||
}
|
||||
}
|
||||
|
||||
libraryVariants.all { variant ->
|
||||
def flavorName = variant.getFlavorName()
|
||||
|
||||
if (flavorName == null || flavorName == "") {
|
||||
throw new GradleException("Invalid product flavor: $flavorName")
|
||||
}
|
||||
|
||||
boolean toolsFlag = flavorName == "editor"
|
||||
|
||||
def buildType = variant.buildType.name
|
||||
if (buildType == null || buildType == "" || !supportedTargetsMap.containsKey(buildType)) {
|
||||
throw new GradleException("Invalid build type: $buildType")
|
||||
}
|
||||
|
||||
def sconsTarget = supportedTargetsMap[buildType]
|
||||
if (sconsTarget == null || sconsTarget == "") {
|
||||
throw new GradleException("Invalid scons target: $sconsTarget")
|
||||
}
|
||||
|
||||
// Update the name of the generated library
|
||||
def outputSuffix = "${buildType}.aar"
|
||||
if (toolsFlag) {
|
||||
outputSuffix = "tools.$outputSuffix"
|
||||
}
|
||||
|
||||
variant.outputs.all { output ->
|
||||
output.outputFileName = "pandemonium-lib.${outputSuffix}"
|
||||
}
|
||||
|
||||
// Find scons' executable path
|
||||
File sconsExecutableFile = null
|
||||
def sconsName = "scons"
|
||||
def sconsExts = (org.gradle.internal.os.OperatingSystem.current().isWindows()
|
||||
? [".bat", ".cmd", ".ps1", ".exe"]
|
||||
: [""])
|
||||
logger.lifecycle("Looking for $sconsName executable path")
|
||||
for (ext in sconsExts) {
|
||||
String sconsNameExt = sconsName + ext
|
||||
logger.lifecycle("Checking $sconsNameExt")
|
||||
|
||||
sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt)
|
||||
if (sconsExecutableFile != null) {
|
||||
// We're done!
|
||||
break
|
||||
}
|
||||
|
||||
// Check all the options in path
|
||||
List<File> allOptions = org.gradle.internal.os.OperatingSystem.current().findAllInPath(sconsNameExt)
|
||||
if (!allOptions.isEmpty()) {
|
||||
// Pick the first option and we're done!
|
||||
sconsExecutableFile = allOptions.get(0)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (sconsExecutableFile == null) {
|
||||
throw new GradleException("Unable to find executable path for the '$sconsName' command.")
|
||||
} else {
|
||||
logger.lifecycle("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}")
|
||||
}
|
||||
|
||||
for (String selectedAbi : selectedAbis) {
|
||||
if (!supportedAbis.contains(selectedAbi)) {
|
||||
throw new GradleException("Invalid selected abi: $selectedAbi")
|
||||
}
|
||||
|
||||
// Creating gradle task to generate the native libraries for the selected abi.
|
||||
def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
|
||||
tasks.create(name: taskName, type: Exec) {
|
||||
executable sconsExecutableFile.absolutePath
|
||||
args "--directory=${pathToRootDir}", "platform=android", "tools=${toolsFlag}", "target=${sconsTarget}", "android_arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
|
||||
}
|
||||
|
||||
// Schedule the tasks so the generated libs are present before the aar file is packaged.
|
||||
tasks["merge${flavorName.capitalize()}${buildType.capitalize()}JniLibFolders"].dependsOn taskName
|
||||
}
|
||||
}
|
||||
}
|
@ -1,300 +0,0 @@
|
||||
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java
|
||||
index ad6ea0de6..452c7d148 100644
|
||||
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java
|
||||
+++ b/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java
|
||||
@@ -32,6 +32,9 @@ import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
+// -- PANDEMONIUM start --
|
||||
+import java.lang.ref.WeakReference;
|
||||
+// -- PANDEMONIUM end --
|
||||
|
||||
|
||||
/**
|
||||
@@ -118,29 +121,46 @@ public class DownloaderClientMarshaller {
|
||||
/**
|
||||
* Target we publish for clients to send messages to IncomingHandler.
|
||||
*/
|
||||
- final Messenger mMessenger = new Messenger(new Handler() {
|
||||
+ // -- PANDEMONIUM start --
|
||||
+ private final MessengerHandlerClient mMsgHandler = new MessengerHandlerClient(this);
|
||||
+ final Messenger mMessenger = new Messenger(mMsgHandler);
|
||||
+
|
||||
+ private static class MessengerHandlerClient extends Handler {
|
||||
+ private final WeakReference<Stub> mDownloader;
|
||||
+ public MessengerHandlerClient(Stub downloader) {
|
||||
+ mDownloader = new WeakReference<>(downloader);
|
||||
+ }
|
||||
+
|
||||
@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;
|
||||
+ Stub downloader = mDownloader.get();
|
||||
+ if (downloader != null) {
|
||||
+ downloader.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
- });
|
||||
+ }
|
||||
+
|
||||
+ private 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;
|
||||
+ }
|
||||
+ }
|
||||
+ // -- PANDEMONIUM end --
|
||||
|
||||
public Stub(IDownloaderClient itf, Class<?> downloaderService) {
|
||||
mItf = itf;
|
||||
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java
|
||||
index 979352299..3771d19c9 100644
|
||||
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java
|
||||
+++ b/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java
|
||||
@@ -25,6 +25,9 @@ import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
|
||||
+// -- PANDEMONIUM start --
|
||||
+import java.lang.ref.WeakReference;
|
||||
+// -- PANDEMONIUM end --
|
||||
|
||||
|
||||
/**
|
||||
@@ -108,32 +111,49 @@ public class DownloaderServiceMarshaller {
|
||||
|
||||
private static class Stub implements IStub {
|
||||
private IDownloaderService mItf = null;
|
||||
- final Messenger mMessenger = new Messenger(new Handler() {
|
||||
+ // -- PANDEMONIUM start --
|
||||
+ private final MessengerHandlerServer mMsgHandler = new MessengerHandlerServer(this);
|
||||
+ final Messenger mMessenger = new Messenger(mMsgHandler);
|
||||
+
|
||||
+ private static class MessengerHandlerServer extends Handler {
|
||||
+ private final WeakReference<Stub> mDownloader;
|
||||
+ public MessengerHandlerServer(Stub downloader) {
|
||||
+ mDownloader = new WeakReference<>(downloader);
|
||||
+ }
|
||||
+
|
||||
@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;
|
||||
+ Stub downloader = mDownloader.get();
|
||||
+ if (downloader != null) {
|
||||
+ downloader.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
- });
|
||||
+ }
|
||||
+
|
||||
+ private 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;
|
||||
+ }
|
||||
+ }
|
||||
+ // -- PANDEMONIUM end --
|
||||
|
||||
public Stub(IDownloaderService itf) {
|
||||
mItf = itf;
|
||||
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/Helpers.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/Helpers.java
|
||||
index e4b1b0f1c..36cd6aacf 100644
|
||||
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/Helpers.java
|
||||
+++ b/platform/android/java/src/com/google/android/vending/expansion/downloader/Helpers.java
|
||||
@@ -24,7 +24,10 @@ import android.os.StatFs;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
-import com.android.vending.expansion.downloader.R;
|
||||
+// -- PANDEMONIUM start --
|
||||
+//import com.android.vending.expansion.downloader.R;
|
||||
+import org.pandemoniumengine.pandemonium.R;
|
||||
+// -- PANDEMONIUM end --
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -146,12 +149,14 @@ public class Helpers {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
- return String.format("%.2f",
|
||||
+ // -- PANDEMONIUM start --
|
||||
+ return String.format(Locale.ENGLISH, "%.2f",
|
||||
(float) overallProgress / (1024.0f * 1024.0f))
|
||||
+ "MB /" +
|
||||
- String.format("%.2f", (float) overallTotal /
|
||||
+ String.format(Locale.ENGLISH, "%.2f", (float) overallTotal /
|
||||
(1024.0f * 1024.0f))
|
||||
+ "MB";
|
||||
+ // -- PANDEMONIUM end --
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,7 +189,9 @@ public class Helpers {
|
||||
}
|
||||
|
||||
public static String getSpeedString(float bytesPerMillisecond) {
|
||||
- return String.format("%.2f", bytesPerMillisecond * 1000 / 1024);
|
||||
+ // -- PANDEMONIUM start --
|
||||
+ return String.format(Locale.ENGLISH, "%.2f", bytesPerMillisecond * 1000 / 1024);
|
||||
+ // -- PANDEMONIUM end --
|
||||
}
|
||||
|
||||
public static String getTimeRemaining(long durationInMilliseconds) {
|
||||
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/SystemFacade.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/SystemFacade.java
|
||||
index 12edd97ab..a0e1165cc 100644
|
||||
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/SystemFacade.java
|
||||
+++ b/platform/android/java/src/com/google/android/vending/expansion/downloader/SystemFacade.java
|
||||
@@ -26,6 +26,10 @@ import android.net.NetworkInfo;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
+// -- PANDEMONIUM start --
|
||||
+import android.annotation.SuppressLint;
|
||||
+// -- PANDEMONIUM end --
|
||||
+
|
||||
/**
|
||||
* Contains useful helper functions, typically tied to the application context.
|
||||
*/
|
||||
@@ -51,6 +55,7 @@ class SystemFacade {
|
||||
return null;
|
||||
}
|
||||
|
||||
+ @SuppressLint("MissingPermission")
|
||||
NetworkInfo activeInfo = connectivity.getActiveNetworkInfo();
|
||||
if (activeInfo == null) {
|
||||
if (Constants.LOGVV) {
|
||||
@@ -69,6 +74,7 @@ class SystemFacade {
|
||||
return false;
|
||||
}
|
||||
|
||||
+ @SuppressLint("MissingPermission")
|
||||
NetworkInfo info = connectivity.getActiveNetworkInfo();
|
||||
boolean isMobile = (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE);
|
||||
TelephonyManager tm = (TelephonyManager) mContext
|
||||
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
|
||||
index f1536e80e..4b214b22d 100644
|
||||
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
|
||||
+++ b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
-import com.android.vending.expansion.downloader.R;
|
||||
+// -- PANDEMONIUM start --
|
||||
+//import com.android.vending.expansion.downloader.R;
|
||||
+import org.pandemoniumengine.pandemonium.R;
|
||||
+// -- PANDEMONIUM end --
|
||||
+
|
||||
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
|
||||
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
|
||||
import com.google.android.vending.expansion.downloader.Helpers;
|
||||
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadThread.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadThread.java
|
||||
index b2e0e7af0..c114b8a64 100644
|
||||
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadThread.java
|
||||
+++ b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadThread.java
|
||||
@@ -146,8 +146,12 @@ public class DownloadThread {
|
||||
|
||||
try {
|
||||
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
- wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
|
||||
- wakeLock.acquire();
|
||||
+ // -- PANDEMONIUM start --
|
||||
+ //wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
|
||||
+ //wakeLock.acquire();
|
||||
+ wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "org.pandemonium.game:wakelock");
|
||||
+ wakeLock.acquire(20 * 60 * 1000L /*20 minutes*/);
|
||||
+ // -- PANDEMONIUM end --
|
||||
|
||||
if (Constants.LOGV) {
|
||||
Log.v(Constants.TAG, "initiating download for " + mInfo.mFileName);
|
||||
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java
|
||||
index 4babe476f..8d41a7690 100644
|
||||
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java
|
||||
+++ b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java
|
||||
@@ -50,6 +50,10 @@ import android.provider.Settings.Secure;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
+// -- PANDEMONIUM start --
|
||||
+import android.annotation.SuppressLint;
|
||||
+// -- PANDEMONIUM end --
|
||||
+
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
@@ -578,6 +582,7 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
Log.w(Constants.TAG,
|
||||
"couldn't get connectivity manager to poll network state");
|
||||
} else {
|
||||
+ @SuppressLint("MissingPermission")
|
||||
NetworkInfo activeInfo = mConnectivityManager
|
||||
.getActiveNetworkInfo();
|
||||
updateNetworkState(activeInfo);
|
@ -1,42 +0,0 @@
|
||||
diff --git a/platform/android/java/src/com/google/android/vending/licensing/PreferenceObfuscator.java b/platform/android/java/src/com/google/android/vending/licensing/PreferenceObfuscator.java
|
||||
index 7c42bfc28..feb579af0 100644
|
||||
--- a/platform/android/java/src/com/google/android/vending/licensing/PreferenceObfuscator.java
|
||||
+++ b/platform/android/java/src/com/google/android/vending/licensing/PreferenceObfuscator.java
|
||||
@@ -45,6 +45,9 @@ public class PreferenceObfuscator {
|
||||
public void putString(String key, String value) {
|
||||
if (mEditor == null) {
|
||||
mEditor = mPreferences.edit();
|
||||
+ // -- PANDEMONIUM start --
|
||||
+ mEditor.apply();
|
||||
+ // -- PANDEMONIUM end --
|
||||
}
|
||||
String obfuscatedValue = mObfuscator.obfuscate(value, key);
|
||||
mEditor.putString(key, obfuscatedValue);
|
||||
diff --git a/platform/android/java/src/com/google/android/vending/licensing/util/Base64.java b/platform/android/java/src/com/google/android/vending/licensing/util/Base64.java
|
||||
index a0d2779af..a8bf65f9c 100644
|
||||
--- a/platform/android/java/src/com/google/android/vending/licensing/util/Base64.java
|
||||
+++ b/platform/android/java/src/com/google/android/vending/licensing/util/Base64.java
|
||||
@@ -31,6 +31,10 @@ package com.google.android.vending.licensing.util;
|
||||
* @version 1.3
|
||||
*/
|
||||
|
||||
+// -- PANDEMONIUM start --
|
||||
import org.pandemoniumengine.pandemonium.BuildConfig;
|
||||
+// -- PANDEMONIUM end --
|
||||
+
|
||||
/**
|
||||
* Base64 converter class. This code is not a full-blown MIME encoder;
|
||||
* it simply converts binary data to base64 data and back.
|
||||
@@ -341,7 +345,11 @@ public class Base64 {
|
||||
e += 4;
|
||||
}
|
||||
|
||||
- assert (e == outBuff.length);
|
||||
+ // -- PANDEMONIUM start --
|
||||
+ //assert (e == outBuff.length);
|
||||
+ if (BuildConfig.DEBUG && e != outBuff.length)
|
||||
+ throw new RuntimeException();
|
||||
+ // -- PANDEMONIUM end --
|
||||
return outBuff;
|
||||
}
|
||||
|
@ -1,165 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/downloaderDashboard"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progressAsFraction"
|
||||
style="@android:style/TextAppearance.Small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginStart="5dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progressAsPercentage"
|
||||
style="@android:style/TextAppearance.Small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignEnd="@+id/progressBar" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/progressAsFraction"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginTop="10dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progressAverageSpeed"
|
||||
style="@android:style/TextAppearance.Small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_below="@+id/progressBar"
|
||||
android:layout_marginStart="5dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progressTimeRemaining"
|
||||
style="@android:style/TextAppearance.Small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignEnd="@+id/progressBar"
|
||||
android:layout_below="@+id/progressBar" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/downloadButton"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancelButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_weight="0"
|
||||
android:minHeight="40dp"
|
||||
android:minWidth="94dp"
|
||||
android:text="@string/text_button_cancel"
|
||||
android:visibility="gone"
|
||||
style="?android:attr/buttonBarButtonStyle" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/pauseButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_weight="0"
|
||||
android:minHeight="40dp"
|
||||
android:minWidth="94dp"
|
||||
android:text="@string/text_button_pause"
|
||||
style="?android:attr/buttonBarButtonStyle" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/approveCellular"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:id="@+id/textPausedParagraph1"
|
||||
android:text="@string/text_paused_cellular" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:id="@+id/textPausedParagraph2"
|
||||
android:text="@string/text_paused_cellular_2" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/buttonRow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/resumeOverCellular"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="10dp"
|
||||
android:text="@string/text_button_resume_cellular"
|
||||
style="?android:attr/buttonBarButtonStyle" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/wifiSettingsButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="10dp"
|
||||
android:text="@string/text_button_wifi_settings"
|
||||
style="?android:attr/buttonBarButtonStyle" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/pandemonium_fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
@ -1,108 +0,0 @@
|
||||
<?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 xmlns:tools="http://schemas.android.com/tools"
|
||||
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_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:src="@android:drawable/stat_sys_download"
|
||||
android:contentDescription="@string/pandemonium_project_name_string" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progress_text"
|
||||
style="@style/NotificationText"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="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:paddingEnd="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/NotificationTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="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_alignParentEnd="true"
|
||||
android:singleLine="true"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
<!-- 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:paddingEnd="25dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
style="@style/NotificationTextShadow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingEnd="25dp"
|
||||
android:singleLine="true" />
|
||||
</FrameLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/icon_background"/>
|
||||
<foreground android:drawable="@mipmap/icon_foreground"/>
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 2.8 KiB |
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="text_edit_height">48dp</dimen>
|
||||
</resources>
|
@ -1,57 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pandemonium_project_name_string">pandemonium-project-name</string>
|
||||
<string name="text_paused_cellular">Would you like to enable downloading over cellular connections? Depending on your data plan, this may cost you money.</string>
|
||||
<string name="text_paused_cellular_2">If you choose not to enable downloading over cellular connections, the download will automatically resume when wi-fi is available.</string>
|
||||
<string name="text_button_resume_cellular">Resume download</string>
|
||||
<string name="text_button_wifi_settings">Wi-Fi settings</string>
|
||||
<string name="text_verifying_download">Verifying Download</string>
|
||||
<string name="text_validation_complete">XAPK File Validation Complete. Select OK to exit.</string>
|
||||
<string name="text_validation_failed">XAPK File Validation Failed.</string>
|
||||
<string name="text_button_pause">Pause Download</string>
|
||||
<string name="text_button_resume">Resume Download</string>
|
||||
<string name="text_button_cancel">Cancel</string>
|
||||
<string name="text_button_cancel_verify">Cancel Verification</string>
|
||||
<string name="text_error_title">Error!</string>
|
||||
<string name="error_engine_setup_message">Unable to setup the Godot Engine! Aborting…</string>
|
||||
|
||||
<!-- APK Expansion Strings -->
|
||||
|
||||
<!-- 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>
|
@ -1,25 +0,0 @@
|
||||
<?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>
|
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<external-path
|
||||
name="public"
|
||||
path="." />
|
||||
|
||||
<external-files-path
|
||||
name="app"
|
||||
path="." />
|
||||
</paths>
|
@ -1,236 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* 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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,297 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
// -- PANDEMONIUM start --
|
||||
import java.lang.ref.WeakReference;
|
||||
// -- PANDEMONIUM end --
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
// -- PANDEMONIUM start --
|
||||
private final MessengerHandlerClient mMsgHandler = new MessengerHandlerClient(this);
|
||||
final Messenger mMessenger = new Messenger(mMsgHandler);
|
||||
|
||||
private static class MessengerHandlerClient extends Handler {
|
||||
private final WeakReference<Stub> mDownloader;
|
||||
public MessengerHandlerClient(Stub downloader) {
|
||||
mDownloader = new WeakReference<>(downloader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
Stub downloader = mDownloader.get();
|
||||
if (downloader != null) {
|
||||
downloader.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
// -- PANDEMONIUM end --
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
// -- PANDEMONIUM start --
|
||||
import java.lang.ref.WeakReference;
|
||||
// -- PANDEMONIUM end --
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
// -- PANDEMONIUM start --
|
||||
private final MessengerHandlerServer mMsgHandler = new MessengerHandlerServer(this);
|
||||
final Messenger mMessenger = new Messenger(mMsgHandler);
|
||||
|
||||
private static class MessengerHandlerServer extends Handler {
|
||||
private final WeakReference<Stub> mDownloader;
|
||||
public MessengerHandlerServer(Stub downloader) {
|
||||
mDownloader = new WeakReference<>(downloader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
Stub downloader = mDownloader.get();
|
||||
if (downloader != null) {
|
||||
downloader.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
// -- PANDEMONIUM end --
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -1,367 +0,0 @@
|
||||
/*
|
||||
* 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.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
// -- PANDEMONIUM start --
|
||||
//import com.android.vending.expansion.downloader.R;
|
||||
import org.pandemoniumengine.pandemonium.R;
|
||||
// -- PANDEMONIUM end --
|
||||
|
||||
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 "";
|
||||
}
|
||||
// -- PANDEMONIUM start --
|
||||
return String.format(Locale.ENGLISH, "%.2f",
|
||||
(float) overallProgress / (1024.0f * 1024.0f))
|
||||
+ "MB /" +
|
||||
String.format(Locale.ENGLISH, "%.2f", (float) overallTotal /
|
||||
(1024.0f * 1024.0f))
|
||||
+ "MB";
|
||||
// -- PANDEMONIUM end --
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// -- PANDEMONIUM start --
|
||||
return String.format(Locale.ENGLISH, "%.2f", bytesPerMillisecond * 1000 / 1024);
|
||||
// -- PANDEMONIUM end --
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
static public String getSaveFilePath(Context c) {
|
||||
// This technically existed since Honeycomb, but it is critical
|
||||
// on KitKat and greater versions since it will create the
|
||||
// directory if needed
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
return c.getObbDir().toString();
|
||||
} else {
|
||||
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 Play --- 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;
|
||||
}
|
||||
|
||||
public static final int FS_READABLE = 0;
|
||||
public static final int FS_DOES_NOT_EXIST = 1;
|
||||
public static final int FS_CANNOT_READ = 2;
|
||||
|
||||
/**
|
||||
* Helper function to ascertain whether a file can be read.
|
||||
*
|
||||
* @param c the app/activity/service context
|
||||
* @param fileName the name (sans path) of the file to query
|
||||
* @return true if it does exist, false otherwise
|
||||
*/
|
||||
static public int getFileStatus(Context c, String fileName) {
|
||||
// the file may have been delivered by Play --- let's make sure
|
||||
// it's the size we expect
|
||||
File fileForNewFile = new File(Helpers.generateSaveFileName(c, fileName));
|
||||
int returnValue;
|
||||
if (fileForNewFile.exists()) {
|
||||
if (fileForNewFile.canRead()) {
|
||||
returnValue = FS_READABLE;
|
||||
} else {
|
||||
returnValue = FS_CANNOT_READ;
|
||||
}
|
||||
} else {
|
||||
returnValue = FS_DOES_NOT_EXIST;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to ascertain whether the application has the correct access to the OBB
|
||||
* directory to allow an OBB file to be written.
|
||||
*
|
||||
* @param c the app/activity/service context
|
||||
* @return true if the application can write an OBB file, false otherwise
|
||||
*/
|
||||
static public boolean canWriteOBBFile(Context c) {
|
||||
String path = getSaveFilePath(c);
|
||||
File fileForNewFile = new File(path);
|
||||
boolean canWrite;
|
||||
if (fileForNewFile.exists()) {
|
||||
canWrite = fileForNewFile.isDirectory() && fileForNewFile.canWrite();
|
||||
} else {
|
||||
canWrite = fileForNewFile.mkdirs();
|
||||
}
|
||||
return canWrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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.content.Context;
|
||||
import android.os.Messenger;
|
||||
|
||||
/**
|
||||
* This is the interface that is used to connect/disconnect from the downloader
|
||||
* service.
|
||||
* <p>
|
||||
* You should get a proxy object that implements this interface by calling
|
||||
* {@link DownloaderClientMarshaller#CreateStub} in your activity when the
|
||||
* downloader service starts. Then, call {@link #connect} during your activity's
|
||||
* onResume() and call {@link #disconnect} during onStop().
|
||||
* <p>
|
||||
* Then during the {@link IDownloaderClient#onServiceConnected} callback, you
|
||||
* should call {@link #getMessenger} to pass the stub's Messenger object to
|
||||
* {@link IDownloaderService#onClientUpdated}.
|
||||
*/
|
||||
public interface IStub {
|
||||
Messenger getMessenger();
|
||||
|
||||
void connect(Context c);
|
||||
|
||||
void disconnect(Context c);
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
/*
|
||||
* 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.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
// -- PANDEMONIUM start --
|
||||
import android.annotation.SuppressLint;
|
||||
// -- PANDEMONIUM end --
|
||||
|
||||
/**
|
||||
* Contains useful helper functions, typically tied to the application context.
|
||||
*/
|
||||
class SystemFacade {
|
||||
private Context mContext;
|
||||
private NotificationManager mNotificationManager;
|
||||
|
||||
public SystemFacade(Context context) {
|
||||
mContext = context;
|
||||
mNotificationManager = (NotificationManager)
|
||||
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
}
|
||||
|
||||
public long currentTimeMillis() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Integer getActiveNetworkType() {
|
||||
ConnectivityManager connectivity =
|
||||
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivity == null) {
|
||||
Log.w(Constants.TAG, "couldn't get connectivity manager");
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
NetworkInfo activeInfo = connectivity.getActiveNetworkInfo();
|
||||
if (activeInfo == null) {
|
||||
if (Constants.LOGVV) {
|
||||
Log.v(Constants.TAG, "network is not available");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return activeInfo.getType();
|
||||
}
|
||||
|
||||
public boolean isNetworkRoaming() {
|
||||
ConnectivityManager connectivity =
|
||||
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivity == null) {
|
||||
Log.w(Constants.TAG, "couldn't get connectivity manager");
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
NetworkInfo info = connectivity.getActiveNetworkInfo();
|
||||
boolean isMobile = (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE);
|
||||
TelephonyManager tm = (TelephonyManager) mContext
|
||||
.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (null == tm) {
|
||||
Log.w(Constants.TAG, "couldn't get telephony manager");
|
||||
return false;
|
||||
}
|
||||
boolean isRoaming = isMobile && tm.isNetworkRoaming();
|
||||
if (Constants.LOGVV && isRoaming) {
|
||||
Log.v(Constants.TAG, "network is roaming");
|
||||
}
|
||||
return isRoaming;
|
||||
}
|
||||
|
||||
public Long getMaxBytesOverMobile() {
|
||||
return (long) Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public Long getRecommendedMaxBytesOverMobile() {
|
||||
return 2097152L;
|
||||
}
|
||||
|
||||
public void sendBroadcast(Intent intent) {
|
||||
mContext.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
public boolean userOwnsPackage(int uid, String packageName) throws NameNotFoundException {
|
||||
return mContext.getPackageManager().getApplicationInfo(packageName, 0).uid == uid;
|
||||
}
|
||||
|
||||
public void postNotification(long id, Notification notification) {
|
||||
/**
|
||||
* TODO: The system notification manager takes ints, not longs, as IDs,
|
||||
* but the download manager uses IDs take straight from the database,
|
||||
* which are longs. This will have to be dealt with at some point.
|
||||
*/
|
||||
mNotificationManager.notify((int) id, notification);
|
||||
}
|
||||
|
||||
public void cancelNotification(long id) {
|
||||
mNotificationManager.cancel((int) id);
|
||||
}
|
||||
|
||||
public void cancelAllNotifications() {
|
||||
mNotificationManager.cancelAll();
|
||||
}
|
||||
|
||||
public void startThread(Thread thread) {
|
||||
thread.start();
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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.impl;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This service differs from IntentService in a few minor ways/ It will not
|
||||
* auto-stop itself after the intent is handled unless the target returns "true"
|
||||
* in should stop. Since the goal of this service is to handle a single kind of
|
||||
* intent, it does not queue up batches of intents of the same type.
|
||||
*/
|
||||
public abstract class CustomIntentService extends Service {
|
||||
private String mName;
|
||||
private boolean mRedelivery;
|
||||
private volatile ServiceHandler mServiceHandler;
|
||||
private volatile Looper mServiceLooper;
|
||||
private static final String LOG_TAG = "CustomIntentService";
|
||||
private static final int WHAT_MESSAGE = -10;
|
||||
|
||||
public CustomIntentService(String paramString) {
|
||||
this.mName = paramString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent paramIntent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
HandlerThread localHandlerThread = new HandlerThread("IntentService["
|
||||
+ this.mName + "]");
|
||||
localHandlerThread.start();
|
||||
this.mServiceLooper = localHandlerThread.getLooper();
|
||||
this.mServiceHandler = new ServiceHandler(this.mServiceLooper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Thread localThread = this.mServiceLooper.getThread();
|
||||
if ((localThread != null) && (localThread.isAlive())) {
|
||||
localThread.interrupt();
|
||||
}
|
||||
this.mServiceLooper.quit();
|
||||
Log.d(LOG_TAG, "onDestroy");
|
||||
}
|
||||
|
||||
protected abstract void onHandleIntent(Intent paramIntent);
|
||||
|
||||
protected abstract boolean shouldStop();
|
||||
|
||||
@Override
|
||||
public void onStart(Intent paramIntent, int startId) {
|
||||
if (!this.mServiceHandler.hasMessages(WHAT_MESSAGE)) {
|
||||
Message localMessage = this.mServiceHandler.obtainMessage();
|
||||
localMessage.arg1 = startId;
|
||||
localMessage.obj = paramIntent;
|
||||
localMessage.what = WHAT_MESSAGE;
|
||||
this.mServiceHandler.sendMessage(localMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent paramIntent, int flags, int startId) {
|
||||
onStart(paramIntent, startId);
|
||||
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
|
||||
}
|
||||
|
||||
public void setIntentRedelivery(boolean enabled) {
|
||||
this.mRedelivery = enabled;
|
||||
}
|
||||
|
||||
private final class ServiceHandler extends Handler {
|
||||
public ServiceHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message paramMessage) {
|
||||
CustomIntentService.this
|
||||
.onHandleIntent((Intent) paramMessage.obj);
|
||||
if (shouldStop()) {
|
||||
Log.d(LOG_TAG, "stopSelf");
|
||||
CustomIntentService.this.stopSelf(paramMessage.arg1);
|
||||
Log.d(LOG_TAG, "afterStopSelf");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
/*
|
||||
* 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.impl;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.Constants;
|
||||
import com.google.android.vending.expansion.downloader.Helpers;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Representation of information about an individual download from the database.
|
||||
*/
|
||||
public class DownloadInfo {
|
||||
public String mUri;
|
||||
public final int mIndex;
|
||||
public final String mFileName;
|
||||
public String mETag;
|
||||
public long mTotalBytes;
|
||||
public long mCurrentBytes;
|
||||
public long mLastMod;
|
||||
public int mStatus;
|
||||
public int mControl;
|
||||
public int mNumFailed;
|
||||
public int mRetryAfter;
|
||||
public int mRedirectCount;
|
||||
|
||||
boolean mInitialized;
|
||||
|
||||
public int mFuzz;
|
||||
|
||||
public DownloadInfo(int index, String fileName, String pkg) {
|
||||
mFuzz = Helpers.sRandom.nextInt(1001);
|
||||
mFileName = fileName;
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
public void resetDownload() {
|
||||
mCurrentBytes = 0;
|
||||
mETag = "";
|
||||
mLastMod = 0;
|
||||
mStatus = 0;
|
||||
mControl = 0;
|
||||
mNumFailed = 0;
|
||||
mRetryAfter = 0;
|
||||
mRedirectCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time when a download should be restarted.
|
||||
*/
|
||||
public long restartTime(long now) {
|
||||
if (mNumFailed == 0) {
|
||||
return now;
|
||||
}
|
||||
if (mRetryAfter > 0) {
|
||||
return mLastMod + mRetryAfter;
|
||||
}
|
||||
return mLastMod +
|
||||
Constants.RETRY_FIRST_DELAY *
|
||||
(1000 + mFuzz) * (1 << (mNumFailed - 1));
|
||||
}
|
||||
|
||||
public void logVerboseInfo() {
|
||||
Log.v(Constants.TAG, "Service adding new entry");
|
||||
Log.v(Constants.TAG, "FILENAME: " + mFileName);
|
||||
Log.v(Constants.TAG, "URI : " + mUri);
|
||||
Log.v(Constants.TAG, "FILENAME: " + mFileName);
|
||||
Log.v(Constants.TAG, "CONTROL : " + mControl);
|
||||
Log.v(Constants.TAG, "STATUS : " + mStatus);
|
||||
Log.v(Constants.TAG, "FAILED_C: " + mNumFailed);
|
||||
Log.v(Constants.TAG, "RETRY_AF: " + mRetryAfter);
|
||||
Log.v(Constants.TAG, "REDIRECT: " + mRedirectCount);
|
||||
Log.v(Constants.TAG, "LAST_MOD: " + mLastMod);
|
||||
Log.v(Constants.TAG, "TOTAL : " + mTotalBytes);
|
||||
Log.v(Constants.TAG, "CURRENT : " + mCurrentBytes);
|
||||
Log.v(Constants.TAG, "ETAG : " + mETag);
|
||||
}
|
||||
}
|