← Back

Stupid Small: beat procrastination by going stupidly small

An AI-powered Android app that breaks scary tasks into stupidly small steps you'll actually finish. Kotlin + Jetpack Compose, Material 3, on-device persistence, AI-assisted breakdowns.

Stupid Small app icon

Launch screen AI breakdown Focus timer Stats


Why Stupid Small exists

Most "productivity" apps are good at cataloguing tasks. They're not good at making you start them. You end up with a tagged, sorted backlog you still won't open, because the tasks themselves are still too big: writing a thesis, cleaning the garage, filing taxes. The problem isn't organisation. It's that step one is invisible.

Stupid Small flips that. Instead of writing down "Write thesis," you ask the AI to break it apart. It hands back five tiny steps:

  1. Open the doc and re-read the last paragraph (2 min)
  2. Write three bullets for the next section (3 min)
  3. Turn the first bullet into a sentence (3 min)
  4. Take a 5-minute break, you earned it
  5. Turn the second bullet into a sentence (3 min)

That first step is always doable.


Features

Core loop

Built around finishing

Polish

Privacy


Tech stack

Layer Technology
Language Kotlin 2.0.21
UI Jetpack Compose, Material 3
Compose BOM 2024.09.03
DI Hilt 2.52
Architecture Single-activity, HiltViewModel + StateFlow + mutableStateOf
Persistence Room 2.6.1 (KSP-generated)
Background work WorkManager 2.9.1 (daily reminder scheduling)
Home-screen widget Glance 1.1.1
Networking OkHttp 4.12 (against a Cloudflare Worker proxy)
Billing Google Play Billing v7.1.1
AI Llama 3.3 70B via Groq, brokered by a Cloudflare Worker
Build AGP 8.7.0, Gradle KTS, R8 minification + resource shrinking
Min / Target SDK 26 / 35

Architecture overview

app/src/main/java/com/stupidsmall/app/
├── MainActivity.kt
├── StupidSmallApp.kt          @HiltAndroidApp + WorkManager bootstrap
├── billing/
│   └── BillingManager.kt      Play Billing v7 wrapper (subscription product, ack, restore)
├── data/
│   ├── ai/
│   │   └── GeminiApiClient.kt OkHttp client → Cloudflare Worker → Groq
│   ├── db/
│   │   ├── AppDatabase.kt     Room DB with TypeConverters
│   │   ├── TaskDao.kt
│   │   ├── MicroStepDao.kt
│   │   └── StreakDao.kt
│   ├── model/                 @Entity data classes (Task, MicroStep, Streak)
│   ├── repository/            Single source of truth for DB access
│   └── TaskTemplates.kt       Built-in starter templates for empty-state demo
├── di/                        @Module / @InstallIn for Room, OkHttp, repos
├── notification/
│   ├── FocusModeManager.kt    NotificationManager.setInterruptionFilter()
│   ├── NotificationHelper.kt  Channel creation + builders
│   ├── ReminderManager.kt     Schedules daily WorkManager job at user's chosen time
│   └── ReminderWorker.kt      Posts the daily nudge
├── premium/
│   └── PremiumManager.kt      SharedPreferences-backed entitlement flag
├── ui/
│   ├── Navigation.kt          NavHost + animated transitions per route
│   ├── theme/                 5 ColorScheme palettes × light + dark
│   ├── tasklist/              All-tasks screen, drag-to-reorder, swipe-to-delete
│   ├── addtask/               Two-step flow: title → AI suggestions
│   ├── focus/                 One-step-at-a-time timer screen
│   ├── celebration/           Confetti particle engine + post-step screen
│   ├── stats/                 Lifetime + streak metrics
│   ├── coach/                 In-app coach (FAQs, tips)
│   ├── settings/              Theme picker, reminders sheet, premium sheet
│   ├── onboarding/            3-page first-run flow
│   └── components/            Shared dialogs + sheets
├── viewmodel/                 6 HiltViewModels (TaskList, AddTask, Focus,
│                              Celebration, Stats, Coach)
├── widget/
│   ├── TaskWidget.kt          Glance composable for the home-screen widget
│   └── TaskWidgetReceiver.kt  GlanceAppWidgetReceiver
└── util/

Notable design decisions


Build

Prerequisites

Configure secrets

The app reads several values from local.properties (gitignored). Copy local.properties.example if present, otherwise create local.properties at the project root with:

sdk.dir=/path/to/Android/sdk

# AI proxy — point at your own Cloudflare Worker (or any compatible endpoint)
PROXY_URL=https://your-proxy.workers.dev
PROXY_APP_SECRET=...
LLM_MODEL=openai/gpt-oss-120b:free

# Optional: legacy direct keys (kept for fallback testing)
GEMINI_API_KEY=
OPENROUTER_API_KEY=

Without PROXY_URL, AI breakdowns fail loudly with a quota or network error. That's by design. You're not meant to ship a build with no AI backend.

Debug build

export JAVA_HOME=/opt/homebrew/opt/openjdk@17
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk

Release build

You'll need your own keystore. Generate once:

keytool -genkeypair -v \
  -keystore app/release.jks \
  -alias stupidsmall \
  -keyalg RSA -keysize 2048 \
  -validity 10000

Add to local.properties:

RELEASE_STORE_FILE=release.jks
RELEASE_STORE_PASSWORD=...
RELEASE_KEY_ALIAS=stupidsmall
RELEASE_KEY_PASSWORD=...

Build:

./gradlew bundleRelease       # AAB for Play Store
./gradlew assembleRelease     # APK for sideloading

R8 minification and resource shrinking are enabled in release. Without RELEASE_STORE_FILE the signing config silently no-ops (intentional: keeps debug builds working when you clone without a keystore).

AI proxy

The Cloudflare Worker source lives in proxy/worker.js. Deploy with:

cd proxy
wrangler deploy
wrangler secret put GROQ_API_KEY
wrangler secret put APP_SECRET    # must match PROXY_APP_SECRET in local.properties

The worker:


Privacy

The full privacy policy is hosted at https://stupidsmall-privacy.pages.dev/privacy.html.

Short version:


Roadmap

Ideas under consideration for v1.x:


Built by

Made by @rjcb-commits. If Stupid Small helps you finish something you'd been avoiding, that's the goal.