This document provides important guidelines, architectural decisions, and context for AI agents (like Jules) working on the LTvLauncher project. Read these instructions carefully before making modifications to ensure consistency, performance, and stability.
- Name: LTvLauncher (a fork of FLauncher)
- Language: Dart / Flutter
- Platform: Android TV
- Package Names:
com.leanbitlab.ltvL(and legacyme.efesser.flauncher)
- State Management: The project uses the
providerpackage (Selector,ChangeNotifierProvider,context.read) for state management and dependency injection. - Service Logic: Complex logic involving joining categories and applications, and visibility filtering, is handled in
AppsService._refreshStaterather than the database layer. Tests for these behaviors reside intest/providers/apps_service_test.dart. - Race Conditions: To prevent race conditions in asynchronous
ChangeNotifiermethods that update state, use a sequence counter (e.g.,_callCount) incremented at method entry and compare it to a local snapshot before callingnotifyListeners(). - Localization: Managed with
flutter gen-l10nvia.arbfiles inlib/l10n/. The generatedAppLocalizationsclasses must be imported viapackage:flutter_gen/gen_l10n/app_localizations.dart(do not use relative paths). - Settings Service Optimization: Cache SharedPreferences values in local fields initialized during construction. Setters must update both the local cache and SharedPreferences to avoid N+1 synchronous read overhead. Use a
Keysuffix for SharedPreferences string constants (e.g.,_themesKey). - Backward Compatibility: When renaming or refactoring settings features, preserve the underlying SharedPreferences string keys (e.g.,
'wifi_usage_period') to maintain compatibility for existing users. - Cache Invalidation: In
AppsService, ensure cache invalidation methods are called not only when the primary collection (_categoriesById) changes, but also when mutable properties of individual items (e.g.,category.name) are updated.
- Test Framework: Use
mockitoandflutter_test. Run the test suite withflutter test(do not usedart test). - Mock Generation: Mocks are defined in
test/mocks.dartusing@GenerateMocks. To generate new mocks, append the target class to the array and runflutter pub run build_runner build --delete-conflicting-outputs. - Model Tests: Unit tests for data models should be organized under
test/models/mirroringlib/models/. - Database Testing:
- Instantiate the database with
FLauncherDatabase.inMemory()to avoid side effects. - When testing Drift database operations with foreign keys, ensure parent records are inserted first (e.g.,
database.persistApps()).
- Instantiate the database with
- Provider / UI Widget Testing:
- Wrap the test widget in a
MultiProviderandDirectionality(e.g.,textDirection: TextDirection.ltr). - Provide default stubs for visual preference getters in mocked
SettingsService(e.g.,accentColorHex,theme,appHighlightAnimationEnabled) to prevent null/type errors. - For
intldate formatting (likeDateTimeWidget), callawait initializeDateFormatting('en_US');insetUpAll.
- Wrap the test widget in a
- FocusNode Geometry: When testing FocusNode geometry or traversal (critical for Android TV), use
WidgetTesterwithStackandPositionedto explicitly lay out nodes, as they need to be attached to a rendered widget tree to have dimensions. - AppsService Testing:
- Wait for the service to signal completion by polling the
initializedproperty (while (!appsService.initialized) { await Future.delayed(...); }). - Ensure
MockFLauncherChannelis stubbed forgetApplicationIconandgetApplicationBannerto avoidMissingStubError.
- Wait for the service to signal completion by polling the
- Asynchronous Interactions: Use
await Future.delayed(Duration.zero);to allow the microtask queue to clear before making assertions on unawaited asynchronous calls. - SharedPreferences & Channels: Initialize test environments using
SharedPreferencesStorePlatform.instance = InMemorySharedPreferencesStore.empty();and mock channel calls viaTestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler. - Test File Naming: Avoid versioned suffixes like
_v2in filenames; use descriptive names (e.g.,apps_service_batch_test.dart).
- File I/O: Avoid synchronous file I/O (e.g.,
File.existsSync) on the main UI thread. PreferFile.existsandawaitthe result to prevent jank. - Map Lookups: Prefer a single lookup with a null check (
final val = map[key]; if (val != null)) overcontainsKeyfollowed by a forced unwrap (map[key]!). - Concurrency: Use
package:poolto manage concurrency limits for unawaited background tasks, particularly for intensive operations like fetching app icons and banners. - Database Operations (Drift): Use the
batchAPI for multiple insertions, or rely on existing custom batch methods (likeinsertAppsCategories) for efficient single-transaction operations. Bulk operations (e.g.,addAllToCategory) prevent N+1 query patterns. - Logging: Prefer
logfromdart:developeroverprint. Use thenameparameter to provide class context (e.g.,name: 'NetworkService') and pass exceptions to theerrorparameter. - Image Rebuilding: To force an
Imagewidget to rebuild when aFileImagechanges but its path remains the same, append a version counter or timestamp to itsKey(e.g.,Key("background_$version")).
- Painting Order: In
ListVieworGridView, items are painted sequentially. To prevent scaled-up focused items (like app banners) from being visually cropped by siblings, cap the maximum scale factor relative to the item'smaxWidthusingLayoutBuilderso expansion stays within spacing gaps. - Keyboard Events: When migrating from
onKeytoonKeyEvent, ensureKeyRepeatEventis explicitly handled alongsideKeyDownEventto preserve continuous/long-press interactions.
- Java Compatibility: The Android project is configured with Java 21 compatibility (source and target).
- URI Injection Prevention: Validate
packageNamestrings received from Flutter MethodChannels (e.g., regex^[a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)+$) before using them inUri.fromPartsto construct Intents. - NetworkStatsManager: When querying
querySummaryForDevice, ensure thesubscriberIdparameter is passed asnullrather than an empty string"". - Backups: Android backups are explicitly disabled via
android:allowBackup="false"and removingandroid:fullBackupContentinAndroidManifest.xmlto prevent unauthorized data extraction via adb backup. - Multi-Manifest: The project follows a multi-manifest structure with source sets for
main,debug, andprofilelocated underandroid/app/src/.
- Git & Environment Files: Never edit the
.gitignorefile. - Dependency Issues (Environment Constraints):
- Commands like
flutter pub getorflutter testmight unintentionally updatepubspec.lockwith newer/beta SDK constraints. Restorepubspec.lock(e.g.,git restore pubspec.lock) before committing. - Constant evaluation errors (
FontWeight) might occur whengoogle_fonts>= 6.3.0. Pinninggoogle_fontsto a lower version like6.2.1inpubspec.yamlcan bypass this during test compilation. - The execution environment may encounter timeouts (>400s) on network-heavy commands (
flutter pub get,flutter test,flutter analyze). Missing.dart_tool/triggerspub getautomatically. - Use
dart analyze <file> | grep "<diagnostic_code>"to quickly verify specific static analysis fixes without resolving all environment dependencies. - Standard Git network operations (e.g.,
git fetch) might timeout. Usegit restoreand manual re-application if necessary for merge conflicts.
- Commands like
- Android Gradle: To execute Android Gradle commands (
gradle test), theandroid/local.propertiesfile must definesdk.dirandflutter.sdk(/opt/flutterin this environment). - Releases & Fastlane:
- When updating
versionCodeinpubspec.yaml, rename the corresponding Fastlane changelog file infastlane/metadata/android/en-US/changelogs/to match (e.g.,6101.txt). - For Reproducible Builds (RB) on F-Droid / IzzyOnDroid, ensure release tags exactly match the commit where the
pubspec.yamlversion aligns with the published APK (e.g.,version: 3.2.2+6101).
- When updating