-
-
Notifications
You must be signed in to change notification settings - Fork 56
Feature Registry
The Feature Registry is the single source of truth for all features in Essentials. It defines every feature's metadata, permissions, and UI behavior in one centralized place.
app/src/main/java/com/sameerasw/essentials/domain/registry/
├── FeatureRegistry.kt — All feature definitions
├── PermissionRegistry.kt — Permission → feature mappings
└── SearchRegistry.kt — Global search index entries
Every feature is an anonymous object extending the Feature abstract class:
object : Feature(
id = "Feature name",
title = R.string.feat_title,
iconRes = R.drawable.rounded_icon_24,
category = R.string.cat_system,
description = R.string.feat_description,
aboutDescription = R.string.about_desc_feature,
permissionKeys = listOf("ACCESSIBILITY", "NOTIFICATION_LISTENER"),
showToggle = true,
hasMoreSettings = true,
isVisibleInMain = true,
isBeta = false,
parentFeatureId = "Parent feature id",
animationRes = R.raw.feature_animation,
searchableSettings = listOf(
SearchSetting(R.string.search_title, R.string.search_desc, "highlight_key")
)
) {
override fun isEnabled(viewModel: MainViewModel): Boolean = viewModel.isFeatureEnabled.value
override fun isToggleEnabled(viewModel: MainViewModel, context: Context): Boolean = viewModel.isPermissionGranted.value
override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) = viewModel.setFeatureEnabled(enabled)
override fun isDeviceSupported(context: Context): Boolean = true // override for device-specific features
override fun onClick(context: Context, viewModel: MainViewModel) { /* optional custom tap handler */ }
}| Property | Type | Description |
|---|---|---|
id |
String |
Unique identifier — used for parent-child linking and navigation |
title |
@StringRes Int |
Display name (from strings.xml) |
iconRes |
@DrawableRes Int |
Icon shown in the feature card |
category |
@StringRes Int |
Category grouping (System, Interface, Connectivity, etc.) |
description |
@StringRes Int |
Short description shown in the feature list |
aboutDescription |
@StringRes Int? |
Longer description shown in the feature detail screen |
permissionKeys |
List<String> |
Required permissions (maps to PermissionRegistry) |
showToggle |
Boolean |
Whether to show an enable/disable toggle on the feature card |
hasMoreSettings |
Boolean |
Whether to show a "Settings" button that opens a detail screen |
isVisibleInMain |
Boolean |
Whether to show in the main features list (false = hidden helper feature) |
isBeta |
Boolean |
Mark with a beta badge in the UI |
parentFeatureId |
String? |
ID of the parent feature for grouping (null = top-level) |
animationRes |
@RawRes Int? |
Lottie animation for the feature detail screen header |
searchableSettings |
List<SearchSetting> |
Individual settings within this feature that are globally searchable |
Features form a tree structure via parentFeatureId. The getFilteredFeatures() method recursively evaluates visibility — a child feature is only shown if its parent is also visible.
Display (parent)
├── Navigation (child of Display)
├── Essentials on Display (child of Display)
├── Always on Display (child of Display)
├── Status bar icons (child of Display)
├── Caffeinate (child of Display)
├── Text and animations (child of Display)
├── Screen refresh rate (child of Display, Pixel only)
├── Lock screen clock (child of Display, Pixel only)
├── Dynamic night light (child of Display)
├── Smart pixels (child of Display)
├── Other customizations (child of Display)
└── Pocket mode (child of Display, non-Pixel only)
isDeviceSupported(context) is overridden for device-specific features:
// Pixel-only feature:
override fun isDeviceSupported(context: Context) = DeviceUtils.isGoogleDevice()
// Non-Pixel only:
override fun isDeviceSupported(context: Context) = !DeviceUtils.isGoogleDevice()getFilteredFeatures(includeUnsupported = false) returns only supported features; passing true includes all regardless of device.
PermissionRegistry maintains the reverse mapping: which features require each permission. This is used to show the affected features list in the permission request UI.
// Register a feature as requiring a permission:
PermissionRegistry.register("ACCESSIBILITY", R.string.feat_screen_off_widget_title)
// Query which features need a permission:
val featuresNeedingAccessibility = PermissionRegistry.getFeatures("ACCESSIBILITY")| Key | Android Permission / System |
|---|---|
ACCESSIBILITY |
Accessibility Service enabled for Essentials |
NOTIFICATION_LISTENER |
Notification Listener Service enabled |
WRITE_SECURE_SETTINGS |
android.permission.WRITE_SECURE_SETTINGS (ADB) |
WRITE_SETTINGS |
android.permission.WRITE_SETTINGS |
SHIZUKU |
Shizuku binder authorized |
ROOT |
su shell available |
DRAW_OVERLAYS |
android.permission.SYSTEM_ALERT_WINDOW |
USAGE_STATS |
Usage access granted |
POST_NOTIFICATIONS |
android.permission.POST_NOTIFICATIONS |
BLUETOOTH_CONNECT |
android.permission.BLUETOOTH_CONNECT |
BLUETOOTH_SCAN |
android.permission.BLUETOOTH_SCAN |
READ_PHONE_STATE |
android.permission.READ_PHONE_STATE |
READ_CALENDAR |
android.permission.READ_CALENDAR |
LOCATION |
android.permission.ACCESS_FINE_LOCATION |
BACKGROUND_LOCATION |
android.permission.ACCESS_BACKGROUND_LOCATION |
DEVICE_ADMIN |
Device Administrator activated |
NOTIFICATION_POLICY |
android.permission.ACCESS_NOTIFICATION_POLICY |
SearchRegistry enables global in-app search across all feature settings. Each SearchSetting inside a feature defines:
SearchSetting(
title = R.string.search_setting_title,
description = R.string.search_setting_desc,
targetSettingHighlightKey = "unique_setting_key", // used to scroll-to and highlight the setting
keywordsRes = R.array.keywords_for_this_setting, // optional keyword synonyms
parentFeatureTitle = R.string.parent_feature_title // optional breadcrumb
)When a user searches, Essentials matches against title, description, and keyword arrays — then deep-links to the feature screen with the target setting highlighted.
- Add a new
object : Feature(...)entry toFeatureRegistry.ALL_FEATURES - Register its permission mappings in
PermissionRegistry(initPermissionRegistry()) - Add any searchable settings as
SearchSettingentries in the feature'ssearchableSettings - Create the corresponding UI composable in
ui/features/<category>/ - Add strings to
strings.xmlfor title, description, and any search entries - Handle ViewModel state in
MainViewModel(or the relevant sub-ViewModel)