This document provides a comprehensive analysis of the current initialization and first-run experience system in the Tracker Android app. The goal is to understand the existing architecture before designing a completely new, clean slate approach.
File: app/src/main/java/com/adsamcik/tracker/app/Application.kt
Responsibilities:
- Initialize critical singletons (Reporter, Logger, CrashHandler)
- Launch background initialization tasks
- Setup lifecycle observers
Key Components:
override fun onCreate() {
super.onCreate()
initializeImportantSingletons()
GlobalScope.launch(Dispatchers.Default) {
initializeClasses()
initializeModules()
initializeDatabaseMaintenance()
initializeFeatures()
}
setupLifecycleListener()
}Module Initialization:
private fun initializeModules() {
ModuleClassLoader.invokeInEachActiveModule<ModuleInitializer>(this) {
it.initialize(this)
}
}File: app/src/main/java/com/adsamcik/tracker/app/activity/MainActivity.kt
First Run Detection:
override fun onStart() {
super.onStart()
if (!Preferences.getPref(this).getBooleanRes(R.string.settings_first_run_key, false)) {
firstRun()
} else {
uiIntroduction()
}
}First Run Orchestration:
private fun firstRun() {
FirstRunDialogBuilder().let { builder ->
builder.addData(AppFirstRun())
builder.addData(TrackerFirstRun())
ModuleClassLoader.invokeInEachActiveModule<FirstRun>(this@MainActivity) {
builder.addData(it)
}
builder.onFirstRunFinished = {
Preferences.getPref(this@MainActivity).edit {
setBoolean(R.string.settings_first_run_key, true)
}
uiIntroduction()
}
builder.show(this@MainActivity)
}
}File: sutils/src/main/java/com/adsamcik/tracker/shared/utils/dialog/FirstRunDialogBuilder.kt
Key Features:
- Sequential dialog presentation
- Coroutine-based async flow control
- Modular dialog registration
- State management with locking mechanism
Flow Control:
private fun next(context: Context, isCloseRequested: Boolean) {
launch {
if (!isCloseRequested && ++currentIndex < dialogDataList.size) {
dialogDataList[currentIndex].onFirstRun(context, this@FirstRunDialogBuilder::next)
} else {
onFirstRunFinished?.invoke()
}
}
}File: sutils/src/main/java/com/adsamcik/tracker/shared/utils/module/FirstRun.kt
Capabilities:
- Styled dialog creation with theming
- Style controller integration
- Callback-based completion handling
Dialog Creation Pattern:
protected fun createDialog(
context: Context,
creator: MaterialAlertDialogBuilder.() -> Unit
) {
MaterialAlertDialogBuilder(context)
.apply(creator)
.also {
it.setCancelable(false)
val dialog = it.show()
dialog.dynamicStyle(layer = 3)
}
}File: app/src/main/java/com/adsamcik/tracker/module/AppFirstRun.kt
Flow:
- Welcome Dialog - App introduction
- Notifications (Android 13+) - POST_NOTIFICATIONS permission
- Error Reporting - Crash reporting preference
Permission Handling:
private fun enableNotifications(context: Context, onDoneListener: OnDoneListener, isCloseRequested: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
createDialog(context) {
setTitle(R.string.first_run_notifications_title)
setMessage(R.string.first_run_notifications_description)
setPositiveButton(com.adsamcik.tracker.shared.base.R.string.generic_yes) { dialog, _ ->
PermissionManager.checkPermissionsWithRationaleDialog(
PermissionRequest.Builder(context)
.permission(PermissionData(POST_NOTIFICATIONS) { /* rationale */ })
.onResult { onDoneListener(context, isCloseRequested) }
.build()
)
}
}
}
}File: tracker/src/main/java/com/adsamcik/tracker/tracker/module/TrackerFirstRun.kt
Complex Multi-Step Flow:
- Automatic Tracking Options - User selects tracking behavior
- Activity Permission (if auto-tracking selected) - ACTIVITY_RECOGNITION
- What to Track Options - Multi-choice tracking features
- Permission Requests - Based on selected features
- Background Location (Android 11+) - ACCESS_BACKGROUND_LOCATION
Feature Selection Logic:
private fun whatToTrackOptions(context: Context, onDoneListener: OnDoneListener) {
val list = getTrackingListResources()
// Activity, Location, WiFi Network, WiFi Location Count, Cell
createDialog(context) {
setTitle(R.string.first_run_what_to_track_title)
setMessage(R.string.first_run_what_to_track_description)
setMultiChoiceItems(titleListCharSequence, initialSelectionBooleanArray) { _, index, isChecked ->
// Handle selection changes
}
setPositiveButton(R.string.generic_ok) { dialog, _ ->
// Save preferences and request permissions
}
}
}Permission Request Strategy:
private fun trackingPermissionRequest(context: Context, onResult: PermissionResultCallback) {
val permissionsList = mutableListOf<PermissionData>()
// Conditional permission additions based on feature selection
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && activityEnabled) {
permissionsList.add(PermissionData(Manifest.permission.ACTIVITY_RECOGNITION))
}
if (cellEnabled && multiplePhones) {
permissionsList.add(PermissionData(Manifest.permission.READ_PHONE_STATE))
}
if (locationEnabled) {
permissionsList.add(PermissionData(Manifest.permission.ACCESS_FINE_LOCATION))
}
}Discovered Modules:
- GameModuleFirstRun - Game feature setup
- StatisticsModuleFirstRun - Statistics configuration
- MapModuleFirstRun - Map preferences
Pattern: Each module implements ModuleInitializer interface
Examples:
TrackerModuleInitializer- Background tracking API + lockerActivityModuleInitializer- Activity recognition receiversGameModuleInitializer- Goal tracking setupPointsInitializer- Points system receivers
Common Pattern:
class [Module]Initializer : ModuleInitializer {
override fun initialize(context: Context) {
// Initialize broadcast receivers
// Setup background services
// Configure module-specific components
}
}Features:
- Rationale dialog generation
- Batch permission requests
- Result callbacks
- Permission checking utilities
Usage Pattern in First Run:
PermissionManager.checkPermissions(
PermissionRequest
.with(context)
.permissions(permissionsList)
.onResult { result ->
// Handle permission results
}
.build()
)Key Management: Resource-based preference keys Scope: Module-specific preferences with shared base
Example:
Preferences.getPref(context).edit {
setBoolean(resources.getString(R.string.settings_location_enabled_key), true)
setInt(R.string.settings_tracking_activity_key, selectedOption)
}File: HomeIntroduction (referenced but not examined)
Integration:
private fun uiIntroduction() {
root.post {
IntroductionManager.showIntroduction(this, HomeIntroduction())
}
}- Extensible: Easy to add new module first-runs
- Separation of Concerns: Each module handles its own setup
- Clean Interfaces: Well-defined abstract base classes
- Conditional Requests: Only requests permissions for enabled features
- Proper Rationales: User-friendly explanations
- Graceful Fallbacks: Handles permission denials appropriately
- Persistent State: Uses SharedPreferences for setup completion
- Flow Control: Proper async dialog sequencing
- User Choice Preservation: Remembers user selections
- API Level Checks: Proper version-specific permission handling
- Background Location Flow: Follows Google Play requirements
- Material Design: Styled dialogs with theming support
- Too Many Steps: 5+ dialogs for complete setup
- Cognitive Overload: Technical choices without context
- Poor Flow: No clear progress indication
- Confusing Options: Technical terminology (WiFi location count, etc.)
- Late Context: Permissions requested after feature selection
- No Explanations: Missing "why we need this" context
- Batch Requests: Multiple permissions at once without individual justification
- Mixed Dialog Systems: Uses both MaterialDialog and MaterialAlertDialogBuilder
- Deprecated APIs: Some older dialog patterns
- Inconsistent Styling: Manual style application
- No Tracking: No data on completion rates
- No A/B Testing: Single flow without optimization
- No Recovery: Poor handling of interrupted flows
- No Accessibility Features: Missing content descriptions, navigation aids
- Complex Text: Technical language difficult to translate
- No Progressive Disclosure: All options presented at once
- Feature-First Approach: Starts with technical features rather than user goals
- No Context Setting: Doesn't explain what the app does before asking for permissions
- Overwhelming Choices: Presents all tracking options without explaining benefits
- Upfront Permission Requests: Asks for permissions before showing value
- Batch Permission Requests: Multiple permissions at once
- Technical Explanations: Permission rationales use technical language
- Missing App Introduction: No explanation of core value proposition
- No Progressive Disclosure: Doesn't build understanding gradually
- No Success States: No confirmation or celebration of completion
- Start with Value: Explain what the app does and why it's useful
- Progressive Disclosure: Build understanding step by step
- Goal-Oriented: Frame features around user benefits, not technical capabilities
- Welcome Screens: Visual introduction with clear value proposition
- Interactive Tutorials: Show features in context
- Progressive Permission Requests: Request permissions when needed
- Success Celebrations: Positive reinforcement for completion
- Single Entry Point: Unified onboarding coordinator
- State Machine: Clear state transitions with progress tracking
- Modern UI Components: Use Compose or modern View system
- Analytics Integration: Track completion rates and optimization opportunities
- Visual Design: Engaging graphics and animations
- Accessibility: Full accessibility support from the start
- Localization: User-friendly language optimized for translation
- Recovery Flows: Handle interruptions gracefully
- Compose UI: Modern declarative UI framework
- ViewModel Architecture: Proper state management
- Repository Pattern: Clean data access layer
- Dependency Injection: Proper component lifecycle management
- User Research: Understand current pain points
- Analytics Implementation: Add tracking to current system
- A/B Testing Framework: Prepare for optimization
- Design System: Create cohesive visual language
- New Architecture: Build clean onboarding system
- Permission Strategy: Implement just-in-time permission requests
- State Management: Create robust state machine
- Analytics Integration: Track user behavior and completion rates
- Content Strategy: Write user-friendly copy
- Visual Design: Create engaging graphics and animations
- Flow Optimization: Implement and test different flows
- Accessibility: Ensure full accessibility compliance
- A/B Testing: Compare new vs. old system
- Gradual Rollout: Phased release to users
- Analytics Analysis: Optimize based on real user data
- Documentation: Create maintenance and iteration guides
The current initialization system is technically sound but suffers from poor user experience design. It follows a feature-first approach that overwhelms users with technical choices and requests permissions without proper context.
A clean slate redesign should prioritize user understanding and gradual engagement over technical completeness. The new system should explain value before requesting permissions, build understanding progressively, and celebrate user success.
The modular architecture foundation is solid and should be preserved, but the user-facing experience needs complete reimagining to create a modern, engaging onboarding flow that respects user agency while maximizing conversion rates.
Next Steps: Create detailed wireframes and user flow diagrams for the new clean slate approach, focusing on value-first onboarding and just-in-time permission requests.