Skip to main content

Prerequisites

Before setting up the EV Sum 2 app, ensure you have the following installed and configured:

Development environment

Android Studio

Latest stable version with Kotlin plugin

Java Development Kit

JDK 11 or higher (required for compilation)

Android SDK

SDK 24 (Nougat) through SDK 36 support

Git

Version control for cloning the repository

Build configuration

The app requires the following SDK versions as defined in app/build.gradle.kts:
app/build.gradle.kts
android {
    namespace = "com.demodogo.ev_sum_2"
    compileSdk = 36

    defaultConfig {
        applicationId = "com.demodogo.ev_sum_2"
        minSdk = 24
        targetSdk = 36
        versionCode = 1
        versionName = "1.0"
    }
    
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
}

kotlin {
    compilerOptions {
        jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
    }
}

Firebase setup

The app uses Firebase Authentication and Cloud Firestore for backend services. Follow these steps to set up your Firebase project:
1

Create a Firebase project

  1. Go to the Firebase Console
  2. Click Add project
  3. Enter a project name (e.g., “EV Sum 2”)
  4. Follow the setup wizard to create your project
2

Register your Android app

In your Firebase project:
  1. Click the Android icon to add an Android app
  2. Enter the package name: com.demodogo.ev_sum_2
  3. Add an app nickname (optional)
  4. Click Register app
3

Download google-services.json

After registering your app:
  1. Download the google-services.json file
  2. Place it in the app/ directory of your project:
ev_sum_2_android/
└── app/
    ├── build.gradle.kts
    ├── google-services.json  # Place file here
    └── src/
Never commit google-services.json to version control if it contains sensitive production credentials. Add it to .gitignore for production projects.
4

Enable Firebase Authentication

In the Firebase Console:
  1. Navigate to Build > Authentication
  2. Click Get started
  3. Go to the Sign-in method tab
  4. Enable Email/Password authentication
  5. Click Save
5

Enable Cloud Firestore

In the Firebase Console:
  1. Navigate to Build > Firestore Database
  2. Click Create database
  3. Select Start in test mode (for development)
  4. Choose your preferred location
  5. Click Enable
For production, configure proper security rules. Test mode allows all reads/writes and should only be used during development.
6

Configure security rules (optional)

For production apps, update your Firestore security rules to restrict access:
Firestore Security Rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Users can only read/write their own phrases
    match /users/{userId}/phrases/{phraseId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

Required permissions

The app requires several runtime permissions defined in AndroidManifest.xml:
app/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <!-- Application configuration -->
</manifest>

Permission details

Required for: Voice input via speech-to-textThis permission allows the app to capture audio from the device microphone for dictating email addresses, passwords, and phrases. The app uses Android’s built-in SpeechRecognizer with intelligent normalization.User impact: Users will be prompted at runtime when they first tap a microphone icon to use voice input.
Required for: Precise GPS coordinatesEnables high-accuracy location tracking using FusedLocationProviderClient. Provides latitude and longitude with meter-level precision.User impact: Users will be prompted when accessing location features. Essential for reverse geocoding functionality.
Required for: Approximate location dataProvides city-level location accuracy as a fallback when fine location isn’t available or needed.User impact: Requested alongside fine location. Improves compatibility with different device configurations.

Handling permission requests

The app handles permissions at runtime following Android best practices. Location permissions are requested when users navigate to location features:
LocationRepository.kt (example pattern)
// The app uses try-catch blocks to handle SecurityException
try {
    fusedLocationClient.lastLocation.await()
} catch (e: SecurityException) {
    // Handle permission denial gracefully
}

Project structure

Understand the codebase organization before making changes:
app/src/main/java/com/demodogo/ev_sum_2/
├── data/
   ├── firebase/          # Firebase initialization
   ├── repositories/      # Data layer (Auth, Phrase, Location)
   └── session/           # DataStore for theme preferences
├── domain/
   ├── errors/            # Error handling models
   ├── models/            # Business models (AppUser, Phrase, etc.)
   └── validators/        # Speech normalization logic
├── services/              # Hardware abstraction layer
   ├── LocationService.kt
   ├── SpeechController.kt
   └── TextToSpeechController.kt
└── ui/
    ├── auth/              # Login, Register, Password Recovery
    ├── home/              # Main dashboard and phrase CRUD
    ├── location/          # Geolocation features
    ├── nav/               # Navigation graph and routing
    └── theme/             # Material Design 3 theme

Key architectural components

Repositories

Handle all async Firebase operations using Kotlin Coroutines. Examples include AuthRepository, PhraseRepository, and LocationRepository.

Services

Abstract hardware interactions like GPS, microphone, and TTS. Wrapped with proper error handling for SecurityException and hardware failures.

Domain models

Pure Kotlin data classes with no framework dependencies. Includes AppUser, Phrase, and DeviceLocation.

UI layer

Built entirely with Jetpack Compose and Material Design 3. Uses NavHost for navigation and Flow for reactive state.

Dependencies

The app uses modern Android libraries defined in app/build.gradle.kts:

Core dependencies

app/build.gradle.kts
dependencies {
    // Firebase
    implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
    implementation("com.google.firebase:firebase-auth-ktx")
    implementation("com.google.firebase:firebase-firestore-ktx")
    
    // Location services
    implementation("com.google.android.gms:play-services-location:21.3.0")
    
    // Compose & Navigation
    implementation("androidx.navigation:navigation-compose:2.8.5")
    implementation("androidx.compose.material3:material3:1.3.0")
    implementation("androidx.compose.material:material-icons-extended:1.7.6")
    
    // Async & State
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.1")
    implementation("androidx.datastore:datastore-preferences:1.1.1")
    
    // Testing
    testImplementation(libs.junit)
}

Plugin configuration

app/build.gradle.kts
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.compose)
    alias(libs.plugins.google.services)  // Required for Firebase
}

Authentication flow

The app implements a complete authentication system using Firebase Auth with persistent sessions:
1

Auth state monitoring

The AuthRepository uses AuthStateListener wrapped in a Flow for reactive session management:
AuthRepository.kt
fun authStateFlow(): Flow<FirebaseUser?> = callbackFlow {
    val listener = FirebaseAuth.AuthStateListener { fa ->
        trySend(fa.currentUser).isSuccess
    }
    auth.addAuthStateListener(listener)
    trySend(auth.currentUser)
    awaitClose { auth.removeAuthStateListener(listener) }
}
2

Automatic routing

The RouterScreen component evaluates auth state in real-time:
  • Logged out: Redirects to /login
  • Logged in: Redirects to /home
This prevents the need for manual re-authentication after app restarts.
3

User operations

Available auth operations:
AuthRepository.kt
// Sign in existing user
suspend fun login(email: String, password: String)

// Create new account
suspend fun register(email: String, password: String)

// Send password reset email
suspend fun sendPasswordReset(email: String)

// Sign out current user
fun logout()

Speech normalization

The app includes sophisticated voice input normalization for Spanish speakers:
SpeechNormalization.kt
fun normalizeEmailFromSpeech(input: String): String {
    var s = input.lowercase()
    
    val wordMap = mapOf(
        "arroba" to "@",
        "punto" to ".",
        "guion bajo" to "_",
        "y griega" to "y"
    )
    
    val numberMap = mapOf(
        "cero" to "0",
        "uno" to "1",
        "dos" to "2",
        // ... through "nueve" to "9"
    )
    
    // Apply transformations and return normalized string
}
The normalization logic includes spelling mode detection - when 60% or more tokens are single characters, “y” is interpreted as the letter “i” rather than the conjunction.

Testing setup

The project includes unit tests with 100% coverage on critical normalization logic:
app/src/test/java/com/demodogo/ev_sum_2/
└── domain/
    └── validators/
        └── SpeechNormalizersTest.kt
Run tests from Android Studio:
  1. Right-click on the test file
  2. Select Run ‘SpeechNormalizersTest’
Or via command line:
./gradlew test

Known limitations

Be aware of these platform limitations when developing:
  • Emulator speech recognition: Google’s recognition engine may fail on emulators without updated Play Store services. Always test voice features on physical devices.
  • Ambient noise: The speech recognition system is sensitive to background noise. Best results in quiet environments.
  • Language support: Speech normalization is optimized for Spanish (Chilean dialect). Other languages may require custom normalization rules.

Next steps

Architecture deep dive

Learn about the Service-Repository pattern and data flow

Authentication guide

Explore Firebase Auth implementation and session management

Voice input

Understand speech normalization algorithms and testing

Geolocation

Learn about FusedLocationProviderClient and reverse geocoding

Build docs developers (and LLMs) love