Technical Documentation

Architecture, APIs, and implementation details

← Back to Home

Tech Stack

Kotlin Jetpack Compose for Wear OS Room Database Retrofit 2 Coroutines & Flow WorkManager Google OAuth 2.0 Google Tasks REST API DataStore Preferences

Architecture Overview

GTasksWear follows a layered architecture pattern with clear separation between data, domain, and presentation concerns.

+-------------------------------------+
| PRESENTATION LAYER |
| Compose UI · ViewModels · Screens |
+-----------------+-------------------+
|
+-----------------v-------------------+
| DOMAIN LAYER |
| Models · SyncManager · SyncWorker |
+-----------------+-------------------+
|
+-----------------v-------------------+
| DATA LAYER |
| Room DB · Retrofit · Repositories |
+-----------------+-------------------+
|
+-----------------v-------------------+
| EXTERNAL SERVICES |
| Google Tasks API · Google OAuth |
+-------------------------------------+

Google Tasks API Integration

GTasksWear communicates with the Google Tasks REST API v1 via Retrofit 2. The base URL is https://www.googleapis.com/tasks/v1/.

API Endpoints Used

Method Endpoint Purpose
GET /users/@me/lists Retrieve all task lists
GET /lists/{id}/tasks Retrieve tasks in a list
POST /lists/{id}/tasks Create a new task
PATCH /lists/{id}/tasks/{taskId} Update an existing task
DELETE /lists/{id}/tasks/{taskId} Delete a task

Authentication Flow

Authentication uses Google OAuth 2.0 with the https://www.googleapis.com/auth/tasks scope. An AuthInterceptor automatically injects the Bearer token into every Retrofit request. Token refresh is handled transparently.

Local Database (Room)

GTasksWear uses Room (version 2) as the local persistence layer, enabling offline-first functionality.

Database Schema

Table Primary Key Key Fields
tasks id (String) taskListId, title, notes, status, due, parent, position, isPendingSync, deleted
task_lists id (String) title, updated
Foreign Key Relationship: Tasks reference their parent task list via taskListId → task_lists.id with CASCADE delete — removing a list automatically removes all its tasks.

Offline Sync Tracking

Each TaskEntity has two fields for offline support:

Locally created tasks use an ID prefixed with local_ to distinguish them from server-assigned IDs.

Synchronization Engine

The SyncManager is the core orchestrator for keeping local and remote data in sync.

Sync Flow

1. PUSH LOCAL CHANGES
   ├── For each task list:
   │   ├── Find tasks with isPendingSync = true
   │   ├── If deleted & local_ → delete from Room
   │   ├── If deleted & remote → DELETE via API, then delete from Room
   │   ├── If local_ (new) → POST via API, replace with server entity
   │   └── If remote (updated) → PATCH via API, update entity
   │
2. FETCH REMOTE STATE
   ├── GET all task lists
   └── For each list → GET all tasks
   │
3. ATOMIC DATABASE UPDATE (Room Transaction)
   ├── Clear all task lists (cascades to tasks)
   ├── Insert all fetched task lists
   └── Insert all fetched tasks
Why atomic transactions? All database writes happen inside a single Room.withTransaction {} block. This prevents the UI from observing a temporarily empty database during sync, eliminating the "flash" effect where tasks briefly disappear.

Sync Intervals

Type Interval Mechanism
Foreground 60 seconds Coroutine loop with delay(60_000) in MainActivity
Background 15 minutes WorkManager periodic task via SyncWorker

UI Layer (Jetpack Compose for Wear OS)

The entire UI is built with Jetpack Compose for Wear OS — no XML layouts.

Key Screens

Screen Components Used
Login Chip (Google Sign-In trigger)
List Picker ScalingLazyColumn with selectable Chip items
Task List ScalingLazyColumn, Chip, CompactChip, collapsible parent/child tree
Task Detail Title, notes, due date editing via RemoteInput

Task Tree Rendering

Tasks support parent-child relationships. The UI builds a flattened tree using a recursive algorithm:

fun addWithChildren(task: Task) {
    orderedList.add(task)
    if (!collapsed[task.id]) {
        childrenMap[task.id]?.forEach { child ->
            addWithChildren(child)
        }
    }
}

This is wrapped in a remember(tasks, sortOption, collapsedParents) block with a TaskTreeResult data class to avoid unnecessary recomputations.

Design Principles

Build & Optimization

Feature Debug Release
Enabled Enabled
Resource Shrinking Enabled
Signing Debug keystore Release keystore
Target SDK 34 (Android 14)
Min SDK 28 (Wear OS 2+)
JVM Target Java 17

Dependencies

Library Purpose
Retrofit 2 + Gson HTTP client for Google Tasks API
Room + KSP Local SQLite database with compile-time query verification
WorkManager Reliable background sync scheduling
DataStore Preferences Key-value storage for auth tokens and settings
Wear Compose Material Wear OS-specific Material Design components
Wear Compose Navigation SwipeDismissable navigation for Wear OS
Google Play Services Auth Google Sign-In on Wear OS
Material Icons Extended Full set of Material Design icons
Wear Input Remote text input (voice/keyboard) for task creation

Security