android-ai-skills 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +250 -0
- package/android-auth-identity/SKILL.md +36 -0
- package/android-auth-identity/references/biometric_auth.md +36 -0
- package/android-auth-identity/references/credential_manager.md +32 -0
- package/android-auth-identity/references/google_sign_in.md +39 -0
- package/android-auth-identity/references/session_security.md +33 -0
- package/android-auth-identity/references/token_management.md +33 -0
- package/android-ci-cd/SKILL.md +36 -0
- package/android-ci-cd/references/build_optimization.md +39 -0
- package/android-ci-cd/references/fastlane.md +34 -0
- package/android-ci-cd/references/github_actions.md +35 -0
- package/android-ci-cd/references/play_store_deployment.md +36 -0
- package/android-ci-cd/references/signing_ci.md +39 -0
- package/android-dependency-injection/SKILL.md +36 -0
- package/android-dependency-injection/references/common_mistakes.md +37 -0
- package/android-dependency-injection/references/hilt_setup.md +32 -0
- package/android-dependency-injection/references/multi_module_di.md +31 -0
- package/android-dependency-injection/references/scoping.md +34 -0
- package/android-dependency-injection/references/testing_di.md +36 -0
- package/android-local-storage/SKILL.md +36 -0
- package/android-local-storage/references/cache_strategies.md +33 -0
- package/android-local-storage/references/datastore.md +35 -0
- package/android-local-storage/references/encryption.md +35 -0
- package/android-local-storage/references/offline_first.md +39 -0
- package/android-local-storage/references/room_patterns.md +34 -0
- package/android-maps-location/SKILL.md +35 -0
- package/android-maps-location/references/fused_location.md +40 -0
- package/android-maps-location/references/geofencing.md +36 -0
- package/android-maps-location/references/google_maps.md +39 -0
- package/android-maps-location/references/location_permissions.md +34 -0
- package/android-media/SKILL.md +36 -0
- package/android-media/references/audio_focus.md +34 -0
- package/android-media/references/camerax.md +40 -0
- package/android-media/references/image_loading.md +37 -0
- package/android-media/references/media3_playback.md +40 -0
- package/android-media/references/picture_in_picture.md +34 -0
- package/android-ml-ondevice/SKILL.md +35 -0
- package/android-ml-ondevice/references/gemini_nano.md +36 -0
- package/android-ml-ondevice/references/ml_kit.md +34 -0
- package/android-ml-ondevice/references/model_management.md +41 -0
- package/android-ml-ondevice/references/tensorflow_lite.md +42 -0
- package/android-networking/SKILL.md +36 -0
- package/android-networking/references/caching.md +31 -0
- package/android-networking/references/error_handling.md +32 -0
- package/android-networking/references/ktor_multiplatform.md +33 -0
- package/android-networking/references/okhttp_interceptors.md +34 -0
- package/android-networking/references/retrofit_setup.md +33 -0
- package/bin/android-ai-skills.js +30 -0
- package/firebase-best-practices/SKILL.md +37 -0
- package/firebase-best-practices/references/analytics.md +36 -0
- package/firebase-best-practices/references/cloud_messaging.md +34 -0
- package/firebase-best-practices/references/crashlytics.md +35 -0
- package/firebase-best-practices/references/firebase_auth.md +34 -0
- package/firebase-best-practices/references/firestore.md +36 -0
- package/firebase-best-practices/references/remote_config.md +34 -0
- package/package.json +11 -1
- package/play-billing-best-practices/SKILL.md +36 -0
- package/play-billing-best-practices/references/billing_client_lifecycle.md +28 -0
- package/play-billing-best-practices/references/one_time_purchases.md +29 -0
- package/play-billing-best-practices/references/server_verification.md +34 -0
- package/play-billing-best-practices/references/subscriptions.md +29 -0
- package/play-billing-best-practices/references/testing_billing.md +34 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Play Store Deployment
|
|
2
|
+
|
|
3
|
+
## Gradle Play Publisher
|
|
4
|
+
- Plugin: com.github.triplet.play — automates Play Store uploads.
|
|
5
|
+
- Configure: play { track.set("internal"); defaultToAppBundles.set(true) }.
|
|
6
|
+
- Tasks: publishBundle (upload AAB), publishListing (update metadata), promoteArtifact (promote track).
|
|
7
|
+
- Authenticate with service account JSON key from Google Cloud.
|
|
8
|
+
- Store service account key as CI secret — never commit.
|
|
9
|
+
|
|
10
|
+
## Service Account Setup
|
|
11
|
+
- Create service account in Google Cloud Console.
|
|
12
|
+
- Grant "Service Account User" role.
|
|
13
|
+
- In Play Console: invite service account email with "Release manager" permissions.
|
|
14
|
+
- Download JSON key — store as CI secret.
|
|
15
|
+
- Key has no expiry — rotate periodically for security.
|
|
16
|
+
|
|
17
|
+
## Deployment Strategy
|
|
18
|
+
- Internal track: every merge to main — continuous delivery.
|
|
19
|
+
- Closed testing: weekly or on-demand for QA team.
|
|
20
|
+
- Production: manual trigger or tag-based release.
|
|
21
|
+
- Staged rollout: start at 5-10%, monitor, expand to 100%.
|
|
22
|
+
- Use GitHub Releases or tags to trigger production deployment.
|
|
23
|
+
|
|
24
|
+
## Metadata Management
|
|
25
|
+
- Store release notes in fastlane/metadata/android/ or play/ directory.
|
|
26
|
+
- Per-locale directories: en-US/, es-ES/, etc.
|
|
27
|
+
- Files: title.txt, short_description.txt, full_description.txt, changelogs/*.txt.
|
|
28
|
+
- Automate metadata upload with publishListing task.
|
|
29
|
+
- Version changelog in changelogs/{versionCode}.txt.
|
|
30
|
+
|
|
31
|
+
## Rollback
|
|
32
|
+
- Play Console: halt rollout to stop distribution of current version.
|
|
33
|
+
- Re-promote previous version to resume old version distribution.
|
|
34
|
+
- Alternatively: upload a hotfix with incremented versionCode.
|
|
35
|
+
- Monitor Android Vitals during rollout — halt on crash rate regression.
|
|
36
|
+
- Have a documented rollback procedure before every release.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Signing in CI
|
|
2
|
+
|
|
3
|
+
## Keystore Management
|
|
4
|
+
- Never commit keystore files to the repository.
|
|
5
|
+
- Encode keystore as base64: base64 -i release.keystore | pbcopy.
|
|
6
|
+
- Store base64 string as CI secret: KEYSTORE_BASE64.
|
|
7
|
+
- Decode in CI: echo $KEYSTORE_BASE64 | base64 --decode > release.keystore.
|
|
8
|
+
- Delete keystore after build completes — don't leave on CI runner.
|
|
9
|
+
|
|
10
|
+
## Secret Configuration
|
|
11
|
+
- Required secrets: KEYSTORE_BASE64, KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD.
|
|
12
|
+
- Reference in build.gradle.kts via System.getenv().
|
|
13
|
+
- signingConfigs.release { storeFile = file(keystorePath); storePassword = System.getenv("KEYSTORE_PASSWORD") }.
|
|
14
|
+
- Never hardcode fallback values — fail the build if secrets are missing.
|
|
15
|
+
|
|
16
|
+
## GitHub Actions Example
|
|
17
|
+
```yaml
|
|
18
|
+
- name: Decode keystore
|
|
19
|
+
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > app/release.keystore
|
|
20
|
+
- name: Build release
|
|
21
|
+
env:
|
|
22
|
+
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
23
|
+
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
24
|
+
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
25
|
+
run: ./gradlew bundleRelease
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Play App Signing
|
|
29
|
+
- Upload key (your key) signs the AAB before uploading.
|
|
30
|
+
- Google re-signs with the app signing key for distribution.
|
|
31
|
+
- If upload key is compromised: request reset in Play Console.
|
|
32
|
+
- Upload key can be different from the app signing key.
|
|
33
|
+
|
|
34
|
+
## Best Practices
|
|
35
|
+
- Use separate upload key from app signing key.
|
|
36
|
+
- Rotate upload key periodically (Play Console allows reset).
|
|
37
|
+
- Audit CI secrets access — limit to deployment workflows only.
|
|
38
|
+
- Use environment-specific signing: debug (auto), staging (shared), release (CI secret).
|
|
39
|
+
- Test signed builds locally before relying on CI: ./gradlew bundleRelease.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
---
|
|
3
|
+
name: android-dependency-injection
|
|
4
|
+
description: Dependency injection with Hilt/Dagger — setup, scoping, multi-module, testing.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Android Dependency Injection
|
|
8
|
+
|
|
9
|
+
Cross-cutting skill — always activates alongside the project-type-specific skill.
|
|
10
|
+
|
|
11
|
+
## Hard Rules
|
|
12
|
+
- Use Hilt as the default DI framework for Android projects.
|
|
13
|
+
- Annotate Application class with @HiltAndroidApp.
|
|
14
|
+
- Annotate all Activities and Fragments that use injection with @AndroidEntryPoint.
|
|
15
|
+
- Scope dependencies correctly — @Singleton for app-wide, @ViewModelScoped for ViewModel lifetime.
|
|
16
|
+
- Never inject Activity or Fragment context into @Singleton-scoped objects — use @ApplicationContext.
|
|
17
|
+
- Define bindings in @Module classes — use @Binds for interface-to-implementation, @Provides for construction.
|
|
18
|
+
- Use @Qualifier annotations to distinguish same-type bindings — not named strings.
|
|
19
|
+
- Keep modules focused — one module per feature boundary.
|
|
20
|
+
- Use constructor injection over field injection wherever possible.
|
|
21
|
+
- Never use Dagger directly when Hilt is available — Hilt reduces boilerplate.
|
|
22
|
+
|
|
23
|
+
## Core Patterns
|
|
24
|
+
- @InstallIn(SingletonComponent::class) for app-wide singletons.
|
|
25
|
+
- @InstallIn(ViewModelComponent::class) for ViewModel-scoped dependencies.
|
|
26
|
+
- Use @AssistedInject for WorkManager Workers and other assisted construction.
|
|
27
|
+
- Define interface in feature-api, implementation binding in feature-impl module.
|
|
28
|
+
- Use @EntryPoint for injecting into classes Hilt doesn't manage (ContentProvider, etc.).
|
|
29
|
+
- Wrap third-party SDK initialization in @Provides methods for testability.
|
|
30
|
+
|
|
31
|
+
## References
|
|
32
|
+
- references/hilt_setup.md
|
|
33
|
+
- references/scoping.md
|
|
34
|
+
- references/multi_module_di.md
|
|
35
|
+
- references/testing_di.md
|
|
36
|
+
- references/common_mistakes.md
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Common DI Mistakes
|
|
2
|
+
|
|
3
|
+
## Scoping Leaks
|
|
4
|
+
- Injecting Activity context into @Singleton scope — Activity leaks.
|
|
5
|
+
- Fix: use @ApplicationContext for singleton-scoped dependencies.
|
|
6
|
+
- Injecting ViewModel into @Singleton — ViewModel outlives its scope.
|
|
7
|
+
- Fix: use @ViewModelScoped or don't scope the consuming class.
|
|
8
|
+
|
|
9
|
+
## Missing Entry Points
|
|
10
|
+
- Forgetting @AndroidEntryPoint on Activity that hosts injected Fragments.
|
|
11
|
+
- Forgetting @HiltAndroidApp on Application class.
|
|
12
|
+
- Forgetting to add hilt-android-compiler to KSP configuration.
|
|
13
|
+
- Error: "Hilt Activity must be attached to an @AndroidEntryPoint Application."
|
|
14
|
+
|
|
15
|
+
## Circular Dependencies
|
|
16
|
+
- A depends on B, B depends on A — Dagger/Hilt fails at compile time.
|
|
17
|
+
- Fix: introduce an interface to break the cycle.
|
|
18
|
+
- Fix: use Provider<T> or Lazy<T> for deferred resolution.
|
|
19
|
+
- Review dependency graph if cycles emerge — usually indicates design issue.
|
|
20
|
+
|
|
21
|
+
## Field Injection Pitfalls
|
|
22
|
+
- @Inject lateinit var in Fragments — must call inject before accessing.
|
|
23
|
+
- Field injection not available in data classes or objects.
|
|
24
|
+
- Prefer constructor injection — field injection hides dependencies.
|
|
25
|
+
- Only use field injection where constructor injection is impossible (Activities, Fragments).
|
|
26
|
+
|
|
27
|
+
## Module Mistakes
|
|
28
|
+
- @Provides without @Module — binding is silently ignored.
|
|
29
|
+
- @Module without @InstallIn — compile error (Hilt requires component).
|
|
30
|
+
- @Binds with implementation class — must be abstract function in abstract module.
|
|
31
|
+
- @Provides returning interface type but Hilt can't find the implementation.
|
|
32
|
+
|
|
33
|
+
## Multi-Module Issues
|
|
34
|
+
- Missing feature-impl dependency in :app module — binding not found at compile time.
|
|
35
|
+
- Duplicate bindings across modules — use @Qualifier to disambiguate.
|
|
36
|
+
- Installing test module in wrong component — bindings not visible to the test.
|
|
37
|
+
- Feature module with @InstallIn(ActivityComponent::class) — consider if SingletonComponent is more appropriate.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Hilt Setup
|
|
2
|
+
|
|
3
|
+
## Gradle Configuration
|
|
4
|
+
- Add hilt-android and hilt-android-compiler (KSP) dependencies.
|
|
5
|
+
- Apply com.google.dagger.hilt.android plugin.
|
|
6
|
+
- Use KSP (not KAPT) for faster annotation processing.
|
|
7
|
+
- Add hilt-navigation-compose for ViewModel injection in Navigation Compose.
|
|
8
|
+
|
|
9
|
+
## Application
|
|
10
|
+
- Annotate Application class with @HiltAndroidApp.
|
|
11
|
+
- This generates the Hilt component hierarchy rooted at SingletonComponent.
|
|
12
|
+
- Application class is required — Hilt cannot work without it.
|
|
13
|
+
|
|
14
|
+
## Android Entry Points
|
|
15
|
+
- @AndroidEntryPoint on Activities that use injection.
|
|
16
|
+
- @AndroidEntryPoint on Fragments that use injection.
|
|
17
|
+
- Activities must be @AndroidEntryPoint if their Fragments use injection.
|
|
18
|
+
- Services, BroadcastReceivers, and Views can also be @AndroidEntryPoint.
|
|
19
|
+
|
|
20
|
+
## Modules
|
|
21
|
+
- @Module + @InstallIn(Component::class) for providing dependencies.
|
|
22
|
+
- @Provides for creating instances with constructor logic.
|
|
23
|
+
- @Binds for mapping interface to implementation (more efficient than @Provides).
|
|
24
|
+
- companion object with @Provides for static factory methods.
|
|
25
|
+
- Separate modules by concern: NetworkModule, DatabaseModule, RepositoryModule.
|
|
26
|
+
|
|
27
|
+
## ViewModel
|
|
28
|
+
- Annotate ViewModel with @HiltViewModel.
|
|
29
|
+
- Use @Inject constructor for ViewModel dependencies.
|
|
30
|
+
- Access in Compose: hiltViewModel() from hilt-navigation-compose.
|
|
31
|
+
- Access in Fragment: by viewModels().
|
|
32
|
+
- SavedStateHandle is automatically available in @HiltViewModel constructor.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Multi-Module DI
|
|
2
|
+
|
|
3
|
+
## Pattern
|
|
4
|
+
- feature-api module: defines interfaces (Repository, UseCase contracts).
|
|
5
|
+
- feature-impl module: implements interfaces, provides Hilt bindings.
|
|
6
|
+
- :app module: depends on all feature-impl modules, wires everything together.
|
|
7
|
+
- Core modules: provide shared utilities (networking, database).
|
|
8
|
+
|
|
9
|
+
## Binding in Feature Modules
|
|
10
|
+
- Define @Module in feature-impl with @Binds mapping interface → implementation.
|
|
11
|
+
- @InstallIn(SingletonComponent::class) for cross-feature shared instances.
|
|
12
|
+
- @InstallIn(ViewModelComponent::class) for per-ViewModel instances.
|
|
13
|
+
- Feature-impl module has implementation dependency on feature-api.
|
|
14
|
+
|
|
15
|
+
## Cross-Feature Dependencies
|
|
16
|
+
- Feature A needs Feature B's repository: depend on feature-b-api (interface only).
|
|
17
|
+
- Hilt resolves the binding from feature-b-impl at compile time in :app.
|
|
18
|
+
- Features never depend on other feature-impl modules directly.
|
|
19
|
+
- This maintains loose coupling and parallel build.
|
|
20
|
+
|
|
21
|
+
## Navigation DI
|
|
22
|
+
- Feature-api exposes route classes — no DI needed for navigation contracts.
|
|
23
|
+
- ViewModels in feature-impl use @HiltViewModel with injected dependencies.
|
|
24
|
+
- Use hiltViewModel() in composable destinations for automatic scoping.
|
|
25
|
+
- Shared data: pass via navigation arguments, not shared ViewModel instances.
|
|
26
|
+
|
|
27
|
+
## Aggregation
|
|
28
|
+
- :app module aggregates all @InstallIn modules automatically.
|
|
29
|
+
- Hilt scans all transitive dependencies for @Module classes.
|
|
30
|
+
- No manual component builder needed — Hilt handles aggregation.
|
|
31
|
+
- Use @EntryPoint when injecting into non-Hilt-managed classes in feature modules.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Scoping
|
|
2
|
+
|
|
3
|
+
## Component Hierarchy
|
|
4
|
+
- SingletonComponent: application lifetime — @Singleton scope.
|
|
5
|
+
- ActivityRetainedComponent: survives configuration changes — @ActivityRetainedScoped.
|
|
6
|
+
- ViewModelComponent: ViewModel lifetime — @ViewModelScoped.
|
|
7
|
+
- ActivityComponent: Activity lifetime — @ActivityScoped.
|
|
8
|
+
- FragmentComponent: Fragment lifetime — @FragmentScoped.
|
|
9
|
+
- ServiceComponent: Service lifetime — @ServiceScoped.
|
|
10
|
+
|
|
11
|
+
## Choosing Scope
|
|
12
|
+
- @Singleton: app-wide singletons (Retrofit, OkHttpClient, Database, DataStore).
|
|
13
|
+
- @ViewModelScoped: shared within a ViewModel's dependency graph.
|
|
14
|
+
- Unscoped (no annotation): new instance every time — default and preferred for stateless objects.
|
|
15
|
+
- Prefer unscoped — only add scope when sharing or lifecycle binding is needed.
|
|
16
|
+
|
|
17
|
+
## Rules
|
|
18
|
+
- Scoped bindings must be installed in a component that matches the scope.
|
|
19
|
+
- @Singleton bindings must be in @InstallIn(SingletonComponent::class).
|
|
20
|
+
- A scoped binding cannot depend on a narrower-scoped binding.
|
|
21
|
+
- Unscoped bindings can be installed in any component.
|
|
22
|
+
- Wider scope = longer lifetime = more memory usage — scope conservatively.
|
|
23
|
+
|
|
24
|
+
## Common Mistakes
|
|
25
|
+
- Over-scoping: making everything @Singleton wastes memory and hides lifecycle issues.
|
|
26
|
+
- Under-scoping: creating expensive objects (database, HTTP client) repeatedly.
|
|
27
|
+
- Injecting Activity/Fragment context into @Singleton — causes memory leaks.
|
|
28
|
+
- Use @ApplicationContext for singletons that need Context.
|
|
29
|
+
- Use @ActivityContext only in @ActivityScoped or narrower components.
|
|
30
|
+
|
|
31
|
+
## Custom Scopes
|
|
32
|
+
- Define custom scopes with @Scope annotation for domain-specific lifetimes.
|
|
33
|
+
- Map to custom Hilt components with @DefineComponent.
|
|
34
|
+
- Rarely needed — standard Hilt scopes cover most cases.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Testing with DI
|
|
2
|
+
|
|
3
|
+
## Unit Tests
|
|
4
|
+
- No Hilt needed — use constructor injection with fakes/mocks.
|
|
5
|
+
- Create fakes of interfaces defined in feature-api modules.
|
|
6
|
+
- Instantiate ViewModel/UseCase directly with fake dependencies.
|
|
7
|
+
- This is the primary reason to use constructor injection everywhere.
|
|
8
|
+
|
|
9
|
+
## Instrumented Tests
|
|
10
|
+
- @HiltAndroidTest on test class.
|
|
11
|
+
- Add HiltAndroidRule as a JUnit rule — must be ordered before other rules.
|
|
12
|
+
- Use hiltRule.inject() in @Before to trigger injection.
|
|
13
|
+
- Test Activity/Fragment with launchActivity or launchFragmentInHiltContainer.
|
|
14
|
+
|
|
15
|
+
## Replacing Bindings
|
|
16
|
+
- @UninstallModules(ProductionModule::class) removes production bindings.
|
|
17
|
+
- Provide test bindings in a @Module @InstallIn @TestInstallIn annotated class.
|
|
18
|
+
- Or use @BindValue to replace a single binding with a field in the test class.
|
|
19
|
+
- @TestInstallIn replaces globally — @UninstallModules + local @Module for per-test replacement.
|
|
20
|
+
|
|
21
|
+
## Custom Test Components
|
|
22
|
+
- Use @CustomTestApplication for tests that need a custom Application class.
|
|
23
|
+
- Combine with @HiltAndroidTest for full DI in custom app contexts.
|
|
24
|
+
- Use HiltTestApplication as the application class in test runner config.
|
|
25
|
+
|
|
26
|
+
## Robolectric
|
|
27
|
+
- Hilt works with Robolectric — use @HiltAndroidTest + @Config(application = HiltTestApplication::class).
|
|
28
|
+
- Combine with RobolectricTestRunner.
|
|
29
|
+
- Faster than instrumented tests — runs on JVM.
|
|
30
|
+
- Some Hilt features may require additional Robolectric configuration.
|
|
31
|
+
|
|
32
|
+
## Best Practices
|
|
33
|
+
- Test business logic without DI framework — pure unit tests.
|
|
34
|
+
- Use DI in tests only when testing integration points (Activity, Fragment, Service).
|
|
35
|
+
- Keep test modules simple — prefer @BindValue over full module replacement.
|
|
36
|
+
- Verify DI graph compiles in CI — Hilt catches missing bindings at compile time.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
---
|
|
3
|
+
name: android-local-storage
|
|
4
|
+
description: Local storage — Room, DataStore, offline-first architecture, caching, encryption.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Android Local Storage
|
|
8
|
+
|
|
9
|
+
Cross-cutting skill — always activates alongside the project-type-specific skill.
|
|
10
|
+
|
|
11
|
+
## Hard Rules
|
|
12
|
+
- Use Room for structured relational data — not raw SQLite.
|
|
13
|
+
- Use DataStore (Preferences or Proto) for key-value data — not SharedPreferences.
|
|
14
|
+
- Export Room schemas (exportSchema = true) and test all migrations.
|
|
15
|
+
- Never perform database operations on the main thread.
|
|
16
|
+
- Use Flow or LiveData for reactive database queries — not one-shot reads for UI.
|
|
17
|
+
- Define a single source of truth (database) — network responses update the database, UI observes the database.
|
|
18
|
+
- Never store sensitive data (tokens, passwords) in Room or DataStore without encryption.
|
|
19
|
+
- Use transactions for multi-table write operations.
|
|
20
|
+
- Keep entities flat — avoid deeply nested objects in Room.
|
|
21
|
+
- Use TypeConverters sparingly — prefer normalized relational design.
|
|
22
|
+
|
|
23
|
+
## Core Patterns
|
|
24
|
+
- Repository pattern: database as the source of truth, network as the updater.
|
|
25
|
+
- Room DAOs return Flow<List<T>> for reactive UI updates.
|
|
26
|
+
- Use @Upsert for insert-or-update operations (Room 2.5+).
|
|
27
|
+
- DataStore with Proto for type-safe structured preferences.
|
|
28
|
+
- Implement cache expiry with timestamps — stale-while-revalidate pattern.
|
|
29
|
+
- Use database-backed pagination with RemoteMediator + Paging 3.
|
|
30
|
+
|
|
31
|
+
## References
|
|
32
|
+
- references/room_patterns.md
|
|
33
|
+
- references/datastore.md
|
|
34
|
+
- references/offline_first.md
|
|
35
|
+
- references/cache_strategies.md
|
|
36
|
+
- references/encryption.md
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Cache Strategies
|
|
2
|
+
|
|
3
|
+
## Time-Based Expiry
|
|
4
|
+
- Store cachedAt timestamp alongside cached data.
|
|
5
|
+
- Check freshness: if (now - cachedAt > maxAge) refresh from network.
|
|
6
|
+
- Different TTLs for different data: user profile (1 hour), feed (5 minutes), config (24 hours).
|
|
7
|
+
- Use stale-while-revalidate: show stale data immediately, refresh in background.
|
|
8
|
+
|
|
9
|
+
## LRU Cache
|
|
10
|
+
- In-memory LRU cache for frequently accessed data.
|
|
11
|
+
- Use LruCache from android.util or custom implementation.
|
|
12
|
+
- Size-based eviction — define max entries or max memory.
|
|
13
|
+
- Use as L1 cache (memory) backed by L2 cache (database).
|
|
14
|
+
|
|
15
|
+
## ETag / Conditional Requests
|
|
16
|
+
- Store ETag from server response alongside cached data.
|
|
17
|
+
- Send If-None-Match header on next request.
|
|
18
|
+
- 304 Not Modified: cache is still valid, no data transfer.
|
|
19
|
+
- Reduces bandwidth while keeping data fresh.
|
|
20
|
+
|
|
21
|
+
## Cache Invalidation
|
|
22
|
+
- User action: pull-to-refresh, data mutation, logout.
|
|
23
|
+
- Push notification: server signals data change via FCM.
|
|
24
|
+
- Time-based: automatic expiry after TTL.
|
|
25
|
+
- Event-based: invalidate related caches when a mutation occurs.
|
|
26
|
+
- Prefer explicit invalidation over time-based for user-facing data.
|
|
27
|
+
|
|
28
|
+
## Multi-Layer Cache
|
|
29
|
+
- L1: in-memory (fast, small, lost on process death).
|
|
30
|
+
- L2: database/disk (slower, persistent across process restarts).
|
|
31
|
+
- L3: network (slowest, always fresh).
|
|
32
|
+
- Read: check L1 → L2 → L3. Write: update all layers.
|
|
33
|
+
- Use for: image caching (Coil/Glide handle this), API response caching, computed results.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# DataStore
|
|
2
|
+
|
|
3
|
+
## Preferences DataStore
|
|
4
|
+
- Replacement for SharedPreferences — coroutine and Flow-based.
|
|
5
|
+
- Create with preferencesDataStore(name = "settings") delegate.
|
|
6
|
+
- Read: dataStore.data.map { prefs -> prefs[KEY] }.
|
|
7
|
+
- Write: dataStore.edit { prefs -> prefs[KEY] = value }.
|
|
8
|
+
- Keys: stringPreferencesKey, intPreferencesKey, booleanPreferencesKey, etc.
|
|
9
|
+
|
|
10
|
+
## Proto DataStore
|
|
11
|
+
- Type-safe structured data — define schema with Protocol Buffers.
|
|
12
|
+
- Create .proto file in src/main/proto/.
|
|
13
|
+
- Implement Serializer<T> for read/write.
|
|
14
|
+
- Read/write are strongly typed — no string keys.
|
|
15
|
+
- Prefer Proto DataStore for complex settings or data with multiple fields.
|
|
16
|
+
|
|
17
|
+
## Migration from SharedPreferences
|
|
18
|
+
- Use SharedPreferencesMigration in DataStore builder.
|
|
19
|
+
- Migration runs once — old SharedPreferences deleted after migration.
|
|
20
|
+
- Handle migration failures — provide default values.
|
|
21
|
+
- Test migration with both old and new data formats.
|
|
22
|
+
|
|
23
|
+
## Best Practices
|
|
24
|
+
- Single DataStore instance per file — create at top level with delegate.
|
|
25
|
+
- Never create DataStore inside Activity/Fragment — use singleton pattern.
|
|
26
|
+
- Handle IOException in data.catch { } — file corruption is possible.
|
|
27
|
+
- Emit default value on error: .catch { emit(emptyPreferences()) }.
|
|
28
|
+
- DataStore writes are atomic — no partial writes.
|
|
29
|
+
- Do not use DataStore for large datasets — use Room instead.
|
|
30
|
+
|
|
31
|
+
## Thread Safety
|
|
32
|
+
- DataStore is thread-safe — concurrent reads and writes are safe.
|
|
33
|
+
- Writes are serialized — ordered by call sequence.
|
|
34
|
+
- Reads are non-blocking — observe via Flow.
|
|
35
|
+
- No need for synchronized blocks or mutexes.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Storage Encryption
|
|
2
|
+
|
|
3
|
+
## EncryptedSharedPreferences
|
|
4
|
+
- Use for key-value secrets (tokens, API keys, small sensitive data).
|
|
5
|
+
- Backed by AES256-SIV (keys) and AES256-GCM (values).
|
|
6
|
+
- Create with MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS).
|
|
7
|
+
- Thread-safe but slower than regular SharedPreferences.
|
|
8
|
+
- Migrate from regular SharedPreferences when adding encryption.
|
|
9
|
+
|
|
10
|
+
## EncryptedFile
|
|
11
|
+
- Use for file-level encryption (documents, exported data).
|
|
12
|
+
- Streaming encryption — suitable for large files.
|
|
13
|
+
- Same MasterKey as EncryptedSharedPreferences.
|
|
14
|
+
- Use for: database exports, log files, downloaded sensitive content.
|
|
15
|
+
|
|
16
|
+
## SQLCipher for Room
|
|
17
|
+
- SQLCipher encrypts the entire Room database file.
|
|
18
|
+
- Use android-database-sqlcipher dependency + SupportFactory.
|
|
19
|
+
- Pass encryption key via SupportFactory(passphrase).
|
|
20
|
+
- Store passphrase in Android Keystore — not hardcoded.
|
|
21
|
+
- Performance impact: ~5-15% slower than unencrypted Room.
|
|
22
|
+
|
|
23
|
+
## Android Keystore
|
|
24
|
+
- Hardware-backed key storage (TEE/StrongBox).
|
|
25
|
+
- Generate keys with KeyGenParameterSpec.
|
|
26
|
+
- Keys never leave the secure hardware.
|
|
27
|
+
- Use for: encrypting local secrets, biometric-bound operations.
|
|
28
|
+
- Keys are not exportable — use Keystore for crypto operations only.
|
|
29
|
+
|
|
30
|
+
## Best Practices
|
|
31
|
+
- Encrypt tokens, passwords, PII, health data, financial data.
|
|
32
|
+
- Do not encrypt non-sensitive data — unnecessary performance cost.
|
|
33
|
+
- Wipe decrypted data from memory after use (overwrite byte arrays).
|
|
34
|
+
- Handle KeyPermanentlyInvalidatedException when biometric enrollment changes.
|
|
35
|
+
- Test encryption on devices with and without hardware-backed Keystore.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Offline-First Architecture
|
|
2
|
+
|
|
3
|
+
## Single Source of Truth
|
|
4
|
+
- Database (Room) is the single source of truth — UI always reads from database.
|
|
5
|
+
- Network responses update the database — never displayed directly to UI.
|
|
6
|
+
- Flow: API → Repository → Database → UI observes database.
|
|
7
|
+
- Benefits: offline access, consistent state, no stale UI.
|
|
8
|
+
|
|
9
|
+
## Repository Pattern
|
|
10
|
+
- Repository exposes Flow<List<T>> from Room DAO.
|
|
11
|
+
- Repository has a refresh() suspend function that fetches from network and updates database.
|
|
12
|
+
- UI calls refresh() on pull-to-refresh or initial load.
|
|
13
|
+
- Database Flow automatically re-emits when data changes.
|
|
14
|
+
|
|
15
|
+
## NetworkBoundResource Pattern
|
|
16
|
+
- Observe database (cache).
|
|
17
|
+
- If cache is stale or empty, fetch from network.
|
|
18
|
+
- Save network response to database.
|
|
19
|
+
- Re-emit updated data from database.
|
|
20
|
+
- Handle loading, success, and error states.
|
|
21
|
+
|
|
22
|
+
## Paging with RemoteMediator
|
|
23
|
+
- Use Paging 3 + RemoteMediator for paginated offline-first lists.
|
|
24
|
+
- RemoteMediator fetches pages from network, stores in Room.
|
|
25
|
+
- PagingSource reads from Room for UI.
|
|
26
|
+
- Room PagingSource auto-invalidates when data changes.
|
|
27
|
+
- Handle PREPEND, APPEND, and REFRESH load types.
|
|
28
|
+
|
|
29
|
+
## Conflict Resolution
|
|
30
|
+
- Last-write-wins: server timestamp determines which version to keep.
|
|
31
|
+
- Client-wins: local changes always override server data.
|
|
32
|
+
- Server-wins: server data always overrides local changes.
|
|
33
|
+
- Choose based on data type — user-generated content needs careful resolution.
|
|
34
|
+
|
|
35
|
+
## Sync Strategy
|
|
36
|
+
- Eager: sync immediately when network is available.
|
|
37
|
+
- Lazy: sync on demand (user opens screen, pull-to-refresh).
|
|
38
|
+
- Periodic: sync on schedule using WorkManager.
|
|
39
|
+
- Push-triggered: sync when FCM notification arrives.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Room Patterns
|
|
2
|
+
|
|
3
|
+
## Database Setup
|
|
4
|
+
- Annotate with @Database(entities = [...], version = N, exportSchema = true).
|
|
5
|
+
- Use singleton pattern: companion object with @Volatile instance + synchronized.
|
|
6
|
+
- Or provide via Hilt @Singleton @Provides.
|
|
7
|
+
- Add fallbackToDestructiveMigration() only for cache databases — never for user data.
|
|
8
|
+
- Configure WAL mode (default on API 16+) for better concurrent read/write.
|
|
9
|
+
|
|
10
|
+
## Entities
|
|
11
|
+
- @Entity with @PrimaryKey — prefer autoGenerate = true for local IDs.
|
|
12
|
+
- Use @ColumnInfo for custom column names.
|
|
13
|
+
- @Embedded for flattening nested objects into the same table.
|
|
14
|
+
- @TypeConverter for complex types (Date, Enum, List) — register in @Database.
|
|
15
|
+
- Keep entities flat — deeply nested objects should be separate entities with relations.
|
|
16
|
+
|
|
17
|
+
## DAOs
|
|
18
|
+
- Return Flow<List<T>> for reactive queries — Room re-emits on table changes.
|
|
19
|
+
- Use suspend for write operations (insert, update, delete).
|
|
20
|
+
- @Upsert (Room 2.5+) for insert-or-update in one operation.
|
|
21
|
+
- @Transaction for multi-table operations.
|
|
22
|
+
- @RawQuery for dynamic queries — use sparingly, prefer type-safe DAO methods.
|
|
23
|
+
|
|
24
|
+
## Relations
|
|
25
|
+
- @Relation for one-to-many and many-to-many relationships.
|
|
26
|
+
- Use intermediary data classes with @Embedded parent + @Relation children.
|
|
27
|
+
- @Junction for many-to-many with a junction table.
|
|
28
|
+
- Always use @Transaction on queries with @Relation — ensures consistency.
|
|
29
|
+
|
|
30
|
+
## Migrations
|
|
31
|
+
- Provide Migration(oldVersion, newVersion) with SQL statements.
|
|
32
|
+
- Test every migration path with MigrationTestHelper.
|
|
33
|
+
- Use @AutoMigration for simple changes (add column, add table).
|
|
34
|
+
- Never use fallbackToDestructiveMigration for databases with user data.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
---
|
|
3
|
+
name: android-maps-location
|
|
4
|
+
description: Maps & location — Google Maps SDK, FusedLocationProvider, geofencing, permissions.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Android Maps & Location
|
|
8
|
+
|
|
9
|
+
Cross-cutting skill — always activates alongside the project-type-specific skill.
|
|
10
|
+
|
|
11
|
+
## Hard Rules
|
|
12
|
+
- Use FusedLocationProviderClient — not LocationManager directly.
|
|
13
|
+
- Request only the location accuracy you need (COARSE vs FINE).
|
|
14
|
+
- Handle both approximate (COARSE) and precise (FINE) location grants (API 31+).
|
|
15
|
+
- Remove location updates when not needed — do not leak location listeners.
|
|
16
|
+
- Use foreground service with location type for background location access.
|
|
17
|
+
- ACCESS_BACKGROUND_LOCATION requires separate permission request and justification.
|
|
18
|
+
- Protect Google Maps API key with app restriction (SHA-1 + package name) in Cloud Console.
|
|
19
|
+
- Do not hardcode API keys in source — use Secrets Gradle Plugin or local.properties.
|
|
20
|
+
- Handle location permission denial gracefully — degrade to manual location entry.
|
|
21
|
+
- Test with mock locations enabled and disabled.
|
|
22
|
+
|
|
23
|
+
## Core Patterns
|
|
24
|
+
- Request FINE + COARSE together — user can downgrade to approximate on API 31+.
|
|
25
|
+
- Use LocationRequest.Builder with appropriate priority and interval.
|
|
26
|
+
- Prefer geofencing over continuous polling for location-triggered actions.
|
|
27
|
+
- Use Maps Compose library for Compose integration.
|
|
28
|
+
- Cluster markers for dense map points — avoid rendering thousands of markers.
|
|
29
|
+
- Cache geocoding results — avoid repeated API calls for the same coordinates.
|
|
30
|
+
|
|
31
|
+
## References
|
|
32
|
+
- references/google_maps.md
|
|
33
|
+
- references/fused_location.md
|
|
34
|
+
- references/geofencing.md
|
|
35
|
+
- references/location_permissions.md
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Fused Location Provider
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
- Use com.google.android.gms:play-services-location dependency.
|
|
5
|
+
- Get client: LocationServices.getFusedLocationProviderClient(context).
|
|
6
|
+
- Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission.
|
|
7
|
+
- Check permission before every location request.
|
|
8
|
+
|
|
9
|
+
## Last Known Location
|
|
10
|
+
- getLastLocation(): fast, no power cost, may return null.
|
|
11
|
+
- Returns null if: location turned off, device just booted, Google Play Services data cleared.
|
|
12
|
+
- Use as initial location while waiting for fresh updates.
|
|
13
|
+
- Always handle null — don't assume location is available.
|
|
14
|
+
|
|
15
|
+
## Location Updates
|
|
16
|
+
- Build LocationRequest with Priority and interval.
|
|
17
|
+
- PRIORITY_HIGH_ACCURACY: GPS + network (most power, ~1m accuracy).
|
|
18
|
+
- PRIORITY_BALANCED_POWER_ACCURACY: network-based (~100m, lower power).
|
|
19
|
+
- PRIORITY_LOW_POWER: passive, minimal power (~1km accuracy).
|
|
20
|
+
- PRIORITY_PASSIVE: only receive locations from other apps' requests.
|
|
21
|
+
|
|
22
|
+
## Request Configuration
|
|
23
|
+
- setIntervalMillis: desired update interval.
|
|
24
|
+
- setMinUpdateIntervalMillis: fastest acceptable interval.
|
|
25
|
+
- setMaxUpdateDelayMillis: batch delivery delay for power saving.
|
|
26
|
+
- setMinUpdateDistanceMeters: minimum displacement for update.
|
|
27
|
+
- Use setWaitForAccurateLocation(true) for initial high-accuracy fix.
|
|
28
|
+
|
|
29
|
+
## Receiving Updates
|
|
30
|
+
- requestLocationUpdates(request, callback, looper) for callback-based.
|
|
31
|
+
- requestLocationUpdates(request, pendingIntent) for background delivery.
|
|
32
|
+
- Always call removeLocationUpdates() when done.
|
|
33
|
+
- Use callbackFlow for coroutine/Flow integration.
|
|
34
|
+
|
|
35
|
+
## Best Practices
|
|
36
|
+
- Request lowest acceptable priority — save battery.
|
|
37
|
+
- Remove updates in onPause/onStop — unless background location is needed.
|
|
38
|
+
- Background location requires foreground service with location type (API 29+).
|
|
39
|
+
- ACCESS_BACKGROUND_LOCATION is a separate permission request.
|
|
40
|
+
- Handle location settings: use SettingsClient.checkLocationSettings() and prompt user.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Geofencing
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
- Use GeofencingClient from play-services-location.
|
|
5
|
+
- Requires ACCESS_FINE_LOCATION (and ACCESS_BACKGROUND_LOCATION for background).
|
|
6
|
+
- Maximum 100 geofences per app per device.
|
|
7
|
+
- Geofences are lost on reboot — re-register in BOOT_COMPLETED receiver.
|
|
8
|
+
|
|
9
|
+
## Creating Geofences
|
|
10
|
+
- Geofence.Builder() with requestId, circularRegion(lat, lng, radius), expirationDuration.
|
|
11
|
+
- Transition types: GEOFENCE_TRANSITION_ENTER, EXIT, DWELL.
|
|
12
|
+
- setLoiteringDelay() for DWELL transitions — how long user must stay.
|
|
13
|
+
- Minimum reliable radius: ~100m (smaller radii are unreliable).
|
|
14
|
+
- Use unique requestId for each geofence — needed for removal.
|
|
15
|
+
|
|
16
|
+
## Registering
|
|
17
|
+
- Build GeofencingRequest with addGeofences() and initial trigger.
|
|
18
|
+
- INITIAL_TRIGGER_ENTER: trigger if already inside geofence.
|
|
19
|
+
- INITIAL_TRIGGER_DWELL: trigger if already dwelling.
|
|
20
|
+
- Register with addGeofences(request, pendingIntent).
|
|
21
|
+
- PendingIntent delivers to BroadcastReceiver or Service.
|
|
22
|
+
|
|
23
|
+
## Handling Transitions
|
|
24
|
+
- Create a BroadcastReceiver to handle geofence events.
|
|
25
|
+
- Extract GeofencingEvent from Intent: GeofencingEvent.fromIntent(intent).
|
|
26
|
+
- Check for errors: geofencingEvent.hasError().
|
|
27
|
+
- Get transition type and triggering geofences from the event.
|
|
28
|
+
- Post notification or trigger WorkManager task on transition.
|
|
29
|
+
|
|
30
|
+
## Best Practices
|
|
31
|
+
- Use geofencing instead of continuous location polling — much lower power.
|
|
32
|
+
- Keep radius ≥ 100m for reliable detection.
|
|
33
|
+
- Handle Geofence expiration — re-register if long-lived.
|
|
34
|
+
- Test with mock locations — real geofence testing is slow.
|
|
35
|
+
- Handle permission denial — geofencing requires background location for useful behavior.
|
|
36
|
+
- Re-register geofences on app update and device reboot.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Google Maps SDK
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
- Add com.google.android.gms:play-services-maps dependency.
|
|
5
|
+
- Add API key in AndroidManifest.xml: <meta-data android:name="com.google.android.geo.API_KEY" android:value="${MAPS_API_KEY}" />.
|
|
6
|
+
- Inject API key from local.properties or CI secrets via Secrets Gradle Plugin.
|
|
7
|
+
- Restrict API key in Google Cloud Console: Android app restriction + Maps SDK API restriction.
|
|
8
|
+
|
|
9
|
+
## Maps Compose
|
|
10
|
+
- Use maps-compose library for Compose integration.
|
|
11
|
+
- GoogleMap composable with CameraPositionState.
|
|
12
|
+
- Marker(state = MarkerState(position = latLng), title = "Label").
|
|
13
|
+
- rememberCameraPositionState for controlling camera position.
|
|
14
|
+
- Use MapProperties for map type, gestures, and controls.
|
|
15
|
+
|
|
16
|
+
## Markers
|
|
17
|
+
- Use Marker composable for individual points.
|
|
18
|
+
- MarkerState for position — can be animated.
|
|
19
|
+
- Custom marker icons: BitmapDescriptorFactory.fromResource or fromBitmap.
|
|
20
|
+
- Use clustering for dense marker sets: maps-utils library + Clustering composable.
|
|
21
|
+
- InfoWindow for tap-to-reveal details on markers.
|
|
22
|
+
|
|
23
|
+
## Camera Control
|
|
24
|
+
- CameraUpdateFactory.newLatLngZoom(position, zoom) for programmatic movement.
|
|
25
|
+
- Animate: cameraPositionState.animate(cameraUpdate, durationMs).
|
|
26
|
+
- Set bounds: CameraUpdateFactory.newLatLngBounds(bounds, padding) to fit all markers.
|
|
27
|
+
- Handle camera move events: onCameraMove callback.
|
|
28
|
+
|
|
29
|
+
## Styling
|
|
30
|
+
- Use MapStyleOptions for custom map styling (JSON style).
|
|
31
|
+
- Cloud-based map styling for dynamic style updates.
|
|
32
|
+
- Dark mode: apply dark map style when app is in dark theme.
|
|
33
|
+
- Hide/show map features: POIs, transit, roads, labels.
|
|
34
|
+
|
|
35
|
+
## Best Practices
|
|
36
|
+
- Load map lazily — don't initialize until the map screen is visible.
|
|
37
|
+
- Handle Google Play Services unavailability gracefully.
|
|
38
|
+
- Set initial camera position from user's last known location.
|
|
39
|
+
- Test on devices without Google Play Services (Huawei, etc.) — provide fallback.
|