← Back to projects

LabFlow

Frontend Design Lead · Four-Person Android Team

A native Android app that guides students through lab checkpoints, tracks their progress live, and lets TAs generate a whole lab from its manual with AI, backed by Spring Boot and MySQL. I led the frontend design and implementation, from the visual system to integration-ready screens.

1st in section · 2nd overall of 40+ teams
Timeline
Jan 2026 – May 2026
Role
Frontend Design Lead
Team
2 frontend · 2 backend
Impact
Up to 60% lab time reduction

Overview

LabFlow turns a lab handout into a guided sequence of checkpoints on Android. Students work through each checkpoint and answer its questions, and their progress is saved as they go. TAs watch progress across a section and answer questions, and admins manage users, courses and approvals.

We built it as a four-person team for Iowa State's COM S 309 (Spring 2026): two frontend and two backend developers. The app is a native Android client in Java that talks to a Spring Boot REST and WebSocket backend backed by MySQL.

AI Lab Constructor: a full lab from the lab manual

TAs don't have to build a lab one checkpoint at a time. On the Create Lab screen, they switch on Use AI Lab Constructor, upload the existing lab manual as a PDF and tap Create with AI. The backend reads the manual and creates the lab's whole checkpoint sequence: titles, instructions, time estimates, and graded questions with answer keys. The TA then lands on the finished checkpoint list to review it and publish.

1 PDF
One uploaded manual becomes every checkpoint in the lab
5
Question types the generator can produce, with answer keys
2 calls
From the app: upload the PDF, then trigger generation

The TA flow, end to end

  1. Create the lab

    The TA enters the lab name and deadline. In the admin flow, a lab section is created as well. With the AI constructor on, the "number of steps" field is hidden, because the manual decides how many checkpoints there are.

  2. Upload the lab manual

    The TA picks the PDF on the device. The app reads its page count and uploads it as multipart form data to POST /labs/pdf/upload/{labId}.

  3. Generate the checkpoints

    The app calls POST /labs/openai/{labId}/checkpoint-creation. The backend extracts the manual's text with PDFBox (sorted by position on the page), cleans up the whitespace, and sends it to OpenAI's Responses API with a strict parser prompt.

  4. Validate and save

    The backend pulls the JSON array of checkpoints out of the reply, stripping code fences and matching brackets, and validates each entry. Every checkpoint needs a title. Answerable checkpoints need a question type and description, and choice-style questions need options and correct answers. Checkpoints are then numbered 1…n, the lab's step count is set, and everything is saved.

  5. Review and publish

    A "Lab Created" dialog takes the TA to the lab's checkpoint list ("Tap a checkpoint to edit or delete"). There they can adjust any generated checkpoint, add more, and publish the lab to students.

What the manual becomes

Generated fieldWhat it holdsWhere it's used
titleThe checkpoint's name (required)Checkpoint lists for students and TAs
descriptionThe instructions for that step, taken from the manualThe student's checkpoint view
estimatedMinutesExpected time for the stepTA step timers and "Step taking too long" alerts
answerable + questionA question, only when the manual actually asks one: multiple choice, multi-select, fill-in-the-blank, short answer or image uploadStudent answers saved to their progress
options + correctAnswerIndexAnswer choices and the answer key, required for the choice-based typesChecking student answers

Grounded in the manual

The prompt tells the model to use only the manual's text, to ignore any instructions inside that text, not to add missing information, and to return a single JSON array. It may only attach a question when a real one exists in the manual, and it must write units in plain ASCII (for example μ → u, Ω → Ohm).

Safe failure

Generation is refused if the lab already has checkpoints or if no manual has been uploaded. If the upload or generation fails, the lab is still created and the TA is sent to its checkpoint list with an explanation, so they can build it by hand. Labs can also start from a saved template instead.

The service calls OpenAI's Responses API with the model set in code (gpt-5.4-mini). The API key comes from the backend config or the OPENAI_API_KEY environment variable. Without a key, the endpoint returns a clear "not configured" error.

My role: frontend design lead

I led frontend design and implementation, covering the visual system, the reusable screens and navigation, and integration-ready interfaces for the backend.

  • Created the app's visual system. It uses an Iowa State cardinal accent (#C8102E), lab and course cards with progress bars, and a persistent Home / Labs / Help / Profile bottom navigation.
  • Wrote the design-document specs for the student Lab List and Lab Instructions screens.
    • Lab List: a summary panel (active labs, average progress, labs due soon) above tappable lab cards with completion bars.
    • Lab Instructions: a checkpoint-by-checkpoint view with progress, Previous/Next, Submit, Help Request and Save / Mark Complete.
  • Built reusable screens and navigation, and wired them to the backend through the shared Retrofit API layer.
  • Worked across both frontend and backend, and helped resolve major integration issues before the final demo.
My Labs (Lab List) screen spec, from my section of the design document.
Lab Detail spec, marking which components were already on screen and which were still planned.

What the app does

These features are in the team's final codebase. The work was shared between the frontend and backend developers, so this section describes the product as a whole and does not list only my parts.

Guided checkpoints

Each lab is split into checkpoints, and each checkpoint can carry questions. The backend defines five question types: multiple choice, multi-select, fill-in-the-blank, short answer and image upload. Students answer them in the app.

Live progress tracking

Per-student progress is saved through REST, and every checkpoint completion or submission is broadcast over a STOMP WebSocket topic. TA dashboards, the student-progress screen and the course roster update as it happens (details below). Submissions can be downloaded as PDFs.

Lab authoring

TAs can generate an entire lab from its manual with the AI Lab Constructor (see above). They can also create and edit labs and checkpoints by hand, start from lab templates, and clone labs across courses.

Equipment in 3D

Checkpoints can link 3D models of lab equipment. The app opens them in an in-app viewer built on a WebView with <model-viewer>.

Help & communication

Students can post doubt threads (questions with images) and use course chat that runs over WebSockets. The app also includes office-hours booking with student and TA views and a student calendar.

Accounts & roles

The app has separate Student, TA and Admin dashboards. Sign-in uses a one-time-code MFA step, and TA signups and course changes go through approval requests. Notifications arrive by Firebase push and over WebSocket.

Real-time layer: WebSockets

WebSockets carry everything in LabFlow that has to show up without a refresh: checkpoint progress, course chat and notifications. The Spring Boot backend runs a STOMP message broker. It registers a /ws endpoint with a SockJS fallback, uses a simple broker on /topic and /queue, and sends client messages to /app.

The Android app connects to /ws/websocket with a small STOMP client the team wrote on top of plain WebSockets (OkHttp, and Java-WebSocket for notifications). It builds the CONNECT, SUBSCRIBE and SEND frames by hand and buffers incoming text until each frame's terminating null byte arrives.

3
STOMP topics the Android app subscribes to
5
Progress event types the backend publishes
2 s
Delay before the progress and chat sockets reconnect

Channels the app uses

ChannelDirectionWhat updates liveWhere it shows up
/topic/labs/progressServer → appOne event per progress change. Each carries the lab and student IDs, a completion percentage, a submitted flag, the event type, and the student's current checkpoint step with its start time and paused time.TA dashboard, TA student-progress screen, course roster
/topic/chats/courses/{courseId}
/app/chats/courses/{courseId}/send
Both waysCourse chat. History loads once over REST, then new messages are sent and received as STOMP frames. Messages are de-duplicated by ID so nothing appears twice.Course chat screen
/topic/notifications/{userId}Server → appPer-user notifications: lab deadline reminders, deadline changes, lab releases, new doubt threads for TAs, approval requests for admins, and approval outcomes.Student home alert card; system notifications on TA and admin dashboards

How a progress update travels

Student completes or submits→ PUT /labs/progress/edit/… or POST …/submit→ Progress saved (JPA)→ convertAndSend /topic/labs/progress→ TA screens update

The backend publishes CHECKPOINT_STARTED, CHECKPOINT_PAUSED, CHECKPOINT_COMPLETED, LAB_SUBMITTED and LAB_UNSUBMITTED. The app's checkpoint-answer and submit calls trigger the completed and submitted events.

What TAs see live

On the TA dashboard, each pinned lab's average completion is recalculated as student events arrive. On the student-progress screen, the affected student's row refreshes and per-step timers count elapsed time minus pauses. When TA alerts are on, a student who runs past a step's expected time triggers an alert ("Step taking too long"). The course roster flips students between Not started, In progress and Submitted.

What students see live

Notifications go to each user's own topic, so the home screen's alert card updates as they arrive. A backend scheduler checks every minute and sends 24-hour and 1-hour deadline reminders. Each notification is also saved to the database and sent as a Firebase push for when the app isn't open.

Socket as signal, REST as truth

On the roster and TA progress screens, a progress event tells the app which student changed. The app then fetches that student's full record over REST before redrawing, so the screen always matches the database.

Connection lifecycle

Sockets are tied to screens. They connect when a screen opens and close when it pauses or stops. The progress socket is one shared connection that several screens register with, and it closes when the last screen leaves. After an unexpected drop, the progress and chat sockets reconnect after 2 seconds. A chat message sent while the socket is still connecting shows "Chat is still connecting" instead of being lost.

Why it matters

  • During a live lab, a TA can see who is falling behind on which step while it is happening, without refreshing.
  • Chat and notifications arrive as pushed messages, so the app doesn't have to poll the server.
  • Broadcasting one progress topic lets several TA screens show the same change from a single event.

The backend also publishes /topic/labs/submissions, direct-chat topics (/topic/chats/{conversationId}) and doubt-thread topics (/topic/doubts/threads/{threadId}), which the Android app doesn't subscribe to. The repo also includes a browser-based SockJS + STOMP.js page for testing the backend's sockets.

Architecture

Activities + XML layouts→ ApiService (Retrofit)→ RetrofitClient (OkHttp + Gson)→ Spring Boot REST / STOMP→ MySQL (JPA)
37
Activities in the Android manifest
103
REST endpoints declared in ApiService
3
Live WebSocket channels: lab progress, course chat and notifications

Android client

The client is written in Java with Material Components and RecyclerView adapters for lab and checkpoint lists, and uses Glide for images. A singleton RetrofitClient is created at app start by LabFlowApp; it sets the base URL, timeouts, interceptors and Gson. The app targets min SDK 24 and target SDK 34.

Spring Boot backend

The backend is built on Spring Boot 3.2 and Java 17, using Spring Data JPA, Security, Mail and WebSocket (STOMP). It exposes controllers for auth, users, courses, labs, checkpoints, progress, chat, office hours and notifications, and documents them with springdoc OpenAPI.

Team block diagram: the Android UI, helper and API layers, the Spring Boot REST and real-time services, and the MySQL schema.

The GitLab CI pipeline runs Maven build, test and deploy stages for the backend, plus Gradle build and test stages for the Android app.

Results

Up to 60%
Lab time reduction measured across three lab types
1st
In section
2nd
Overall among 40+ teams

Tech

Frontend
AndroidJavaMaterial ComponentsRetrofitOkHttpGsonGlideOkHttp WebSocketJava-WebSocketFirebase Cloud Messaging
Backend
Spring BootSpring Data JPASpring SecurityWebSocket / STOMPSockJSMySQLspringdoc OpenAPIPDFBoxOpenAI Responses API
Tooling
GitLab CIGradleMaven