Inspiration
The forgetting curve starts the moment class ends — exactly when a student has the least time. This semester I have been running a desktop version of this loop on my own classes: it has turned 27 of my lectures across 5 courses into review pages since September. LectureLoop is that loop rebuilt as a phone app anyone can use in the minute between two classes.
What it does
- Capture: record in class (it keeps going with the screen off) or import/share any recording.
- Review card: the points the lecturer said will be tested — with the lecturer's own words and the second they said it (tap to hear it) — plus 3–5 key ideas, and homework with the deadline as spoken. It is not a summary: nothing the lecturer didn't say, and no invented exam hints.
- The loop: a 5-question quiz, one question per screen, comes back on day 1, 3 and 7. 4 of 5 moves the lecture on; a miss brings it back tomorrow.
- Semester Pass: 2 lectures a week are free; the Semester Pass (6 months — one term plus the break) or a monthly plan unlocks unlimited lectures.
How we built it
- Kotlin + Jetpack Compose, Clean Architecture enforced by Gradle modules:
domainandapplicationare plain Kotlin and cannot import Android, RevenueCat or HTTP — the same rules run on the phone and on the server. - Gemini, one call per 10-minute window, answering in a strict JSON schema with an
MM:SStimestamp on every item. The domain (ReviewCardRules) rejects cards with the wrong number of questions or choices, blank text or timestamps past the end; the app retries once with the exact violations as feedback. Failed attempts never use the free allowance. - Long lectures: we measured that one call over a 74-minute recording put some timestamps 2–6 minutes off and drew every question from the first 21 minutes. So long recordings are cut into 10-minute windows (on the phone with MediaExtractor → MediaMuxer, on the server with ffmpeg, no re-encoding), heard in parallel, shifted back onto the lecture's timeline and merged across it. Result: 74 minutes → a card in 14 s on the server, every item in the right sentence, questions from 05:00 to 1:10:41.
- RevenueCat:
- one entitlement,
pro— the app never checks product IDs; - current offering with
$rc_six_month(Semester Pass) and$rc_monthly; prices from the store; savings and weekly price computed in the domain only when currencies match; - the free allowance and the paywall headline come from offering metadata (
free_lectures_per_week,headline), so they can be tuned or A/B tested without a release; - a custom paywall that shows the student's own situation ("2/2 free lectures used · resets Monday"), reports
trackCustomPaywallImpression, and keeps the recording that hit the paywall and builds it right after the purchase; - restore, an
UpdatedCustomerInfoListener, and Customer Center for managing the plan; - server-side entitlement check: AI costs money per lecture, so in server mode the phone holds no AI key — the server asks RevenueCat (
GET /v1/subscribers/{app_user_id}) whetherprois active and enforces the weekly allowance before any AI call. If the server and the app disagree, the server wins and the paywall shows the server's numbers. - Test Store key in debug builds, Play key in release builds.
- one entitlement,
- Tests first: SPEC.md with 24 acceptance criteria; 90 tests (domain 40, application 25, adapters 10, server 13, app 2) plus live checks against the real API; CI on every push. Physical checks are logged in
docs/VERIFICATION.md.
Challenges we ran into
Timestamps. On a 2:28 lecture every timestamp was within 2 s of an independent Whisper transcript, but on a 74-minute recording a single call drifted by minutes. Measuring that, instead of trusting it, is what led to the windowed design. We also found the model giving a reading the homework's deadline; the prompt now ties each deadline to its own task, and later runs got both right.
Accomplishments that we're proud of
Every item on a card points to the second it came from, and the app refuses cards that break its rules — the student can trust it the night before an exam.
What we learned
RevenueCat is not only the paywall: entitlements and offering metadata let the business rules (who is Pro, how many free lectures) live outside the app binary, and the REST API lets the server enforce them where the AI key lives.
What's next
Sign-in or Play Integrity so a reinstall doesn't reset the free allowance; a database for server usage; Kotlin Multiplatform for iOS (domain and application are already plain Kotlin); a Play Store release.
Log in or sign up for Devpost to join the conversation.