Examples
One hundred and seven examples are included under examples/, organized by category. The launcher that multi-app boards boot into lives in system-apps/launcher/: built the same way, but linked into the firmware. See the launcher guide.
New to Picodroid? Start with the two guided tutorials below — they walk through building a real app step by step. The rest of the catalog is reference material to copy from.
Tutorials
Companion apps for the step-by-step Tutorials. Build them with the guide open alongside.
| Example | Class | Description |
|---|---|---|
tutorial_screens | tutorial_screens.TutorialScreensApp | A Home hub that pushes Counter and About screens — Activities, the back stack, lifecycle, and view preservation. Follow Multi-screen app. |
tutorial_service | tutorial_service.TutorialServiceApp | An uptime-logging Service that survives navigation, bound from a viewer screen for live snapshots. Follow Background service. |
Getting Started
Simple apps to verify your setup and get familiar with the build/flash workflow.
| Example | Class | Description |
|---|---|---|
helloworld | helloworld.HelloWorld | Prints “Hello, World!” via Log.i() |
blinky | blinky.LedBlink | Blinks the onboard LED on GP25 every 500 ms, and reads GP16 back as an input (Gpio.DIRECTION_IN / getValue()) |
Peripherals
Hardware interaction through the picodroid.pio.PeripheralManager API. Reference: Peripherals.
| Example | Class | Description |
|---|---|---|
uart | uart.UartEcho | Configures UART0 at 115200 baud and echoes received bytes |
i2cdemo | i2cdemo.I2cDemo | Scans the I2C0 bus (SDA=GP4, SCL=GP5) and logs the 7-bit address of every ACKing device |
spidemo | spidemo.SpiDemo | Full-duplex loopback over SPI0 (SCK=GP2, MOSI=GP3, MISO=GP0): sends 0x00-0x0F and logs received bytes |
adcdemo | adcdemo.AdcDemo | Opens the ADC on GP26 and takes 5 voltage readings, logging each value |
pwmdemo | pwmdemo.PwmDemo | Fades the onboard LED on GP25 using PWM at 1 kHz — duty cycle sweeps 0%->100%->0% three times |
tonedemo | tonedemo.ToneDemoActivity | Plays Android tones and a melody on the buzzer via ToneGenerator; requires an [audio] section in board.toml, and reports false on a board without one |
Filesystem and Preferences
On-device persistent storage via LittleFS (picodroid.io) and the DataStore-style key-value API (picodroid.content.SharedPreferences). Reference: Storage.
| Example | Class | Description |
|---|---|---|
bootcount | bootcount.BootCount | Persists a boot counter across reboots using picodroid.io.File / FileInputStream / FileOutputStream |
prefs_demo | prefsdemo.PrefsDemo | Stores typed key/value settings (String, int, long, float, boolean) via SharedPreferences.open() / edit().commit(), and walks the File path helpers (getName, getParent, getParentFile, mkdirs, createNewFile) |
filesdemo | filesdemo.FilesDemo | Conformance checks for the storage sandbox and the Context file API: every path the app names lives under its own directory, getFilesDir() is /files, openFileOutput / openFileInput / fileList / deleteFile round-trip, File.list works, and a path that climbs out or is too long is refused |
quotademo | quotademo.QuotaDemo | Conformance checks for the storage quota on multi-app boards: StatFs reports the volume and what the app may still write, 16 KB files are written until the board’s per-app cap refuses the next with IOException, a delete and a mkdir move the number by exactly what they cost, and StorageStatsManager agrees with the accounting. Build names the board |
Networking
TCP/UDP sockets via picodroid.net. On hardware these require a Pico 2 W (--board testbench_rp2350w); under the simulator they hit the host network stack. Reference: Networking.
| Example | Class | Description |
|---|---|---|
netdemo | netdemo.NetDemo | Checks NetworkInfo, opens a TCP Socket, sends “Hello” to a localhost echo server on port 7000, and logs the response. On hardware it first waits up to 30 s for WiFi join + DHCP — see WiFi & networking setup |
netexception | netexception.NetException | Asserts the typed network-exception taxonomy — ConnectException on a refused loopback port, SocketTimeoutException from ServerSocket.accept() and from a blocked read, and a dotted-quad InetAddress resolve. Fully local: no external server, network, or resolver needed |
connectivity | connectivity.Connectivity | ConnectivityManager with a NetworkCallback, the Android way to hear about the link instead of polling NetworkInfo.isConnected(), beside a BroadcastReceiver for CONNECTIVITY_ACTION / NETWORK_STATE_CHANGED_ACTION hearing the same edges: onAvailable / onCapabilitiesChanged for the link up at boot, onLost and a fresh Network when it drops and returns (net down / net up on the simulator’s control channel), and the register-twice / unregister-unknown / unsatisfiable-request contracts. Needs a board with a network (--board testbench_rp2350w) |
http_get | http_get.HttpGet | Android-style HttpURLConnection demo: performs a GET and a POST against a localhost HTTP/1.1 server, reading the response body through HttpInputStream. On hardware it waits up to 30 s for the network, and BASE_URL must point at an HTTP server reachable on your LAN — see WiFi & networking setup |
https_get | https_get.HttpsGet | The TLS client, and the nightly’s net row for it: HttpURLConnection GETs over https (the platform’s time service has set the clock by then) against the test host’s TLS listener (scripts/tls-listener.py), three handshakes that must be refused (an expired leaf, a chain under no known root, a certificate for another name), and two public hosts whose chains end at an ECDSA and an RSA root. Needs has_tls = true (every RP2350 WiFi board) |
Sensors
Environmental / hardware sensors exposed through the Android-compatible SensorManager. Reference: Sensors.
| Example | Class | Description |
|---|---|---|
sensordemo | sensordemo.SensorDemoActivity | Registers a SensorEventListener on the default ambient-temperature sensor (BME688) and logs each reading; requires a [[sensor]] entry in board.toml |
Concurrency
Executors and cross-thread dispatch. Reference: api/system.md → Executors.
| Example | Class | Description |
|---|---|---|
executordemo | executordemo.ExecutorDemoActivity | Posts Runnables via both Executors.mainExecutor() and Executors.backgroundExecutor(); verifies main-thread FIFO ordering and cross-pool dispatch |
Services
Background components with lifecycle independent of any Activity. Reference: Services & DI; for a guided build see the Background service tutorial.
| Example | Class | Description |
|---|---|---|
bootdemo | bootdemo.BootDemoApp | A manifest receiver for BOOT_COMPLETED: at boot the framework runs the app headless and the receiver arms a PendingIntent.getBroadcast alarm; four seconds after the launcher is up the alarm wakes the app to deliver it to a second receiver, which opens the Activity. The sim’s boot lane; needs a launcher |
alarmdemo | alarmdemo.AlarmDemoApp | Sets an AlarmManager alarm three seconds out, leaves for the launcher, and is started again by the framework to receive it in WokeActivity — the whole point of an alarm over a thread that sleeps. Falls back to waiting in place on a board with no launcher |
servicedemo | servicedemo.ServiceDemoApp | Drives a CounterService through the full Service v1 lifecycle in one non-UI run: two startService calls (one onCreate, two onStartCommand), bindService with a LocalBinder peek, unbindService, then stopService (triggers onDestroy and the foreground-notification cancel). Uses startForeground(id, Notification) |
Language Features
Demonstrate Java language features supported by the JVM interpreter. Reference: Core language.
| Example | Class | Description |
|---|---|---|
inherit | inherit.InheritDemo | Demonstrates class inheritance, field inheritance, method overriding, and super() |
interfacedemo | interfacedemo.InterfaceDemo | Demonstrates interface dispatch (invokeinterface) with Dog and Cat implementing Speakable |
defaultmethods | defaultmethods.DefaultMethodsDemo | Test-harness coverage of interface default methods: resolution without an override, sub-interface overrides (whatever the implements order), I.super.f() in a diamond, defaults reached through abstract and builtin superclasses, defaults calling abstract methods on this, interface static methods, and a user Iterable in a for-each loop |
floatdemo | floatdemo.FloatDemo | Demonstrates float, long, and double arithmetic and type conversions (f2i, i2l, i2d, etc.) |
exceptiondemo | exceptiondemo.ExceptionDemo | Demonstrates throw, try/catch, and custom exception classes |
threaddemo | threaddemo.ThreadDemo | Demonstrates spawning concurrent FreeRTOS tasks via picodroid.concurrent.Thread |
mainhog | mainhog.Main | Keeps the main queue non-empty for 600 ms while a child thread counts its sleeps: the JVM run lock must hand off to a waiting sibling even when the UI loop never blocks |
mathsdemo | mathsdemo.MathsDemo | Demonstrates integer/long/double arithmetic, bitwise/shift ops, cross-type conversions, tableswitch, instanceof, checkcast, reference arrays, and java.lang.Math |
stringdemo | stringdemo.StringDemo | Test-harness coverage of java.lang.String, StringBuilder, and String.format: predicates, search, transforms, valueOf, concat/replace/split/toCharArray/hashCode, "" + obj/append(Object)/valueOf(Object) through toString(), toUpperCase(Locale), plus exhaustive printf-style conversions, flags, widths, and precision; String.join and the java.util.Objects helpers |
enumdemo | enumdemo.EnumDemo | Demonstrates Java enum declarations, values(), name(), ordinal(), and switch over enums |
timedemo | timedemo.TimeDemo | Self-checking java.time: LocalDate/LocalTime/LocalDateTime arithmetic and ISO text, Instant, Duration, ZoneOffset and TimeZone.setDefault, DateTimeFormatter.ofPattern, Math.floorDiv/floorMod; logs === ALL PASSED ===. RP2350 boards only (the RP2040 image excludes java.time) |
trywithresourcesdemo | trywithresourcesdemo.TryWithResourcesDemo | Demonstrates try-with-resources (AutoCloseable) — opens an ADC pin in a try block and confirms close() is called on exit |
lambdademo | lambdademo.LambdaDemo | Demonstrates Java lambdas via invokedynamic: non-capturing, capturing, callbacks, and static method references |
anondemo | anondemo.AnonDemo | Demonstrates anonymous classes implementing interfaces, with local variable capture |
clinitdemo | clinitdemo.ClinitDemo | Demonstrates static class initializers (<clinit>): field initializers, static {} blocks, and cross-class chaining |
classlit | classlit.ClassLit | Class literals (T.class): demonstrates getName() and that repeated T.class evaluations return the same Class instance |
clonedemo | clonedemo.CloneDemo | Object.clone(): shallow-copy semantics (reference fields stay shared), identity, class preservation, and the canonical (T) super.clone() override inside a Cloneable class |
rttidemo | rttidemo.RttiDemo | Runtime type information: instanceof / checkcast over strings, arrays, collections under their interfaces and boxes under Number; transitive superinterfaces; a catchable ClassCastException; the boxed equals / hashCode / compare family; Comparable through Arrays.sort(Object[]); enum identity |
syncdemo | syncdemo.SyncDemo | Demonstrates synchronized blocks (monitorenter/monitorexit) and reentrant locking |
threadparity | threadparity.ThreadParity | Test-harness coverage of the java.lang.Thread API on picodroid.concurrent.Thread: synchronized methods under real contention, join, sleep/interrupt -> InterruptedException, currentThread().getName(), a second start() -> IllegalThreadStateException, Object.wait/notify producer-consumer, timed wait expiry, subclassed run(), and an uncaught exception routed to a setDefaultUncaughtExceptionHandler |
jucdemo | jucdemo.JucDemo | Test-harness coverage of the picodroid.concurrent java.util.concurrent subset: newFixedThreadPool/newSingleThreadExecutor, submit(Runnable)/submit(Callable) and Future.get/cancel/timeout, shutdown/shutdownNow/awaitTermination, contended AtomicInteger/AtomicLong/AtomicBoolean/AtomicReference, and CountDownLatch fan-in (excluded from testbench_rp2040 builds — see the compatibility matrix) |
jsondemo | jsondemo.JsonDemo | Test-harness coverage of picodroid.json (org.json parity): parsing the open-meteo weather reply, nesting, escapes and number typing, get/opt coercion, put/remove/accumulate/append, JSONObject.NULL, identity through child wrappers, arrays, toString/quote/numberToString/wrap, the error cases, and a GC-stress loop over the native node pool (needs has_json = true — every RP2350 board, not testbench_rp2040) |
protodemo | protodemo.ProtoDemo | Test-harness coverage of picodroid.protobuf and protoc-gen-picodroid: a message with every proto3 field kind round-tripped through toByteArray/parseFrom, unknown fields of every wire type skipped, truncated and malformed input throwing InvalidProtocolBufferException from the native codec, packed vs unpacked repeated fields, the streams and their size arithmetic on their own, and 300 parses (needs has_protobuf = true — every RP2350 board, not testbench_rp2040) |
collectionsdemo | collectionsdemo.CollectionsDemo | Test-harness coverage of java.util.*: ArrayList add/get/set/remove/contains/clear plus Integer/Boolean autoboxing, Arrays.sort/copyOf/fill/toString, Arrays.sort(Object[]) and Collections.sort/reverse over Comparable<T>, explicit Iterator and enhanced for-each over lists/maps, HashMap and HashSet, entrySet()/Map.Entry, the LinkedHashMap/LinkedHashSet aliases and toArray |
randomdemo | randomdemo.RandomDemo | Demonstrates java.util.Random — nextInt, nextLong, nextFloat, seeded reproducibility |
clockdemo | clockdemo.ClockDemo | Demonstrates System.currentTimeMillis() for boot-elapsed wallclock-style timing |
langsuite | langsuite.LangSuite | Aggregated language-feature test runner — exercises every JVM language feature in one APK |
bytecodecoverage | bytecodecoverage.BytecodeCoverage | JVM bytecode coverage harness — exercises long/double arrays, multianewarray, wide, goto_w, and stack-manipulation opcodes |
Kotlin
Kotlin apps compile to the same bytecode and run on the same JVM; a small
kotlin-shim supplies the stdlib entry points the compiler emits. What is and
is not supported is tabulated in the Android compatibility matrix.
| Example | Class | Description |
|---|---|---|
hellokt | hellokt.HelloKt | The smallest Kotlin app: a string template, one !! (the Intrinsics.checkNotNull the shim serves), and a SAM lambda for a Java interface |
langsuite_kt | langsuitekt.LangSuiteKt | Kotlin language suite — data classes, sealed classes and when, extension functions, default and named arguments, varargs, null safety, scope functions, lazy/Pair, objects and companions, interface defaults, lambdas, type checks, exceptions, synchronized |
langsuite_kt_stdlib | langsuitektstdlib.LangSuiteKtStdlib | Kotlin stdlib suite — collections, maps, sets, arrays, ranges, sorting, strings, and math over the shim |
injectdemo_kt | injectdemokt.InjectDemoKtApp | The Kotlin twin of injectdemo: the same compile-time DI graph through kapt — @Inject lateinit var fields, constructor and method injection, Provider<T> / picodroid.di.Lazy<T>, a @Module object with @JvmStatic @Provides and an instance @Module class — across an Application, two Activities, and a Service |
gcstress_kt | gcstresskt.GcStressKt | GC stress through the churn Kotlin codegen mints: per-iteration lambda proxies, Ref$IntRef boxes for captured locals, autoboxing through generic collections, Pair destructuring, string templates, and map entry-view iteration; asserts identity-hashCode slot stability and lambda-capture rooting across collections |
picoenvmon_kt | picoenvmonkt.EnvApp | The Kotlin twin of picoenvmon — same screens, Service, RGB LED, and dashboard HTTP server, same @Inject/@Module object graph, written under the class-metadata frugality rules of the Kotlin guide |
Graphics and Display
Full graphical UI with touch input, demonstrating the Activity lifecycle and LVGL widget system. Reference: Graphics & UI; see also Embedded gotchas for the UI pitfalls these apps avoid.
| Example | Class | Description |
|---|---|---|
displaydemo | displaydemo.DisplayDemoApp | Showcases the full widget set on a 320x240 display: LinearLayout, ScrollView, TextView, Button, ToggleButton, Switch, CheckBox, SeekBar, Spinner, EditText, touch input, event handlers, a moving-average FPS overlay (DisplayDebug.showFps()), and a themed-widgets section using a custom Theme palette with GradientDrawable (gradient header, surface card, pill / ghost buttons) |
keydemo | keydemo.KeyDemoActivity | Hardware-button demo: a focusable Button’s OnKeyListener takes DPAD_CENTER and the Activity’s onKeyDown / onKeyLongPress / onKeyUp take the rest — DPAD_UP has a short and a long action (startTracking / isCanceled), DPAD_DOWN shows auto-repeat (getRepeatCount); requires [[button]] entries in board.toml |
keynav | keynav.KeyNavActivity | Every stock value widget driven by the four keys: a SeekBar, a Spinner and a TimePicker edited with SELECT then UP/DOWN, a ScrollView of text scrolled by UP/DOWN, a list dialog walked and picked, and a clickable TextView the keys reach because a click listener makes a view focusable on a keypad board. Each change is logged, so the sim’s control channel can drive it. See Button-only navigation |
menudemo | menudemo.MenuDemoActivity | The options menu as the write-once action surface: three actions declared once in onCreateOptionsMenu, reached by holding SELECT on a four-key board, through the menu control the framework draws bottom-right on a touch board, and by a MENU key anywhere; onPrepareOptionsMenu and onOptionsMenuClosed counted and logged. See the options menu |
powerdemo | powerdemo.PowerDemoActivity | Display power: the panel dozes on KEYCODE_SLEEP, wakes on a tap that lands as no click, toggles on KEYCODE_POWER, dozes on the idle timeout the app shortens through Settings.System.SCREEN_OFF_TIMEOUT, and stays lit under View.setKeepScreenOn(true). One button fills the window; each click is numbered in the log |
touchprobe | touchprobe.TouchProbeActivity | A bench probe for the touch calibration: one full-screen view that logs every touch-down in panel pixels (TouchProbe: down x,y) and shows it, so a finger on the four corners and the centre says whether a board’s [touch] cal_* table maps the panel’s edges to the screen’s |
callbacktest | callbacktest.CallbackTestActivity | Regression harness for widget callback dispatch under both shrink modes — registers a lambda listener on every widget type and synthetically fires its event |
dialogdemo | dialogdemo.DialogDemoApp | Toast.makeText().show() and AlertDialog.Builder with positive / negative listeners; demonstrates onBackPressed() confirmation pattern |
gesturedemo | gesturedemo.GestureDemoApp | GestureDetector with onSingleTap / onLongPress / onFling listeners on a single View |
dragdemo | dragdemo.DragDemoActivity | Touch-driven drag using FrameLayout + OnTouchListener; tracks MotionEvent.ACTION_DOWN/MOVE/UP and updates a tile’s absolute position via setPosition(). The only example using FrameLayout for absolute placement (a LinearLayout would re-flow on every layout pass) |
animdemo | animdemo.AnimDemoApp | view.animate().alpha(…).x(…).rotation(…).scaleX(…).setStartDelay(…).withEndAction(…).start() — to-only property animations (the start value is read from the view, as on Android), easing interpolators, chained end actions, and the View transform getters |
keyboarddemo | keyboarddemo.KeyboardDemoApp | Soft keyboard: both system-on-touch (default) and explicit Keyboard instances bound to EditText. Includes the soft-keyboard polish pass: slide-up animation, OnEditorActionListener for the Done key, dismiss-on-outside-tap |
navdemo | navdemo.NavDemoApp | Multi-Activity back-stack — startActivity() push, finish() pop, lifecycle callbacks (onPause / onStop / onResume) |
bundledemo | bundledemo.BundleDemoApp | Bundle conformance, Intent.putExtras() / getExtras(), and saved instance state across Activity.recreate() — onSaveInstanceState → onCreate(Bundle) → onRestoreInstanceState |
reclaimdemo | reclaimdemo.ReclaimDemoApp | Reclaimed Activities: run with PICODROID_DONT_KEEP_ACTIVITIES=1 and every covered Activity is destroyed with its state saved, then re-created from the Bundle when uncovered — including a startActivityForResult caller that gets its result as a new instance |
fragmentdemo | fragmentdemo.MainActivity | Fragment conformance: the callback order against the host’s, replace with a back stack, hide / show, detach / attach, a fragment without a view, the transactions the manager refuses, BACK popping the back stack instead of finishing, and — under PICODROID_DONT_KEEP_ACTIVITIES=1 — the host reclaimed and re-created with its fragments, arguments, saved state and back stack restored through a FragmentFactory |
pagerdemo | pagerdemo.PagerDemoActivity | Three fragments in a <ViewPager2> from XML with a dots row on OnPageChangeCallback: a smooth turn (SETTLING, the old page destroyed, IDLE), instant turns, a page’s saved state coming back with it, then recreate() and a covering Activity, after each of which the pager is back on the same page with every page’s state |
injectdemo | injectdemo.InjectDemoApp | Compile-time DI end to end: @Inject constructor / field / method injection, @Singleton identity across an Application, two Activities and a Service (all injected automatically before onCreate), an unscoped class per injection site, a leaf Activity that only inherits its @Inject members, Foo_Factory.get() from plain code, Provider<T> / Lazy<T> wrappers, a @Module providing an interface and a @Singleton value, and coexistence with a hand-written ApplicationComponent |
imagedemo | imagedemo.ImageDemoApp | ImageView.setImageSource("name.png") resolving against the PAPK ASSETS section (v1.1 bundled images). Demonstrates setScaleType / setTint / setScale. See Bundled image assets |
resdemo | resdemo.ResDemoApp | The resource system end to end: res/values through getResources(), setContentView(R.layout.activity_main), findViewById, LayoutInflater.inflate into a parent, @drawable images, and the laid-out geometry of match_parent / layout_weight. Checks itself and logs ResDemo PASS. See Resources, R and XML layouts |
layoutdemo | layoutdemo.LayoutDemoActivity | What an Android page layout is written with, checked end to end: layout_margin in a LinearLayout, layout_gravity plus margins in a FrameLayout (from XML and from code), a <shape> background, <include>, a style and the theme (?attr/…, setTheme), a view class of the app’s own made by Activity.onCreateView and sized by onMeasure, and the same layout inflated a slice per tick by AsyncLayoutInflater; with them the setters that do nothing when nothing changes, setBackgroundTintList, getString with arguments, runOnUiThread and ComponentName. Logs === ALL PASSED ===. RP2350 boards (the custom view draws itself). See Resources, R and XML layouts |
pickerdemo | pickerdemo.PickerDemoApp | DatePicker (lv_calendar binding) and TimePicker (lv_roller binding) with 12-hour / AM-PM mode and value-changed listeners |
ellipsizedemo | ellipsizedemo.EllipsizeDemoApp | Conformance for TextView.setSingleLine / setEllipsize / setMaxLines: a long text in a 96 px label is one line tall with an ellipsis while getText() still returns the whole text, a max-lines label is that many lines tall, and lifting the limit restores the wrapped height. Logs === ALL PASSED === or one FAIL line per check |
snackbardemo | snackbardemo.SnackbarDemoApp | Snackbar.make().setAction().show() — toast with a clickable action lozenge, auto-dismiss, click-through-to-listener |
swipedemo | swipedemo.SwipeDemoApp | OnSwipeListener (UP / DOWN / LEFT / RIGHT direction constants) on a single view; SwipeRefreshLayout pull-to-refresh container |
calculator | calculator.CalculatorActivity | Four-function touch calculator built for the 320x480 pico_touch_kit panel: a 4x5 key grid of nested LinearLayout rows, Gravity.BOTTOM/Gravity.RIGHT to right-align the two display lines, GradientDrawable key faces and flat layout backgrounds, and an arithmetic model (CalculatorEngine) kept free of any framework call. Every box is derived from the Display size, so it fills any panel |
Performance and Testing
Benchmarks and stress tests for the JVM runtime and allocator. Reference: System & concurrency (Runtime.gcCount(), Runtime.gcTimeNanos()); for the numbers these probe, see Limits & memory budgets and JVM tunables.
| Example | Class | Description |
|---|---|---|
benchmark | benchmark.Benchmark | JVM performance benchmark: times int/long/float/double arithmetic, method dispatch, interface dispatch, object allocation, array ops, string ops, and control flow; logs per-category and total elapsed time |
perfbench | perfbench.PerfBench | Unified speed + memory benchmark — rolls execution-time and heap-usage measurements into a single composite SCORE for tracking runtime regressions |
graphicsbench | graphicsbench.GraphicsBench | LVGL render-pipeline benchmark — exercises the draw/refresh path across several test cases and reports a composite SCORE |
gcstress | gcstress.GcStress | GC stress test: exercises the mark-sweep collector under object churn, linked chains, circular references, string churn, and array churn; reports cycle count, freed entries, and GC time via picodroid.os.Runtime |
heapstress | heapstress.HeapStress | Allocation/fragmentation stress test exercising the array arena allocator and emergency-GC path |
tracedemo | tracedemo.TraceDemo | Stack-trace showcase — a three-deep uncaught RuntimeException prints at tracedemo.TraceDemo.deepest(TraceDemo.java:41) in the sim and in debug firmware; release firmware prints (pc=N), which scripts/retrace.sh --app tracedemo resolves on the host |
bugbash | bugbash.BugBash | Pure-logic regression checks for the 2026-08-30 bug bash — each check names the defect id it pins, proving through real bytecode what the unit tests guard in Rust |
bugbash_ui | bugbashui.BugBashUiApp | Lifecycle half of the bug-bash regression app — self-driving Activity/Service walk covering finish() idempotence, service bind limits, and pending-op delivery |
executorstress | executorstress.ExecutorStress | GC-rooting stress for Runnables in flight in the executor queues: posts lambdas whose only reference is the queued executor word, then forces collections before the queue drains |
threadstress | threadstress.ThreadStress | Concurrent-allocation stress for the compound-heap atomic sections — three child threads plus the main task churn the shared heap; run under --mem-diag with offensive checks armed |
postloop | postloop.Main | Hop-timing probe for the main executor: a fresh child Thread per hop sleeps 80 ms and posts the next hop, every tenth hop commits a SharedPreferences write, and each hop logs its per-stage millisecond costs (start, up, slept, post, deliver) so a slow hop names the stage that ate the time |
qa_lang | qa_lang.QaLang | QA suite, language and bytecode semantics: numeric conversions and overflow, shift masking, switch forms, evaluation order, try/finally ordering and the catchable exception family. A section that throws is reported and the run continues |
qa_io | qa_io.QaIo | QA suite, the java.io stream hierarchy: the byte-array streams, the base-class defaults an app’s own InputStream / OutputStream inherits, BufferedReader.readLine across every line ending and past the buffer, writers, and the file streams through their java.io supertypes, try-with-resources and instanceof |
qa_oop | qa_oop.QaOop | QA suite, object model: construction order, field shadowing, static hiding, virtual dispatch from constructors, inner / anonymous / local classes, lambdas and method references, enums with bodies, static initialisation order, generics bridges, varargs, and the equals / hashCode / toString / compareTo upcalls the collections rely on |
qa_coll | qa_coll.QaColl | QA suite, java.util / java.lang under load and at the edges: identity hash codes across a compacting GC, HashMap growth and views, ArrayList growth, boxed keys of different types, string and String.format corner cases, interleaved StringBuilders, wrapper parsing and printing, and java.util.Random’s exact stream |
qa_thr | qa_thr.QaThr | QA suite, threads: start / join / isAlive, interrupt and the flag, wait / notify hand-offs and timeouts, monitor exclusion on instance and class locks, uncaught-exception handlers, latches, atomics, the ExecutorService / Future contract, and clock and sleep accuracy |
qa_store | qa_store.QaStore | QA suite, storage: File predicates and renames, stream offsets and EOF, partial reads, truncation, directory listing, refused paths, the Context private-file helpers, SharedPreferences types, limits and editor semantics, StatFs. Run it twice against the same volume to check persistence across a reboot |
qa_bcast | qa_bcast.QaBcastApp | QA suite, broadcasts: the registry’s rules, in-app sendBroadcast to registered and explicit receivers, a receiver unregistered mid-round, one that throws, the leak reap at an Activity’s destroy, TIME_CHANGED → TIME_TICK from a clock set half a second before the minute, TIMEZONE_CHANGED, SCREEN_OFF / SCREEN_ON from a shortened timeout, and two PendingIntent.getBroadcast alarms. Multi-app boards |
qa_life | qa_life.QaLifeApp | QA suite, lifecycle: Intent extras, an Activity started for a result, and a Service walked through unbind / rebind / stop / restart one step per frame |
qa_ui | qa_ui.QaUiApp | QA suite, the view tree and widget state machine driven without a finger: child order after add / remove, visibility and enabled state, listener replacement, compound buttons and radio groups, seek / progress clamping, adapters, focus hand-off, then sizes, animation end actions, dialogs, toasts, snackbars and a view churn under GC |
Feature Showcase
End-to-end apps that combine multiple subsystems. These are the closest reference for the layout and structure of a “real” picodroid app.
| Example | Class | Description |
|---|---|---|
picoenvmon | picoenvmon.EnvApp | Environmental monitor for the Pimoroni Enviro+ Pack (on the Pico 2 W variant: a web dashboard, an NTP-anchored clock and an open-meteo weather row fetched over HTTPS). Multi-Activity (HomeActivity, settings, history, network) with a sub-package layout (ui/, service/, net/, hardware/, data/, util/); customizes the global Theme palette in Application.onCreate(); runs a SensorLoggerService ring-buffering BME688 + LTR559 readings; drives an RGB LED. Wired with @Inject / @Singleton throughout — app-scoped ThresholdConfig, Formatter, LatestReadings, RgbLed, NetworkManager, plus SharedPreferences from an @Provides method in EnvModule, injected into every Activity and the Service |
picoclock | picoclock.ClockApp | Clock and alarms for the 320x480 pico_touch_kit panel. Five screens over a shared BaseActivity (clock face, alarm list, editor, set-time, full-screen ring); an AlarmService whose 4 Hz thread is the whole app’s heartbeat, so no screen owns a timer; a seven-segment face built from plain views, a few per UI tick, because there is no text-size API; alarms in SharedPreferences and the wall clock set by hand, the board having no RTC; the carrier’s buzzer on GP13 through Pwm. The date and schedule arithmetic is pure and self-checked at startup |
claudeusage | claudeusage.ui.MainActivity | A desk display for Claude usage limits on the 320x240, four-button pico_display2_w: 5-hour session, weekly and per-model caps, burn rate and token history, fetched as protobuf over plain HTTP from a small bridge on the LAN (bridge/claude_usage_bridge.py, with a --demo mode for the simulator). One Activity declared in the manifest; the whole UI in res/: the chrome and every page are XML layouts (layout_weight, FrameLayout margins, <include>, styles), cards and dots are <shape> drawables, colours come from the theme (setTheme, ?attr/…); four screens as Fragments in a <ViewPager2> with a dots row driven by OnPageChangeCallback, each page inflated over several UI ticks by AsyncLayoutInflater and fading in, the status screen a fragment laid over the pager; the numerals through android:textSize; the two limits as ring gauges and the charts as custom views named in the layouts, made by Activity.onCreateView, sized by onMeasure and drawn in onDraw(Canvas), the per-model meters as severity-coloured, tinted ProgressBars; the data flow of an Android app: a started Service with a poll thread and a ConnectivityManager.NetworkCallback fills a repository, a ViewModel turns that and the clock into one immutable UI state published through LiveData only when it would paint differently, and the Activity and the pages observe it; every failure (PC off, bridge down, login expired, stale data, a window resetting while offline) has its own presentation |
askclaude | askclaude.MainActivity | Ask Claude from a Pico, the HTTPS showcase: A/B pick a prompt, X POSTs it to the Claude Messages API (HttpsURLConnection, setFixedLengthStreamingMode, request headers, getErrorStream()), the reply fills the screen with the round trip and token usage on the status line; on the Enviro+ board the prompt carries the room readings. The API key and model are BuildConfig constants (picodroidBuildConfig, Android’s buildConfigField); the nightly points the endpoint at the test listener’s canned /v1/messages |
weather | weather.MainActivity | A weather app in the shape of the phone ones, for the four-button pico_enviro_mon_w: the place, the temperature in the 64 px face with the condition glyph beside it, high and low, the next hours in a strip; a page of the week with a range bar per day; a details page (feels-like, humidity, wind, pressure, sunrise/sunset, and the room’s own readings from the Enviro+ sensors). One open-meteo request over HTTPS every 15 minutes (the platform’s time service sets the clock the certificate check needs), parsed with picodroid.json; the glyphs, the hourly strip and the seven-day list are View.onDraw(Canvas) drawings, one view each; the sky is a GradientDrawable that follows the conditions. Place and units are BuildConfig constants; the nightly runs it against the test listener’s canned /v1/forecast |
Running an Example
./scripts/build.sh --app <name>./scripts/flash.sh --app <name>Or test on the host simulator without hardware:
./scripts/sim.sh --app <name>See Build & flash for full build and flash options.