summaryrefslogtreecommitdiff
path: root/platform/android/export
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/export')
-rw-r--r--platform/android/export/export.cpp214
1 files changed, 142 insertions, 72 deletions
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index e1ff12c5ac..255413bf2c 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -192,6 +192,19 @@ static const char *android_perms[] = {
NULL
};
+struct LauncherIcon {
+ char *option_id;
+ char *export_path;
+};
+
+static const LauncherIcon launcher_icons[] = {
+ { "launcher_icons/xxxhdpi_192x192", "res/drawable-xxxhdpi-v4/icon.png" },
+ { "launcher_icons/xxhdpi_144x144", "res/drawable-xxhdpi-v4/icon.png" },
+ { "launcher_icons/xhdpi_96x96", "res/drawable-xhdpi-v4/icon.png" },
+ { "launcher_icons/hdpi_72x72", "res/drawable-hdpi-v4/icon.png" },
+ { "launcher_icons/mdpi_48x48", "res/drawable-mdpi-v4/icon.png" }
+};
+
class EditorExportAndroid : public EditorExportPlatform {
GDCLASS(EditorExportAndroid, EditorExportPlatform)
@@ -467,52 +480,72 @@ class EditorExportAndroid : public EditorExportPlatform {
return zipfi;
}
- static Set<String> get_abis() {
- Set<String> abis;
- abis.insert("armeabi");
- abis.insert("armeabi-v7a");
- abis.insert("arm64-v8a");
- abis.insert("x86");
- abis.insert("x86_64");
- abis.insert("mips");
- abis.insert("mips64");
+ static Vector<String> get_abis() {
+ // mips and armv6 are dead (especially for games), so not including them
+ Vector<String> abis;
+ abis.push_back("armeabi-v7a");
+ abis.push_back("arm64-v8a");
+ abis.push_back("x86");
+ abis.push_back("x86_64");
return abis;
}
- static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
- APKExportData *ed = (APKExportData *)p_userdata;
- String dst_path = p_path;
- static Set<String> android_abis = get_abis();
-
- if (dst_path.ends_with(".so")) {
- String abi = dst_path.get_base_dir().get_file().strip_edges(); // parent dir name
- if (android_abis.has(abi)) {
- dst_path = "lib/" + abi + "/" + dst_path.get_file();
- } else {
- String err = "Dynamic libraries must be located in the folder named after Android ABI they were compiled for. " +
- p_path + " does not follow this convention.";
- ERR_PRINT(err.utf8().get_data());
- return ERR_FILE_BAD_PATH;
- }
- } else {
- dst_path = dst_path.replace_first("res://", "assets/");
- }
-
+ static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) {
zip_fileinfo zipfi = get_zip_fileinfo();
-
zipOpenNewFileInZip(ed->apk,
- dst_path.utf8().get_data(),
+ p_path.utf8().get_data(),
&zipfi,
NULL,
0,
NULL,
0,
NULL,
- _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0,
+ compression_method,
Z_DEFAULT_COMPRESSION);
zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size());
zipCloseFileInZip(ed->apk);
+
+ return OK;
+ }
+
+ static Error save_apk_so(void *p_userdata, const SharedObject &p_so) {
+ if (!p_so.path.get_file().begins_with("lib")) {
+ String err = "Android .so file names must start with \"lib\", but got: " + p_so.path;
+ ERR_PRINT(err.utf8().get_data());
+ return FAILED;
+ }
+ APKExportData *ed = (APKExportData *)p_userdata;
+ Vector<String> abis = get_abis();
+ bool exported = false;
+ for (int i = 0; i < p_so.tags.size(); ++i) {
+ // shared objects can be fat (compatible with multiple ABIs)
+ int start_pos = 0;
+ int abi_index = abis.find(p_so.tags[i]);
+ if (abi_index != -1) {
+ exported = true;
+ start_pos = abi_index + 1;
+ String abi = abis[abi_index];
+ String dst_path = "lib/" + abi + "/" + p_so.path.get_file();
+ Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path);
+ Error store_err = store_in_apk(ed, dst_path, array);
+ ERR_FAIL_COND_V(store_err, store_err);
+ }
+ }
+ if (!exported) {
+ String abis_string = String(" ").join(abis);
+ String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string;
+ ERR_PRINT(err.utf8().get_data());
+ return FAILED;
+ }
+ return OK;
+ }
+
+ static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
+ APKExportData *ed = (APKExportData *)p_userdata;
+ String dst_path = p_path.replace_first("res://", "assets/");
+
+ store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0);
ed->ep->step("File: " + p_path, 3 + p_file * 100 / p_total);
return OK;
}
@@ -935,6 +968,18 @@ class EditorExportAndroid : public EditorExportPlatform {
//printf("end\n");
}
+ static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset) {
+ Vector<String> abis = get_abis();
+ Vector<String> enabled_abis;
+ for (int i = 0; i < abis.size(); ++i) {
+ bool is_enabled = p_preset->get("architectures/" + abis[i]);
+ if (is_enabled) {
+ enabled_abis.push_back(abis[i]);
+ }
+ }
+ return enabled_abis;
+ }
+
public:
enum {
MAX_USER_PERMISSIONS = 20
@@ -951,6 +996,11 @@ public:
r_features->push_back("etc");
else*/
r_features->push_back("etc2");
+
+ Vector<String> abis = get_enabled_abis(p_preset);
+ for (int i = 0; i < abis.size(); ++i) {
+ r_features->push_back(abis[i]);
+ }
}
virtual void get_export_options(List<ExportOption> *r_options) {
@@ -965,16 +1015,18 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name"), "org.godotengine.$genname"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/icon", PROPERTY_HINT_FILE, "png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architecture/arm"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architecture/x86"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true));
+
+ for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icons[i].option_id, PROPERTY_HINT_FILE, "png"), ""));
+ }
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "keystore"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
@@ -982,6 +1034,13 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ Vector<String> abis = get_abis();
+ for (int i = 0; i < abis.size(); ++i) {
+ String abi = abis[i];
+ bool is_default = (abi == "armeabi-v7a");
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
+ }
+
const char **perms = android_perms;
while (*perms) {
@@ -1295,10 +1354,6 @@ public:
String unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
zipFile unaligned_apk = zipOpen2(unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
- bool export_x86 = p_preset->get("architecture/x86");
- bool export_arm = p_preset->get("architecture/arm");
- bool export_arm64 = p_preset->get("architecture/arm64");
-
bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer");
bool immersive = p_preset->get("screen/immersive_mode");
@@ -1318,6 +1373,8 @@ public:
String release_username = p_preset->get("keystore/release_user");
String release_password = p_preset->get("keystore/release_password");
+ Vector<String> enabled_abis = get_enabled_abis(p_preset);
+
while (ret == UNZ_OK) {
//get filename
@@ -1334,7 +1391,7 @@ public:
//read
unzOpenCurrentFile(pkg);
- unzReadCurrentFile(pkg, data.ptr(), data.size());
+ unzReadCurrentFile(pkg, data.ptrw(), data.size());
unzCloseCurrentFile(pkg);
//write
@@ -1350,23 +1407,20 @@ public:
}
if (file == "res/drawable/icon.png") {
-
- String icon = p_preset->get("package/icon");
- icon = icon.strip_edges();
bool found = false;
-
- if (icon != "" && icon.ends_with(".png")) {
-
- FileAccess *f = FileAccess::open(icon, FileAccess::READ);
- if (f) {
-
- data.resize(f->get_len());
- f->get_buffer(data.ptr(), data.size());
- memdelete(f);
- found = true;
+ for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) {
+ String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges();
+ if (icon_path != "" && icon_path.ends_with(".png")) {
+ FileAccess *f = FileAccess::open(icon_path, FileAccess::READ);
+ if (f) {
+ data.resize(f->get_len());
+ f->get_buffer(data.ptrw(), data.size());
+ memdelete(f);
+ found = true;
+ break;
+ }
}
}
-
if (!found) {
String appicon = ProjectSettings::get_singleton()->get("application/config/icon");
@@ -1374,32 +1428,32 @@ public:
FileAccess *f = FileAccess::open(appicon, FileAccess::READ);
if (f) {
data.resize(f->get_len());
- f->get_buffer(data.ptr(), data.size());
+ f->get_buffer(data.ptrw(), data.size());
memdelete(f);
}
}
}
}
- if (file == "lib/x86/*.so" && !export_x86) {
- skip = true;
- }
-
- if (file.match("lib/armeabi*/*.so") && !export_arm) {
- skip = true;
- }
-
- if (file.match("lib/arm64*/*.so") && !export_arm64) {
- skip = true;
+ if (file.ends_with(".so")) {
+ bool enabled = false;
+ for (int i = 0; i < enabled_abis.size(); ++i) {
+ if (file.begins_with("lib/" + enabled_abis[i] + "/")) {
+ enabled = true;
+ break;
+ }
+ }
+ if (!enabled) {
+ skip = true;
+ }
}
if (file.begins_with("META-INF") && _signed) {
skip = true;
}
- print_line("ADDING: " + file);
-
if (!skip) {
+ print_line("ADDING: " + file);
// Respect decision on compression made by AAPT for the export template
const bool uncompressed = info.compression_method == 0;
@@ -1473,7 +1527,20 @@ public:
ed.ep = &ep;
ed.apk = unaligned_apk;
- err = export_project_files(p_preset, save_apk_file, &ed);
+ err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so);
+ }
+
+ if (!err) {
+ APKExportData ed;
+ ed.ep = &ep;
+ ed.apk = unaligned_apk;
+ for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) {
+ String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges();
+ if (icon_path != "" && icon_path.ends_with(".png") && FileAccess::exists(icon_path)) {
+ Vector<uint8_t> data = FileAccess::get_file_as_array(icon_path);
+ store_in_apk(&ed, launcher_icons[i].export_path, data);
+ }
+ }
}
}
@@ -1490,12 +1557,15 @@ public:
encode_uint32(cl.size(), &clf[0]);
for (int i = 0; i < cl.size(); i++) {
+ print_line(itos(i) + " param: " + cl[i]);
CharString txt = cl[i].utf8();
int base = clf.size();
- clf.resize(base + 4 + txt.length());
- encode_uint32(txt.length(), &clf[base]);
- copymem(&clf[base + 4], txt.ptr(), txt.length());
- print_line(itos(i) + " param: " + cl[i]);
+ int length = txt.length();
+ if (!length)
+ continue;
+ clf.resize(base + 4 + length);
+ encode_uint32(length, &clf[base]);
+ copymem(&clf[base + 4], txt.ptr(), length);
}
zip_fileinfo zipfi = get_zip_fileinfo();
@@ -1636,7 +1706,7 @@ public:
int method, level;
unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read
long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned);
- unzReadCurrentFile(tmp_unaligned, data.ptr(), data.size());
+ unzReadCurrentFile(tmp_unaligned, data.ptrw(), data.size());
unzCloseCurrentFile(tmp_unaligned);
// align