Comprehensive technical documentation for developers working with the Smoking Cessation App.
This technical documentation provides comprehensive information for developers working with the Smoking Cessation App. It covers the system architecture, core modules, database schema, API integrations, and other technical aspects of the application. Whether you're maintaining the existing codebase, extending functionality, or integrating with our systems, this documentation will serve as your primary reference.
The Smoking Cessation App is built using modern development practices and follows the MVVM (Model-View-ViewModel) architectural pattern. It leverages a combination of native Android development with Kotlin for the mobile application, along with cloud-based services for backend functionality, data storage, and AI integration. The application is designed to be scalable, maintainable, and extensible, with plans for cross-platform expansion to iOS and web platforms.
Note: This documentation assumes familiarity with Android development using Kotlin, MVVM architecture, and modern mobile development practices. If you're new to these concepts, we recommend reviewing the relevant resources before diving into the codebase.
The Smoking Cessation App follows a clean architecture approach with the MVVM (Model-View-ViewModel) pattern. This separation of concerns enhances testability, maintainability, and scalability. The architecture is organized into the following layers:
Presentation Layer: This layer contains the UI components (Activities, Fragments, Views) and ViewModels. The UI components are responsible for displaying data and capturing user interactions, while ViewModels handle the UI-related business logic and serve as a bridge between the UI and the domain layer. ViewModels expose LiveData or StateFlow objects that the UI observes for changes.
Domain Layer: This layer contains the business logic and rules of the application. It includes use cases (interactors) that encapsulate specific business operations, domain models that represent the core entities, and repository interfaces that define how data is accessed and manipulated.
Data Layer: This layer is responsible for data management and includes repository implementations, data sources (local and remote), and data models. It handles data retrieval, storage, and synchronization between different sources. The local data source uses Room for database operations, while the remote data source uses Retrofit for API calls.
Framework Layer: This layer contains framework-specific implementations and utilities, such as database configuration, network clients, dependency injection setup, and other infrastructure components.
The architecture follows a unidirectional data flow pattern, where data flows from the data layer through the domain layer to the presentation layer, and user actions flow in the opposite direction.
┌─────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Views │◄─────────►│ ViewModels │ │
│ │ (Activities,│ │ (UI Logic, State Management) │ │
│ │ Fragments) │ └───────────────┬─────────────┘ │
│ └─────────────┘ │ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Domain Layer │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │
│ │ Use Cases │◄─────────►│ Domain │ │ Repository │ │
│ │(Interactors)│ │ Models │ │ Interfaces │ │
│ └─────────────┘ └─────────────┘ └──────┬─────┘ │
│ │ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Data Layer │
│ │
│ ┌─────────────────────┐ ┌───────────────────────────┐ │
│ │ Repository │ │ Data Sources │ │
│ │ Implementations │◄────►│ (Local DB, Remote API, │ │
│ │ │ │ Preferences, etc.) │ │
│ └─────────────────────┘ └───────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
The Smoking Cessation App implements several design patterns to enhance code quality, maintainability, and scalability:
MVVM (Model-View-ViewModel): The core architectural pattern that separates the UI (View) from the business logic (ViewModel) and data (Model). This separation allows for better testability and maintainability.
Repository Pattern: Used to abstract the data sources and provide a clean API for data access. Repositories handle the complexity of data retrieval, storage, and synchronization, shielding the rest of the application from these details.
Factory Pattern: Implemented for creating complex objects, particularly in the Plan Generator module where different types of cessation plans need to be created based on user assessment data.
Observer Pattern: Used extensively through LiveData and StateFlow to enable reactive UI updates based on data changes. This pattern ensures that the UI always reflects the current state of the data.
Dependency Injection: Implemented using Hilt to manage dependencies and promote loose coupling between components. This pattern enhances testability and modularity.
Strategy Pattern: Applied in the Plan Generator and Craving Management modules to select appropriate algorithms or approaches based on user characteristics and context.
Builder Pattern: Used for constructing complex objects with many optional parameters, particularly in the Assessment and Plan Generator modules.
The Smoking Cessation App leverages a modern technology stack for Android development:
Programming Language:
Android Architecture Components:
Dependency Injection:
Networking:
UI Components:
Testing:
Analytics and Monitoring:
Cloud Services:
The User Management module handles user authentication, profile management, and user preferences. It provides a secure and personalized experience for each user.
Key Components:
UserRepository: The central component that manages user data and operations. It coordinates between local storage (Room database) and remote services (Firebase Authentication and Firestore).
UserViewModel: Handles UI-related user operations and exposes user state to the UI layer. It manages login, registration, profile updates, and preference changes.
AuthManager: Encapsulates authentication logic, including login, registration, password reset, and session management. It uses Firebase Authentication for secure user authentication.
UserPreferencesManager: Manages user preferences using DataStore, providing a reactive API for reading and updating preferences.
Key Functionalities:
Implementation Example:
// UserRepository implementation
class UserRepositoryImpl @Inject constructor(
private val authManager: AuthManager,
private val userDao: UserDao,
private val userRemoteDataSource: UserRemoteDataSource,
private val preferencesManager: UserPreferencesManager
) : UserRepository {
override suspend fun login(email: String, password: String): Result {
return try {
val authResult = authManager.login(email, password)
val userId = authResult.user.uid
// Fetch user profile from remote
val userProfile = userRemoteDataSource.getUserProfile(userId)
// Cache user locally
userDao.insertUser(userProfile.toEntity())
Result.success(userProfile)
} catch (e: Exception) {
Result.failure(e)
}
}
override fun getCurrentUser(): Flow {
return userDao.observeCurrentUser().map { it?.toDomain() }
}
// Other repository methods...
}
The Assessment Module handles the initial questionnaire that collects information about the user's smoking habits, previous quit attempts, triggers, and preferences. This data forms the foundation for the personalized cessation plan.
Key Components:
AssessmentRepository: Manages assessment data, including storage, retrieval, and updates. It coordinates between local storage and remote services to ensure data consistency.
AssessmentViewModel: Handles the UI logic for the assessment process, including validation, navigation between assessment sections, and submission of completed assessments.
QuestionnaireManager: Manages the structure and flow of the assessment questionnaire, including question sequencing, conditional logic, and response validation.
ScoringEngine: Analyzes assessment responses to calculate key metrics like nicotine dependence level, readiness to quit, and trigger patterns. These scores are used by the Plan Generator module.
Key Functionalities:
Assessment Sections:
The assessment is divided into six sections, each focusing on different aspects of the user's smoking behavior and quit journey:
Implementation Example:
// ScoringEngine implementation
class ScoringEngineImpl @Inject constructor() : ScoringEngine {
override fun calculateNicotineDependence(assessment: Assessment): DependenceLevel {
var score = 0
// Time to first cigarette scoring
score += when (assessment.timeToFirstCigarette) {
TimeToFirstCigarette.WITHIN_5_MINUTES -> 3
TimeToFirstCigarette.WITHIN_30_MINUTES -> 2
TimeToFirstCigarette.WITHIN_60_MINUTES -> 1
TimeToFirstCigarette.AFTER_60_MINUTES -> 0
}
// Cigarettes per day scoring
score += when (assessment.cigarettesPerDay) {
in 0..10 -> 0
in 11..20 -> 1
in 21..30 -> 2
else -> 3
}
// Additional scoring factors...
return when (score) {
in 0..2 -> DependenceLevel.LOW
in 3..5 -> DependenceLevel.MODERATE
else -> DependenceLevel.HIGH
}
}
// Other scoring methods...
}
The Plan Generator module creates personalized cessation plans based on the user's assessment data. It applies evidence-based algorithms to determine the most appropriate quit approach, timeline, and support strategies for each user.
Key Components:
PlanRepository: Manages cessation plan data, including creation, storage, retrieval, and updates. It ensures that plan data is synchronized between local and remote storage.
PlanGeneratorService: The core service that generates personalized cessation plans based on assessment data. It applies various algorithms and rules to create tailored plans.
PlanViewModel: Handles UI-related plan operations, including displaying plan details, updating plan progress, and modifying plan parameters.
ActivityScheduler: Manages the scheduling of plan activities and notifications based on the user's plan timeline and preferences.
Key Functionalities:
Plan Generation Algorithm:
The plan generation process follows these steps:
Implementation Example:
// PlanGeneratorService implementation
class PlanGeneratorServiceImpl @Inject constructor(
private val scoringEngine: ScoringEngine,
private val approachSelector: QuitApproachSelector,
private val timelineGenerator: TimelineGenerator,
private val strategySelector: CopingStrategySelector,
private val activityScheduler: ActivityScheduler
) : PlanGeneratorService {
override suspend fun generatePlan(assessment: Assessment): CessationPlan {
// Calculate dependence level
val dependenceLevel = scoringEngine.calculateNicotineDependence(assessment)
// Identify trigger patterns
val triggerPatterns = scoringEngine.identifyTriggerPatterns(assessment)
// Select quit approach
val quitApproach = approachSelector.selectApproach(
dependenceLevel = dependenceLevel,
previousAttempts = assessment.previousQuitAttempts,
userPreference = assessment.preferredApproach
)
// Generate timeline
val timeline = timelineGenerator.generateTimeline(
dependenceLevel = dependenceLevel,
quitApproach = quitApproach,
readinessLevel = assessment.readinessLevel
)
// Select coping strategies
val copingStrategies = strategySelector.selectStrategies(
triggerPatterns = triggerPatterns,
userPreferences = assessment.supportPreferences
)
// Schedule activities
val activities = activityScheduler.scheduleActivities(
timeline = timeline,
quitApproach = quitApproach,
triggerPatterns = triggerPatterns
)
// Create and return the complete plan
return CessationPlan(
userId = assessment.userId,
quitApproach = quitApproach,
timeline = timeline,
copingStrategies = copingStrategies,
activities = activities,
supportRecommendations = generateSupportRecommendations(assessment, dependenceLevel),
createdAt = System.currentTimeMillis(),
modifiedAt = System.currentTimeMillis()
)
}
// Other methods...
}
The Gemini Chat Integration module provides the AI-powered conversational support system using Google's Gemini 2.5 Pro model. It enables personalized, context-aware interactions that provide emotional support and practical advice throughout the user's quit journey.
Key Components:
ChatRepository: Manages chat data, including message history, conversation context, and user interactions. It ensures synchronization between local and remote storage.
GeminiService: Handles communication with the Gemini API, including request formatting, context preparation, and response processing. It encapsulates the complexity of working with the AI model.
ChatViewModel: Manages the UI state for the chat interface, including message display, user input handling, and conversation flow.
ContextPreparationService: Prepares relevant context for each chat interaction, including user profile data, cessation plan details, progress information, and conversation history.
Key Functionalities:
Gemini Integration Architecture:
The Gemini integration follows this architecture:
Implementation Example:
// GeminiService implementation
class GeminiServiceImpl @Inject constructor(
private val geminiApiClient: GeminiApiClient,
private val contextPreparationService: ContextPreparationService
) : GeminiService {
override suspend fun sendMessage(
userId: String,
message: String,
conversationId: String?
): ChatResponse {
// Prepare context for the request
val context = contextPreparationService.prepareContext(
userId = userId,
conversationId = conversationId
)
// Build the request
val request = GeminiRequest(
model = "gemini-2.5-pro",
messages = buildMessageList(context, message),
temperature = 0.7,
topP = 0.95,
maxOutputTokens = 1024
)
// Send request to Gemini API
val response = geminiApiClient.generateContent(request)
// Process and return the response
return ChatResponse(
conversationId = conversationId ?: generateConversationId(),
message = processResponse(response),
timestamp = System.currentTimeMillis()
)
}
private fun buildMessageList(context: UserContext, userMessage: String): List {
val messages = mutableListOf()
// System message with instructions and context
messages.add(GeminiMessage(
role = "system",
content = buildSystemPrompt(context)
))
// Add conversation history if available
context.conversationHistory?.forEach { historyItem ->
messages.add(GeminiMessage(
role = historyItem.role,
content = historyItem.content
))
}
// Add the current user message
messages.add(GeminiMessage(
role = "user",
content = userMessage
))
return messages
}
private fun buildSystemPrompt(context: UserContext): String {
return """
You are an empathetic, supportive AI assistant for a smoking cessation app.
Your role is to provide emotional support and practical advice to help the user quit smoking.
User Information:
- Nicotine dependence level: ${context.dependenceLevel}
- Current quit phase: ${context.currentPhase}
- Days since quit date: ${context.daysSinceQuit}
- Recent challenges: ${context.recentChallenges}
Guidelines:
- Be empathetic and non-judgmental
- Provide evidence-based advice
- Celebrate progress and achievements
- Offer specific coping strategies for cravings
- If the user reports a slip or relapse, be supportive and help them get back on track
- Keep responses concise (2-3 paragraphs maximum)
""".trimIndent()
}
private fun processResponse(response: GeminiResponse): String {
// Extract and process the assistant's response
return response.choices.firstOrNull()?.message?.content ?:
"I'm sorry, I'm having trouble responding right now. Please try again."
}
private fun generateConversationId(): String {
return UUID.randomUUID().toString()
}
}
The Progress Tracking module monitors and visualizes the user's quit journey, including smoke-free days, money saved, health improvements, craving patterns, and achievements. It provides motivational feedback and reinforces the benefits of quitting.
Key Components:
ProgressRepository: Manages progress data, including tracking metrics, achievements, and historical data. It ensures data consistency between local and remote storage.
ProgressViewModel: Handles UI-related progress operations, including displaying progress metrics, updating tracking data, and managing achievements.
MetricsCalculator: Calculates various progress metrics based on user data, including money saved, health improvements, and statistical trends.
AchievementManager: Manages the achievement system, including unlocking achievements, tracking progress toward achievements, and notifying users of new achievements.
Key Functionalities:
Implementation Example:
// MetricsCalculator implementation
class MetricsCalculatorImpl @Inject constructor() : MetricsCalculator {
override fun calculateMoneySaved(
cigarettesPerDay: Int,
costPerPack: Float,
cigarettesPerPack: Int,
daysSinceQuit: Int
): Float {
val cigaretteCost = costPerPack / cigarettesPerPack
val totalCigarettesNotSmoked = cigarettesPerDay * daysSinceQuit
return totalCigarettesNotSmoked * cigaretteCost
}
override fun getHealthImprovements(daysSinceQuit: Int): List {
val improvements = mutableListOf()
// Add improvements based on timeline
if (daysSinceQuit >= 1) {
improvements.add(HealthImprovement(
id = "carbon_monoxide",
title = "Carbon Monoxide Levels Normalized",
description = "Carbon monoxide levels in your blood have dropped to normal.",
dayMilestone = 1,
achieved = true
))
}
if (daysSinceQuit >= 2) {
improvements.add(HealthImprovement(
id = "nicotine_expelled",
title = "Nicotine Expelled",
description = "Nicotine has been expelled from your body.",
dayMilestone = 2,
achieved = true
))
}
// Additional health improvements...
return improvements
}
override fun analyzeCravingTrends(
cravingEntries: List
): CravingTrendAnalysis {
// Skip if not enough data
if (cravingEntries.size < 3) {
return CravingTrendAnalysis(
frequencyTrend = TrendDirection.STABLE,
intensityTrend = TrendDirection.STABLE,
averageIntensity = cravingEntries.map { it.intensity }.average().toFloat(),
dailyFrequency = cravingEntries.size.toFloat() /
(cravingEntries.maxOfOrNull { it.timestamp } ?: 0 -
cravingEntries.minOfOrNull { it.timestamp } ?: 0).toFloat()
)
}
// Group by day
val entriesByDay = cravingEntries
.groupBy { TimeUnit.MILLISECONDS.toDays(it.timestamp) }
// Calculate trends
val frequencyByDay = entriesByDay.map { it.value.size }
val frequencyTrend = calculateTrend(frequencyByDay)
val intensityByDay = entriesByDay.map { entries ->
entries.value.map { it.intensity }.average()
}
val intensityTrend = calculateTrend(intensityByDay)
return CravingTrendAnalysis(
frequencyTrend = frequencyTrend,
intensityTrend = intensityTrend,
averageIntensity = cravingEntries.map { it.intensity }.average().toFloat(),
dailyFrequency = frequencyByDay.average().toFloat()
)
}
private fun calculateTrend(values: List): TrendDirection {
// Simple linear regression to determine trend
// Implementation details...
return TrendDirection.DECREASING
}
}
The Smoking Cessation App uses a relational database structure implemented with Room for local storage and Firestore for cloud storage. The database schema is designed to efficiently store and retrieve user data, assessment information, cessation plans, progress metrics, and chat history.
The core entities and their relationships are as follows:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ User │ │ Assessment │ │ CessationPlan│
│ │1 1│ │1 1│ │
│ - userId │───────│ - userId │───────│ - planId │
│ - email │ │ - responses │ │ - userId │
│ - profile │ │ - scores │ │ - approach │
└─────────────┘ └─────────────┘ │ - timeline │
│ │ - activities│
│ └─────────────┘
│ │
│ │
┌─────┴───────┐ ┌──────┴──────┐
│ UserProgress│ │ChatConversation│
│ │ │ │
│ - userId │ │ - convId │
│ - metrics │ │ - userId │
│ - achievements│ │ - messages │
└─────────────┘ └──────────────┘
│ │
│ │
┌─────┴───────┐ ┌──────┴──────┐
│ CravingEntry│ │ ChatMessage │
│ │ │ │
│ - entryId │ │ - messageId │
│ - userId │ │ - convId │
│ - timestamp │ │ - content │
│ - intensity │ │ - role │
│ - trigger │ │ - timestamp │
└─────────────┘ └─────────────┘
The app uses a three-tier data model approach:
Entity Models: These represent the database tables and are used by Room for local storage. They include annotations for defining table structure, relationships, and indices.
Domain Models: These are the core business models used throughout the application logic. They are independent of any storage mechanism and represent the essential data structures of the application.
DTO Models: These are used for data transfer, particularly for API communication and Firestore storage. They include serialization annotations for JSON conversion.
Here are examples of the key data models:
User Model:
// Entity Model
@Entity(tableName = "users")
data class UserEntity(
@PrimaryKey
val userId: String,
val email: String,
val displayName: String?,
val profileImageUrl: String?,
val createdAt: Long,
val lastLoginAt: Long
)
// Domain Model
data class User(
val userId: String,
val email: String,
val displayName: String?,
val profileImageUrl: String?,
val createdAt: Long,
val lastLoginAt: Long
)
// DTO Model
@Serializable
data class UserDto(
val userId: String,
val email: String,
val displayName: String?,
val profileImageUrl: String?,
val createdAt: Long,
val lastLoginAt: Long
)
Assessment Model:
// Entity Model
@Entity(tableName = "assessments")
data class AssessmentEntity(
@PrimaryKey
val userId: String,
val cigarettesPerDay: Int,
val timeToFirstCigarette: String,
val yearsSmoked: Int,
val previousQuitAttempts: Int,
val longestQuitDuration: Int,
val smokersInEnvironment: Boolean,
val commonTriggers: String, // JSON string of triggers
val motivationLevel: Int,
val preferredApproach: String,
val healthConditions: String, // JSON string of conditions
val stressLevel: Int,
val completedAt: Long,
val updatedAt: Long
)
// Domain Model
data class Assessment(
val userId: String,
val cigarettesPerDay: Int,
val timeToFirstCigarette: TimeToFirstCigarette,
val yearsSmoked: Int,
val previousQuitAttempts: Int,
val longestQuitDuration: Int,
val smokersInEnvironment: Boolean,
val commonTriggers: List,
val motivationLevel: Int,
val preferredApproach: QuitApproach,
val healthConditions: List,
val stressLevel: Int,
val completedAt: Long,
val updatedAt: Long
)
CessationPlan Model:
// Entity Model
@Entity(tableName = "cessation_plans")
data class CessationPlanEntity(
@PrimaryKey
val planId: String,
val userId: String,
val quitApproach: String,
val preparationPhaseLength: Int,
val quitDate: Long,
val timelineJson: String, // JSON string of timeline
val activitiesJson: String, // JSON string of activities
val strategiesJson: String, // JSON string of strategies
val recommendationsJson: String, // JSON string of recommendations
val createdAt: Long,
val modifiedAt: Long
)
// Domain Model
data class CessationPlan(
val planId: String,
val userId: String,
val quitApproach: QuitApproach,
val timeline: Timeline,
val activities: List,
val copingStrategies: List,
val supportRecommendations: List,
val createdAt: Long,
val modifiedAt: Long
) {
val preparationPhaseLength: Int
get() = timeline.preparationPhase.durationDays
val quitDate: Long
get() = timeline.quitDate
}
The app implements a robust database migration strategy to handle schema changes across app updates. This ensures that user data is preserved and properly transformed when the database structure evolves.
Room Migrations: For local database migrations, the app uses Room's migration system. Each migration is defined as a separate Migration class that handles the transformation from one version to another.
Firestore Migrations: For cloud database migrations, the app uses a combination of client-side and server-side migration strategies. Cloud Functions are used to perform batch migrations of existing data when the schema changes.
Migration Testing: All migrations are thoroughly tested with both empty databases and databases containing realistic data to ensure they work correctly in all scenarios.
Implementation Example:
// Room migration example
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
// Add new column to assessment table
database.execSQL(
"ALTER TABLE assessments ADD COLUMN stress_level INTEGER NOT NULL DEFAULT 5"
)
// Create new table for craving entries
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS craving_entries (
entry_id TEXT NOT NULL PRIMARY KEY,
user_id TEXT NOT NULL,
timestamp INTEGER NOT NULL,
intensity INTEGER NOT NULL,
trigger TEXT,
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
)
"""
)
// Create index for faster queries
database.execSQL(
"CREATE INDEX IF NOT EXISTS index_craving_entries_user_id ON craving_entries(user_id)"
)
}
}
// Database setup with migrations
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"smoking_cessation_db"
)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
.fallbackToDestructiveMigration() // Only for development
.build()
The Smoking Cessation App integrates with Google's Gemini API to provide the AI-powered chat support feature. This integration enables personalized, context-aware conversations that help users throughout their quit journey.
Integration Overview:
The app communicates with the Gemini API using a REST client implemented with Retrofit. The integration includes request formatting, context preparation, response processing, and error handling.
Key Features:
Implementation Example:
// Gemini API client interface
interface GeminiApiClient {
@POST("v1/models/gemini-2.5-pro:generateContent")
suspend fun generateContent(
@Body request: GeminiRequest,
@Header("Authorization") authHeader: String = "Bearer ${BuildConfig.GEMINI_API_KEY}"
): GeminiResponse
}
// Request and response models
data class GeminiRequest(
val model: String,
val messages: List,
val temperature: Double,
val topP: Double,
val maxOutputTokens: Int
)
data class GeminiMessage(
val role: String, // "system", "user", or "assistant"
val content: String
)
data class GeminiResponse(
val id: String,
val object: String,
val created: Long,
val model: String,
val choices: List,
val usage: GeminiUsage
)
data class GeminiChoice(
val index: Int,
val message: GeminiMessage,
val finishReason: String
)
data class GeminiUsage(
val promptTokens: Int,
val completionTokens: Int,
val totalTokens: Int
)
The app uses Firebase Authentication for secure user authentication and session management. This integration provides a robust authentication system with multiple sign-in methods and security features.
Integration Overview:
The app integrates with Firebase Authentication through the Firebase SDK. The integration includes user registration, login, password reset, and session management.
Key Features:
Implementation Example:
// AuthManager implementation using Firebase
class FirebaseAuthManager @Inject constructor(
private val firebaseAuth: FirebaseAuth
) : AuthManager {
override suspend fun login(email: String, password: String): AuthResult {
return suspendCoroutine { continuation ->
firebaseAuth.signInWithEmailAndPassword(email, password)
.addOnSuccessListener { authResult ->
continuation.resume(AuthResult(
user = User(
userId = authResult.user?.uid ?: "",
email = email,
displayName = authResult.user?.displayName,
profileImageUrl = authResult.user?.photoUrl?.toString(),
createdAt = authResult.user?.metadata?.creationTimestamp ?: 0,
lastLoginAt = System.currentTimeMillis()
),
isNewUser = authResult.additionalUserInfo?.isNewUser ?: false
))
}
.addOnFailureListener { exception ->
continuation.resumeWithException(mapFirebaseException(exception))
}
}
}
override suspend fun register(email: String, password: String): AuthResult {
return suspendCoroutine { continuation ->
firebaseAuth.createUserWithEmailAndPassword(email, password)
.addOnSuccessListener { authResult ->
continuation.resume(AuthResult(
user = User(
userId = authResult.user?.uid ?: "",
email = email,
displayName = null,
profileImageUrl = null,
createdAt = System.currentTimeMillis(),
lastLoginAt = System.currentTimeMillis()
),
isNewUser = true
))
}
.addOnFailureListener { exception ->
continuation.resumeWithException(mapFirebaseException(exception))
}
}
}
// Other authentication methods...
private fun mapFirebaseException(exception: Exception): Exception {
return when (exception) {
is FirebaseAuthInvalidCredentialsException -> InvalidCredentialsException(exception.message)
is FirebaseAuthInvalidUserException -> UserNotFoundException(exception.message)
is FirebaseAuthUserCollisionException -> UserAlreadyExistsException(exception.message)
else -> exception
}
}
}
The app integrates with Firebase Analytics to track user behavior, app usage patterns, and key events. This data helps improve the app's effectiveness and user experience.
Integration Overview:
The app uses the Firebase Analytics SDK to log events, user properties, and session data. The integration includes custom event tracking, user property management, and conversion tracking.
Key Features:
Implementation Example:
// AnalyticsManager implementation using Firebase
class FirebaseAnalyticsManager @Inject constructor(
private val firebaseAnalytics: FirebaseAnalytics
) : AnalyticsManager {
override fun logEvent(eventName: String, params: Map) {
val bundle = Bundle().apply {
params.forEach { (key, value) ->
when (value) {
is String -> putString(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Double -> putDouble(key, value)
is Boolean -> putBoolean(key, value)
is Float -> putFloat(key, value)
// Add other types as needed
}
}
}
firebaseAnalytics.logEvent(eventName, bundle)
}
override fun setUserProperty(propertyName: String, value: String?) {
firebaseAnalytics.setUserProperty(propertyName, value)
}
override fun logAssessmentCompleted(assessmentData: Assessment) {
logEvent(AnalyticsEvents.ASSESSMENT_COMPLETED, mapOf(
AnalyticsParams.CIGARETTES_PER_DAY to assessmentData.cigarettesPerDay,
AnalyticsParams.YEARS_SMOKED to assessmentData.yearsSmoked,
AnalyticsParams.PREVIOUS_ATTEMPTS to assessmentData.previousQuitAttempts,
AnalyticsParams.MOTIVATION_LEVEL to assessmentData.motivationLevel,
AnalyticsParams.PREFERRED_APPROACH to assessmentData.preferredApproach.name
))
// Set user properties for segmentation
setUserProperty(UserProperties.DEPENDENCE_LEVEL,
calculateDependenceLevel(assessmentData).name)
setUserProperty(UserProperties.YEARS_SMOKING,
assessmentData.yearsSmoked.toString())
}
override fun logPlanGenerated(plan: CessationPlan) {
logEvent(AnalyticsEvents.PLAN_GENERATED, mapOf(
AnalyticsParams.QUIT_APPROACH to plan.quitApproach.name,
AnalyticsParams.PREPARATION_DAYS to plan.preparationPhaseLength,
AnalyticsParams.STRATEGIES_COUNT to plan.copingStrategies.size
))
}
// Other analytics methods...
private fun calculateDependenceLevel(assessment: Assessment): DependenceLevel {
// Simplified calculation for example
return when {
assessment.cigarettesPerDay > 20 -> DependenceLevel.HIGH
assessment.cigarettesPerDay > 10 -> DependenceLevel.MODERATE
else -> DependenceLevel.LOW
}
}
object AnalyticsEvents {
const val ASSESSMENT_COMPLETED = "assessment_completed"
const val PLAN_GENERATED = "plan_generated"
const val CHAT_INTERACTION = "chat_interaction"
const val CRAVING_LOGGED = "craving_logged"
const val ACHIEVEMENT_UNLOCKED = "achievement_unlocked"
// Other events...
}
object AnalyticsParams {
const val CIGARETTES_PER_DAY = "cigarettes_per_day"
const val YEARS_SMOKED = "years_smoked"
const val PREVIOUS_ATTEMPTS = "previous_attempts"
const val MOTIVATION_LEVEL = "motivation_level"
const val PREFERRED_APPROACH = "preferred_approach"
const val QUIT_APPROACH = "quit_approach"
const val PREPARATION_DAYS = "preparation_days"
const val STRATEGIES_COUNT = "strategies_count"
// Other parameters...
}
object UserProperties {
const val DEPENDENCE_LEVEL = "dependence_level"
const val YEARS_SMOKING = "years_smoking"
const val QUIT_DATE = "quit_date"
// Other properties...
}
}
Security is a critical aspect of the Smoking Cessation App, particularly given the sensitive nature of health-related data. The app implements multiple security measures to protect user data and ensure secure operations.
Authentication Security:
Data Security:
Code Security:
API Security:
Privacy Considerations:
The Smoking Cessation App implements various performance optimization techniques to ensure a smooth, responsive user experience while minimizing resource usage.
UI Performance:
Data Performance:
Network Performance:
Background Processing:
Memory Management:
Performance Monitoring:
While the initial implementation of the Smoking Cessation App is for Android, the architecture is designed with cross-platform expansion in mind. This section outlines the approach for extending the app to iOS and web platforms.
Shared Business Logic:
The core business logic, including the assessment analysis, plan generation algorithms, and progress tracking calculations, is designed to be platform-agnostic. This logic will be extracted into a shared codebase that can be used across platforms.
Kotlin Multiplatform Mobile (KMM):
For the iOS expansion, Kotlin Multiplatform Mobile will be used to share business logic between Android and iOS. This approach allows for code sharing while still using native UI components for each platform.
Key components that will be shared:
Platform-Specific Implementation:
Each platform will have its own implementation of:
Web Implementation:
For the web version, a React-based implementation is planned, with:
Backend Services:
All platforms will connect to the same backend services, ensuring consistent data and functionality across devices. This includes:
The Smoking Cessation App implements a comprehensive testing strategy to ensure reliability, functionality, and quality. The testing framework includes multiple levels of testing, from unit tests to end-to-end tests.
Unit Testing:
Unit tests verify the functionality of individual components in isolation. The app uses JUnit for unit testing, with Mockito for mocking dependencies.
Key areas covered by unit tests:
Integration Testing:
Integration tests verify that components work together correctly. The app uses AndroidX Test for integration testing.
Key areas covered by integration tests:
UI Testing:
UI tests verify that the user interface works correctly. The app uses Espresso for UI testing.
Key areas covered by UI tests:
End-to-End Testing:
End-to-end tests verify complete user journeys through the app. The app uses a combination of Espresso and Firebase Test Lab for end-to-end testing.
Key user journeys tested:
Test Automation:
Tests are automated as part of the CI/CD pipeline, with:
Test Coverage:
The app aims for high test coverage, with:
The Smoking Cessation App follows a structured deployment process to ensure reliable, high-quality releases. This process includes multiple environments, automated testing, and phased rollouts.
Development Environment:
The development environment is used for active development and initial testing. It includes:
Staging Environment:
The staging environment mirrors the production environment for pre-release testing. It includes:
Production Environment:
The production environment is the live environment used by end users. It includes:
CI/CD Pipeline:
The app uses a CI/CD pipeline for automated building, testing, and deployment. The pipeline includes:
Release Process:
The release process follows these steps:
Versioning Strategy:
The app uses semantic versioning (MAJOR.MINOR.PATCH) with:
Monitoring and Feedback:
After deployment, the app is monitored using: