{"id":7501,"date":"2025-10-26T19:10:42","date_gmt":"2025-10-26T19:10:42","guid":{"rendered":"https:\/\/robertjwallace.com\/?p=7501"},"modified":"2025-11-30T14:54:04","modified_gmt":"2025-11-30T14:54:04","slug":"how-i-turned-a-simple-html-file-into-an-android-app-a-love-letter-to-complexity","status":"publish","type":"post","link":"https:\/\/robertjwallace.com\/es\/how-i-turned-a-simple-html-file-into-an-android-app-a-love-letter-to-complexity\/","title":{"rendered":"How I Turned a Simple HTML File Into an Android App: A Love Letter to Complexity"},"content":{"rendered":"<h2 class=\"wp-block-heading\">Or: How I Learned to Stop Worrying and Embrace 47 Dependencies<\/h2>\n\n\n\n<p class=\"\"><strong>TL;DR:<\/strong> I had a perfectly functional HTML file. It worked in every browser. It was beautiful. Then I decided to turn it into an Android app.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<!--more-->\n\n\n\n<h3 class=\"wp-block-heading\">Chapter 1: The Innocence<\/h3>\n\n\n\n<p class=\"\">It started so simply. I had a web page. Just HTML, CSS, and JavaScript. It worked. It was fast. It was elegant. It embodied the KISS principle &#8211; Keep It Simple, Stupid.<\/p>\n\n\n\n<p class=\"\">&#8220;I should turn this into an Android app,&#8221; I thought, with the blissful ignorance of someone who&#8217;s never done this before.<\/p>\n\n\n\n<p class=\"\">&#8220;Apache Cordova makes it easy!&#8221; the internet promised. &#8220;Just wrap your web app in a native container!&#8221;<\/p>\n\n\n\n<p class=\"\"><em>Narrator: It would not be easy.<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Chapter 2: The Descent Begins<\/h3>\n\n\n\n<p class=\"\">First, I needed to install some software. Just a few things:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"\">Node.js (but not just any Node.js &#8211; the LTS version via nvm)<\/li>\n\n\n\n<li class=\"\">npm (comes with Node, but you need to know that)<\/li>\n\n\n\n<li class=\"\">Java JDK (OpenJDK 17 specifically, not 16, not 18)<\/li>\n\n\n\n<li class=\"\">Android SDK Command Line Tools (11,076,708 bytes of &#8220;simplicity&#8221;)<\/li>\n\n\n\n<li class=\"\">Android Platform Tools<\/li>\n\n\n\n<li class=\"\">Android Build Tools (version 34.0.0, or wait, maybe 35.0.0?)<\/li>\n\n\n\n<li class=\"\">Gradle (the build system that builds build systems)<\/li>\n\n\n\n<li class=\"\">Apache Cordova itself<\/li>\n<\/ol>\n\n\n\n<p class=\"\">Each of these requires setting environment variables. ANDROID_HOME, JAVA_HOME, PATH modifications. Your .bashrc file, once a pristine 10 lines, is now a novel.<\/p>\n\n\n\n<p class=\"\"><strong>Installation time:<\/strong> 45 minutes<br><strong>Lines of bash script to automate this:<\/strong> 200+<br><strong>Times I questioned my life choices:<\/strong> 7<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Chapter 3: Version Hell<\/h3>\n\n\n\n<p class=\"\">&#8220;Let&#8217;s create a Cordova project!&#8221; I said, still optimistic.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cordova create myapp com.example.myapp MyApp\n<\/code><\/pre>\n\n\n\n<p class=\"\">This worked! Hope surged through my veins.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cordova platform add android\n<\/code><\/pre>\n\n\n\n<p class=\"\">This also worked! I was unstoppable!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cordova build android\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>FAILURE: Build failed with an exception.\n* What went wrong:\nNo usable Android build tools found. Highest 35.x installed version is 34.0.0; \nRecommended version is 35.0.0.\n<\/code><\/pre>\n\n\n\n<p class=\"\">Ah yes. Version incompatibility. The universal language of software development.<\/p>\n\n\n\n<p class=\"\">But wait, there&#8217;s more:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">Cordova Android 14.0.1? Doesn&#8217;t work.<\/li>\n\n\n\n<li class=\"\">Cordova Android 13.0.0? Still has issues.<\/li>\n\n\n\n<li class=\"\">Cordova Android 12.0.1? NOW we&#8217;re talking!<\/li>\n<\/ul>\n\n\n\n<p class=\"\"><strong>Time spent googling error messages:<\/strong> 1.5 hours<br><strong>Number of Stack Overflow tabs open:<\/strong> 23<br><strong>Faith in humanity:<\/strong> Declining<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Chapter 4: The Mystery of the Missing gradlew<\/h3>\n\n\n\n<p class=\"\">In the Java\/Android world, there&#8217;s a magical file called <code>gradlew<\/code>. It&#8217;s the Gradle Wrapper. It&#8217;s supposed to be automatically generated when you add the Android platform.<\/p>\n\n\n\n<p class=\"\">Mine was not.<\/p>\n\n\n\n<p class=\"\">Was it user error? A cosmic alignment issue? Mercury in retrograde? Nobody knows.<\/p>\n\n\n\n<p class=\"\">I tried:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">Removing and re-adding the platform (3 times)<\/li>\n\n\n\n<li class=\"\">Manually downloading gradle wrapper jars<\/li>\n\n\n\n<li class=\"\">Creating gradlew by hand (spoiler: I corrupted it)<\/li>\n\n\n\n<li class=\"\">Questioning the fundamental nature of reality<\/li>\n<\/ul>\n\n\n\n<p class=\"\">Eventually, we just used the system Gradle directly. Why have a wrapper when you can have&#8230; no wrapper?<\/p>\n\n\n\n<p class=\"\"><strong>Philosophical crises experienced:<\/strong> 2<br><strong>Files manually created that should have been automatic:<\/strong> 4<br><strong>Gradle daemon restarts:<\/strong> Too many to count<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Chapter 5: The Config File Chronicles<\/h3>\n\n\n\n<p class=\"\">Every Cordova project has a <code>config.xml<\/code>. This file is supposed to configure your app. Simple, right?<\/p>\n\n\n\n<p class=\"\">Wrong.<\/p>\n\n\n\n<p class=\"\">My automatically generated config.xml referenced:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">Splash screens that don&#8217;t exist<\/li>\n\n\n\n<li class=\"\">Icons that were never created<\/li>\n\n\n\n<li class=\"\">A network security config file that was definitely not included<\/li>\n\n\n\n<li class=\"\">Preferences that were deprecated two versions ago<\/li>\n<\/ul>\n\n\n\n<p class=\"\">Each missing file produced a warning. Or an error. Sometimes both! Russian roulette for your build process.<\/p>\n\n\n\n<p class=\"\">The solution? Manually create every missing file, or delete every reference to them. Choose your own adventure!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;icon src=\"www\/res\/icon\/android\/drawable-hdpi-icon.png\" density=\"hdpi\" \/&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"\"><em>File not found.<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;resource-file src=\"resources\/android\/xml\/network_security_config.xml\" ... \/&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"\"><em>File not found.<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>The \"&lt;splash&gt;\" tags were detected and are no longer supported.\n<\/code><\/pre>\n\n\n\n<p class=\"\">Then why are they IN THE DEFAULT CONFIG FILE?<\/p>\n\n\n\n<p class=\"\"><strong>Time spent editing XML:<\/strong> 30 minutes<br><strong>Nostalgia for simpler times (like yesterday):<\/strong> Intense<br><strong>Urge to just email the APK to myself:<\/strong> Growing<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Chapter 6: Build Tools: A Version Odyssey<\/h3>\n\n\n\n<p class=\"\">Let&#8217;s play a game called &#8220;Which Build Tools Version Do You Actually Need?&#8221;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">The error said version 35.0.0<\/li>\n\n\n\n<li class=\"\">I had version 34.0.0<\/li>\n\n\n\n<li class=\"\">The config wanted version 34<\/li>\n\n\n\n<li class=\"\">Cordova defaulted to version 35<\/li>\n\n\n\n<li class=\"\">The internet suggested version 33<\/li>\n<\/ul>\n\n\n\n<p class=\"\">The solution? Edit <code>cdv-gradle-config.json<\/code> to use version 34. Because manually specifying versions in a configuration file is EXACTLY what &#8220;easy cross-platform development&#8221; means.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"BUILD_TOOLS_VERSION\": \"34.0.0\"\n}\n<\/code><\/pre>\n\n\n\n<p class=\"\">Then the build still failed because of an &#8220;ambiguous method overloading&#8221; error in Cordova&#8217;s own code. The fix? Downgrade Cordova Android from 14.0.1 to 13.0.0. Then to 12.0.1 when 13 also had issues.<\/p>\n\n\n\n<p class=\"\"><strong>Versions tried:<\/strong> 4<br><strong>Versions that worked:<\/strong> 1<br><strong>Sense of accomplishment:<\/strong> Replaced by exhaustion<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Chapter 7: The Network Security Config That Time Forgot<\/h3>\n\n\n\n<p class=\"\">Finally, a real error with a real solution:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ERROR: resource xml\/network_security_config not found\n<\/code><\/pre>\n\n\n\n<p class=\"\">This file is supposed to tell Android that your app can make network requests. A reasonable thing for a web app to need. But Cordova doesn&#8217;t create it by default.<\/p>\n\n\n\n<p class=\"\">The solution? Manually create the file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;network-security-config&gt;\n    &lt;base-config cleartextTrafficPermitted=\"true\"&gt;\n        &lt;trust-anchors&gt;\n            &lt;certificates src=\"system\" \/&gt;\n        &lt;\/trust-anchors&gt;\n    &lt;\/base-config&gt;\n&lt;\/network-security-config&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"\">Put it in: <code>platforms\/android\/app\/src\/main\/res\/xml\/<\/code><\/p>\n\n\n\n<p class=\"\">A path so intuitive, so obvious, that only someone who&#8217;s memorized the entire Android project structure would know it.<\/p>\n\n\n\n<p class=\"\"><strong>Time to find the solution:<\/strong> 15 minutes<br><strong>Time to implement it:<\/strong> 30 seconds<br><strong>Ratio of searching to doing:<\/strong> 30:1<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Chapter 8: Victory (?)<\/h3>\n\n\n\n<p class=\"\">After installing seven pieces of software, resolving four version conflicts, creating three missing files, and downgrading two packages, I ran:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gradle assembleDebug\n<\/code><\/pre>\n\n\n\n<p class=\"\">And it worked.<\/p>\n\n\n\n<p class=\"\">BUILD SUCCESSFUL.<\/p>\n\n\n\n<p class=\"\">My HTML file, which was 50 lines of perfectly functional code, was now wrapped in:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">501 directories<\/li>\n\n\n\n<li class=\"\">2,978 files<\/li>\n\n\n\n<li class=\"\">Several hundred megabytes of dependencies<\/li>\n\n\n\n<li class=\"\">A build process that takes 2 minutes<\/li>\n<\/ul>\n\n\n\n<p class=\"\">The APK installed on my phone. The app launched. It displayed my HTML file.<\/p>\n\n\n\n<p class=\"\"><em>Exactly as it had in the browser 3 hours ago.<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Epilogue: Lessons Learned<\/h3>\n\n\n\n<p class=\"\"><strong>What the KISS Principle Says:<\/strong> &#8220;Keep it simple, stupid. Use the simplest solution that works.&#8221;<\/p>\n\n\n\n<p class=\"\"><strong>What I Actually Did:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">Installed a JavaScript runtime to run a build tool<\/li>\n\n\n\n<li class=\"\">Installed a Java runtime to run another build tool<\/li>\n\n\n\n<li class=\"\">Installed an SDK to compile Android apps<\/li>\n\n\n\n<li class=\"\">Installed a framework to wrap web apps<\/li>\n\n\n\n<li class=\"\">Installed a package manager to manage the framework<\/li>\n\n\n\n<li class=\"\">Installed a version manager to manage the package manager<\/li>\n\n\n\n<li class=\"\">Spent 3 hours debugging version conflicts<\/li>\n\n\n\n<li class=\"\">Manually created files that should have been automatic<\/li>\n\n\n\n<li class=\"\">Downgraded software because newer isn&#8217;t always better<\/li>\n<\/ul>\n\n\n\n<p class=\"\"><strong>The Punchline:<\/strong> I could have just:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"\">Added a <code>manifest.json<\/code> to my HTML file (5 minutes)<\/li>\n\n\n\n<li class=\"\">Made it a Progressive Web App<\/li>\n\n\n\n<li class=\"\">Had Android prompt users to &#8220;Add to Home Screen&#8221;<\/li>\n\n\n\n<li class=\"\">Called it a day<\/li>\n<\/ol>\n\n\n\n<p class=\"\">But where&#8217;s the adventure in that?<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Appendix A: The REAL KISS Solution<\/h3>\n\n\n\n<p class=\"\">Want to turn your web app into an Android app the KISS way?<\/p>\n\n\n\n<p class=\"\"><strong>Option 1: PWA (Progressive Web App)<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"\">Create a <code>manifest.json<\/code>:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"name\": \"My App\",\n  \"short_name\": \"App\",\n  \"start_url\": \"\/\",\n  \"display\": \"standalone\",\n  \"icons\": &#91;{\"src\": \"icon.png\", \"sizes\": \"192x192\"}]\n}\n<\/code><\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li class=\"\">Add to your HTML:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;link rel=\"manifest\" href=\"manifest.json\"&gt;\n<\/code><\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li class=\"\">Done. Users can &#8220;Add to Home Screen&#8221; from their browser.<\/li>\n<\/ol>\n\n\n\n<p class=\"\"><strong>Time required:<\/strong> 5 minutes<br><strong>Dependencies installed:<\/strong> 0<br><strong>Version conflicts resolved:<\/strong> 0<br><strong>Sanity preserved:<\/strong> 100%<\/p>\n\n\n\n<p class=\"\"><strong>Option 2: Just Use the Browser<\/strong> Your HTML file already works in every mobile browser. Maybe just&#8230; use that?<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Appendix B: What I Actually Learned<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li class=\"\"><strong>Cordova is powerful<\/strong> &#8211; When it works, it gives you access to native device features<\/li>\n\n\n\n<li class=\"\"><strong>Cordova is complex<\/strong> &#8211; The toolchain is fragile and version-sensitive<\/li>\n\n\n\n<li class=\"\"><strong>Documentation is optimistic<\/strong> &#8211; &#8220;Easy!&#8221; means &#8220;Easy if everything goes perfectly&#8221;<\/li>\n\n\n\n<li class=\"\"><strong>The KISS principle exists for a reason<\/strong> &#8211; Complexity is exponential<\/li>\n\n\n\n<li class=\"\"><strong>Sometimes the simple solution is the right solution<\/strong> &#8211; A PWA would have been fine<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusi\u00f3n<\/h3>\n\n\n\n<p class=\"\">I now have an Android app. It works. It&#8217;s installed on my phone. Mission accomplished.<\/p>\n\n\n\n<p class=\"\">Was it worth it? That depends. Do you measure worth in:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\"><strong>Time spent?<\/strong> No.<\/li>\n\n\n\n<li class=\"\"><strong>Lines of code written?<\/strong> Definitely no.<\/li>\n\n\n\n<li class=\"\"><strong>Software dependencies added?<\/strong> Please no.<\/li>\n\n\n\n<li class=\"\"><strong>Character building through adversity?<\/strong> &#8230;Maybe?<\/li>\n<\/ul>\n\n\n\n<p class=\"\">Would I do it again?<\/p>\n\n\n\n<p class=\"\">Ask me after I&#8217;ve recovered from this trauma.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"\"><strong>Final Stats:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\"><strong>Total time:<\/strong> 3+ hours<\/li>\n\n\n\n<li class=\"\"><strong>Coffee consumed:<\/strong> 2 cups<\/li>\n\n\n\n<li class=\"\"><strong>Software packages installed:<\/strong> 7 major, 50+ dependencies<\/li>\n\n\n\n<li class=\"\"><strong>Error messages encountered:<\/strong> 15+<\/li>\n\n\n\n<li class=\"\"><strong>Successful builds:<\/strong> 1<\/li>\n\n\n\n<li class=\"\"><strong>Lines of HTML that needed to become an app:<\/strong> 50<\/li>\n\n\n\n<li class=\"\"><strong>Times I considered just using the browser:<\/strong> 12<\/li>\n\n\n\n<li class=\"\"><strong>KISS principle violations:<\/strong> All of them<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"\"><em>This blog post was written in Markdown, compiled to HTML, and viewed in a browser &#8211; a process that took 30 seconds and required zero dependencies. The irony is not lost on me.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">After thoughts<\/h2>\n\n\n\n<p class=\"\">I had another web site I wanted to turn into an app and the second time around was much easier, but still had some issues.  I created a folder with a &#8220;www&#8221; subfolder with the files in it.  But when you create a cordova project it overwrites those.  So you have to first create the project and then move your html into the www folder.  But overall it was pretty painless the second time around.<\/p>","protected":false},"excerpt":{"rendered":"<p>Or: How I Learned to Stop Worrying and Embrace 47 Dependencies TL;DR: I had a perfectly functional HTML file. It worked in every browser. It was beautiful. Then I decided to turn it into an Android app.<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","_eb_attr":"","footnotes":""},"categories":[171,192,143],"tags":[],"class_list":["post-7501","post","type-post","status-publish","format-standard","hentry","category-ai","category-android-app","category-computer-stuff"],"featured_image_src":null,"featured_image_src_square":null,"author_info":{"display_name":"Bob","author_link":"https:\/\/robertjwallace.com\/es\/author\/admin\/"},"_links":{"self":[{"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/posts\/7501","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/comments?post=7501"}],"version-history":[{"count":2,"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/posts\/7501\/revisions"}],"predecessor-version":[{"id":7506,"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/posts\/7501\/revisions\/7506"}],"wp:attachment":[{"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/media?parent=7501"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/categories?post=7501"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/robertjwallace.com\/es\/wp-json\/wp\/v2\/tags?post=7501"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}