Skip to content
juandguzCasadiego edited this page Sep 30, 2026 · 3 revisions

ParchApp Android Wiki

VIDEO DE FUNCIONAMIENTO

https://youtube.com/playlist?list=PLIambY0uc7Io&si=XtWiF_KQspTCbIws

Overview

ParchApp is a native Android application that helps university students plan activities together. A user can share their live availability, see when friends and roommates are free, compare the schedules of a whole group, join or propose group plans, and confirm activities with a group in a few taps.

This page documents the current state of the codebase after Sprint 2: what it is built with, how the architecture is organized, how the main flows work, how analytics answers our Business Questions, how the app connects to the shared backend, and how to build, run and test everything.

Tech Stack

  • Language: Java. There is no Kotlin anywhere in the project.
  • Platform: native Android SDK, no cross platform framework.
  • UI toolkit: XML layouts with AndroidX AppCompat and Material Components.
  • Minimum SDK: 24 (Android 7.0). Target SDK: 37.
  • Build system: Gradle with a version catalog (libs.versions.toml).
  • Backend services: Firebase Authentication, Cloud Firestore and Firebase Analytics, added through the Firebase BoM so the three libraries always use compatible versions.
  • Shared backend: the Backend repository, used by this app and by the Flutter app. It holds the Firestore security rules, the Cloud Functions and the BigQuery queries.

Two Modes: Firebase and Demo

The app runs in one of two modes, and it chooses on its own at startup.

  • Firebase mode. When the file app/google-services.json exists, Gradle applies the Google Services plugin, and the app uses Firebase Authentication, Firestore and Firebase Analytics. This file comes from the team's Firebase console and is never committed.
  • Demo mode. Without that file, the app still builds and runs. It uses in memory repositories filled with the same demo groups as the backend, accepts any sign in, and writes analytics events to Logcat under the tag ParchAnalytics instead of sending them to Firebase.

This way anyone on the team can clone the project and run it right away, and the screens never need to know which mode is active.

Architecture

MVC adapted to Android

  • View: the XML layouts. They contain no logic.
  • Controller: the Activities and bottom sheets. They react to taps, call the model and update the views.
  • Model: the domain classes, domain services and repositories.

Controllers never call Firebase directly. They only talk to repositories and services, which keeps each subsystem easy to replace and lets the team work on different screens at the same time. At system level the app follows a client server style: the app is the client, and Firebase is the server side.

Repository pattern for data access

Each kind of data has one interface: GroupRepository, ActivityRepository, AuthRepository and InsightsRepository. Each interface has two implementations, one for Firestore and one in memory for demo mode. Repositories creates them once at startup and hands them to the controllers.

Every result comes back through a Callback on the main thread, so the interface never waits for the network. FirestoreGroupRepository reads the local cache first and then the server. Offline persistence is turned on, so screens keep working without signal, and changes made offline are sent when the connection returns.

Strategy pattern for slot ranking

AvailabilityEngine intersects the busy blocks of every member of a group and finds the windows where members are free. It does not decide the order of those windows itself. It delegates that to a SlotRanker, and there are three:

  • OverlapRanking puts first the slots where the most members are free, then the longest ones.
  • PreferenceRanking puts first the slots that best match the preferred hours of the free members.
  • SoonestRanking puts first the earliest slots.

The Compare Availability sheet lets the user switch between them, and the engine receives the chosen one through setRanker(). A new criterion, for example one trained on the BQ3 results, only needs a new class. The calculation runs on a background thread with an ExecutorService, so the screen never freezes.

Observer pattern for real time group updates

DashboardActivity, GroupDetailActivity and CreateGroupActivity implement GroupListener. Each screen subscribes to a group in onStart() and unsubscribes in onStop(), which avoids memory leaks. When the group changes, the repository notifies every open screen, and each one updates its own part: the RSVP badge and progress bar, the common free time, or the list of pending invitations. With Firestore this happens through a snapshot listener, so a change made by one person appears on everyone's screen without reloading.

StatusService follows the same idea for the quick status on the dashboard (free until and next activity) and for the availability status chosen in the status sheet. It saves these values on the device, so they survive when the app is closed.

Singleton and Facade patterns for analytics

AnalyticsTracker has a single shared instance, so every event in the app uses the same format. Screens only call track() or trackFeature(), and the tracker hides everything else:

  • Events wait in EventQueue, which is saved on the device, so events logged without signal are not lost if the app closes.
  • ConnectivityMonitor tells the tracker when the phone is online.
  • Events are sent in batches: when 10 are waiting, when the connection comes back, or when the app goes to the background. This saves battery.
  • User IDs are replaced with a SHA256 hash before being sent, and no event includes calendar titles, places or attendees.

Project Structure

All source code lives under app/src/main/java/com/example/parchapp.

Screens (package root)

  • MainActivity: the Sign In screen and entry point. It signs in or creates an account through AuthRepository, validates the email and password, sends password reset emails, and shows "Continue as" when a previous session was restored.
  • DashboardActivity: the Home Dashboard hub.
  • GroupDetailActivity: the detail screen of a group, Roomies Main St by default.
  • CreateGroupActivity: the Propose Plan form, including invitations by email.
  • ActivityCreatedActivity: the confirmation screen after creating an activity.
  • AvailabilityActivity: legacy screen that nothing opens anymore (see Known Limitations).
  • ParchApplication: starts the repositories and the analytics tracker when the app launches, and restores the signed in user.
  • UserProfile and UserSession: the profile of the signed in user as shown in the interface.

Bottom sheets (package root)

  • QuickStatusSheet: edits the free until and next activity values of the dashboard.
  • AvailabilityStatusSheet: changes the availability status the user broadcasts.
  • CalendarSheet: shows the weekly calendar and synced free blocks.
  • PlanDetailsSheet: shows the details of a confirmed plan.
  • GroupDetailsSheet: a reusable summary for any group.
  • AllGroupsSheet: lists every group the user belongs to.
  • CompareAvailabilitySheet: calculates and shows the best common free slots of a group.
  • CreateActivitySheet: a short form to create an activity, opened from the dashboard, the group screen, or directly from a slot in Compare Availability.

domain

  • model: Group, Member, TimeBlock, TimeSlot and PlannedActivity. Times of day are stored as minutes since midnight.
  • availability: AvailabilityEngine, SlotRanker and the three rankings.
  • StatusService: the user's own availability status.

data

  • The four repository interfaces, their Firestore and in memory implementations, BaseGroupRepository (the shared observer bookkeeping), GroupListener, DemoData and Repositories.

analytics

  • AnalyticsTracker, AnalyticsEvents (every event and parameter name), EventQueue, ConnectivityMonitor, and the two destinations FirebaseAnalyticsSink and LogcatSink.

util

  • Callback and MainThread.

Resources live under app/src/main/res: layout for screen and sheet XML files, drawable for vector icons and shape backgrounds, values for colors.xml and strings.xml, and mipmap for the launcher icon.

Screens and Navigation

Sign In leads to the Home Dashboard, the central hub, with a bottom navigation bar (Home, Groups, Schedule) and a floating button to create a new activity. From the dashboard, most actions open a bottom sheet instead of a new screen, and the Roomies card opens the Group Detail screen. From Group Detail, Propose Plan opens the Create Group form, which leads to the Activity Created screen when it is submitted. That screen offers two exits: continue forward, or go back to the dashboard.

For a breakdown of each screen and the UI and UX concepts applied on it, see the Implemented Views wiki page.

How the Main Flows Work

Sign in. MainActivity sends the email and password to AuthRepository. In Firebase mode, Firebase Authentication checks them, keeps the session token and restores it on the next launch, so returning users only tap "Continue as". The app never stores passwords.

Compare availability and create an activity.

  1. The user opens Compare Availability from the dashboard or from a group.
  2. The sheet gets the group from GroupRepository and asks AvailabilityEngine for the common slots, using the chosen ranking.
  3. The engine runs on a background thread, measures how long the calculation took and logs it (BQ1).
  4. The sheet shows the best slot and up to two alternatives. The slot position that organisers accept most often is marked as Recommended; that position comes from InsightsRepository (BQ3).
  5. When the user schedules a slot, CreateActivitySheet opens with the time already filled in. When the activity is created, the app logs whether the suggested slot was kept exactly as proposed (BQ3) and opens the Activity Created screen.

RSVP. Tapping Going or Maybe on the dashboard calls GroupRepository.setRsvp(). The button does not change the view itself. The view updates when the repository notifies the change, which is the same path a change from another person follows.

Analytics and Business Questions

Each implemented Business Question has its own event. The names live in AnalyticsEvents.java, and the BigQuery queries depend on them, so they must not change without changing the queries.

  • BQ1, App Telemetry. Event availability_calc, logged by the engine with the group size, the calculation time in milliseconds, the number of slots and the ranking used. It answers how fast the engine is for groups of different sizes.
  • BQ3, Direct User Experience Improvement. Event slot_accepted, logged when an activity is created from a suggested slot, with the slot position and whether the organiser changed it. A nightly Cloud Function writes the answer to the Firestore document insights/bq3_slot_acceptance, and the app reads it to mark the Recommended slot. This closes the Type 2 feedback loop.
  • BQ8, Feature Analysis. Event feature_used, logged every time someone opens Groups, Compare Availability, Schedule, Invitations or Alerts, together with the screen it was opened from. It shows which coordination features are used least.

The full pipeline is: every event goes through AnalyticsTracker, waits in the local queue, is sent in batches to Firebase Analytics, is exported every day to BigQuery, and is aggregated by one query per Business Question. BQ1 and BQ8 are meant for the team dashboard in Looker Studio, and BQ3 goes back into the app through Firestore.

Backend

The backend is shared with the Flutter app and lives in its own Backend repository. It contains:

  • Firestore security rules. Only members of a group can read it. A member can only change their own RSVP and the pending invitations. Only the backend can create groups or change who belongs to them. A new activity must come from a member of its group, carry that person's own user ID and use the server time. The insights documents are read only for the apps.
  • Cloud Functions. refreshSlotRecommendation runs the BQ3 query every night and writes the recommended slot position. It keeps position 0 until at least 20 accepted slots exist, so a few events cannot change the recommendation. joinDemoGroupsOnSignUp adds every new account to the demo groups, so a new user can RSVP and create activities right away.
  • A seed script that writes the five demo groups (Roomies Main St, Engineering 2026, Campus Intramurals, Thesis Support Group and University Choir) with the same data as DemoData.java.
  • The BigQuery queries for BQ1, BQ3 and BQ8.
  • The data model document, which lists every Firestore field, who is allowed to write it, and examples in Java and Dart. Both apps must follow it.

Design System

Colors are centralized in colors.xml. The primary brand color is a deep blue (#003D9B), complemented by a success green, a purple, a rose and an amber accent used to color code group avatars, status badges and category icons.

Icons are custom vector drawables built for this project (roughly two dozen), since the original mockups referenced a web icon font that native Android cannot load directly. Reusable shape drawables cover the recurring visual patterns: rounded cards, selected and unselected chips, pill shaped buttons, circular avatars and progress bars.

Typography uses the system default sans serif font, with weight and size varied per component. No custom font family has been bundled yet.

Known Issue That Was Found and Fixed

While testing on an emulator, every button rendered in a generic purple instead of the brand blue, even though each one had its own background drawable in XML. The cause was that the app theme extends Theme.Material3, which makes Android replace every plain Button with a MaterialButton when the layout is inflated. MaterialButton ignores a manually set android:background and falls back to the Material 3 default color.

The fix, applied in every layout, was to declare buttons as androidx.appcompat.widget.AppCompatButton, which is not replaced and therefore respects the custom drawables. Toggle switches follow the same reasoning and are declared as androidx.appcompat.widget.SwitchCompat.

Building and Running

  1. Open the project root in Android Studio.
  2. Create a local.properties file whose sdk.dir points to your Android SDK. This file is excluded from version control.
  3. Optional: to use Firebase instead of demo mode, download google-services.json from the team's Firebase project and place it in the app folder. Never commit it.
  4. Build the debug APK with ./gradlew :app:assembleDebug, or press Run in Android Studio.
  5. Install it on a device or emulator by pressing Run with a device selected, or install the APK found in app/build/outputs/apk/debug.
  6. The app needs Android 7.0 (API 24) or newer.

In demo mode, the analytics events can be checked in the Logcat window of Android Studio by filtering by the tag ParchAnalytics.

To set up the backend (deploying the rules, seeding the demo groups, linking BigQuery and deploying the functions), follow the README of the Backend repository.

Testing

Automated tests in this repository. Run them with ./gradlew :app:testDebugUnitTest.

  • AvailabilityEngineTest checks the slot calculation and the three rankings.
  • AnalyticsTrackerTest checks the anonymisation of user IDs.
  • UserProfileTest checks how a profile is built from an account, for example turning maria.lopez@uni.edu into Maria Lopez.

Automated tests in the Backend repository. Unit tests for the Cloud Functions, and security rules tests that run the exact writes this app makes (RSVP, invitations, activity creation) against the Firestore emulator and check what outsiders and signed out users are denied.

Manual testing. The full flow was tested on an Android emulator: sign in, dashboard, RSVP, Compare Availability with each ranking, scheduling a slot, creating the activity and the confirmation screen. The analytics events were checked in Logcat.

Known Limitations and Next Steps

  • The app has not been connected to a real Firebase project yet, so the Firestore and Firebase Authentication code has only been tested through the emulators and demo mode.
  • The Looker Studio dashboard for BQ1 and BQ8 does not exist yet. It needs the BigQuery export and real events first.
  • Google sign in is not available yet; the button shows a message.
  • Some tactics from the architecture design are still pending: the Distance Matrix client with its 8 second timeout and fallback message, and adaptive sensor sampling in StatusService.
  • The app does not ask users for consent before collecting analytics, and there is no option to hide your availability from a group. Both should exist before a real launch.
  • New accounts join the groups with empty schedules, because calendar sync is not implemented.
  • FirestoreGroupRepository still tries to create a demo group when it is missing. The security rules now reject that write because the backend seeds the groups, so this code can be removed.
  • The folder analytics/sql is a copy of the queries that now live in the Backend repository and should be removed to avoid two versions.
  • AvailabilityActivity and its layout activity_availability.xml are legacy code replaced by AvailabilityStatusSheet. Nothing opens them, and they remain only until the team decides to delete them.