SYS.ONLINE // NOTHING OS 3.0 // BUILD 2410
OPENROUTER WIDGET
PROJECT OPENROUTER • REMOTE_VIEWS ENGINE

ONE GLANCE.
ZERO STEPS.

:: NATIVE NOTHING OS 3.0 HOME SCREEN WIDGET

Checking your OpenRouter balance used to mean unlocking your device, opening a browser tab, logging in, and navigating to Settings → Credits. Three redundant steps for a number that belongs on your home screen.

Engineered with classic Android RemoteViews and hardware-backed EncryptedSharedPreferences. Pure AMOLED black, VT323 dot-matrix typography, and zero third-party telemetry.

45 MIN Sync Interval
<0.02% Battery / Day
AES-256 Keystore GCM
0 KB Cloud Data
Tested on Nothing Phone (1), (2), (2a) • Android 8.0+ (API 26+)
09:41
98%
MILANO • 18°C
TUE 24 OCT
ACCOUNT SALDO
$14.82
CREDITO RESIDUO • SUFFICIENT
SYNC JUST NOW • THR <$2.00
LIVE ENGINE DEDUCT
01 // INTERACTIVE SANDBOX

VIRTUAL NOTHING PHONE (2a) CONTAINER

Inspect the widget inside an authentic Nothing OS 3.0 environment. Switch widget formats (2×1, 2×2, 4×2), simulate token burn across top LLMs, test inverted alert logic, and monitor the Glyph LED strip.

SANDBOX READY
09:41
5G
NOTHING OS 3.0
REMOTE_VIEWS
ACCOUNT SALDO
$14.82
BALANCE SUFFICIENT
SYNC JUST NOW THR: <$2.00
Shell
Keystore
Claude
Router

Tap the widget in the phone frame to open simulated MainActivity

Widget Size & Layout

[ REMOTE_VIEWS RESIZABLE ]
Display Currency:

Real-Time Token Burn Engine

Trigger live deductions against simulated OpenRouter models to observe state transitions.

Simulate Alert Breach:
ENGINE READY. WAITING FOR API PROMPTS...
$0.0000 BURNED

MainActivity In-App Settings

ENCRYPTED AT REST

Account Mode monitors remaining credits (total_credits − total_usage). Red indicator dot triggers when balance falls below threshold.

$2.00 USD
$0.50 $5.00 $10.00
02 // ARCHITECTURAL PILLARS

ENGINEERED FOR STABILITY, NOT HYPE.

Every technical decision was made deliberately. No unnecessary Jetpack Compose overhead, zero third-party telemetry, and absolute hardware-level security.

DECISION 01 • ENGINE ROCK SOLID

Classic RemoteViews, Not Jetpack Glance

Google heavily evangelizes Jetpack Glance for modern Compose-driven widgets. However, in AGP 8.5 and Kotlin 1.9+, the Glance compiler plugin generates unpredictable crashes on OEM launchers—specifically across Nothing OS 2.5 and 3.0.

Classic RemoteViews and standard XML layouts have been refined across 15 years of Android releases. They boot with 0ms compiler penalty, never freeze launcher threads, and guarantee pixel-perfect Nothing OS rendering.

XML LAYOUT INFLATION ✓ 100% OEM COMPATIBILITY
SECURITY • STORAGE

Hardware Keystore AES-256 GCM

Your OpenRouter API token grants direct financial access. We persist it using EncryptedSharedPreferences backed by the Android Keystore.

Keys are sealed inside your device's Trusted Execution Environment (TEE) or StrongBox chip. Even a rooted device cannot extract keys in plaintext.

ENCRYPTION AES-256-GCM-SIV
BACKGROUND • SYNC

Constraint-Aware WorkManager

Rather than draining battery with high-frequency alarms or persistent foreground services, background refreshes run through WorkManager every 45 minutes.

Refreshes require NetworkType.CONNECTED constraints and honor Android Doze mode, consuming < 0.02% battery over a 24-hour cycle.

BATTERY IMPACT < 0.02% / 24H
NETWORK • PRIVACY

Zero Proxies. Zero Middlemen.

The widget uses OkHttp to speak directly with openrouter.ai/api/v1 over TLS 1.3.

There are no intermediary proxy servers, no Google Analytics, no crashlytics beacons, and no proprietary backend. Your API key never touches any server besides OpenRouter.

TELEMETRY 0 TRACKERS
LOGIC • ADAPTIVE

Inverted Threshold Semantics

Depending on whether you monitor total account balance or single key usage, alert semantics automatically invert:

  • Account Mode: Alert when balance < threshold
  • Key Mode: Alert when usage > threshold
ALERT EVALUATION KOTLIN WHEN-EXPR
03 // ARCHITECTURE & SOURCE

FOUR STAGES. ZERO REDUNDANCY.

Native Android execution pipeline, from Keystore secret validation to launcher RemoteViews dispatch.

STAGE 01

MainActivity

User enters API key. Validates via /credits. Stores AES-256 encrypted preferences in Keystore.

TRIGGER: USER CONFIG
STAGE 02

RefreshWorker

WorkManager periodic job fires every 45 min on active Wi-Fi/Cellular. OkHttp fetches fresh balances asynchronously.

TRIGGER: WORKMANAGER / TAP
STAGE 03

WidgetUpdater

Evaluates mode + threshold. Formats VT323 dot-matrix string. Wires PendingIntents for tap-to-configure and manual sync.

TRIGGER: WORKER COMPLETE
STAGE 04

Launcher Surface

AppWidgetManager receives RemoteViews bundle and renders directly onto Nothing Launcher grid without waking app process.

RENDER: REMOTEVIEWS XML
// File: app/src/main/java/com/nothingwidget/openrouter/widget/WidgetUpdater.kt
package com.nothingwidget.openrouter.widget

import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.widget.RemoteViews
import com.nothingwidget.openrouter.R
import com.nothingwidget.openrouter.data.Prefs

object WidgetUpdater {

    fun update(context: Context, mgr: AppWidgetManager, id: Int) {
        val prefs = Prefs(context)
        val views = RemoteViews(context.packageName, R.layout.widget_openrouter)

        val balance = prefs.cachedBalance
        val label = prefs.cachedLabel ?: "OPENROUTER"
        val threshold = prefs.threshold
        val mode = prefs.displayMode // "ACCOUNT" or "KEY"

        // Inverted alert semantics
        val isAlert = when (mode) {
            "ACCOUNT" -> balance < threshold // Alert when balance is low
            "KEY"     -> balance > threshold // Alert when single-key usage exceeds quota
            else      -> false
        }

        // Bind formatted VT323 dot-matrix balance & alert indicator
        views.setTextViewText(R.id.txtBalance, String.format("$%.2f", balance))
        views.setImageViewResource(
            R.id.imgStatusDot,
            if (isAlert) R.drawable.dot_red else R.drawable.dot_white
        )

        // PendingIntent: Tap widget opens MainActivity
        val clickIntent = Intent(context, MainActivity::class.java)
        val pi = PendingIntent.getActivity(context, 0, clickIntent, PendingIntent.FLAG_IMMUTABLE)
        views.setOnClickPendingIntent(R.id.widgetRoot, pi)

        mgr.updateAppWidget(id, views)
    }
}
KOTLIN 1.9.24 • TARGET SDK 34 View full repository on GitHub

• Architectural Tradeoff Evaluation

Architectural Dilemma Selected Implementation Rejected Alternative Technical Justification
Widget UI Framework RemoteViews (XML) Jetpack Glance Glance compiler produces runtime crashes on Nothing OS 3.0 / AGP 8.5. RemoteViews provides 100% OEM stability.
Authentication Flow Manual API Key Paste OAuth 2.0 PKCE OpenRouter OAuth rejects custom redirect schemes (openrouterwidget://). Manual key works unconditionally.
Secret Persistence EncryptedSharedPreferences Jetpack DataStore DataStore lacks an official Keystore-backed encryption module. Keystore AES-256 SIV is strictly required.
Background Scheduler WorkManager (45m) AlarmManager / Service OEM skins throttle background intervals below 30m. 45m guarantees reliable execution with < 0.02% battery drain.
04 // HARDWARE GALLERY

CAPTURED ON NOTHING PHONE (2a)

Real hardware captures running Nothing OS 3.0 (Android 14). Authentic dot-matrix typography and color-coded alert thresholds.

OpenRouter Widget Configuration Screen
Inspect
FIGURE 01

Configuration Screen

Paste API key and test connection

Post Setup Screen
Inspect
FIGURE 02

Post-Setup Preferences

Live balance, threshold & modes

Account Balance Widget on Nothing OS Home Screen
Inspect
FIGURE 03

Account Balance (2×2)

Healthy credit state on Nothing OS

Single Key Usage Widget with Red Alert Indicator
Inspect
FIGURE 04

Single Key Alert (2×2)

Over-budget state with Nothing Red LED

05 // QUICK START

GET IT ON YOUR PHONE

Two hassle-free paths: download the pre-compiled debug APK generated automatically by GitHub Actions CI, or compile locally with Gradle.

METHOD 01 • RECOMMENDED PRE-BUILT APK

Download Pre-Built APK

Clean, verifiable debug APK compiled directly from source on every git push via GitHub Actions clean Ubuntu runners. No Android Studio required.

SIZE: ~4.2 MB SIGNED: DEBUG KEY
METHOD 02 • DEVELOPER GRADLE 8.7

Build From Source

Clone the repository and compile using JDK 17 and Android SDK 34 command line tools or Android Studio Iguana/Jellyfish.

# Bash / PowerShell
git clone https://github.com/mirconegri/openrouter-widget.git
cd openrouter-widget
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
KOTLIN: 1.9.24 MIN_SDK: 26 (ANDROID 8.0)

• First-Time Launch Checklist

STEP 01

Sideload APK

Transfer app-debug.apk to your device. Tap Install and enable Install unknown apps if prompted.

PERMISSIONS: INTERNET ONLY
STEP 02

Paste API Key

Open OpenRouter Widget. Paste your sk-or-v1-… key. Tap VERIFICA E SALVA to encrypt into Keystore.

KEY STORED: HARDWARE TEE
STEP 03

Place Widget

Return to home screen. Long-press empty space → Widgets → OpenRouter. Place and resize to 2×1, 2×2, or 4×2.

AUTO-SYNC: 45 MINUTES
06 // HONEST ENGINEERING

WHAT IT DOES NOT DO — ON PURPOSE

Deliberate scope boundaries, upstream OpenRouter API constraints, and battery protection trade-offs.

No Workspace Mode on Personal Accounts

OpenRouter restricts its /workspaces and provisioning endpoints exclusively to Enterprise and Business tiers. On standard individual accounts, that API key does not exist.

No OAuth Web Flow

OpenRouter's OAuth 2.0 implementation refuses custom URI schemes (openrouterwidget://) as valid redirect targets. A complete PKCE flow was built and dead-ends at the browser step. Manual key paste is 100% reliable.

No Background Sync Below 30 Minutes

Although WorkManager allows a 15-minute periodic schedule, modern Android OS and Nothing OS power management throttles jobs under 30 minutes when idle. The 45-minute interval reliably runs without silent system cancellation. Manual refresh is always instantaneous.

Global Shared Config Per App

Multiple widget instances can be added to your home screen, but they currently share the same active configuration. Per-widget independent key binding is actively being developed for v2.1.

07 // ROADMAP

Planned For Future Releases

01
Per-Widget Display Override Allow one widget on page 1 to show Account Balance while another on page 2 tracks a specific developer API key.
02
Rolling Sparkline Buffer Store the last 7 days of balance points in SQLite to draw native vector sparklines on 4×2 widget formats.
03
Nothing Glyph LED Broadcaster Trigger physical Glyph LED pulses on budget depletion via reverse-engineered Nothing Phone intent broadcasters.
Action performed