Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/PunctuOwlity/llms.txt

Use this file to discover all available pages before exploring further.

The Android application is the original implementation of PunctuOwlity. It was built first, and the static web app was later created to reproduce the same interface and workflow in a browser. The Android source lives in the android-fixed-source/ directory of the repository and contains a standard Gradle project targeting the com.example.punctuowlityeventtracker package. All application logic is written in Java; the project also declares Kotlin and Compose dependencies in build.gradle but the five screen-level classes are plain Java Activity subclasses backed by XML layouts.

Directory Structure

The key paths within the Android project are:
android-fixed-source/
└── PunctuOwlityEventTracker/
    ├── app/
    │   ├── build.gradle
    │   ├── proguard-rules.pro
    │   └── src/
    │       └── main/
    │           ├── AndroidManifest.xml
    │           ├── java/com/example/punctuowlityeventtracker/
    │           │   ├── LoginActivity.java
    │           │   ├── SignupActivity.java
    │           │   ├── SmsPermissionActivity.java
    │           │   ├── AddEventActivity.java
    │           │   ├── MainActivity.java
    │           │   ├── DatabaseHelper.java
    │           │   └── Event.java
    │           └── res/
    │               ├── colors/          (toolbar_coral.xml, date_banner_teal.xml, …)
    │               ├── drawable/        (vector icons: ic_alarm_on, ic_edit_outline, …)
    │               ├── font/            (136 Google Font XML references)
    │               ├── layout/          (activity_*.xml, event_card.xml)
    │               ├── mipmap-*/        (launcher icon densities)
    │               └── values/

Activity Classes

The application declares five activities in AndroidManifest.xml. LoginActivity carries the LAUNCHER intent filter and is the entry point; all other activities are exported="false".

LoginActivity — Launcher

The first screen the user sees. Reads the username and password fields, then calls DatabaseHelper.checkUser(username, password). On success it starts MainActivity and calls finish() so the user cannot navigate back to the login screen. The “Sign Up” button starts SignupActivity without finishing the current activity.

SignupActivity

Collects an email address (used as the username), a password, and a confirmation password. If the two password fields do not match, a toast is shown and no record is written. On a successful call to DatabaseHelper.insertUser(), the activity shows a success toast, starts LoginActivity, and calls finish().

SmsPermissionActivity

Presents two buttons: Allow SMS Notifications and No, thanks. The allow button calls ActivityCompat.requestPermissions() for Manifest.permission.SEND_SMS if the permission has not already been granted. Both paths ultimately call goToMainActivity(), which starts MainActivity and finishes the current activity. The result of the permission dialog is handled in onRequestPermissionsResult(), which also calls goToMainActivity() regardless of whether the user granted or denied the permission.

AddEventActivity

Serves as both the add and edit form. On onCreate(), the activity checks getIntent().hasExtra("event_id"). If the extra is present, it retrieves the integer ID, loads the corresponding record with DatabaseHelper.getEventById(eventId), and pre-fills the title, date, and time fields. The save button calls db.insertEvent() for new records or db.updateEvent(eventId, …) for existing ones, shows a toast, and calls finish() to return to MainActivity. A back ImageButton also calls finish().

MainActivity

The main events screen. Sets up a DatabaseHelper instance and a reference to the GridLayout with id eventsGrid, then calls loadEvents(). The FAB (fabAddEvent) starts AddEventActivity with a plain intent (no extras) for the add-new-event flow. MainActivity also checks whether SEND_SMS permission has been granted; if not, it starts SmsPermissionActivity. onResume() calls loadEvents() again so the grid refreshes after returning from AddEventActivity.

DatabaseHelper

DatabaseHelper extends SQLiteOpenHelper with database name punctuowlity.db and version 1. It exposes the following public methods used by the activities:
MethodUsed byDescription
insertUser(username, password)SignupActivityInserts a new row into users; returns false if the username already exists (UNIQUE constraint)
checkUser(username, password)LoginActivityQueries users for a matching username + password pair; returns true if found
insertEvent(title, date, time)AddEventActivityInserts a new row into events
updateEvent(id, title, date, time)AddEventActivityUpdates an existing events row by primary key
deleteEvent(id)MainActivityDeletes an events row by primary key
getAllEvents()MainActivityReturns all rows as an ArrayList<Event> via SELECT * FROM events
getEventById(id)AddEventActivityReturns a single Event for pre-filling the edit form, or null if not found

Build Configuration

The project is configured in app/build.gradle with the following settings:
SettingValue
namespacecom.example.punctuowlityeventtracker
compileSdk35
minSdk21
targetSdk35
versionCode1
versionName"1.0"
sourceCompatibilityJavaVersion.VERSION_11
targetCompatibilityJavaVersion.VERSION_11

Key Dependencies

implementation libs.appcompat
implementation libs.material
implementation libs.activity
implementation libs.constraintlayout
implementation libs.gridlayout
implementation libs.legacy.support.v4
implementation libs.lifecycle.runtime.ktx
implementation libs.activity.compose
implementation platform(libs.compose.bom)
implementation libs.ui
implementation libs.ui.graphics
implementation libs.ui.tooling.preview
implementation libs.material3
The gridlayout dependency provides the GridLayout widget used to render event cards in MainActivity. The Compose BOM and related libraries are declared but the current screen-level classes use traditional XML layouts, not Compose UI.

Permissions

AndroidManifest.xml declares one permission:
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-feature android:name="android.hardware.telephony" android:required="false" />
uses-feature is set to required="false" so the app is not filtered out of the Play Store for devices without telephony hardware (e.g. tablets). The runtime permission request is handled in SmsPermissionActivity.

Loading Events in MainActivity

The loadEvents() method is the core rendering loop for the events grid. It clears the GridLayout, fetches all records from SQLite, inflates a CardView for each one, binds the data fields, and attaches edit and delete listeners.
private void loadEvents() {
    eventsGrid.removeAllViews();
    ArrayList<Event> events = db.getAllEvents();

    for (Event event : events) {
        CardView card = (CardView) getLayoutInflater().inflate(
            R.layout.event_card, eventsGrid, false);

        TextView textDay   = card.findViewById(R.id.tempTextDay);
        TextView textDate  = card.findViewById(R.id.tempTextDate);
        TextView eventTitle = card.findViewById(R.id.tempEventTitle);
        TextView textTime  = card.findViewById(R.id.tempTextTime);
        ImageButton buttonEdit   = card.findViewById(R.id.tempButtonEdit);
        ImageButton buttonDelete = card.findViewById(R.id.tempButtonDelete);

        textDay.setText(event.getDayOfWeek());  // e.g. "SAT", "MON"
        textDate.setText(event.getDateDay());   // e.g. "05", "12"
        eventTitle.setText(event.getTitle());   // e.g. "Project Two Due"
        textTime.setText(event.getTime());      // e.g. "ALL DAY", "01:30PM"

        buttonEdit.setOnClickListener(v -> {
            Intent intent = new Intent(MainActivity.this, AddEventActivity.class);
            intent.putExtra("event_id", event.getId());
            startActivity(intent);
        });

        buttonDelete.setOnClickListener(v -> {
            db.deleteEvent(event.getId());
            loadEvents();
            Toast.makeText(MainActivity.this, "Event Deleted", Toast.LENGTH_SHORT).show();
        });

        eventsGrid.addView(card);
    }
}
The event.getDayOfWeek() and event.getDateDay() calls are methods on the Event model class that parse the stored MM/dd/yyyy date string using SimpleDateFormat to produce the three-letter weekday abbreviation and the zero-padded day number respectively.
The res/font/ directory contains 136 XML font reference files covering a wide selection of Google Fonts (ABeeZee, Abril Fatface, Aclonica, Acme, and many more). These are declared as downloadable font resources and referenced in AndroidManifest.xml via a preloaded_fonts metadata entry. The large font catalogue was included during the design exploration phase of the project and does not affect app size at build time because only the fonts actually referenced in layouts are bundled into the APK.

Build docs developers (and LLMs) love