Skip to main content

Build native Android apps in pure Python โ€” no Java, no Android Studio required.

Project description

๐Ÿš€ ApkPy โ€” Build Native Android Apps in Pure Python

Welcome to ApkPy, the revolutionary transpiler framework that empowers you to build lightning-fast, native Android applications using only Python and CSS-style definitions. We don't use heavy WebViews or slow runtime rendering engines. ApkPy parses your Python logic and directly generates pure Android Java and XML components, ready to be compiled into a .apk.


1. The Vision & Philosophy ๐ŸŒŸ

Forget the steep learning curves of Java or Kotlin, and avoid the massive footprint of cross-platform engines. Why choose ApkPy over interpreted frameworks like Kivy or Flet?

  • Zero Bloat, Smaller APKs: Because we transpile to pure Android projects, you avoid bundling heavy Python interpreters into your deployment.
  • Flawless Performance: By generating true native Activity, Button, ImageView, and EditText elements under the hood, your app runs at the maximum possible speed the OS allows.
  • 100% Native Look & Feel: Because your components interact directly with the Android SDK, OS-level features like haptics, text-selection, and system animations are preserved automatically.

2. How it Works (The Technical Secret) ๐Ÿ› ๏ธ

How it works: ApkPy parses your Python code into an Abstract Syntax Tree (AST) and maps UI calls to their corresponding Android XML Layouts and Java Activity classes. It's not a simulation; it's code generation.

Here's what happens under the hood every time you run apkpy build:

  1. Parsing โ€” ApkPy reads your writehere.py file and builds a full AST.
  2. Component Mapping โ€” Each label(), button(), inputs(), image() call is translated to its native Android equivalent (TextView, Button, EditText, ImageView).
  3. CSS Translation โ€” Your style = """ ... """ string is parsed and converted to Android XML drawables, dp values, and color attributes.
  4. Java/XML Generation โ€” Full Java Activity classes and XML layout files are generated from scratch.
  5. ZIP Packaging โ€” Everything is bundled into a .zip file you can open directly in Android Studio.

3. Full Installation & Setup ๐Ÿ“ฅ

Getting started is instantly accessible. You do not need any prior knowledge of Android Studio to start designing.

pip install apkpy

(Need the new features? Run pip install --upgrade apkpy)

Your First App โ€” Full CLI Workflow

# 1. Create a new project
apkpy start my_project

# 2. Enter your project folder
cd my_project

# 3. Open writehere.py and design your app
# (edit it with any editor โ€” VS Code, PyCharm, Notepad, etc.)

# 4. Preview it instantly on your computer (no Android needed!)
python writehere.py

# 5. When you're happy, build the native Android project
apkpy build
# โ†’ It will ask: "What do you want to call it?"
# โ†’ Type your app name, e.g: coffee_haven
# โ†’ A file called coffee_haven.zip is generated!

# 6. Open the .zip in Android Studio and compile your APK!

4. Complete Syntax Guide (v0.9.7 Standard) ๐ŸŽจ

ApkPy v0.9.7 introduces native SQLite database support and a powerful json_get() helper, making it easier than ever to build data-driven Android apps in pure Python.

The Screen

Everything belongs to a Screen. Screens translate directly to native Android Activities.

login_screen = Screen(id="login_container")

The Container (Nesting)

Containers allow you to group components together, creating complex layouts like cards, headers, or custom grids.

# Create a container on the screen
header_box = container(id="header_style", screen=login_screen)

# Add a label inside that container
label("Welcome Back", parent=header_box)

The CSS Engine

Say goodbye to complex dictionary configurations. ApkPy uses a multi-line string approach with standard CSS syntax. No quotes are needed around values!

style = """
login_container {
    gap: 15px;
    flex-direction: column;
}
"""

New Layout & Styling Features (v0.9.3+)

Our Android-generation engine has been completely rewritten to use XML-driven layouts, ensuring 1:1 parity between your code and the native Android rendering.

  • gap: 20px; โ€“ Automatically places calculated dp margins between elements in your layout container.
  • flex-direction: row; / column; โ€“ Orients your components mapped directly to android:orientation="horizontal" or "vertical".
  • padding: 10px 20px; โ€“ Pushes text content away from your component edges.
  • border-radius: 15px; โ€“ Generates native XML shape drawables for smooth rounded corners.
  • border-color: #000; and border-width: 2px; โ€“ Easily define component outlines.
  • pressed-color: #cccccc; โ€“ Defines the feedback color when a button is clicked (native <selector>).
  • focus-border-color: #2196F3; โ€“ When a user taps your inputs, ApkPy dynamically swaps the border colors via native XML states!
  • font-size: 18px; โ€“ Set text size in pixels (automatically converted to sp for Android).
  • font-weight: bold; โ€“ Makes your text thicker. Supports bold or numeric values (700, 800, 900).
  • font-family: 'sans-serif'; โ€“ Choose between native font styles.

Typography Support ๐Ÿ” 

ApkPy ensures your text looks great and consistent across platforms by mapping CSS generic families to native system fonts:

  • sans-serif: The modern look. Maps to Roboto (Android) and Segoe UI (Windows) or Helvetica (macOS).
  • serif: The classic look. Maps to Times New Roman.
  • monospace: Perfect for code or alignment. Maps to Consolas (Windows) or Courier.

Note: You can also use font-style: italic; to add emphasis to your labels.


5. Component & Logic Workflow โš™๏ธ

Adding elements and routing interactions is declarative and incredibly clean.

Adding Components: Simply call the component and attach it to your screen. Give it an id to map it to your CSS string.

btn = button("Login", id="primary_btn", screen=login_screen)
inputs("Your email", type="text", id="email_input", screen=login_screen)

Nesting with parent: Every component (label, button, inputs, container) now supports a parent parameter. If set, the component will be rendered inside that container instead of directly on the screen.

card = container(id="card_bg", screen=my_screen)
label("Title", parent=card)
button("Click Me", parent=card)

Supported Input Types: ApkPy handles complex input mapping for you:

  • type="text": Standard keyboard entry.
  • type="password": Secure text entry with hidden characters.
  • type="search": Specialized search field with a clear (โœ•) button.
  • type="checkbox": Native Boolean toggle.
  • type="range": Interactive slider (0-100).
  • type="radio": Selection group (format: inputs("Option1|Option2", type="radio")).

Linking Logic & Navigation: Instead of fighting with Android Intent classes, handle navigation with a single method.

login_screen.on_click_navigate(button=btn, to=dashboard_screen)

The Application Lifecycle: Your file must end by invoking the execution layer with run().

run(start_screen=login_screen)

6. The Two-Step Execution Flow ๐Ÿ”„

ApkPy makes the development lifecycle effortless via two distinct phases:

Phase 1: Development (Hot Previewer)

Simply run your Python file normally:

python writehere.py

This instantly boots up our Tkinter-based Hot Previewer on your computer. You get immediate visual feedback for your screens, interactive inputs, focus colors, buttons, and navigation without needing an emulator!

Phase 2: Production (Native Compilation)

When you're ready to deploy, run the CLI tool in your project folder:

apkpy build

This triggers the transpiler! ApkPy generates all Java classes, Manifests, XML Layouts, and Drawables completely from scratch. This new XML-driven approach ensures that complex layouts, margins, and alignments are handled natively by the Android OS for maximum stability. Open the resulting bundled .zip project and generate your production .apk!


7. What's New & Bug Fixes (v0.9.7) ๐Ÿš€

We've been hard at work making ApkPy the most reliable Python-to-Android framework. Here's what's new in v0.9.7:

  • ๐Ÿ—„๏ธ SQLite Database API: Build fully offline, data-persistent Android apps with the new db object. Use db.execute() for writes and db.query() for reads โ€” with zero Java knowledge required.
  • ๐ŸŒ HTTPS Network API: Connect your app to any REST API on the internet with https.get() and https.post(). Supports custom headers for Bearer tokens and API keys. Runs in a background thread โ€” no UI freezes. Compiles to native Android HttpURLConnection.
  • ๐Ÿ”Ž json_get() Helper: Safely navigate JSON results from db.query() or https responses using dot-notation paths (e.g., json_get(result, "main.temp")). Works identically in the Previewer and on real Android.
  • Cross-platform parity: All new features are fully functional in Phase 1 (Tkinter Previewer) and compile to native Android code in Phase 2.

Previous Highlights (v0.9.3)

  • XML Layout Engine: Moved from programmatic Java UI to native Android XML layouts. This fixes 99% of layout inconsistencies.
  • Named Builds: Running apkpy build now interactively asks you for the project name.
  • Pixel 9 Pro Previewer: The Hot Previewer is now perfectly calibrated to match the Pixel 9 Pro screen.
  • Automatic Gravity & Centering: Fixed bugs where elements wouldn't center correctly.
  • Animation Stability: Fixed glitches in @keyframes animations.

8. FAQ (Quick Answers) โ“

"Does it support Android APIs?" Yes! We are currently expanding support for native permissions, GPS, Camera, and more in upcoming versions.

"Do I need the Android SDK installed?" To write and preview your app using python writehere.py, no SDK is required. However, apkpy build generates a native Android Studio project. To compile that project into a final .apk, you will need the Android SDK/Java installed on your machine (or just upload the generated folder to a CI/CD service like GitHub Actions).

"Can I use images in my app?" Yes! See Section 10 below. Just put your image file next to writehere.py and use the image() component โ€” ApkPy handles copying assets into Android's drawable folder automatically.


9. Spacing & Margins (Precision Layout) ๐Ÿ“

Margins allow you to create "invisible space" around your components. This is essential for moving buttons down, separating inputs, or creating breathing room between elements.

Example Code:

my_button {
    /* Move the button 50 pixels down from whatever is above it */
    margin-top: 50px;

    /* Push the button 20 pixels away from the left edge */
    margin-left: 20px;
}

header {
    /* Put a 30 pixel gap at the bottom of the title */
    margin-bottom: 30px;
}

Margin Guide (For Absolute Beginners):

Property What it does (in simple terms)
margin Adds space to all four sides (top, bottom, left, and right) at once. It's like putting a bubble around the whole component.
margin-top Pushes the component down. It adds space to the top edge. Use this if you want something to move lower on the screen.
margin-bottom Pushes the next component away. It adds space to the bottom edge.
margin-left Pushes the component to the right. It adds space to the left side.
margin-right Pushes the component to the left. It adds space to the right side.

[!TIP] In ApkPy, you can use px (like 20px). The compiler automatically converts this to dp for Android, so your app looks perfect on every phone screen size!


10. Images & Toast Notifications ๐Ÿ–ผ๏ธ

Images

The image() component renders a native Android ImageView. Just place your image file (.png, .jpg) in the same folder as writehere.py and reference it by filename. ApkPy will automatically copy it into the Android res/drawable folder during the build step.

from apkpy_lib import image, Screen

home = Screen(id="home_screen")

# Just place 'logo.png' next to your writehere.py file!
image("logo.png", id="app_logo", screen=home)

You can fully style the image through CSS just like any other component:

app_logo {
    width: 150px;
    height: 150px;
    border-radius: 75px;      /* Makes it circular! */
    margin-top: 60px;
    box-shadow: 0 8px 20px #000;
    animation-name: fadeIn;
    animation-duration: 1000ms;
}

[!IMPORTANT] ApkPy automatically handles the conversion of your image file into an Android-compatible resource. You do not need to manually add it to any resource folder โ€” just place it next to your script and it works.


Toast Notifications ๐Ÿž

Toasts are small, temporary pop-up messages that appear at the bottom of the screen โ€” a staple of Android UX. In ApkPy, triggering one is a single line of Python:

from apkpy_lib import toast

def on_save():
    toast("Your data was saved! โœ…")

In the Hot Previewer (Phase 1), toasts appear as an overlay message at the bottom. On a real Android device (Phase 2), they compile to the native Toast.makeText(...) Java call โ€” identical to what professional apps use.

You can call toast() from any function โ€” button clicks, storage operations, permission results, or even on app startup:

from apkpy_lib import toast, run, Screen

home = Screen(id="home")
run(start_screen=home)
toast("Welcome back! ๐Ÿ‘‹")  # Shows when the app launches

11. Storage & Persistence ๐Ÿ’พ

ApkPy provides a powerful, built-in storage API that allows you to persistently save data across app sessions โ€” things like user preferences, saved form values, login tokens, and more.

How It Works Under the Hood

Environment What storage uses
Hot Previewer (your computer) A local .json file saved next to your script
Android Build (real device) Native Android SharedPreferences โ€” the fastest and most battery-efficient persistent storage on Android

This means your code is 100% identical in both environments. No special cases, no if platform: checks.

Importing Storage

from apkpy_lib import storage

The Full API

Method Description Example
storage.set(key, value) Saves a value permanently storage.set("username", "Alice")
storage.get(key, default) Reads a value (returns default if not found) storage.get("username", "Guest")
storage.delete(key) Removes a single key storage.delete("token")
storage.clear() Wipes all stored data storage.clear()

Basic Example โ€” Saving a User's Name

from apkpy_lib import Screen, inputs, button, toast, storage, run

my_screen = Screen(id="main")

name_field = inputs("Enter your name", type="text", id="name_field", screen=my_screen)

def save_name():
    name = name_field.get_value()
    if name != "":
        storage.set("username", name)
        toast(f"Saved: {name} โœ…")

def load_name():
    saved = storage.get("username", "")
    if saved != "":
        name_field.set_value(saved)
        toast(f"Welcome back, {saved}! ๐Ÿ‘‹")
    else:
        toast("No name saved yet.")

button("Save", id="btn_save", command=save_name, screen=my_screen)
button("Load", id="btn_load", command=load_name, screen=my_screen)

run(start_screen=my_screen)

Advanced Example โ€” Auto-loading Saved Preferences

One of the most useful patterns is auto-loading data when the app starts โ€” so the user never has to type the same thing twice:

from apkpy_lib import Screen, inputs, button, toast, storage, run

settings_screen = Screen(id="settings")

# Input fields
language_select = inputs("English|Portuguese|Spanish", type="radio", id="lang_radio", screen=settings_screen)
notifications_toggle = inputs("Enable notifications", type="checkbox", id="notif_toggle", screen=settings_screen)

# Auto-load saved preferences when the app opens
saved_language = storage.get("language", "English")
if saved_language != "":
    language_select.set_value(saved_language)

saved_notifs = storage.get("notifications", "false")
if saved_notifs == "true":
    notifications_toggle.set_value("true")

def save_settings():
    storage.set("language", language_select.get_value())
    storage.set("notifications", notifications_toggle.get_value())
    toast("Settings saved! โš™๏ธ")

button("Save Settings", id="btn_settings", command=save_settings, screen=settings_screen)

run(start_screen=settings_screen)

[!IMPORTANT] The .get_value() and .set_value() methods work on virtually all interactive inputs โ€” text fields, checkboxes, radio button groups, sliders, and even labels. String comparisons with == or != work natively in both the Previewer (Python) and on the final Android device (Java).

[!TIP] Use storage.get("key", "default_value") to safely provide a fallback for first-time users who have no saved data yet. This prevents crashes and unexpected empty states.


12. Native Permissions & Features ๐Ÿ“ฑ

ApkPy allows you to interact directly with Android system features using a simple Python API.

Declaring Manifest Permissions

Declare what your app needs so the compiler can update AndroidManifest.xml automatically.

from apkpy_lib import declare_permissions
declare_permissions(["CAMERA", "LOCATION_FINE", "INTERNET"])

Runtime Permission Requests & Toasts

Prompt users for permissions and provide instant feedback with native Toasts.

from apkpy_lib import permissions, toast

def ask_camera():
    def on_result(granted):
        if granted:
            toast("Camera access granted! ๐Ÿ“ธ")
        else:
            toast("We need camera permission to continue.")

    permissions.request("CAMERA", on_response=on_result)

13. Master Example: "Coffee Haven" โ˜•

This is a complete, real-world multi-screen app built entirely with ApkPy. It demonstrates images, navigation, radio buttons, text inputs, storage persistence, toast notifications, and a fully custom CSS design all working together.

from apkpy_lib import Screen, button, label, inputs, image, run, toast, storage

# 1. Setup Screens
welcome_screen = Screen(id="welcome_container")
order_screen = Screen(id="order_container")

# 2. Logic: Handle Order
def place_order():
    # Save preferences to storage so the user's choices are remembered
    storage.set("coffee_select", coffee_select.get_value())
    storage.set("special_notes", special_notes.get_value())
    toast("Order placed! Your coffee will be ready soon. โ˜•")

# 3. Welcome Screen UI
image("logo.png", id="welcome_logo", screen=welcome_screen)
label("COFFEE HAVEN", id="welcome_title", screen=welcome_screen)
label("The best brew in town.", id="welcome_subtitle", screen=welcome_screen)
btn_start = button("EXPLORE MENU", id="btn_primary", screen=welcome_screen)

# Navigate to Order Screen on button click
welcome_screen.on_click_navigate(button=btn_start, to=order_screen)

# 4. Order Screen UI
label("SELECT YOUR COFFEE", id="menu_title", screen=order_screen)

# Radio buttons for coffee selection
coffee_select = inputs("Espresso|Latte|Cappuccino|Mocha", type="radio", id="coffee_select", screen=order_screen)

# Free-text for special instructions
special_notes = inputs("Special instructions (e.g., extra sugar)", type="text", id="special_notes", screen=order_screen)

# Auto-load saved preferences from last session!
saved_coffee = storage.get("coffee_select", "")
if saved_coffee != "":
    coffee_select.set_value(saved_coffee)

saved_notes = storage.get("special_notes", "")
if saved_notes != "":
    special_notes.set_value(saved_notes)

# Place Order button
button("PLACE ORDER", id="btn_order", command=place_order, screen=order_screen)

# 5. Full Custom CSS
style = """
welcome_container {
    background-color: #2D1E17;
    flex-direction: column;
    gap: 0px;
}

order_container {
    background-color: #FDF8F5;
    flex-direction: column;
    padding: 30px;
    gap: 20px;
}

welcome_logo {
    width: 180px;
    height: 180px;
    border-radius: 90px;
    margin-top: 100px;
    box-shadow: 0 10px 20px #000;
    animation-name: fadeInDown;
    animation-duration: 1000ms;
}

welcome_title {
    color: #FDF8F5;
    font-size: 32px;
    font-weight: bold;
    margin-top: 40px;
    animation-name: fadeInDown;
    animation-duration: 1200ms;
}

welcome_subtitle {
    color: #D4A373;
    font-size: 18px;
    margin-bottom: 60px;
    animation-name: fadeIn;
    animation-duration: 2000ms;
}

btn_primary {
    background-color: #D4A373;
    color: #2D1E17;
    border-radius: 30px;
    font-weight: bold;
    font-size: 18px;
    padding: 18px 45px;
    pressed-color: #B88B5B;
    animation-name: fadeInUp;
    animation-duration: 1000ms;
}

menu_title {
    color: #2D1E17;
    font-size: 24px;
    font-weight: bold;
    animation-name: fadeIn;
    animation-duration: 1000ms;
}

coffee_select {
    color: #3E2723;
    font-size: 18px;
}

special_notes {
    border-color: #D4A373;
    border-radius: 12px;
    padding: 20px;
    focus-border-color: #2D1E17;
}

btn_order {
    background-color: #2D1E17;
    color: #FDF8F5;
    border-radius: 15px;
    font-weight: bold;
    font-size: 18px;
    padding: 20px;
    pressed-color: #1A110D;
    margin-top: 30px;
    animation-name: zoomIn;
    animation-duration: 800ms;
}

@keyframes fadeInDown {
    from { opacity: 0; margin-top: -40px; }
    to   { opacity: 1; margin-top: 0px; }
}

@keyframes fadeInUp {
    from { opacity: 0; margin-top: 40px; }
    to   { opacity: 1; margin-top: 0px; }
}

@keyframes zoomIn {
    from { opacity: 0; scale: 0.8; }
    to   { opacity: 1; scale: 1.0; }
}

@keyframes fadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}
"""

if __name__ == "__main__":
    run(start_screen=welcome_screen)

14. Master Example: Mini Profile App ๐Ÿ‘ค

This example demonstrates inputs, permissions, toasts, and advanced CSS styling in a focused single-screen app.

from apkpy_lib import Screen, button, label, inputs, run, toast, declare_permissions, permissions

# 1. Declare permission for the compiler
declare_permissions(["CAMERA"])

# 2. Setup the Screen
profile_screen = Screen(id="profile_container")

# 3. Logic: Handle Camera Request
def update_photo():
    def on_perm(granted):
        if granted:
            toast("Thanks! Accessing camera for your photo...")
        else:
            toast("We need camera access to take a photo!")

    permissions.request("CAMERA", on_response=on_perm)

# 4. Build the UI
label("Mini Profile App", id="header", screen=profile_screen)
inputs("Enter your Full Name", type="text", id="name_field", screen=profile_screen)
inputs("Short Bio", type="text", id="bio_field", screen=profile_screen)

btn_photo = button("Set Profile Picture", id="btn_outline", command=update_photo, screen=profile_screen)
btn_save  = button("Save Profile", id="btn_primary", screen=profile_screen)

# 5. Advanced CSS System
style = """
profile_container {
    flex-direction: column;
    gap: 20px;
    background-color: #ffffff;
    padding: 30px;
}

header {
    color: #1a1a1a;
    font-size: 24px;
    font-weight: bold;
}

name_field, bio_field {
    border-color: #e0e0e0;
    border-radius: 12px;
    padding: 14px;
    focus-border-color: #6200EE;
}

btn_outline {
    background-color: #ffffff;
    color: #6200EE;
    border-color: #6200EE;
    border-width: 2px;
    border-radius: 20px;
    pressed-color: #f3e5f5;
}

btn_primary {
    background-color: #6200EE;
    color: white;
    border-radius: 20px;
    pressed-color: #3700B3;
}
"""

run(start_screen=profile_screen)

15. Declarative CSS Animations ๐ŸŽฌ

ApkPy supports native declarative animations using a syntax inspired by CSS Keyframes. You can define how a component should transition from one state to another, and the framework will generate the corresponding native Android Animation XML and Java logic.

How it Works: The @keyframes Block

An animation is defined using the @keyframes keyword followed by a name. Inside, you define two states:

  • from (or 0%): The starting state of the component when it appears.
  • to (or 100%): The final state where the component should end.
@keyframes slideUp {
  from {
      opacity: 0;
      margin-top: 50px;
  }
  to {
      opacity: 1;
      margin-top: 0px;
  }
}

Applying the Animation

my_button {
    animation-name: slideUp;
    animation-duration: 1500ms;
}

Supported Properties:

Property Description Example
opacity Fades the component in or out (0.0 is invisible, 1.0 is solid). opacity: 0; to opacity: 1;
margin-top Moves the component vertically (Y-axis). margin-top: 100px; to 0px;
margin-left Moves the component horizontally (X-axis). margin-left: -50px; to 0px;
scale Resizes the component (1.0 is normal size). scale: 0.5; to scale: 1.0;

Ready-to-Use Animation Presets:

1. Smooth Fade-In

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

2. Slide Down From Top

@keyframes fadeInDown {
    from { opacity: 0; margin-top: -40px; }
    to { opacity: 1; margin-top: 0px; }
}

3. Slide Up From Bottom

@keyframes fadeInUp {
    from { opacity: 0; margin-top: 40px; }
    to { opacity: 1; margin-top: 0px; }
}

4. The "Pop" / Zoom Effect

@keyframes zoomIn {
    from { scale: 0.5; opacity: 0; }
    to { scale: 1.0; opacity: 1; }
}

5. Side Slide From Left

@keyframes slideFromLeft {
    from { margin-left: -200px; opacity: 0; }
    to { margin-left: 0px; opacity: 1; }
}

[!IMPORTANT] Cross-Platform Support: These animations are fully functional in the Tkinter Previewer (Phase 1) so you can test the "vibe" of your app, and they compile to Native Android XML (Phase 2) for maximum performance on real devices.


16. SQLite Database ๐Ÿ—„๏ธ

ApkPy now supports native local databases powered by SQLite. Build offline-first apps that store structured data permanently on the device โ€” no internet required.

How It Works Under the Hood

Environment What db uses
Hot Previewer (your computer) Python's built-in sqlite3 module, saved as apkpy_app.db next to your script
Android Build (real device) Native android.database.sqlite.SQLiteDatabase โ€” the same engine used by Google apps

Your code is 100% identical in both environments.

Importing the Database

from apkpy_lib import db, json_get

The Full API

Method Description Example
db.execute(sql) Runs an SQL statement that modifies data db.execute("INSERT INTO users (name) VALUES ('Alice')")
db.query(sql) Runs a SELECT and returns a JSON string result = db.query("SELECT * FROM users")

Reading Data with json_get()

Since db.query() returns a JSON string, use json_get() to read values without complex parsing:

result = db.query("SELECT * FROM users ORDER BY id DESC")
first_name = json_get(result, "0.name")   # First row, "name" column
first_id   = json_get(result, "0.id")     # First row, "id" column

[!TIP] The dot-notation path works like: "<row_index>.<column_name>". So "0.name" means "the name column of the first row (index 0)".

Full Example โ€” Offline User Manager

from apkpy_lib import Screen, button, label, input_field, run, toast, db, json_get

# Create the table once on startup
db.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)")

my_screen = Screen(id="main_container")

nome_input  = input_field("Enter a name", id="input_nome", screen=my_screen)
lbl_total   = label("Total users: 0", id="lbl_total", screen=my_screen)
lbl_last    = label("Last added: --", id="lbl_ultimo", screen=my_screen)

def refresh_ui():
    rows   = db.query("SELECT * FROM users ORDER BY id DESC")
    count  = db.query("SELECT COUNT(*) as total FROM users")
    lbl_last.set_value(f"Last: {json_get(rows, '0.name')}")
    lbl_total.set_value(f"Total users: {json_get(count, '0.total')}")

def add_user():
    name = nome_input.get_value()
    if name != "":
        db.execute(f"INSERT INTO users (name) VALUES ('{name}')")
        toast("User added! โœ…")
        nome_input.set_value("")
        refresh_ui()

button("ADD USER", id="btn_add", command=add_user, screen=my_screen)
refresh_ui()

if __name__ == "__main__":
    run(start_screen=my_screen)

[!IMPORTANT] Always create your table with CREATE TABLE IF NOT EXISTS โ€” this is safe to run every time the app starts and won't overwrite existing data.


17. HTTPS & Network Requests ๐ŸŒ

ApkPy gives you a simple, non-blocking HTTP client that works identically in both environments. Connect your app to any REST API on the internet using https.get() and https.post().

How It Works Under the Hood

Environment What https uses
Hot Previewer (your computer) Python's urllib.request running in a background thread โ€” the UI never freezes
Android Build (real device) Native HttpURLConnection running in a Java background thread via AsyncTask pattern

Importing

from apkpy_lib import https, json_get

The Full API

Method Description
https.get(url, headers={}, on_response=callback) Makes a GET request asynchronously
https.post(url, data={}, headers={}, on_response=callback) Makes a POST request asynchronously

The on_response callback always receives two arguments:

  • success โ€” True if the HTTP status was 2xx, False otherwise.
  • response โ€” The response body as a plain String.

Basic GET Request

from apkpy_lib import Screen, button, label, run, toast, https, json_get

my_screen = Screen(id="main")
result_lbl = label("Press the button!", id="result", screen=my_screen)

def on_response(success, response):
    if success:
        title = json_get(response, "title")  # Reads the 'title' key from JSON
        result_lbl.set_value(title)
    else:
        toast("Request failed: " + response)

def fetch_data():
    result_lbl.set_value("Loading...")
    https.get("https://jsonplaceholder.typicode.com/todos/1", on_response=on_response)

button("Fetch Data", id="btn", command=fetch_data, screen=my_screen)

if __name__ == "__main__":
    run(start_screen=my_screen)

Using Headers (API Keys & Bearer Tokens)

The headers parameter lets you pass any HTTP headers as a Python dictionary. This is how you authenticate with APIs that require tokens or keys:

# Example: Calling an API protected by a Bearer token
def fetch_private_data():
    my_headers = {
        "Authorization": "Bearer YOUR_TOKEN_HERE",
        "Content-Type": "application/json"
    }
    https.get("https://api.example.com/profile", headers=my_headers, on_response=on_response)

POST Request with JSON Body

def send_data():
    payload = {"title": "My Post", "body": "Hello World", "userId": 1}
    headers = {"Content-Type": "application/json"}
    https.post(
        "https://jsonplaceholder.typicode.com/posts",
        data=payload,
        headers=headers,
        on_response=on_post_response
    )

Real-World Example โ€” Weather App

from apkpy_lib import Screen, button, label, inputs, run, toast, https, json_get

API_KEY = "your_openweathermap_api_key"

weather_screen = Screen(id="weather_screen")
city_input = inputs("Enter city...", type="text", id="city_input", screen=weather_screen)
temp_label  = label("-- ยฐC", id="temp", screen=weather_screen)
desc_label  = label("---", id="desc", screen=weather_screen)

def on_weather(success, response):
    if success:
        temp = json_get(response, "main.temp")
        desc = json_get(response, "weather.0.description")
        temp_label.set_value(f"{temp} ยฐC")
        desc_label.set_value(desc.capitalize())
    else:
        toast("Failed to get weather.")

def get_weather():
    city = city_input.get_value()
    if city != "":
        url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}&units=metric"
        https.get(url, on_response=on_weather)

button("GET WEATHER", id="btn_weather", command=get_weather, screen=weather_screen)

if __name__ == "__main__":
    run(start_screen=weather_screen)

[!IMPORTANT] The https API is always non-blocking. Requests always run in a background thread, so your UI will never freeze while waiting for a response. The on_response callback is always called safely back on the main UI thread.

[!TIP] Use json_get() to read specific fields from API responses without complex JSON parsing. The dot-notation supports nested objects ("main.temp") and list indices ("weather.0.description").


๐Ÿค Community & Support

Found a bug? Open an issue on Reddit!

Want to contribute? We are looking for contributors to expand our Native Component library! Join us in making Python a first-class citizen for Android development.


๐Ÿ“š Full Documentation

This README is the primary reference for ApkPy. It covers all currently available features, from UI components and CSS styling to SQLite databases, HTTPS requests, animations, and permissions.

Have questions or found a bug? Reach out on Reddit: u/idkaesd


Made with โค๏ธ for the Python Community.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apkpy-0.9.7.tar.gz (178.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apkpy-0.9.7-py3-none-any.whl (159.1 kB view details)

Uploaded Python 3

File details

Details for the file apkpy-0.9.7.tar.gz.

File metadata

  • Download URL: apkpy-0.9.7.tar.gz
  • Upload date:
  • Size: 178.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for apkpy-0.9.7.tar.gz
Algorithm Hash digest
SHA256 2eacc4bc31920c3cbe804854f89c537cedd90557a231e030b5e238804596f93d
MD5 00e5ca9b06c7ff559f5f51466a6d9200
BLAKE2b-256 dbec606faba662f32d57807f6936b3284cfcc9947e061e54d6bd178be3a88c76

See more details on using hashes here.

File details

Details for the file apkpy-0.9.7-py3-none-any.whl.

File metadata

  • Download URL: apkpy-0.9.7-py3-none-any.whl
  • Upload date:
  • Size: 159.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for apkpy-0.9.7-py3-none-any.whl
Algorithm Hash digest
SHA256 e6ab02820eeb1194bdd6ba638135044086041285229ac618009ca027afeab0c3
MD5 122ae4b9efc503452220b171854ec68b
BLAKE2b-256 ebe94ab849838b470b1e88dc3d6571f9e8211af9933cebe4455863fa97d1130a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page