Quick Reference Guides
Adding a New Provider
Step 1: Create Provider Class
code
app/src/main/java/com/mrndstvndv/search/provider/{type}/{Name}Provider.kt
Step 2: Implement Provider Interface
kotlin
class NewProvider(
private val context: Context,
private val settingsRepository: ProviderSettingsRepository
) : Provider {
override val id = "new-provider"
override val displayName = "New Provider"
override val refreshSignal = MutableSharedFlow<Unit>()
override fun initialize() { /* Heavy setup */ }
override fun canHandle(query: Query): Boolean {
return query.text.isNotBlank()
}
override suspend fun query(query: Query): List<ProviderResult> {
// Return search results
}
override fun dispose() { /* Cleanup */ }
}
Step 3: Register in MainActivity (~line 150)
kotlin
val providers = remember {
listOf(
// ... existing providers
NewProvider(context, settingsRepository)
)
}
Step 4: Add Toggle in ProvidersSettingsScreen
kotlin
SettingsSwitch(
title = "New Provider",
checked = isEnabled,
onCheckedChange = { /* save state */ }
)
Step 5: Add to DEFAULT_PROVIDER_ORDER
In ProviderRankingRepository:
kotlin
private val DEFAULT_PROVIDER_ORDER = listOf(
// ... existing IDs
"new-provider"
)
Adding a New Setting
Step 1: Add Key Constant
In ProviderSettingsRepository:
kotlin
private const val KEY_NEW_SETTING = "new_setting"
Step 2: Add StateFlow
kotlin
private val _newSetting = MutableStateFlow(loadNewSetting()) val newSetting: StateFlow<Boolean> = _newSetting.asStateFlow()
Step 3: Add Load/Save Methods
kotlin
private fun loadNewSetting(): Boolean {
return prefs.getBoolean(KEY_NEW_SETTING, true)
}
fun setNewSetting(value: Boolean) {
prefs.edit().putBoolean(KEY_NEW_SETTING, value).apply()
_newSetting.value = value
}
Step 4: Add UI
In the appropriate settings screen:
kotlin
val newSetting by settingsRepository.newSetting.collectAsState()
SettingsSwitch(
title = "New Setting",
description = "Description of what this does",
checked = newSetting,
onCheckedChange = { settingsRepository.setNewSetting(it) }
)
Adding a New Settings Screen
Step 1: Create Screen File
code
app/src/main/java/com/mrndstvndv/search/ui/settings/NewSettingsScreen.kt
Step 2: Implement Composable
kotlin
@Composable
fun NewSettingsScreen(
settingsRepository: ProviderSettingsRepository,
onNavigateBack: () -> Unit
) {
Column {
// Back button header
TopAppBar(
title = { Text("New Settings") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back")
}
}
)
// Settings content
SettingsGroup(title = "Section") {
// Settings components...
}
}
}
Step 3: Add to Screen Enum
In SettingsActivity.kt:
kotlin
private enum class Screen {
// ... existing screens
NewScreen
}
Step 4: Add Navigation Case
kotlin
Screen.NewScreen -> NewSettingsScreen(
settingsRepository = settingsRepository,
onNavigateBack = { currentScreen = Screen.Home }
)
Step 5: Add Navigation Row
In parent screen:
kotlin
SettingsNavigationRow(
title = "New Settings",
onClick = { onNavigate(Screen.NewScreen) }
)
Database Migrations (Room)
Step 1: Update Entity
In provider/files/index/:
kotlin
@Entity(tableName = "files")
data class FileEntity(
@PrimaryKey val path: String,
val name: String,
val newColumn: String = "" // Add new field
)
Step 2: Add Migration
In FileSearchDatabase:
kotlin
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"ALTER TABLE files ADD COLUMN newColumn TEXT NOT NULL DEFAULT ''"
)
}
}
Step 3: Increment Version and Add Migration
kotlin
@Database(entities = [FileEntity::class], version = 2)
abstract class FileSearchDatabase : RoomDatabase() {
companion object {
fun getInstance(context: Context) = Room.databaseBuilder(...)
.addMigrations(MIGRATION_1_2)
.build()
}
}
Common File Locations
| Task | Location |
|---|---|
| Add new provider | provider/{type}/{Name}Provider.kt |
| Add provider settings | settings/ProviderSettingsRepository.kt |
| Add settings UI | ui/settings/{Name}SettingsScreen.kt |
| Add reusable component | ui/components/{ComponentName}.kt |
| Modify theme | ui/theme/Theme.kt, Color.kt, Type.kt |
| Add permission | AndroidManifest.xml |
| Add dependency | gradle/libs.versions.toml + app/build.gradle.kts |
| Modify file search index | provider/files/index/ |
Naming Conventions
| Type | Convention | Example |
|---|---|---|
| Providers | *Provider.kt | AppListProvider.kt |
| Repositories | *Repository.kt | AliasRepository.kt |
| Settings Data | *Settings | WebSearchSettings |
| Composables | PascalCase functions | SearchField() |
| Screen files | *Screen.kt | GeneralSettingsScreen.kt |
| State variables | camelCase with prefix | isLoading, hasError |
Package Organization
code
provider/
├── Provider.kt # Base interface
├── ProviderResult.kt # Result model
├── model/ # Shared models (Query, etc.)
├── {type}/ # Provider implementation
│ ├── {Type}Provider.kt
│ └── {Type}Repository.kt (if needed)
ui/
├── theme/ # Design system
├── components/ # Reusable components
│ └── settings/ # Settings-specific components
└── settings/ # Settings screens
settings/ # Non-UI settings utilities
├── ProviderSettingsRepository.kt
└── ProviderRankingRepository.kt
Design Patterns Used
| Pattern | Usage | Location |
|---|---|---|
| Provider Pattern | Unified search source abstraction | provider/Provider.kt |
| Repository Pattern | Data access abstraction | *Repository.kt files |
| Singleton Pattern | Heavy shared resources | FileSearchRepository |
| Observer Pattern | Reactive settings via StateFlow | All repositories |
| Sealed Classes | Type-safe result/target hierarchies | AliasTarget, ProviderResult |
| Fuzzy Matching | Raycast-style character matching | FuzzyMatcher.kt |
Related Skills
- •
search-overview- Project structure and architecture - •
search-provider- Provider interface details - •
search-repository- Repository patterns - •
search-ui- UI components and navigation - •
search-build- Build configuration