<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Indie-Developer</title>
    <description>The latest articles on DEV Community by Indie-Developer (@securityf).</description>
    <link>https://dev.to/securityf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3976352%2F38de7570-1606-45cd-8279-36fc75163de7.png</url>
      <title>DEV Community: Indie-Developer</title>
      <link>https://dev.to/securityf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/securityf"/>
    <language>en</language>
    <item>
      <title>What one button becomes in an ApkPy build</title>
      <dc:creator>Indie-Developer</dc:creator>
      <pubDate>Fri, 30 Oct 2026 17:57:14 +0000</pubDate>
      <link>https://dev.to/securityf/what-one-button-becomes-in-an-apkpy-build-34dl</link>
      <guid>https://dev.to/securityf/what-one-button-becomes-in-an-apkpy-build-34dl</guid>
      <description>&lt;p&gt;When I write a button in ApkPy, I write one line of Python. By the time the app runs on a phone, that line has become a layout entry, a Java listener and a callback method. I want to show the whole path, because it explains why the APK has no Python interpreter inside.&lt;/p&gt;

&lt;p&gt;The line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Continue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;continue_button&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;continue_flow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;home&lt;/code&gt; is a &lt;code&gt;Screen&lt;/code&gt;. Each screen becomes an Android Activity, and &lt;code&gt;screen=&lt;/code&gt; puts the button at the root of that screen. If I pass &lt;code&gt;parent=summary&lt;/code&gt; instead, it goes inside a container or card.&lt;/p&gt;

&lt;p&gt;The compiler does not run this file and take a screenshot of a desktop window. It parses the Python syntax tree, collects screens, components and callbacks, and resolves the theme and style selectors before writing anything. The style block is a build-time input: the generated project gets colors, drawable shapes and dimensions, not a CSS engine running on the phone.&lt;/p&gt;

&lt;p&gt;For the button above, the layout gets this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;com.google.android.material.button.MaterialButton&lt;/span&gt;
    &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/continue_button"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"match_parent"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:minHeight=&lt;/span&gt;&lt;span class="s"&gt;"48dp"&lt;/span&gt;
    &lt;span class="na"&gt;android:text=&lt;/span&gt;&lt;span class="s"&gt;"Continue"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the Activity gets this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;MaterialButton&lt;/span&gt; &lt;span class="n"&gt;continue_button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;findViewById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;continue_button&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;continue_button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setOnClickListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pythonCallback_continue_flow&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The callback is a real Java method generated from my Python function. Conditions, loops, conversions and component calls inside it are translated to their Java equivalents. The snippets are shortened; the real layout also carries margins, colors, accessibility text and drawable references.&lt;/p&gt;

&lt;p&gt;One detail worth knowing: helpers are emitted only when the source asks for them. A plain screen does not get a media service. Call &lt;code&gt;audio.play_background&lt;/code&gt; and the build adds a foreground service, a MediaSession and the notification. Call &lt;code&gt;https.get&lt;/code&gt; and it adds the INTERNET permission and background networking. This is why a simple app stays small.&lt;/p&gt;

&lt;p&gt;The generated project is meant to be read. Open the Gradle output from &lt;code&gt;apkpy build&lt;/code&gt; in Android Studio and the button is there, with the ID I gave it.&lt;/p&gt;

&lt;p&gt;The rule that matters: &lt;code&gt;writehere.py&lt;/code&gt; is the source of truth. The generated Android project is an output. Rebuild and the generated files can be replaced, so app behavior stays in Python. &lt;code&gt;apkpy.toml&lt;/code&gt; holds the app name, package ID, version and icon.&lt;/p&gt;

&lt;p&gt;1.14.1, out on October 25, added classes, dataclasses and custom errors to the translated Python. That reaches the button too: a method can be a command now, &lt;code&gt;command=cart.clear&lt;/code&gt;, instead of only a function.&lt;/p&gt;

&lt;p&gt;To see the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; apkpy
apkpy start hello
&lt;span class="nb"&gt;cd &lt;/span&gt;hello
python writehere.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;writehere.py&lt;/code&gt; opens the Hot Previewer on the desktop. &lt;code&gt;apkpy build&lt;/code&gt; writes the Gradle project. &lt;code&gt;apkpy run&lt;/code&gt; compiles a debug APK.&lt;/p&gt;

</description>
      <category>python</category>
      <category>android</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why there is no Python inside an ApkPy APK</title>
      <dc:creator>Indie-Developer</dc:creator>
      <pubDate>Sat, 24 Oct 2026 19:01:39 +0000</pubDate>
      <link>https://dev.to/securityf/why-there-is-no-python-inside-an-apkpy-apk-2ecm</link>
      <guid>https://dev.to/securityf/why-there-is-no-python-inside-an-apkpy-apk-2ecm</guid>
      <description>&lt;p&gt;When I show ApkPy to someone, the first question is almost always the same: how does this run Python on Android?&lt;/p&gt;

&lt;p&gt;It doesn't. ApkPy reads &lt;code&gt;writehere.py&lt;/code&gt; and writes Java and XML. The APK from &lt;code&gt;apkpy run&lt;/code&gt; has activities, layouts, drawables and a manifest, the same as an app built by hand in Android Studio. There is no Python interpreter inside, no WebView, and no CPython compiled as a library.&lt;/p&gt;

&lt;p&gt;I chose translation over embedding because of size. In the benchmark I published, the same 100-note app was 5.37 MiB in debug and 1.48 MiB in the signed release. With an interpreter inside, the starting point would be different. And because the generated code is plain Java, the camera and background audio are Android APIs, not bridges to them.&lt;/p&gt;

&lt;p&gt;The cost shows up when I write Python ApkPy cannot translate yet. The build stops on that line and says what to write instead.&lt;/p&gt;

&lt;p&gt;1.14.0, out on October 20, was mostly about that. Most of the Python people write every day now goes through: &lt;code&gt;a, b = parts&lt;/code&gt;, &lt;code&gt;for key, value in d.items()&lt;/code&gt;, &lt;code&gt;items.pop()&lt;/code&gt;, &lt;code&gt;sorted()&lt;/code&gt;, comprehensions inside functions, f-string widths (&lt;code&gt;{n:05d}&lt;/code&gt;), &lt;code&gt;*args&lt;/code&gt;, functions inside functions, recursion, &lt;code&gt;try/except&lt;/code&gt; and &lt;code&gt;for ... else&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some of it was less visible than new syntax. The generated Java is now checked against CPython on the same inputs, accents and emoji included.&lt;/p&gt;

&lt;p&gt;1.14.0 also tightened installation. &lt;code&gt;apkpy setup&lt;/code&gt; now checks the sha256 of everything it downloads.&lt;/p&gt;

&lt;p&gt;The Python that translates is listed on Compatibility and limits.&lt;/p&gt;

&lt;p&gt;If you want to try it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; apkpy
apkpy start hello
&lt;span class="nb"&gt;cd &lt;/span&gt;hello
python writehere.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;writehere.py&lt;/code&gt; opens the Previewer. &lt;code&gt;apkpy run&lt;/code&gt; generates the Android project and builds a debug APK next to it.&lt;/p&gt;

</description>
      <category>python</category>
      <category>android</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
