export.cpp 93 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896
  1. /*************************************************************************/
  2. /* export.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "export.h"
  31. #include "core/io/image_loader.h"
  32. #include "core/io/marshalls.h"
  33. #include "core/io/zip_io.h"
  34. #include "core/os/dir_access.h"
  35. #include "core/os/file_access.h"
  36. #include "core/os/os.h"
  37. #include "core/project_settings.h"
  38. #include "core/version.h"
  39. #include "drivers/png/png_driver_common.h"
  40. #include "editor/editor_export.h"
  41. #include "editor/editor_log.h"
  42. #include "editor/editor_node.h"
  43. #include "editor/editor_settings.h"
  44. #include "platform/android/logo.gen.h"
  45. #include "platform/android/plugin/godot_plugin_config.h"
  46. #include "platform/android/run_icon.gen.h"
  47. #include <string.h>
  48. static const char *android_perms[] = {
  49. "ACCESS_CHECKIN_PROPERTIES",
  50. "ACCESS_COARSE_LOCATION",
  51. "ACCESS_FINE_LOCATION",
  52. "ACCESS_LOCATION_EXTRA_COMMANDS",
  53. "ACCESS_MOCK_LOCATION",
  54. "ACCESS_NETWORK_STATE",
  55. "ACCESS_SURFACE_FLINGER",
  56. "ACCESS_WIFI_STATE",
  57. "ACCOUNT_MANAGER",
  58. "ADD_VOICEMAIL",
  59. "AUTHENTICATE_ACCOUNTS",
  60. "BATTERY_STATS",
  61. "BIND_ACCESSIBILITY_SERVICE",
  62. "BIND_APPWIDGET",
  63. "BIND_DEVICE_ADMIN",
  64. "BIND_INPUT_METHOD",
  65. "BIND_NFC_SERVICE",
  66. "BIND_NOTIFICATION_LISTENER_SERVICE",
  67. "BIND_PRINT_SERVICE",
  68. "BIND_REMOTEVIEWS",
  69. "BIND_TEXT_SERVICE",
  70. "BIND_VPN_SERVICE",
  71. "BIND_WALLPAPER",
  72. "BLUETOOTH",
  73. "BLUETOOTH_ADMIN",
  74. "BLUETOOTH_PRIVILEGED",
  75. "BRICK",
  76. "BROADCAST_PACKAGE_REMOVED",
  77. "BROADCAST_SMS",
  78. "BROADCAST_STICKY",
  79. "BROADCAST_WAP_PUSH",
  80. "CALL_PHONE",
  81. "CALL_PRIVILEGED",
  82. "CAMERA",
  83. "CAPTURE_AUDIO_OUTPUT",
  84. "CAPTURE_SECURE_VIDEO_OUTPUT",
  85. "CAPTURE_VIDEO_OUTPUT",
  86. "CHANGE_COMPONENT_ENABLED_STATE",
  87. "CHANGE_CONFIGURATION",
  88. "CHANGE_NETWORK_STATE",
  89. "CHANGE_WIFI_MULTICAST_STATE",
  90. "CHANGE_WIFI_STATE",
  91. "CLEAR_APP_CACHE",
  92. "CLEAR_APP_USER_DATA",
  93. "CONTROL_LOCATION_UPDATES",
  94. "DELETE_CACHE_FILES",
  95. "DELETE_PACKAGES",
  96. "DEVICE_POWER",
  97. "DIAGNOSTIC",
  98. "DISABLE_KEYGUARD",
  99. "DUMP",
  100. "EXPAND_STATUS_BAR",
  101. "FACTORY_TEST",
  102. "FLASHLIGHT",
  103. "FORCE_BACK",
  104. "GET_ACCOUNTS",
  105. "GET_PACKAGE_SIZE",
  106. "GET_TASKS",
  107. "GET_TOP_ACTIVITY_INFO",
  108. "GLOBAL_SEARCH",
  109. "HARDWARE_TEST",
  110. "INJECT_EVENTS",
  111. "INSTALL_LOCATION_PROVIDER",
  112. "INSTALL_PACKAGES",
  113. "INSTALL_SHORTCUT",
  114. "INTERNAL_SYSTEM_WINDOW",
  115. "INTERNET",
  116. "KILL_BACKGROUND_PROCESSES",
  117. "LOCATION_HARDWARE",
  118. "MANAGE_ACCOUNTS",
  119. "MANAGE_APP_TOKENS",
  120. "MANAGE_DOCUMENTS",
  121. "MASTER_CLEAR",
  122. "MEDIA_CONTENT_CONTROL",
  123. "MODIFY_AUDIO_SETTINGS",
  124. "MODIFY_PHONE_STATE",
  125. "MOUNT_FORMAT_FILESYSTEMS",
  126. "MOUNT_UNMOUNT_FILESYSTEMS",
  127. "NFC",
  128. "PERSISTENT_ACTIVITY",
  129. "PROCESS_OUTGOING_CALLS",
  130. "READ_CALENDAR",
  131. "READ_CALL_LOG",
  132. "READ_CONTACTS",
  133. "READ_EXTERNAL_STORAGE",
  134. "READ_FRAME_BUFFER",
  135. "READ_HISTORY_BOOKMARKS",
  136. "READ_INPUT_STATE",
  137. "READ_LOGS",
  138. "READ_PHONE_STATE",
  139. "READ_PROFILE",
  140. "READ_SMS",
  141. "READ_SOCIAL_STREAM",
  142. "READ_SYNC_SETTINGS",
  143. "READ_SYNC_STATS",
  144. "READ_USER_DICTIONARY",
  145. "REBOOT",
  146. "RECEIVE_BOOT_COMPLETED",
  147. "RECEIVE_MMS",
  148. "RECEIVE_SMS",
  149. "RECEIVE_WAP_PUSH",
  150. "RECORD_AUDIO",
  151. "REORDER_TASKS",
  152. "RESTART_PACKAGES",
  153. "SEND_RESPOND_VIA_MESSAGE",
  154. "SEND_SMS",
  155. "SET_ACTIVITY_WATCHER",
  156. "SET_ALARM",
  157. "SET_ALWAYS_FINISH",
  158. "SET_ANIMATION_SCALE",
  159. "SET_DEBUG_APP",
  160. "SET_ORIENTATION",
  161. "SET_POINTER_SPEED",
  162. "SET_PREFERRED_APPLICATIONS",
  163. "SET_PROCESS_LIMIT",
  164. "SET_TIME",
  165. "SET_TIME_ZONE",
  166. "SET_WALLPAPER",
  167. "SET_WALLPAPER_HINTS",
  168. "SIGNAL_PERSISTENT_PROCESSES",
  169. "STATUS_BAR",
  170. "SUBSCRIBED_FEEDS_READ",
  171. "SUBSCRIBED_FEEDS_WRITE",
  172. "SYSTEM_ALERT_WINDOW",
  173. "TRANSMIT_IR",
  174. "UNINSTALL_SHORTCUT",
  175. "UPDATE_DEVICE_STATS",
  176. "USE_CREDENTIALS",
  177. "USE_SIP",
  178. "VIBRATE",
  179. "WAKE_LOCK",
  180. "WRITE_APN_SETTINGS",
  181. "WRITE_CALENDAR",
  182. "WRITE_CALL_LOG",
  183. "WRITE_CONTACTS",
  184. "WRITE_EXTERNAL_STORAGE",
  185. "WRITE_GSERVICES",
  186. "WRITE_HISTORY_BOOKMARKS",
  187. "WRITE_PROFILE",
  188. "WRITE_SECURE_SETTINGS",
  189. "WRITE_SETTINGS",
  190. "WRITE_SMS",
  191. "WRITE_SOCIAL_STREAM",
  192. "WRITE_SYNC_SETTINGS",
  193. "WRITE_USER_DICTIONARY",
  194. NULL
  195. };
  196. struct LauncherIcon {
  197. const char *export_path;
  198. int dimensions;
  199. };
  200. static const int icon_densities_count = 6;
  201. static const char *launcher_icon_option = "launcher_icons/main_192x192";
  202. static const char *launcher_adaptive_icon_foreground_option = "launcher_icons/adaptive_foreground_432x432";
  203. static const char *launcher_adaptive_icon_background_option = "launcher_icons/adaptive_background_432x432";
  204. static const LauncherIcon launcher_icons[icon_densities_count] = {
  205. { "res/mipmap-xxxhdpi-v4/icon.png", 192 },
  206. { "res/mipmap-xxhdpi-v4/icon.png", 144 },
  207. { "res/mipmap-xhdpi-v4/icon.png", 96 },
  208. { "res/mipmap-hdpi-v4/icon.png", 72 },
  209. { "res/mipmap-mdpi-v4/icon.png", 48 },
  210. { "res/mipmap/icon.png", 192 }
  211. };
  212. static const LauncherIcon launcher_adaptive_icon_foregrounds[icon_densities_count] = {
  213. { "res/mipmap-xxxhdpi-v4/icon_foreground.png", 432 },
  214. { "res/mipmap-xxhdpi-v4/icon_foreground.png", 324 },
  215. { "res/mipmap-xhdpi-v4/icon_foreground.png", 216 },
  216. { "res/mipmap-hdpi-v4/icon_foreground.png", 162 },
  217. { "res/mipmap-mdpi-v4/icon_foreground.png", 108 },
  218. { "res/mipmap/icon_foreground.png", 432 }
  219. };
  220. static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_count] = {
  221. { "res/mipmap-xxxhdpi-v4/icon_background.png", 432 },
  222. { "res/mipmap-xxhdpi-v4/icon_background.png", 324 },
  223. { "res/mipmap-xhdpi-v4/icon_background.png", 216 },
  224. { "res/mipmap-hdpi-v4/icon_background.png", 162 },
  225. { "res/mipmap-mdpi-v4/icon_background.png", 108 },
  226. { "res/mipmap/icon_background.png", 432 }
  227. };
  228. class EditorExportPlatformAndroid : public EditorExportPlatform {
  229. GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform);
  230. Ref<ImageTexture> logo;
  231. Ref<ImageTexture> run_icon;
  232. struct Device {
  233. String id;
  234. String name;
  235. String description;
  236. int api_level;
  237. };
  238. struct APKExportData {
  239. zipFile apk;
  240. EditorProgress *ep;
  241. };
  242. Vector<PluginConfig> plugins;
  243. String last_plugin_names;
  244. uint64_t last_custom_build_time = 0;
  245. volatile bool plugins_changed;
  246. Mutex *plugins_lock;
  247. Vector<Device> devices;
  248. volatile bool devices_changed;
  249. Mutex *device_lock;
  250. Thread *check_for_changes_thread;
  251. volatile bool quit_request;
  252. static void _check_for_changes_poll_thread(void *ud) {
  253. EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
  254. while (!ea->quit_request) {
  255. // Check for plugins updates
  256. {
  257. // Nothing to do if we already know the plugins have changed.
  258. if (!ea->plugins_changed) {
  259. Vector<PluginConfig> loaded_plugins = get_plugins();
  260. ea->plugins_lock->lock();
  261. if (ea->plugins.size() != loaded_plugins.size()) {
  262. ea->plugins_changed = true;
  263. } else {
  264. for (int i = 0; i < ea->plugins.size(); i++) {
  265. if (ea->plugins[i].name != loaded_plugins[i].name) {
  266. ea->plugins_changed = true;
  267. break;
  268. }
  269. }
  270. }
  271. if (ea->plugins_changed) {
  272. ea->plugins = loaded_plugins;
  273. }
  274. ea->plugins_lock->unlock();
  275. }
  276. }
  277. // Check for devices updates
  278. String adb = EditorSettings::get_singleton()->get("export/android/adb");
  279. if (FileAccess::exists(adb)) {
  280. String devices;
  281. List<String> args;
  282. args.push_back("devices");
  283. int ec;
  284. OS::get_singleton()->execute(adb, args, true, NULL, &devices, &ec);
  285. Vector<String> ds = devices.split("\n");
  286. Vector<String> ldevices;
  287. for (int i = 1; i < ds.size(); i++) {
  288. String d = ds[i];
  289. int dpos = d.find("device");
  290. if (dpos == -1) {
  291. continue;
  292. }
  293. d = d.substr(0, dpos).strip_edges();
  294. ldevices.push_back(d);
  295. }
  296. ea->device_lock->lock();
  297. bool different = false;
  298. if (ea->devices.size() != ldevices.size()) {
  299. different = true;
  300. } else {
  301. for (int i = 0; i < ea->devices.size(); i++) {
  302. if (ea->devices[i].id != ldevices[i]) {
  303. different = true;
  304. break;
  305. }
  306. }
  307. }
  308. if (different) {
  309. Vector<Device> ndevices;
  310. for (int i = 0; i < ldevices.size(); i++) {
  311. Device d;
  312. d.id = ldevices[i];
  313. for (int j = 0; j < ea->devices.size(); j++) {
  314. if (ea->devices[j].id == ldevices[i]) {
  315. d.description = ea->devices[j].description;
  316. d.name = ea->devices[j].name;
  317. d.api_level = ea->devices[j].api_level;
  318. }
  319. }
  320. if (d.description == "") {
  321. //in the oven, request!
  322. args.clear();
  323. args.push_back("-s");
  324. args.push_back(d.id);
  325. args.push_back("shell");
  326. args.push_back("getprop");
  327. int ec2;
  328. String dp;
  329. OS::get_singleton()->execute(adb, args, true, NULL, &dp, &ec2);
  330. Vector<String> props = dp.split("\n");
  331. String vendor;
  332. String device;
  333. d.description = "Device ID: " + d.id + "\n";
  334. d.api_level = 0;
  335. for (int j = 0; j < props.size(); j++) {
  336. // got information by `shell cat /system/build.prop` before and its format is "property=value"
  337. // it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above
  338. // its format is "[property]: [value]" so changed it as like build.prop
  339. String p = props[j];
  340. p = p.replace("]: ", "=");
  341. p = p.replace("[", "");
  342. p = p.replace("]", "");
  343. if (p.begins_with("ro.product.model=")) {
  344. device = p.get_slice("=", 1).strip_edges();
  345. } else if (p.begins_with("ro.product.brand=")) {
  346. vendor = p.get_slice("=", 1).strip_edges().capitalize();
  347. } else if (p.begins_with("ro.build.display.id=")) {
  348. d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n";
  349. } else if (p.begins_with("ro.build.version.release=")) {
  350. d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n";
  351. } else if (p.begins_with("ro.build.version.sdk=")) {
  352. d.api_level = p.get_slice("=", 1).to_int();
  353. } else if (p.begins_with("ro.product.cpu.abi=")) {
  354. d.description += "CPU: " + p.get_slice("=", 1).strip_edges() + "\n";
  355. } else if (p.begins_with("ro.product.manufacturer=")) {
  356. d.description += "Manufacturer: " + p.get_slice("=", 1).strip_edges() + "\n";
  357. } else if (p.begins_with("ro.board.platform=")) {
  358. d.description += "Chipset: " + p.get_slice("=", 1).strip_edges() + "\n";
  359. } else if (p.begins_with("ro.opengles.version=")) {
  360. uint32_t opengl = p.get_slice("=", 1).to_int();
  361. d.description += "OpenGL: " + itos(opengl >> 16) + "." + itos((opengl >> 8) & 0xFF) + "." + itos((opengl)&0xFF) + "\n";
  362. }
  363. }
  364. d.name = vendor + " " + device;
  365. if (device == String()) continue;
  366. }
  367. ndevices.push_back(d);
  368. }
  369. ea->devices = ndevices;
  370. ea->devices_changed = true;
  371. }
  372. ea->device_lock->unlock();
  373. }
  374. uint64_t sleep = OS::get_singleton()->get_power_state() == OS::POWERSTATE_ON_BATTERY ? 1000 : 100;
  375. uint64_t wait = 3000000;
  376. uint64_t time = OS::get_singleton()->get_ticks_usec();
  377. while (OS::get_singleton()->get_ticks_usec() - time < wait) {
  378. OS::get_singleton()->delay_usec(1000 * sleep);
  379. if (ea->quit_request)
  380. break;
  381. }
  382. }
  383. if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) {
  384. String adb = EditorSettings::get_singleton()->get("export/android/adb");
  385. if (!FileAccess::exists(adb)) {
  386. return; //adb not configured
  387. }
  388. List<String> args;
  389. args.push_back("kill-server");
  390. OS::get_singleton()->execute(adb, args, true);
  391. };
  392. }
  393. String get_project_name(const String &p_name) const {
  394. String aname;
  395. if (p_name != "") {
  396. aname = p_name;
  397. } else {
  398. aname = ProjectSettings::get_singleton()->get("application/config/name");
  399. }
  400. if (aname == "") {
  401. aname = VERSION_NAME;
  402. }
  403. return aname;
  404. }
  405. String get_package_name(const String &p_package) const {
  406. String pname = p_package;
  407. String basename = ProjectSettings::get_singleton()->get("application/config/name");
  408. basename = basename.to_lower();
  409. String name;
  410. bool first = true;
  411. for (int i = 0; i < basename.length(); i++) {
  412. CharType c = basename[i];
  413. if (c >= '0' && c <= '9' && first) {
  414. continue;
  415. }
  416. if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
  417. name += String::chr(c);
  418. first = false;
  419. }
  420. }
  421. if (name == "")
  422. name = "noname";
  423. pname = pname.replace("$genname", name);
  424. return pname;
  425. }
  426. bool is_package_name_valid(const String &p_package, String *r_error = NULL) const {
  427. String pname = p_package;
  428. if (pname.length() == 0) {
  429. if (r_error) {
  430. *r_error = TTR("Package name is missing.");
  431. }
  432. return false;
  433. }
  434. int segments = 0;
  435. bool first = true;
  436. for (int i = 0; i < pname.length(); i++) {
  437. CharType c = pname[i];
  438. if (first && c == '.') {
  439. if (r_error) {
  440. *r_error = TTR("Package segments must be of non-zero length.");
  441. }
  442. return false;
  443. }
  444. if (c == '.') {
  445. segments++;
  446. first = true;
  447. continue;
  448. }
  449. if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) {
  450. if (r_error) {
  451. *r_error = vformat(TTR("The character '%s' is not allowed in Android application package names."), String::chr(c));
  452. }
  453. return false;
  454. }
  455. if (first && (c >= '0' && c <= '9')) {
  456. if (r_error) {
  457. *r_error = TTR("A digit cannot be the first character in a package segment.");
  458. }
  459. return false;
  460. }
  461. if (first && c == '_') {
  462. if (r_error) {
  463. *r_error = vformat(TTR("The character '%s' cannot be the first character in a package segment."), String::chr(c));
  464. }
  465. return false;
  466. }
  467. first = false;
  468. }
  469. if (segments == 0) {
  470. if (r_error) {
  471. *r_error = TTR("The package must have at least one '.' separator.");
  472. }
  473. return false;
  474. }
  475. if (first) {
  476. if (r_error) {
  477. *r_error = TTR("Package segments must be of non-zero length.");
  478. }
  479. return false;
  480. }
  481. return true;
  482. }
  483. static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) {
  484. /*
  485. * By not compressing files with little or not benefit in doing so,
  486. * a performance gain is expected attime. Moreover, if the APK is
  487. * zip-aligned, assets stored as they are can be efficiently read by
  488. * Android by memory-mapping them.
  489. */
  490. // -- Unconditional uncompress to mimic AAPT plus some other
  491. static const char *unconditional_compress_ext[] = {
  492. // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp
  493. // These formats are already compressed, or don't compress well:
  494. ".jpg", ".jpeg", ".png", ".gif",
  495. ".wav", ".mp2", ".mp3", ".ogg", ".aac",
  496. ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
  497. ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
  498. ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
  499. ".amr", ".awb", ".wma", ".wmv",
  500. // Godot-specific:
  501. ".webp", // Same reasoning as .png
  502. ".cfb", // Don't let small config files slow-down startup
  503. ".scn", // Binary scenes are usually already compressed
  504. ".stex", // Streamable textures are usually already compressed
  505. // Trailer for easier processing
  506. NULL
  507. };
  508. for (const char **ext = unconditional_compress_ext; *ext; ++ext) {
  509. if (p_path.to_lower().ends_with(String(*ext))) {
  510. return false;
  511. }
  512. }
  513. // -- Compressed resource?
  514. if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') {
  515. // Already compressed
  516. return false;
  517. }
  518. // --- TODO: Decide on texture resources according to their image compression setting
  519. return true;
  520. }
  521. static zip_fileinfo get_zip_fileinfo() {
  522. OS::Time time = OS::get_singleton()->get_time();
  523. OS::Date date = OS::get_singleton()->get_date();
  524. zip_fileinfo zipfi;
  525. zipfi.tmz_date.tm_hour = time.hour;
  526. zipfi.tmz_date.tm_mday = date.day;
  527. zipfi.tmz_date.tm_min = time.min;
  528. zipfi.tmz_date.tm_mon = date.month;
  529. zipfi.tmz_date.tm_sec = time.sec;
  530. zipfi.tmz_date.tm_year = date.year;
  531. zipfi.dosDate = 0;
  532. zipfi.external_fa = 0;
  533. zipfi.internal_fa = 0;
  534. return zipfi;
  535. }
  536. static Vector<String> get_abis() {
  537. Vector<String> abis;
  538. abis.push_back("armeabi-v7a");
  539. abis.push_back("arm64-v8a");
  540. abis.push_back("x86");
  541. abis.push_back("x86_64");
  542. return abis;
  543. }
  544. /// List the gdap files in the directory specified by the p_path parameter.
  545. static Vector<String> list_gdap_files(const String &p_path) {
  546. Vector<String> dir_files;
  547. DirAccessRef da = DirAccess::open(p_path);
  548. if (da) {
  549. da->list_dir_begin();
  550. while (true) {
  551. String file = da->get_next();
  552. if (file == "") {
  553. break;
  554. }
  555. if (da->current_is_dir() || da->current_is_hidden()) {
  556. continue;
  557. }
  558. if (file.ends_with(PLUGIN_CONFIG_EXT)) {
  559. dir_files.push_back(file);
  560. }
  561. }
  562. da->list_dir_end();
  563. }
  564. return dir_files;
  565. }
  566. static Vector<PluginConfig> get_plugins() {
  567. Vector<PluginConfig> loaded_plugins;
  568. String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
  569. // Add the prebuilt plugins
  570. loaded_plugins.append_array(get_prebuilt_plugins(plugins_dir));
  571. if (DirAccess::exists(plugins_dir)) {
  572. Vector<String> plugins_filenames = list_gdap_files(plugins_dir);
  573. if (!plugins_filenames.empty()) {
  574. Ref<ConfigFile> config_file = memnew(ConfigFile);
  575. for (int i = 0; i < plugins_filenames.size(); i++) {
  576. PluginConfig config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i]));
  577. if (config.valid_config) {
  578. loaded_plugins.push_back(config);
  579. } else {
  580. print_error("Invalid plugin config file " + plugins_filenames[i]);
  581. }
  582. }
  583. }
  584. }
  585. return loaded_plugins;
  586. }
  587. static Vector<PluginConfig> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
  588. Vector<PluginConfig> enabled_plugins;
  589. Vector<PluginConfig> all_plugins = get_plugins();
  590. for (int i = 0; i < all_plugins.size(); i++) {
  591. PluginConfig plugin = all_plugins[i];
  592. bool enabled = p_presets->get("plugins/" + plugin.name);
  593. if (enabled) {
  594. enabled_plugins.push_back(plugin);
  595. }
  596. }
  597. return enabled_plugins;
  598. }
  599. static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) {
  600. zip_fileinfo zipfi = get_zip_fileinfo();
  601. zipOpenNewFileInZip(ed->apk,
  602. p_path.utf8().get_data(),
  603. &zipfi,
  604. NULL,
  605. 0,
  606. NULL,
  607. 0,
  608. NULL,
  609. compression_method,
  610. Z_DEFAULT_COMPRESSION);
  611. zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size());
  612. zipCloseFileInZip(ed->apk);
  613. return OK;
  614. }
  615. static Error save_apk_so(void *p_userdata, const SharedObject &p_so) {
  616. if (!p_so.path.get_file().begins_with("lib")) {
  617. String err = "Android .so file names must start with \"lib\", but got: " + p_so.path;
  618. ERR_PRINTS(err);
  619. return FAILED;
  620. }
  621. APKExportData *ed = (APKExportData *)p_userdata;
  622. Vector<String> abis = get_abis();
  623. bool exported = false;
  624. for (int i = 0; i < p_so.tags.size(); ++i) {
  625. // shared objects can be fat (compatible with multiple ABIs)
  626. int abi_index = abis.find(p_so.tags[i]);
  627. if (abi_index != -1) {
  628. exported = true;
  629. String abi = abis[abi_index];
  630. String dst_path = String("lib").plus_file(abi).plus_file(p_so.path.get_file());
  631. Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path);
  632. Error store_err = store_in_apk(ed, dst_path, array);
  633. ERR_FAIL_COND_V_MSG(store_err, store_err, "Cannot store in apk file '" + dst_path + "'.");
  634. }
  635. }
  636. if (!exported) {
  637. String abis_string = String(" ").join(abis);
  638. String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string;
  639. ERR_PRINTS(err);
  640. return FAILED;
  641. }
  642. return OK;
  643. }
  644. static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
  645. APKExportData *ed = (APKExportData *)p_userdata;
  646. String dst_path = p_path.replace_first("res://", "assets/");
  647. store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0);
  648. return OK;
  649. }
  650. static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
  651. return OK;
  652. }
  653. void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) {
  654. // Leaving the unused types commented because looking these constants up
  655. // again later would be annoying
  656. // const int CHUNK_AXML_FILE = 0x00080003;
  657. // const int CHUNK_RESOURCEIDS = 0x00080180;
  658. const int CHUNK_STRINGS = 0x001C0001;
  659. // const int CHUNK_XML_END_NAMESPACE = 0x00100101;
  660. const int CHUNK_XML_END_TAG = 0x00100103;
  661. // const int CHUNK_XML_START_NAMESPACE = 0x00100100;
  662. const int CHUNK_XML_START_TAG = 0x00100102;
  663. // const int CHUNK_XML_TEXT = 0x00100104;
  664. const int UTF8_FLAG = 0x00000100;
  665. Vector<String> string_table;
  666. uint32_t ofs = 8;
  667. uint32_t string_count = 0;
  668. //uint32_t styles_count = 0;
  669. uint32_t string_flags = 0;
  670. uint32_t string_data_offset = 0;
  671. //uint32_t styles_offset = 0;
  672. uint32_t string_table_begins = 0;
  673. uint32_t string_table_ends = 0;
  674. Vector<uint8_t> stable_extra;
  675. String version_name = p_preset->get("version/name");
  676. int version_code = p_preset->get("version/code");
  677. String package_name = p_preset->get("package/unique_name");
  678. int orientation = p_preset->get("screen/orientation");
  679. bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name") == "GLES3" &&
  680. !ProjectSettings::get_singleton()->get("rendering/quality/driver/fallback_to_gles2");
  681. bool screen_support_small = p_preset->get("screen/support_small");
  682. bool screen_support_normal = p_preset->get("screen/support_normal");
  683. bool screen_support_large = p_preset->get("screen/support_large");
  684. bool screen_support_xlarge = p_preset->get("screen/support_xlarge");
  685. int xr_mode_index = p_preset->get("xr_features/xr_mode");
  686. bool focus_awareness = p_preset->get("xr_features/focus_awareness");
  687. String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
  688. Vector<String> perms;
  689. const char **aperms = android_perms;
  690. while (*aperms) {
  691. bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower());
  692. if (enabled)
  693. perms.push_back("android.permission." + String(*aperms));
  694. aperms++;
  695. }
  696. PoolStringArray user_perms = p_preset->get("permissions/custom_permissions");
  697. for (int i = 0; i < user_perms.size(); i++) {
  698. String user_perm = user_perms[i].strip_edges();
  699. if (!user_perm.empty()) {
  700. perms.push_back(user_perm);
  701. }
  702. }
  703. if (p_give_internet) {
  704. if (perms.find("android.permission.INTERNET") == -1)
  705. perms.push_back("android.permission.INTERNET");
  706. }
  707. while (ofs < (uint32_t)p_manifest.size()) {
  708. uint32_t chunk = decode_uint32(&p_manifest[ofs]);
  709. uint32_t size = decode_uint32(&p_manifest[ofs + 4]);
  710. switch (chunk) {
  711. case CHUNK_STRINGS: {
  712. int iofs = ofs + 8;
  713. string_count = decode_uint32(&p_manifest[iofs]);
  714. //styles_count = decode_uint32(&p_manifest[iofs + 4]);
  715. string_flags = decode_uint32(&p_manifest[iofs + 8]);
  716. string_data_offset = decode_uint32(&p_manifest[iofs + 12]);
  717. //styles_offset = decode_uint32(&p_manifest[iofs + 16]);
  718. /*
  719. printf("string count: %i\n",string_count);
  720. printf("flags: %i\n",string_flags);
  721. printf("sdata ofs: %i\n",string_data_offset);
  722. printf("styles ofs: %i\n",styles_offset);
  723. */
  724. uint32_t st_offset = iofs + 20;
  725. string_table.resize(string_count);
  726. uint32_t string_end = 0;
  727. string_table_begins = st_offset;
  728. for (uint32_t i = 0; i < string_count; i++) {
  729. uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]);
  730. string_at += st_offset + string_count * 4;
  731. ERR_FAIL_COND_MSG(string_flags & UTF8_FLAG, "Unimplemented, can't read UTF-8 string table.");
  732. if (string_flags & UTF8_FLAG) {
  733. } else {
  734. uint32_t len = decode_uint16(&p_manifest[string_at]);
  735. Vector<CharType> ucstring;
  736. ucstring.resize(len + 1);
  737. for (uint32_t j = 0; j < len; j++) {
  738. uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]);
  739. ucstring.write[j] = c;
  740. }
  741. string_end = MAX(string_at + 2 + 2 * len, string_end);
  742. ucstring.write[len] = 0;
  743. string_table.write[i] = ucstring.ptr();
  744. }
  745. }
  746. for (uint32_t i = string_end; i < (ofs + size); i++) {
  747. stable_extra.push_back(p_manifest[i]);
  748. }
  749. string_table_ends = ofs + size;
  750. } break;
  751. case CHUNK_XML_START_TAG: {
  752. int iofs = ofs + 8;
  753. uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
  754. String tname = string_table[name];
  755. uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]);
  756. iofs += 28;
  757. for (uint32_t i = 0; i < attrcount; i++) {
  758. uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]);
  759. uint32_t attr_name = decode_uint32(&p_manifest[iofs + 4]);
  760. uint32_t attr_value = decode_uint32(&p_manifest[iofs + 8]);
  761. uint32_t attr_resid = decode_uint32(&p_manifest[iofs + 16]);
  762. const String value = (attr_value != 0xFFFFFFFF) ? string_table[attr_value] : "Res #" + itos(attr_resid);
  763. String attrname = string_table[attr_name];
  764. const String nspace = (attr_nspace != 0xFFFFFFFF) ? string_table[attr_nspace] : "";
  765. //replace project information
  766. if (tname == "manifest" && attrname == "package") {
  767. string_table.write[attr_value] = get_package_name(package_name);
  768. }
  769. if (tname == "manifest" && attrname == "versionCode") {
  770. encode_uint32(version_code, &p_manifest.write[iofs + 16]);
  771. }
  772. if (tname == "manifest" && attrname == "versionName") {
  773. if (attr_value == 0xFFFFFFFF) {
  774. WARN_PRINT("Version name in a resource, should be plain text");
  775. } else
  776. string_table.write[attr_value] = version_name;
  777. }
  778. if (tname == "instrumentation" && attrname == "targetPackage") {
  779. string_table.write[attr_value] = get_package_name(package_name);
  780. }
  781. if (tname == "activity" && attrname == "screenOrientation") {
  782. encode_uint32(orientation == 0 ? 0 : 1, &p_manifest.write[iofs + 16]);
  783. }
  784. if (tname == "supports-screens") {
  785. if (attrname == "smallScreens") {
  786. encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
  787. } else if (attrname == "normalScreens") {
  788. encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
  789. } else if (attrname == "largeScreens") {
  790. encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
  791. } else if (attrname == "xlargeScreens") {
  792. encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
  793. }
  794. }
  795. if (tname == "uses-feature" && attrname == "glEsVersion") {
  796. encode_uint32(min_gles3 ? 0x00030000 : 0x00020000, &p_manifest.write[iofs + 16]);
  797. }
  798. // FIXME: `attr_value != 0xFFFFFFFF` below added as a stopgap measure for GH-32553,
  799. // but the issue should be debugged further and properly addressed.
  800. if (tname == "meta-data" && attrname == "name" && value == "xr_mode_metadata_name") {
  801. // Update the meta-data 'android:name' attribute based on the selected XR mode.
  802. if (xr_mode_index == 1 /* XRMode.OVR */) {
  803. string_table.write[attr_value] = "com.samsung.android.vr.application.mode";
  804. }
  805. }
  806. if (tname == "meta-data" && attrname == "value" && value == "xr_mode_metadata_value") {
  807. // Update the meta-data 'android:value' attribute based on the selected XR mode.
  808. if (xr_mode_index == 1 /* XRMode.OVR */) {
  809. string_table.write[attr_value] = "vr_only";
  810. }
  811. }
  812. if (tname == "meta-data" && attrname == "value" && value == "oculus_focus_aware_value") {
  813. // Update the focus awareness meta-data value
  814. string_table.write[attr_value] = xr_mode_index == /* XRMode.OVR */ 1 && focus_awareness ? "true" : "false";
  815. }
  816. if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.empty()) {
  817. // Update the meta-data 'android:value' attribute with the list of enabled plugins.
  818. string_table.write[attr_value] = plugins_names;
  819. }
  820. iofs += 20;
  821. }
  822. } break;
  823. case CHUNK_XML_END_TAG: {
  824. int iofs = ofs + 8;
  825. uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
  826. String tname = string_table[name];
  827. if (tname == "uses-feature") {
  828. Vector<String> feature_names;
  829. Vector<bool> feature_required_list;
  830. Vector<int> feature_versions;
  831. if (xr_mode_index == 1 /* XRMode.OVR */) {
  832. // Check for degrees of freedom
  833. int dof_index = p_preset->get("xr_features/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof
  834. if (dof_index > 0) {
  835. feature_names.push_back("android.hardware.vr.headtracking");
  836. feature_required_list.push_back(dof_index == 2);
  837. feature_versions.push_back(1);
  838. }
  839. // Check for hand tracking
  840. int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
  841. if (hand_tracking_index > 0) {
  842. feature_names.push_back("oculus.software.handtracking");
  843. feature_required_list.push_back(hand_tracking_index == 2);
  844. feature_versions.push_back(-1); // no version attribute should be added.
  845. if (perms.find("com.oculus.permission.HAND_TRACKING") == -1) {
  846. perms.push_back("com.oculus.permission.HAND_TRACKING");
  847. }
  848. }
  849. }
  850. if (feature_names.size() > 0) {
  851. ofs += 24; // skip over end tag
  852. // save manifest ending so we can restore it
  853. Vector<uint8_t> manifest_end;
  854. uint32_t manifest_cur_size = p_manifest.size();
  855. manifest_end.resize(p_manifest.size() - ofs);
  856. memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
  857. int32_t attr_name_string = string_table.find("name");
  858. ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute.");
  859. int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android");
  860. if (ns_android_string == -1) {
  861. string_table.push_back("http://schemas.android.com/apk/res/android");
  862. ns_android_string = string_table.size() - 1;
  863. }
  864. int32_t attr_uses_feature_string = string_table.find("uses-feature");
  865. if (attr_uses_feature_string == -1) {
  866. string_table.push_back("uses-feature");
  867. attr_uses_feature_string = string_table.size() - 1;
  868. }
  869. int32_t attr_required_string = string_table.find("required");
  870. if (attr_required_string == -1) {
  871. string_table.push_back("required");
  872. attr_required_string = string_table.size() - 1;
  873. }
  874. for (int i = 0; i < feature_names.size(); i++) {
  875. String feature_name = feature_names[i];
  876. bool feature_required = feature_required_list[i];
  877. int feature_version = feature_versions[i];
  878. bool has_version_attribute = feature_version != -1;
  879. print_line("Adding feature " + feature_name);
  880. int32_t feature_string = string_table.find(feature_name);
  881. if (feature_string == -1) {
  882. string_table.push_back(feature_name);
  883. feature_string = string_table.size() - 1;
  884. }
  885. String required_value_string = feature_required ? "true" : "false";
  886. int32_t required_value = string_table.find(required_value_string);
  887. if (required_value == -1) {
  888. string_table.push_back(required_value_string);
  889. required_value = string_table.size() - 1;
  890. }
  891. int32_t attr_version_string = -1;
  892. int32_t version_value = -1;
  893. int tag_size;
  894. int attr_count;
  895. if (has_version_attribute) {
  896. attr_version_string = string_table.find("version");
  897. if (attr_version_string == -1) {
  898. string_table.push_back("version");
  899. attr_version_string = string_table.size() - 1;
  900. }
  901. version_value = string_table.find(itos(feature_version));
  902. if (version_value == -1) {
  903. string_table.push_back(itos(feature_version));
  904. version_value = string_table.size() - 1;
  905. }
  906. tag_size = 96; // node and three attrs + end node
  907. attr_count = 3;
  908. } else {
  909. tag_size = 76; // node and two attrs + end node
  910. attr_count = 2;
  911. }
  912. manifest_cur_size += tag_size + 24;
  913. p_manifest.resize(manifest_cur_size);
  914. // start tag
  915. encode_uint16(0x102, &p_manifest.write[ofs]); // type
  916. encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
  917. encode_uint32(tag_size, &p_manifest.write[ofs + 4]); // size
  918. encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
  919. encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
  920. encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
  921. encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name
  922. encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start
  923. encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size
  924. encode_uint16(attr_count, &p_manifest.write[ofs + 28]); // num_attrs
  925. encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index
  926. encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index
  927. encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index
  928. // android:name attribute
  929. encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns
  930. encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name'
  931. encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value
  932. encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size
  933. p_manifest.write[ofs + 50] = 0; // typedvalue_always0
  934. p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string)
  935. encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference
  936. // android:required attribute
  937. encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns
  938. encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name'
  939. encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value
  940. encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size
  941. p_manifest.write[ofs + 70] = 0; // typedvalue_always0
  942. p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string)
  943. encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference
  944. ofs += 76;
  945. if (has_version_attribute) {
  946. // android:version attribute
  947. encode_uint32(ns_android_string, &p_manifest.write[ofs]); // ns
  948. encode_uint32(attr_version_string, &p_manifest.write[ofs + 4]); // 'name'
  949. encode_uint32(version_value, &p_manifest.write[ofs + 8]); // raw_value
  950. encode_uint16(8, &p_manifest.write[ofs + 12]); // typedvalue_size
  951. p_manifest.write[ofs + 14] = 0; // typedvalue_always0
  952. p_manifest.write[ofs + 15] = 0x03; // typedvalue_type (string)
  953. encode_uint32(version_value, &p_manifest.write[ofs + 16]); // typedvalue reference
  954. ofs += 20;
  955. }
  956. // end tag
  957. encode_uint16(0x103, &p_manifest.write[ofs]); // type
  958. encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
  959. encode_uint32(24, &p_manifest.write[ofs + 4]); // size
  960. encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
  961. encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
  962. encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
  963. encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name
  964. ofs += 24;
  965. }
  966. memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size());
  967. ofs -= 24; // go back over back end
  968. }
  969. }
  970. if (tname == "manifest") {
  971. // save manifest ending so we can restore it
  972. Vector<uint8_t> manifest_end;
  973. uint32_t manifest_cur_size = p_manifest.size();
  974. manifest_end.resize(p_manifest.size() - ofs);
  975. memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
  976. int32_t attr_name_string = string_table.find("name");
  977. ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute.");
  978. int32_t ns_android_string = string_table.find("android");
  979. ERR_FAIL_COND_MSG(ns_android_string == -1, "Template does not have 'android' namespace.");
  980. int32_t attr_uses_permission_string = string_table.find("uses-permission");
  981. if (attr_uses_permission_string == -1) {
  982. string_table.push_back("uses-permission");
  983. attr_uses_permission_string = string_table.size() - 1;
  984. }
  985. for (int i = 0; i < perms.size(); ++i) {
  986. print_line("Adding permission " + perms[i]);
  987. manifest_cur_size += 56 + 24; // node + end node
  988. p_manifest.resize(manifest_cur_size);
  989. // Add permission to the string pool
  990. int32_t perm_string = string_table.find(perms[i]);
  991. if (perm_string == -1) {
  992. string_table.push_back(perms[i]);
  993. perm_string = string_table.size() - 1;
  994. }
  995. // start tag
  996. encode_uint16(0x102, &p_manifest.write[ofs]); // type
  997. encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
  998. encode_uint32(56, &p_manifest.write[ofs + 4]); // size
  999. encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
  1000. encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
  1001. encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
  1002. encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
  1003. encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start
  1004. encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size
  1005. encode_uint16(1, &p_manifest.write[ofs + 28]); // num_attrs
  1006. encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index
  1007. encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index
  1008. encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index
  1009. // attribute
  1010. encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns
  1011. encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name'
  1012. encode_uint32(perm_string, &p_manifest.write[ofs + 44]); // raw_value
  1013. encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size
  1014. p_manifest.write[ofs + 50] = 0; // typedvalue_always0
  1015. p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string)
  1016. encode_uint32(perm_string, &p_manifest.write[ofs + 52]); // typedvalue reference
  1017. ofs += 56;
  1018. // end tag
  1019. encode_uint16(0x103, &p_manifest.write[ofs]); // type
  1020. encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
  1021. encode_uint32(24, &p_manifest.write[ofs + 4]); // size
  1022. encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
  1023. encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
  1024. encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
  1025. encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
  1026. ofs += 24;
  1027. }
  1028. // copy footer back in
  1029. memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size());
  1030. }
  1031. } break;
  1032. }
  1033. ofs += size;
  1034. }
  1035. //create new andriodmanifest binary
  1036. Vector<uint8_t> ret;
  1037. ret.resize(string_table_begins + string_table.size() * 4);
  1038. for (uint32_t i = 0; i < string_table_begins; i++) {
  1039. ret.write[i] = p_manifest[i];
  1040. }
  1041. ofs = 0;
  1042. for (int i = 0; i < string_table.size(); i++) {
  1043. encode_uint32(ofs, &ret.write[string_table_begins + i * 4]);
  1044. ofs += string_table[i].length() * 2 + 2 + 2;
  1045. }
  1046. ret.resize(ret.size() + ofs);
  1047. string_data_offset = ret.size() - ofs;
  1048. uint8_t *chars = &ret.write[string_data_offset];
  1049. for (int i = 0; i < string_table.size(); i++) {
  1050. String s = string_table[i];
  1051. encode_uint16(s.length(), chars);
  1052. chars += 2;
  1053. for (int j = 0; j < s.length(); j++) {
  1054. encode_uint16(s[j], chars);
  1055. chars += 2;
  1056. }
  1057. encode_uint16(0, chars);
  1058. chars += 2;
  1059. }
  1060. for (int i = 0; i < stable_extra.size(); i++) {
  1061. ret.push_back(stable_extra[i]);
  1062. }
  1063. //pad
  1064. while (ret.size() % 4)
  1065. ret.push_back(0);
  1066. uint32_t new_stable_end = ret.size();
  1067. uint32_t extra = (p_manifest.size() - string_table_ends);
  1068. ret.resize(new_stable_end + extra);
  1069. for (uint32_t i = 0; i < extra; i++)
  1070. ret.write[new_stable_end + i] = p_manifest[string_table_ends + i];
  1071. while (ret.size() % 4)
  1072. ret.push_back(0);
  1073. encode_uint32(ret.size(), &ret.write[4]); //update new file size
  1074. encode_uint32(new_stable_end - 8, &ret.write[12]); //update new string table size
  1075. encode_uint32(string_table.size(), &ret.write[16]); //update new number of strings
  1076. encode_uint32(string_data_offset - 8, &ret.write[28]); //update new string data offset
  1077. p_manifest = ret;
  1078. }
  1079. static String _parse_string(const uint8_t *p_bytes, bool p_utf8) {
  1080. uint32_t offset = 0;
  1081. uint32_t len = 0;
  1082. if (p_utf8) {
  1083. uint8_t byte = p_bytes[offset];
  1084. if (byte & 0x80)
  1085. offset += 2;
  1086. else
  1087. offset += 1;
  1088. byte = p_bytes[offset];
  1089. offset++;
  1090. if (byte & 0x80) {
  1091. len = byte & 0x7F;
  1092. len = (len << 8) + p_bytes[offset];
  1093. offset++;
  1094. } else {
  1095. len = byte;
  1096. }
  1097. } else {
  1098. len = decode_uint16(&p_bytes[offset]);
  1099. offset += 2;
  1100. if (len & 0x8000) {
  1101. len &= 0x7FFF;
  1102. len = (len << 16) + decode_uint16(&p_bytes[offset]);
  1103. offset += 2;
  1104. }
  1105. }
  1106. if (p_utf8) {
  1107. Vector<uint8_t> str8;
  1108. str8.resize(len + 1);
  1109. for (uint32_t i = 0; i < len; i++) {
  1110. str8.write[i] = p_bytes[offset + i];
  1111. }
  1112. str8.write[len] = 0;
  1113. String str;
  1114. str.parse_utf8((const char *)str8.ptr());
  1115. return str;
  1116. } else {
  1117. String str;
  1118. for (uint32_t i = 0; i < len; i++) {
  1119. CharType c = decode_uint16(&p_bytes[offset + i * 2]);
  1120. if (c == 0)
  1121. break;
  1122. str += String::chr(c);
  1123. }
  1124. return str;
  1125. }
  1126. }
  1127. void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest) {
  1128. const int UTF8_FLAG = 0x00000100;
  1129. uint32_t string_block_len = decode_uint32(&p_manifest[16]);
  1130. uint32_t string_count = decode_uint32(&p_manifest[20]);
  1131. uint32_t string_flags = decode_uint32(&p_manifest[28]);
  1132. const uint32_t string_table_begins = 40;
  1133. Vector<String> string_table;
  1134. String package_name = p_preset->get("package/name");
  1135. for (uint32_t i = 0; i < string_count; i++) {
  1136. uint32_t offset = decode_uint32(&p_manifest[string_table_begins + i * 4]);
  1137. offset += string_table_begins + string_count * 4;
  1138. String str = _parse_string(&p_manifest[offset], string_flags & UTF8_FLAG);
  1139. if (str.begins_with("godot-project-name")) {
  1140. if (str == "godot-project-name") {
  1141. //project name
  1142. str = get_project_name(package_name);
  1143. } else {
  1144. String lang = str.substr(str.find_last("-") + 1, str.length()).replace("-", "_");
  1145. String prop = "application/config/name_" + lang;
  1146. if (ProjectSettings::get_singleton()->has_setting(prop)) {
  1147. str = ProjectSettings::get_singleton()->get(prop);
  1148. } else {
  1149. str = get_project_name(package_name);
  1150. }
  1151. }
  1152. }
  1153. string_table.push_back(str);
  1154. }
  1155. //write a new string table, but use 16 bits
  1156. Vector<uint8_t> ret;
  1157. ret.resize(string_table_begins + string_table.size() * 4);
  1158. for (uint32_t i = 0; i < string_table_begins; i++) {
  1159. ret.write[i] = p_manifest[i];
  1160. }
  1161. int ofs = 0;
  1162. for (int i = 0; i < string_table.size(); i++) {
  1163. encode_uint32(ofs, &ret.write[string_table_begins + i * 4]);
  1164. ofs += string_table[i].length() * 2 + 2 + 2;
  1165. }
  1166. ret.resize(ret.size() + ofs);
  1167. uint8_t *chars = &ret.write[ret.size() - ofs];
  1168. for (int i = 0; i < string_table.size(); i++) {
  1169. String s = string_table[i];
  1170. encode_uint16(s.length(), chars);
  1171. chars += 2;
  1172. for (int j = 0; j < s.length(); j++) {
  1173. encode_uint16(s[j], chars);
  1174. chars += 2;
  1175. }
  1176. encode_uint16(0, chars);
  1177. chars += 2;
  1178. }
  1179. //pad
  1180. while (ret.size() % 4)
  1181. ret.push_back(0);
  1182. //change flags to not use utf8
  1183. encode_uint32(string_flags & ~0x100, &ret.write[28]);
  1184. //change length
  1185. encode_uint32(ret.size() - 12, &ret.write[16]);
  1186. //append the rest...
  1187. int rest_from = 12 + string_block_len;
  1188. int rest_to = ret.size();
  1189. int rest_len = (p_manifest.size() - rest_from);
  1190. ret.resize(ret.size() + (p_manifest.size() - rest_from));
  1191. for (int i = 0; i < rest_len; i++) {
  1192. ret.write[rest_to + i] = p_manifest[rest_from + i];
  1193. }
  1194. //finally update the size
  1195. encode_uint32(ret.size(), &ret.write[4]);
  1196. p_manifest = ret;
  1197. //printf("end\n");
  1198. }
  1199. void _process_launcher_icons(const String &p_processing_file_name, const Ref<Image> &p_source_image, const LauncherIcon p_icon, Vector<uint8_t> &p_data) {
  1200. if (p_processing_file_name == p_icon.export_path) {
  1201. Ref<Image> working_image = p_source_image;
  1202. if (p_source_image->get_width() != p_icon.dimensions || p_source_image->get_height() != p_icon.dimensions) {
  1203. working_image = p_source_image->duplicate();
  1204. working_image->resize(p_icon.dimensions, p_icon.dimensions, Image::Interpolation::INTERPOLATE_LANCZOS);
  1205. }
  1206. PoolVector<uint8_t> png_buffer;
  1207. Error err = PNGDriverCommon::image_to_png(working_image, png_buffer);
  1208. if (err == OK) {
  1209. p_data.resize(png_buffer.size());
  1210. memcpy(p_data.ptrw(), png_buffer.read().ptr(), p_data.size());
  1211. } else {
  1212. String err_str = String("Failed to convert resized icon (") + p_processing_file_name + ") to png.";
  1213. WARN_PRINT(err_str.utf8().get_data());
  1214. }
  1215. }
  1216. }
  1217. static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset) {
  1218. Vector<String> abis = get_abis();
  1219. Vector<String> enabled_abis;
  1220. for (int i = 0; i < abis.size(); ++i) {
  1221. bool is_enabled = p_preset->get("architectures/" + abis[i]);
  1222. if (is_enabled) {
  1223. enabled_abis.push_back(abis[i]);
  1224. }
  1225. }
  1226. return enabled_abis;
  1227. }
  1228. public:
  1229. typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total);
  1230. public:
  1231. virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
  1232. String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name");
  1233. if (driver == "GLES2") {
  1234. r_features->push_back("etc");
  1235. } else if (driver == "GLES3") {
  1236. r_features->push_back("etc2");
  1237. if (ProjectSettings::get_singleton()->get("rendering/quality/driver/fallback_to_gles2")) {
  1238. r_features->push_back("etc");
  1239. }
  1240. }
  1241. Vector<String> abis = get_enabled_abis(p_preset);
  1242. for (int i = 0; i < abis.size(); ++i) {
  1243. r_features->push_back(abis[i]);
  1244. }
  1245. }
  1246. virtual void get_export_options(List<ExportOption> *r_options) {
  1247. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true));
  1248. r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0));
  1249. r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0));
  1250. r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0));
  1251. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "xr_features/focus_awareness"), false));
  1252. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false));
  1253. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
  1254. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
  1255. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false));
  1256. Vector<PluginConfig> plugins_configs = get_plugins();
  1257. for (int i = 0; i < plugins_configs.size(); i++) {
  1258. print_verbose("Found Android plugin " + plugins_configs[i].name);
  1259. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false));
  1260. }
  1261. plugins_changed = false;
  1262. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
  1263. r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
  1264. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
  1265. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname"));
  1266. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), ""));
  1267. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true));
  1268. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true));
  1269. r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0));
  1270. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
  1271. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true));
  1272. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true));
  1273. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true));
  1274. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/opengl_debug"), false));
  1275. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
  1276. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
  1277. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
  1278. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"), ""));
  1279. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));
  1280. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), ""));
  1281. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"), ""));
  1282. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
  1283. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
  1284. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false));
  1285. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), ""));
  1286. r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), ""));
  1287. Vector<String> abis = get_abis();
  1288. for (int i = 0; i < abis.size(); ++i) {
  1289. String abi = abis[i];
  1290. bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a");
  1291. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
  1292. }
  1293. r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "permissions/custom_permissions"), PoolStringArray()));
  1294. const char **perms = android_perms;
  1295. while (*perms) {
  1296. r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false));
  1297. perms++;
  1298. }
  1299. }
  1300. virtual String get_name() const {
  1301. return "Android";
  1302. }
  1303. virtual String get_os_name() const {
  1304. return "Android";
  1305. }
  1306. virtual Ref<Texture> get_logo() const {
  1307. return logo;
  1308. }
  1309. virtual bool should_update_export_options() {
  1310. bool export_options_changed = plugins_changed;
  1311. if (export_options_changed) {
  1312. // don't clear unless we're reporting true, to avoid race
  1313. plugins_changed = false;
  1314. }
  1315. return export_options_changed;
  1316. }
  1317. virtual bool poll_export() {
  1318. bool dc = devices_changed;
  1319. if (dc) {
  1320. // don't clear unless we're reporting true, to avoid race
  1321. devices_changed = false;
  1322. }
  1323. return dc;
  1324. }
  1325. virtual int get_options_count() const {
  1326. device_lock->lock();
  1327. int dc = devices.size();
  1328. device_lock->unlock();
  1329. return dc;
  1330. }
  1331. virtual String get_options_tooltip() const {
  1332. return TTR("Select device from the list");
  1333. }
  1334. virtual String get_option_label(int p_index) const {
  1335. ERR_FAIL_INDEX_V(p_index, devices.size(), "");
  1336. device_lock->lock();
  1337. String s = devices[p_index].name;
  1338. device_lock->unlock();
  1339. return s;
  1340. }
  1341. virtual String get_option_tooltip(int p_index) const {
  1342. ERR_FAIL_INDEX_V(p_index, devices.size(), "");
  1343. device_lock->lock();
  1344. String s = devices[p_index].description;
  1345. if (devices.size() == 1) {
  1346. // Tooltip will be:
  1347. // Name
  1348. // Description
  1349. s = devices[p_index].name + "\n\n" + s;
  1350. }
  1351. device_lock->unlock();
  1352. return s;
  1353. }
  1354. virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
  1355. ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);
  1356. String can_export_error;
  1357. bool can_export_missing_templates;
  1358. if (!can_export(p_preset, can_export_error, can_export_missing_templates)) {
  1359. EditorNode::add_io_error(can_export_error);
  1360. return ERR_UNCONFIGURED;
  1361. }
  1362. device_lock->lock();
  1363. EditorProgress ep("run", "Running on " + devices[p_device].name, 3);
  1364. String adb = EditorSettings::get_singleton()->get("export/android/adb");
  1365. // Export_temp APK.
  1366. if (ep.step("Exporting APK...", 0)) {
  1367. device_lock->unlock();
  1368. return ERR_SKIP;
  1369. }
  1370. const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
  1371. const bool use_reverse = devices[p_device].api_level >= 21;
  1372. if (use_reverse)
  1373. p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
  1374. String tmp_export_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport.apk");
  1375. #define CLEANUP_AND_RETURN(m_err) \
  1376. { \
  1377. DirAccess::remove_file_or_error(tmp_export_path); \
  1378. device_lock->unlock(); \
  1379. return m_err; \
  1380. }
  1381. // Export to temporary APK before sending to device.
  1382. Error err = export_project(p_preset, true, tmp_export_path, p_debug_flags);
  1383. if (err != OK) {
  1384. CLEANUP_AND_RETURN(err);
  1385. }
  1386. List<String> args;
  1387. int rv;
  1388. bool remove_prev = p_preset->get("one_click_deploy/clear_previous_install");
  1389. String version_name = p_preset->get("version/name");
  1390. String package_name = p_preset->get("package/unique_name");
  1391. if (remove_prev) {
  1392. if (ep.step("Uninstalling...", 1)) {
  1393. CLEANUP_AND_RETURN(ERR_SKIP);
  1394. }
  1395. print_line("Uninstalling previous version: " + devices[p_device].name);
  1396. args.push_back("-s");
  1397. args.push_back(devices[p_device].id);
  1398. args.push_back("uninstall");
  1399. args.push_back(get_package_name(package_name));
  1400. err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
  1401. }
  1402. print_line("Installing to device (please wait...): " + devices[p_device].name);
  1403. if (ep.step("Installing to device, please wait...", 2)) {
  1404. CLEANUP_AND_RETURN(ERR_SKIP);
  1405. }
  1406. args.clear();
  1407. args.push_back("-s");
  1408. args.push_back(devices[p_device].id);
  1409. args.push_back("install");
  1410. args.push_back("-r");
  1411. args.push_back(tmp_export_path);
  1412. err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
  1413. if (err || rv != 0) {
  1414. EditorNode::add_io_error("Could not install to device.");
  1415. CLEANUP_AND_RETURN(ERR_CANT_CREATE);
  1416. }
  1417. if (use_remote) {
  1418. if (use_reverse) {
  1419. static const char *const msg = "--- Device API >= 21; debugging over USB ---";
  1420. EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR);
  1421. print_line(String(msg).to_upper());
  1422. args.clear();
  1423. args.push_back("-s");
  1424. args.push_back(devices[p_device].id);
  1425. args.push_back("reverse");
  1426. args.push_back("--remove-all");
  1427. OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
  1428. if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
  1429. int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
  1430. args.clear();
  1431. args.push_back("-s");
  1432. args.push_back(devices[p_device].id);
  1433. args.push_back("reverse");
  1434. args.push_back("tcp:" + itos(dbg_port));
  1435. args.push_back("tcp:" + itos(dbg_port));
  1436. OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
  1437. print_line("Reverse result: " + itos(rv));
  1438. }
  1439. if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) {
  1440. int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port");
  1441. args.clear();
  1442. args.push_back("-s");
  1443. args.push_back(devices[p_device].id);
  1444. args.push_back("reverse");
  1445. args.push_back("tcp:" + itos(fs_port));
  1446. args.push_back("tcp:" + itos(fs_port));
  1447. err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
  1448. print_line("Reverse result2: " + itos(rv));
  1449. }
  1450. } else {
  1451. static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---";
  1452. EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR);
  1453. print_line(String(msg).to_upper());
  1454. }
  1455. }
  1456. if (ep.step("Running on device...", 3)) {
  1457. CLEANUP_AND_RETURN(ERR_SKIP);
  1458. }
  1459. args.clear();
  1460. args.push_back("-s");
  1461. args.push_back(devices[p_device].id);
  1462. args.push_back("shell");
  1463. args.push_back("am");
  1464. args.push_back("start");
  1465. if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17
  1466. args.push_back("--user");
  1467. args.push_back("0");
  1468. }
  1469. args.push_back("-a");
  1470. args.push_back("android.intent.action.MAIN");
  1471. args.push_back("-n");
  1472. args.push_back(get_package_name(package_name) + "/com.godot.game.GodotApp");
  1473. err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
  1474. if (err || rv != 0) {
  1475. EditorNode::add_io_error("Could not execute on device.");
  1476. CLEANUP_AND_RETURN(ERR_CANT_CREATE);
  1477. }
  1478. CLEANUP_AND_RETURN(OK);
  1479. #undef CLEANUP_AND_RETURN
  1480. }
  1481. virtual Ref<Texture> get_run_icon() const {
  1482. return run_icon;
  1483. }
  1484. virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
  1485. String err;
  1486. bool valid = false;
  1487. // Look for export templates (first official, and if defined custom templates).
  1488. if (!bool(p_preset->get("custom_template/use_custom_build"))) {
  1489. String template_err;
  1490. bool dvalid = false;
  1491. bool rvalid = false;
  1492. if (p_preset->get("custom_template/debug") != "") {
  1493. dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
  1494. if (!dvalid) {
  1495. template_err += TTR("Custom debug template not found.") + "\n";
  1496. }
  1497. } else {
  1498. dvalid = exists_export_template("android_debug.apk", &template_err);
  1499. }
  1500. if (p_preset->get("custom_template/release") != "") {
  1501. rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
  1502. if (!rvalid) {
  1503. template_err += TTR("Custom release template not found.") + "\n";
  1504. }
  1505. } else {
  1506. rvalid = exists_export_template("android_release.apk", &template_err);
  1507. }
  1508. valid = dvalid || rvalid;
  1509. if (!valid) {
  1510. err += template_err;
  1511. }
  1512. } else {
  1513. valid = exists_export_template("android_source.zip", &err);
  1514. }
  1515. r_missing_templates = !valid;
  1516. // Validate the rest of the configuration.
  1517. String adb = EditorSettings::get_singleton()->get("export/android/adb");
  1518. if (!FileAccess::exists(adb)) {
  1519. valid = false;
  1520. err += TTR("ADB executable not configured in the Editor Settings.") + "\n";
  1521. }
  1522. String js = EditorSettings::get_singleton()->get("export/android/jarsigner");
  1523. if (!FileAccess::exists(js)) {
  1524. valid = false;
  1525. err += TTR("OpenJDK jarsigner not configured in the Editor Settings.") + "\n";
  1526. }
  1527. String dk = p_preset->get("keystore/debug");
  1528. if (!FileAccess::exists(dk)) {
  1529. dk = EditorSettings::get_singleton()->get("export/android/debug_keystore");
  1530. if (!FileAccess::exists(dk)) {
  1531. valid = false;
  1532. err += TTR("Debug keystore not configured in the Editor Settings nor in the preset.") + "\n";
  1533. }
  1534. }
  1535. if (bool(p_preset->get("custom_template/use_custom_build"))) {
  1536. String sdk_path = EditorSettings::get_singleton()->get("export/android/custom_build_sdk_path");
  1537. if (sdk_path == "") {
  1538. err += TTR("Custom build requires a valid Android SDK path in Editor Settings.") + "\n";
  1539. valid = false;
  1540. } else {
  1541. Error errn;
  1542. DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn);
  1543. if (errn != OK) {
  1544. err += TTR("Invalid Android SDK path for custom build in Editor Settings.") + "\n";
  1545. valid = false;
  1546. }
  1547. }
  1548. if (!FileAccess::exists("res://android/build/build.gradle")) {
  1549. err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n";
  1550. valid = false;
  1551. }
  1552. }
  1553. bool apk_expansion = p_preset->get("apk_expansion/enable");
  1554. if (apk_expansion) {
  1555. String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
  1556. if (apk_expansion_pkey == "") {
  1557. valid = false;
  1558. err += TTR("Invalid public key for APK expansion.") + "\n";
  1559. }
  1560. }
  1561. String pn = p_preset->get("package/unique_name");
  1562. String pn_err;
  1563. if (!is_package_name_valid(get_package_name(pn), &pn_err)) {
  1564. valid = false;
  1565. err += TTR("Invalid package name:") + " " + pn_err + "\n";
  1566. }
  1567. String etc_error = test_etc2();
  1568. if (etc_error != String()) {
  1569. valid = false;
  1570. err += etc_error;
  1571. }
  1572. // The GodotPaymentV3 module was converted to the GodotPayment plugin in Godot 3.2.2,
  1573. // this check helps users to notice the change to ensure that they change their settings.
  1574. String modules = ProjectSettings::get_singleton()->get("android/modules");
  1575. if (modules.find("org/godotengine/godot/GodotPaymentV3") != -1) {
  1576. bool godot_payment_enabled = p_preset->get("plugins/" + GODOT_PAYMENT.name);
  1577. if (!godot_payment_enabled) {
  1578. valid = false;
  1579. err += TTR("Invalid \"GodotPaymentV3\" module included in the \"android/modules\" project setting (changed in Godot 3.2.2).\n"
  1580. "Replace it by the \"GodotPayment\" plugin, which should be enabled in the \"Plugins\" preset section.\n"
  1581. "Note that the singleton was also renamed from \"GodotPayments\" to \"GodotPayment\".");
  1582. err += "\n";
  1583. }
  1584. }
  1585. // Ensure that `Use Custom Build` is enabled if a plugin is selected.
  1586. String enabled_plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
  1587. bool custom_build_enabled = p_preset->get("custom_template/use_custom_build");
  1588. if (!enabled_plugins_names.empty() && !custom_build_enabled) {
  1589. valid = false;
  1590. err += TTR("\"Use Custom Build\" must be enabled to use the plugins.");
  1591. err += "\n";
  1592. }
  1593. // Validate the Xr features are properly populated
  1594. int xr_mode_index = p_preset->get("xr_features/xr_mode");
  1595. int degrees_of_freedom = p_preset->get("xr_features/degrees_of_freedom");
  1596. int hand_tracking = p_preset->get("xr_features/hand_tracking");
  1597. bool focus_awareness = p_preset->get("xr_features/focus_awareness");
  1598. if (xr_mode_index != /* XRMode.OVR*/ 1) {
  1599. if (degrees_of_freedom > 0) {
  1600. valid = false;
  1601. err += TTR("\"Degrees Of Freedom\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
  1602. err += "\n";
  1603. }
  1604. if (hand_tracking > 0) {
  1605. valid = false;
  1606. err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
  1607. err += "\n";
  1608. }
  1609. if (focus_awareness) {
  1610. valid = false;
  1611. err += TTR("\"Focus Awareness\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
  1612. err += "\n";
  1613. }
  1614. }
  1615. r_error = err;
  1616. return valid;
  1617. }
  1618. virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
  1619. List<String> list;
  1620. list.push_back("apk");
  1621. return list;
  1622. }
  1623. void _update_custom_build_project() {
  1624. DirAccessRef da = DirAccess::open("res://android");
  1625. ERR_FAIL_COND_MSG(!da, "Cannot open directory 'res://android'.");
  1626. Map<String, List<String> > directory_paths;
  1627. Map<String, List<String> > manifest_sections;
  1628. Map<String, List<String> > gradle_sections;
  1629. da->list_dir_begin();
  1630. String d = da->get_next();
  1631. while (d != String()) {
  1632. if (!d.begins_with(".") && d != "build" && da->current_is_dir()) { //a dir and not the build dir
  1633. //add directories found
  1634. DirAccessRef ds = DirAccess::open(String("res://android").plus_file(d));
  1635. if (ds) {
  1636. ds->list_dir_begin();
  1637. String sd = ds->get_next();
  1638. while (sd != String()) {
  1639. if (!sd.begins_with(".") && ds->current_is_dir()) {
  1640. String key = sd.to_upper();
  1641. if (!directory_paths.has(key)) {
  1642. directory_paths[key] = List<String>();
  1643. }
  1644. String path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android").plus_file(d).plus_file(sd);
  1645. directory_paths[key].push_back(path);
  1646. print_line("Add: " + sd + ":" + path);
  1647. }
  1648. sd = ds->get_next();
  1649. }
  1650. ds->list_dir_end();
  1651. }
  1652. //parse manifest
  1653. {
  1654. FileAccessRef f = FileAccess::open(String("res://android").plus_file(d).plus_file("AndroidManifest.conf"), FileAccess::READ);
  1655. if (f) {
  1656. String section;
  1657. while (!f->eof_reached()) {
  1658. String l = f->get_line();
  1659. String k = l.strip_edges();
  1660. if (k.begins_with("[")) {
  1661. section = k.substr(1, k.length() - 2).strip_edges().to_upper();
  1662. print_line("Section: " + section);
  1663. } else if (k != String()) {
  1664. if (!manifest_sections.has(section)) {
  1665. manifest_sections[section] = List<String>();
  1666. }
  1667. manifest_sections[section].push_back(l);
  1668. }
  1669. }
  1670. f->close();
  1671. }
  1672. }
  1673. //parse gradle
  1674. {
  1675. FileAccessRef f = FileAccess::open(String("res://android").plus_file(d).plus_file("gradle.conf"), FileAccess::READ);
  1676. if (f) {
  1677. String section;
  1678. while (!f->eof_reached()) {
  1679. String l = f->get_line().strip_edges();
  1680. String k = l.strip_edges();
  1681. if (k.begins_with("[")) {
  1682. section = k.substr(1, k.length() - 2).strip_edges().to_upper();
  1683. print_line("Section: " + section);
  1684. } else if (k != String()) {
  1685. if (!gradle_sections.has(section)) {
  1686. gradle_sections[section] = List<String>();
  1687. }
  1688. gradle_sections[section].push_back(l);
  1689. }
  1690. }
  1691. }
  1692. }
  1693. }
  1694. d = da->get_next();
  1695. }
  1696. da->list_dir_end();
  1697. { //fix gradle build
  1698. String new_file;
  1699. {
  1700. FileAccessRef f = FileAccess::open("res://android/build/build.gradle", FileAccess::READ);
  1701. if (f) {
  1702. while (!f->eof_reached()) {
  1703. String l = f->get_line();
  1704. bool append_line = false;
  1705. if (l.begins_with("//CHUNK_")) {
  1706. String text = l.replace_first("//CHUNK_", "");
  1707. int begin_pos = text.find("_BEGIN");
  1708. if (begin_pos != -1) {
  1709. text = text.substr(0, begin_pos);
  1710. text = text.to_upper(); //just in case
  1711. String end_marker = "//CHUNK_" + text + "_END";
  1712. size_t pos = f->get_position();
  1713. bool found = false;
  1714. while (!f->eof_reached()) {
  1715. l = f->get_line();
  1716. if (l.begins_with(end_marker)) {
  1717. found = true;
  1718. break;
  1719. }
  1720. }
  1721. new_file += "//CHUNK_" + text + "_BEGIN\n";
  1722. if (!found) {
  1723. ERR_PRINTS("No end marker found in build.gradle for chunk: " + text);
  1724. f->seek(pos);
  1725. } else {
  1726. //add chunk lines
  1727. if (gradle_sections.has(text)) {
  1728. for (List<String>::Element *E = gradle_sections[text].front(); E; E = E->next()) {
  1729. new_file += E->get() + "\n";
  1730. }
  1731. }
  1732. if (f->eof_reached()) {
  1733. new_file += end_marker;
  1734. } else {
  1735. new_file += end_marker + "\n";
  1736. }
  1737. }
  1738. } else {
  1739. append_line = true;
  1740. }
  1741. } else if (l.begins_with("//DIR_")) {
  1742. String text = l.replace_first("//DIR_", "");
  1743. int begin_pos = text.find("_BEGIN");
  1744. if (begin_pos != -1) {
  1745. text = text.substr(0, begin_pos);
  1746. text = text.to_upper(); //just in case
  1747. String end_marker = "//DIR_" + text + "_END";
  1748. size_t pos = f->get_position();
  1749. bool found = false;
  1750. while (!f->eof_reached()) {
  1751. l = f->get_line();
  1752. if (l.begins_with(end_marker)) {
  1753. found = true;
  1754. break;
  1755. }
  1756. }
  1757. new_file += "//DIR_" + text + "_BEGIN\n";
  1758. if (!found) {
  1759. ERR_PRINTS("No end marker found in build.gradle for dir: " + text);
  1760. f->seek(pos);
  1761. } else {
  1762. //add chunk lines
  1763. if (directory_paths.has(text)) {
  1764. for (List<String>::Element *E = directory_paths[text].front(); E; E = E->next()) {
  1765. new_file += ",'" + E->get().replace("'", "\'") + "'";
  1766. new_file += "\n";
  1767. }
  1768. }
  1769. if (f->eof_reached()) {
  1770. new_file += end_marker;
  1771. } else {
  1772. new_file += end_marker + "\n";
  1773. }
  1774. }
  1775. } else {
  1776. append_line = true;
  1777. }
  1778. } else {
  1779. append_line = true;
  1780. }
  1781. if (append_line) {
  1782. if (f->eof_reached()) {
  1783. new_file += l;
  1784. } else {
  1785. new_file += l + "\n";
  1786. }
  1787. }
  1788. }
  1789. }
  1790. }
  1791. FileAccessRef f = FileAccess::open("res://android/build/build.gradle", FileAccess::WRITE);
  1792. f->store_string(new_file);
  1793. f->close();
  1794. }
  1795. { //fix manifest
  1796. String new_file;
  1797. {
  1798. FileAccessRef f = FileAccess::open("res://android/build/AndroidManifest.xml", FileAccess::READ);
  1799. if (f) {
  1800. while (!f->eof_reached()) {
  1801. String l = f->get_line();
  1802. bool append_line = false;
  1803. if (l.begins_with("<!--CHUNK_")) {
  1804. String text = l.replace_first("<!--CHUNK_", "");
  1805. int begin_pos = text.find("_BEGIN-->");
  1806. if (begin_pos != -1) {
  1807. text = text.substr(0, begin_pos);
  1808. text = text.to_upper(); //just in case
  1809. String end_marker = "<!--CHUNK_" + text + "_END-->";
  1810. size_t pos = f->get_position();
  1811. bool found = false;
  1812. while (!f->eof_reached()) {
  1813. l = f->get_line();
  1814. if (l.begins_with(end_marker)) {
  1815. found = true;
  1816. break;
  1817. }
  1818. }
  1819. new_file += "<!--CHUNK_" + text + "_BEGIN-->\n";
  1820. if (!found) {
  1821. ERR_PRINTS("No end marker found in AndroidManifest.xml for chunk: " + text);
  1822. f->seek(pos);
  1823. } else {
  1824. //add chunk lines
  1825. if (manifest_sections.has(text)) {
  1826. for (List<String>::Element *E = manifest_sections[text].front(); E; E = E->next()) {
  1827. new_file += E->get() + "\n";
  1828. }
  1829. }
  1830. if (f->eof_reached()) {
  1831. new_file += end_marker;
  1832. } else {
  1833. new_file += end_marker + "\n";
  1834. }
  1835. }
  1836. } else {
  1837. append_line = true;
  1838. }
  1839. } else if (l.strip_edges().begins_with("<application")) {
  1840. String last_tag = "android:icon=\"@mipmap/icon\"";
  1841. int last_tag_pos = l.find(last_tag);
  1842. if (last_tag_pos == -1) {
  1843. ERR_PRINTS("Not adding application attributes as the expected tag was not found in '<application': " + last_tag);
  1844. append_line = true;
  1845. } else {
  1846. String base = l.substr(0, last_tag_pos + last_tag.length());
  1847. if (manifest_sections.has("application_attribs")) {
  1848. for (List<String>::Element *E = manifest_sections["application_attribs"].front(); E; E = E->next()) {
  1849. String to_add = E->get().strip_edges();
  1850. base += " " + to_add + " ";
  1851. }
  1852. }
  1853. base += ">\n";
  1854. new_file += base;
  1855. }
  1856. } else {
  1857. append_line = true;
  1858. }
  1859. if (append_line) {
  1860. new_file += l;
  1861. if (!f->eof_reached()) {
  1862. new_file += "\n";
  1863. }
  1864. }
  1865. }
  1866. }
  1867. }
  1868. FileAccessRef f = FileAccess::open("res://android/build/AndroidManifest.xml", FileAccess::WRITE);
  1869. f->store_string(new_file);
  1870. f->close();
  1871. }
  1872. }
  1873. inline bool is_clean_build_required(Vector<PluginConfig> enabled_plugins) {
  1874. String plugin_names = get_plugins_names(enabled_plugins);
  1875. bool first_build = last_custom_build_time == 0;
  1876. bool have_plugins_changed = false;
  1877. if (!first_build) {
  1878. have_plugins_changed = plugin_names != last_plugin_names;
  1879. if (!have_plugins_changed) {
  1880. for (int i = 0; i < enabled_plugins.size(); i++) {
  1881. if (enabled_plugins.get(i).last_updated > last_custom_build_time) {
  1882. have_plugins_changed = true;
  1883. break;
  1884. }
  1885. }
  1886. }
  1887. }
  1888. last_custom_build_time = OS::get_singleton()->get_unix_time();
  1889. last_plugin_names = plugin_names;
  1890. return have_plugins_changed || first_build;
  1891. }
  1892. virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
  1893. ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
  1894. String src_apk;
  1895. EditorProgress ep("export", "Exporting for Android", 105, true);
  1896. if (bool(p_preset->get("custom_template/use_custom_build"))) { //custom build
  1897. //re-generate build.gradle and AndroidManifest.xml
  1898. { //test that installed build version is alright
  1899. FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ);
  1900. if (!f) {
  1901. EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu."));
  1902. return ERR_UNCONFIGURED;
  1903. }
  1904. String version = f->get_line().strip_edges();
  1905. if (version != VERSION_FULL_CONFIG) {
  1906. EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG));
  1907. return ERR_UNCONFIGURED;
  1908. }
  1909. }
  1910. //build project if custom build is enabled
  1911. String sdk_path = EDITOR_GET("export/android/custom_build_sdk_path");
  1912. ERR_FAIL_COND_V_MSG(sdk_path == "", ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/custom_build_sdk_path'.");
  1913. _update_custom_build_project();
  1914. OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required
  1915. String build_command;
  1916. #ifdef WINDOWS_ENABLED
  1917. build_command = "gradlew.bat";
  1918. #else
  1919. build_command = "gradlew";
  1920. #endif
  1921. String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build");
  1922. build_command = build_path.plus_file(build_command);
  1923. String package_name = get_package_name(p_preset->get("package/unique_name"));
  1924. Vector<PluginConfig> enabled_plugins = get_enabled_plugins(p_preset);
  1925. String local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins);
  1926. String remote_plugins_binaries = get_plugins_binaries(BINARY_TYPE_REMOTE, enabled_plugins);
  1927. String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins);
  1928. bool clean_build_required = is_clean_build_required(enabled_plugins);
  1929. List<String> cmdline;
  1930. if (clean_build_required) {
  1931. cmdline.push_back("clean");
  1932. }
  1933. cmdline.push_back("build");
  1934. cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
  1935. cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies.
  1936. cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies.
  1937. cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies.
  1938. cmdline.push_back("-p"); // argument to specify the start directory.
  1939. cmdline.push_back(build_path); // start directory.
  1940. /*{ used for debug
  1941. int ec;
  1942. String pipe;
  1943. OS::get_singleton()->execute(build_command, cmdline, true, NULL, NULL, &ec);
  1944. print_line("exit code: " + itos(ec));
  1945. }
  1946. */
  1947. int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
  1948. if (result != 0) {
  1949. EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation."));
  1950. return ERR_CANT_CREATE;
  1951. }
  1952. if (p_debug) {
  1953. src_apk = build_path.plus_file("build/outputs/apk/debug/android_debug.apk");
  1954. } else {
  1955. src_apk = build_path.plus_file("build/outputs/apk/release/android_release.apk");
  1956. }
  1957. if (!FileAccess::exists(src_apk)) {
  1958. EditorNode::get_singleton()->show_warning(TTR("No build apk generated at: ") + "\n" + src_apk);
  1959. return ERR_CANT_CREATE;
  1960. }
  1961. } else {
  1962. if (p_debug)
  1963. src_apk = p_preset->get("custom_template/debug");
  1964. else
  1965. src_apk = p_preset->get("custom_template/release");
  1966. src_apk = src_apk.strip_edges();
  1967. if (src_apk == "") {
  1968. if (p_debug) {
  1969. src_apk = find_export_template("android_debug.apk");
  1970. } else {
  1971. src_apk = find_export_template("android_release.apk");
  1972. }
  1973. if (src_apk == "") {
  1974. EditorNode::add_io_error("Package not found: " + src_apk);
  1975. return ERR_FILE_NOT_FOUND;
  1976. }
  1977. }
  1978. }
  1979. if (!DirAccess::exists(p_path.get_base_dir())) {
  1980. return ERR_FILE_BAD_PATH;
  1981. }
  1982. FileAccess *src_f = NULL;
  1983. zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
  1984. if (ep.step("Creating APK...", 0)) {
  1985. return ERR_SKIP;
  1986. }
  1987. unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io);
  1988. if (!pkg) {
  1989. EditorNode::add_io_error("Could not find template APK to export:\n" + src_apk);
  1990. return ERR_FILE_NOT_FOUND;
  1991. }
  1992. int ret = unzGoToFirstFile(pkg);
  1993. zlib_filefunc_def io2 = io;
  1994. FileAccess *dst_f = NULL;
  1995. io2.opaque = &dst_f;
  1996. String tmp_unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
  1997. #define CLEANUP_AND_RETURN(m_err) \
  1998. { \
  1999. DirAccess::remove_file_or_error(tmp_unaligned_path); \
  2000. return m_err; \
  2001. }
  2002. zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
  2003. bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer");
  2004. bool immersive = p_preset->get("screen/immersive_mode");
  2005. bool debug_opengl = p_preset->get("screen/opengl_debug");
  2006. bool _signed = p_preset->get("package/signed");
  2007. bool apk_expansion = p_preset->get("apk_expansion/enable");
  2008. String cmdline = p_preset->get("command_line/extra_args");
  2009. int version_code = p_preset->get("version/code");
  2010. String version_name = p_preset->get("version/name");
  2011. String package_name = p_preset->get("package/unique_name");
  2012. String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
  2013. String release_keystore = p_preset->get("keystore/release");
  2014. String release_username = p_preset->get("keystore/release_user");
  2015. String release_password = p_preset->get("keystore/release_password");
  2016. Vector<String> enabled_abis = get_enabled_abis(p_preset);
  2017. String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
  2018. // Prepare images to be resized for the icons. If some image ends up being uninitialized, the default image from the export template will be used.
  2019. Ref<Image> launcher_icon_image;
  2020. Ref<Image> launcher_adaptive_icon_foreground_image;
  2021. Ref<Image> launcher_adaptive_icon_background_image;
  2022. launcher_icon_image.instance();
  2023. launcher_adaptive_icon_foreground_image.instance();
  2024. launcher_adaptive_icon_background_image.instance();
  2025. // Regular icon: user selection -> project icon -> default.
  2026. String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges();
  2027. if (path.empty() || ImageLoader::load_image(path, launcher_icon_image) != OK) {
  2028. ImageLoader::load_image(project_icon_path, launcher_icon_image);
  2029. }
  2030. // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default).
  2031. path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges();
  2032. if (path.empty() || ImageLoader::load_image(path, launcher_adaptive_icon_foreground_image) != OK) {
  2033. launcher_adaptive_icon_foreground_image = launcher_icon_image;
  2034. }
  2035. // Adaptive background: user selection -> default.
  2036. path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges();
  2037. if (!path.empty()) {
  2038. ImageLoader::load_image(path, launcher_adaptive_icon_background_image);
  2039. }
  2040. Vector<String> invalid_abis(enabled_abis);
  2041. while (ret == UNZ_OK) {
  2042. //get filename
  2043. unz_file_info info;
  2044. char fname[16384];
  2045. ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);
  2046. bool skip = false;
  2047. String file = fname;
  2048. Vector<uint8_t> data;
  2049. data.resize(info.uncompressed_size);
  2050. //read
  2051. unzOpenCurrentFile(pkg);
  2052. unzReadCurrentFile(pkg, data.ptrw(), data.size());
  2053. unzCloseCurrentFile(pkg);
  2054. //write
  2055. if (file == "AndroidManifest.xml") {
  2056. _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG));
  2057. }
  2058. if (file == "resources.arsc") {
  2059. _fix_resources(p_preset, data);
  2060. }
  2061. for (int i = 0; i < icon_densities_count; ++i) {
  2062. if (launcher_icon_image.is_valid() && !launcher_icon_image->empty()) {
  2063. _process_launcher_icons(file, launcher_icon_image, launcher_icons[i], data);
  2064. }
  2065. if (launcher_adaptive_icon_foreground_image.is_valid() && !launcher_adaptive_icon_foreground_image->empty()) {
  2066. _process_launcher_icons(file, launcher_adaptive_icon_foreground_image, launcher_adaptive_icon_foregrounds[i], data);
  2067. }
  2068. if (launcher_adaptive_icon_background_image.is_valid() && !launcher_adaptive_icon_background_image->empty()) {
  2069. _process_launcher_icons(file, launcher_adaptive_icon_background_image, launcher_adaptive_icon_backgrounds[i], data);
  2070. }
  2071. }
  2072. if (file.ends_with(".so")) {
  2073. bool enabled = false;
  2074. for (int i = 0; i < enabled_abis.size(); ++i) {
  2075. if (file.begins_with("lib/" + enabled_abis[i] + "/")) {
  2076. invalid_abis.erase(enabled_abis[i]);
  2077. enabled = true;
  2078. break;
  2079. }
  2080. }
  2081. if (!enabled) {
  2082. skip = true;
  2083. }
  2084. }
  2085. if (file.begins_with("META-INF") && _signed) {
  2086. skip = true;
  2087. }
  2088. if (!skip) {
  2089. print_line("ADDING: " + file);
  2090. // Respect decision on compression made by AAPT for the export template
  2091. const bool uncompressed = info.compression_method == 0;
  2092. zip_fileinfo zipfi = get_zip_fileinfo();
  2093. zipOpenNewFileInZip(unaligned_apk,
  2094. file.utf8().get_data(),
  2095. &zipfi,
  2096. NULL,
  2097. 0,
  2098. NULL,
  2099. 0,
  2100. NULL,
  2101. uncompressed ? 0 : Z_DEFLATED,
  2102. Z_DEFAULT_COMPRESSION);
  2103. zipWriteInFileInZip(unaligned_apk, data.ptr(), data.size());
  2104. zipCloseFileInZip(unaligned_apk);
  2105. }
  2106. ret = unzGoToNextFile(pkg);
  2107. }
  2108. if (!invalid_abis.empty()) {
  2109. String unsupported_arch = String(", ").join(invalid_abis);
  2110. EditorNode::add_io_error("Missing libraries in the export template for the selected architectures: " + unsupported_arch + ".\n" +
  2111. "Please build a template with all required libraries, or uncheck the missing architectures in the export preset.");
  2112. CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
  2113. }
  2114. if (ep.step("Adding files...", 1)) {
  2115. CLEANUP_AND_RETURN(ERR_SKIP);
  2116. }
  2117. Error err = OK;
  2118. Vector<String> cl = cmdline.strip_edges().split(" ");
  2119. for (int i = 0; i < cl.size(); i++) {
  2120. if (cl[i].strip_edges().length() == 0) {
  2121. cl.remove(i);
  2122. i--;
  2123. }
  2124. }
  2125. gen_export_flags(cl, p_flags);
  2126. if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
  2127. APKExportData ed;
  2128. ed.ep = &ep;
  2129. ed.apk = unaligned_apk;
  2130. err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so);
  2131. } else {
  2132. //all files
  2133. if (apk_expansion) {
  2134. String apkfname = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb";
  2135. String fullpath = p_path.get_base_dir().plus_file(apkfname);
  2136. err = save_pack(p_preset, fullpath);
  2137. if (err != OK) {
  2138. unzClose(pkg);
  2139. EditorNode::add_io_error("Could not write expansion package file: " + apkfname);
  2140. CLEANUP_AND_RETURN(ERR_SKIP);
  2141. }
  2142. cl.push_back("--use_apk_expansion");
  2143. cl.push_back("--apk_expansion_md5");
  2144. cl.push_back(FileAccess::get_md5(fullpath));
  2145. cl.push_back("--apk_expansion_key");
  2146. cl.push_back(apk_expansion_pkey.strip_edges());
  2147. } else {
  2148. APKExportData ed;
  2149. ed.ep = &ep;
  2150. ed.apk = unaligned_apk;
  2151. err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so);
  2152. }
  2153. }
  2154. int xr_mode_index = p_preset->get("xr_features/xr_mode");
  2155. if (xr_mode_index == 1 /* XRMode.OVR */) {
  2156. cl.push_back("--xr_mode_ovr");
  2157. } else {
  2158. // XRMode.REGULAR is the default.
  2159. cl.push_back("--xr_mode_regular");
  2160. }
  2161. if (use_32_fb)
  2162. cl.push_back("--use_depth_32");
  2163. if (immersive)
  2164. cl.push_back("--use_immersive");
  2165. if (debug_opengl)
  2166. cl.push_back("--debug_opengl");
  2167. if (cl.size()) {
  2168. //add comandline
  2169. Vector<uint8_t> clf;
  2170. clf.resize(4);
  2171. encode_uint32(cl.size(), &clf.write[0]);
  2172. for (int i = 0; i < cl.size(); i++) {
  2173. print_line(itos(i) + " param: " + cl[i]);
  2174. CharString txt = cl[i].utf8();
  2175. int base = clf.size();
  2176. int length = txt.length();
  2177. if (!length)
  2178. continue;
  2179. clf.resize(base + 4 + length);
  2180. encode_uint32(length, &clf.write[base]);
  2181. copymem(&clf.write[base + 4], txt.ptr(), length);
  2182. }
  2183. zip_fileinfo zipfi = get_zip_fileinfo();
  2184. zipOpenNewFileInZip(unaligned_apk,
  2185. "assets/_cl_",
  2186. &zipfi,
  2187. NULL,
  2188. 0,
  2189. NULL,
  2190. 0,
  2191. NULL,
  2192. 0, // No compress (little size gain and potentially slower startup)
  2193. Z_DEFAULT_COMPRESSION);
  2194. zipWriteInFileInZip(unaligned_apk, clf.ptr(), clf.size());
  2195. zipCloseFileInZip(unaligned_apk);
  2196. }
  2197. zipClose(unaligned_apk, NULL);
  2198. unzClose(pkg);
  2199. if (err != OK) {
  2200. CLEANUP_AND_RETURN(err);
  2201. }
  2202. if (_signed) {
  2203. String jarsigner = EditorSettings::get_singleton()->get("export/android/jarsigner");
  2204. if (!FileAccess::exists(jarsigner)) {
  2205. EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting APK is unsigned.");
  2206. CLEANUP_AND_RETURN(OK);
  2207. }
  2208. String keystore;
  2209. String password;
  2210. String user;
  2211. if (p_debug) {
  2212. keystore = p_preset->get("keystore/debug");
  2213. password = p_preset->get("keystore/debug_password");
  2214. user = p_preset->get("keystore/debug_user");
  2215. if (keystore.empty()) {
  2216. keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore");
  2217. password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass");
  2218. user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user");
  2219. }
  2220. if (ep.step("Signing debug APK...", 103)) {
  2221. CLEANUP_AND_RETURN(ERR_SKIP);
  2222. }
  2223. } else {
  2224. keystore = release_keystore;
  2225. password = release_password;
  2226. user = release_username;
  2227. if (ep.step("Signing release APK...", 103)) {
  2228. CLEANUP_AND_RETURN(ERR_SKIP);
  2229. }
  2230. }
  2231. if (!FileAccess::exists(keystore)) {
  2232. EditorNode::add_io_error("Could not find keystore, unable to export.");
  2233. CLEANUP_AND_RETURN(ERR_FILE_CANT_OPEN);
  2234. }
  2235. List<String> args;
  2236. args.push_back("-digestalg");
  2237. args.push_back("SHA-256");
  2238. args.push_back("-sigalg");
  2239. args.push_back("SHA256withRSA");
  2240. String tsa_url = EditorSettings::get_singleton()->get("export/android/timestamping_authority_url");
  2241. if (tsa_url != "") {
  2242. args.push_back("-tsa");
  2243. args.push_back(tsa_url);
  2244. }
  2245. args.push_back("-verbose");
  2246. args.push_back("-keystore");
  2247. args.push_back(keystore);
  2248. args.push_back("-storepass");
  2249. args.push_back(password);
  2250. args.push_back(tmp_unaligned_path);
  2251. args.push_back(user);
  2252. int retval;
  2253. OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval);
  2254. if (retval) {
  2255. EditorNode::add_io_error("'jarsigner' returned with error #" + itos(retval));
  2256. CLEANUP_AND_RETURN(ERR_CANT_CREATE);
  2257. }
  2258. if (ep.step("Verifying APK...", 104)) {
  2259. CLEANUP_AND_RETURN(ERR_SKIP);
  2260. }
  2261. args.clear();
  2262. args.push_back("-verify");
  2263. args.push_back("-keystore");
  2264. args.push_back(keystore);
  2265. args.push_back(tmp_unaligned_path);
  2266. args.push_back("-verbose");
  2267. OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval);
  2268. if (retval) {
  2269. EditorNode::add_io_error("'jarsigner' verification of APK failed. Make sure to use a jarsigner from OpenJDK 8.");
  2270. CLEANUP_AND_RETURN(ERR_CANT_CREATE);
  2271. }
  2272. }
  2273. // Let's zip-align (must be done after signing)
  2274. static const int ZIP_ALIGNMENT = 4;
  2275. if (ep.step("Aligning APK...", 105)) {
  2276. CLEANUP_AND_RETURN(ERR_SKIP);
  2277. }
  2278. unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io);
  2279. if (!tmp_unaligned) {
  2280. EditorNode::add_io_error("Could not unzip temporary unaligned APK.");
  2281. CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
  2282. }
  2283. ret = unzGoToFirstFile(tmp_unaligned);
  2284. io2 = io;
  2285. dst_f = NULL;
  2286. io2.opaque = &dst_f;
  2287. zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
  2288. // Take files from the unaligned APK and write them out to the aligned one
  2289. // in raw mode, i.e. not uncompressing and recompressing, aligning them as needed,
  2290. // following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp
  2291. int bias = 0;
  2292. while (ret == UNZ_OK) {
  2293. unz_file_info info;
  2294. memset(&info, 0, sizeof(info));
  2295. char fname[16384];
  2296. char extra[16384];
  2297. ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, NULL, 0);
  2298. String file = fname;
  2299. Vector<uint8_t> data;
  2300. data.resize(info.compressed_size);
  2301. // read
  2302. int method, level;
  2303. unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read
  2304. long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned);
  2305. unzReadCurrentFile(tmp_unaligned, data.ptrw(), data.size());
  2306. unzCloseCurrentFile(tmp_unaligned);
  2307. // align
  2308. int padding = 0;
  2309. if (!info.compression_method) {
  2310. // Uncompressed file => Align
  2311. long new_offset = file_offset + bias;
  2312. padding = (ZIP_ALIGNMENT - (new_offset % ZIP_ALIGNMENT)) % ZIP_ALIGNMENT;
  2313. }
  2314. memset(extra + info.size_file_extra, 0, padding);
  2315. // write
  2316. zip_fileinfo zipfi = get_zip_fileinfo();
  2317. zipOpenNewFileInZip2(final_apk,
  2318. file.utf8().get_data(),
  2319. &zipfi,
  2320. extra,
  2321. info.size_file_extra + padding,
  2322. NULL,
  2323. 0,
  2324. NULL,
  2325. method,
  2326. level,
  2327. 1); // raw write
  2328. zipWriteInFileInZip(final_apk, data.ptr(), data.size());
  2329. zipCloseFileInZipRaw(final_apk, info.uncompressed_size, info.crc);
  2330. bias += padding;
  2331. ret = unzGoToNextFile(tmp_unaligned);
  2332. }
  2333. zipClose(final_apk, NULL);
  2334. unzClose(tmp_unaligned);
  2335. CLEANUP_AND_RETURN(OK);
  2336. }
  2337. virtual void get_platform_features(List<String> *r_features) {
  2338. r_features->push_back("mobile");
  2339. r_features->push_back("Android");
  2340. }
  2341. virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
  2342. }
  2343. EditorExportPlatformAndroid() {
  2344. Ref<Image> img = memnew(Image(_android_logo));
  2345. logo.instance();
  2346. logo->create_from_image(img);
  2347. img = Ref<Image>(memnew(Image(_android_run_icon)));
  2348. run_icon.instance();
  2349. run_icon->create_from_image(img);
  2350. device_lock = Mutex::create();
  2351. devices_changed = true;
  2352. plugins_lock = Mutex::create();
  2353. plugins_changed = true;
  2354. quit_request = false;
  2355. check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this);
  2356. }
  2357. ~EditorExportPlatformAndroid() {
  2358. quit_request = true;
  2359. Thread::wait_to_finish(check_for_changes_thread);
  2360. memdelete(plugins_lock);
  2361. memdelete(device_lock);
  2362. memdelete(check_for_changes_thread);
  2363. }
  2364. };
  2365. void register_android_exporter() {
  2366. String exe_ext;
  2367. if (OS::get_singleton()->get_name() == "Windows") {
  2368. exe_ext = "*.exe";
  2369. }
  2370. EDITOR_DEF("export/android/adb", "");
  2371. EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/adb", PROPERTY_HINT_GLOBAL_FILE, exe_ext));
  2372. EDITOR_DEF("export/android/jarsigner", "");
  2373. EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/jarsigner", PROPERTY_HINT_GLOBAL_FILE, exe_ext));
  2374. EDITOR_DEF("export/android/debug_keystore", "");
  2375. EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"));
  2376. EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey");
  2377. EDITOR_DEF("export/android/debug_keystore_pass", "android");
  2378. EDITOR_DEF("export/android/force_system_user", false);
  2379. EDITOR_DEF("export/android/custom_build_sdk_path", "");
  2380. EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/custom_build_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
  2381. EDITOR_DEF("export/android/timestamping_authority_url", "");
  2382. EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
  2383. Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid));
  2384. EditorExport::get_singleton()->add_export_platform(exporter);
  2385. }