{"owner":"duckduckgo","repo":"Android","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md"],"skills":{"CLAUDE.md":"# CLAUDE.md — DuckDuckGo Android Browser\n\nDuckDuckGo Android is a privacy-focused browser built as a large multi-module Gradle project,\nproviding built-in search, tracker blocking, HTTPS enforcement and other privacy features.\n\n**Versions** (SDK levels, Kotlin, Gradle, libraries) live in the build files — don't restate them here:\n`min_sdk` / `target_sdk` / `compile_sdk` in `build.gradle`, `version.kotlin` in `versions.properties`,\nand the Gradle version in `gradle/wrapper/gradle-wrapper.properties`.\n**Build:** AGP via refreshVersions. DI is Anvil/Dagger2 today, with a migration to **Metro** in flight\n(dual-build selected by the `ddg.di` Gradle property; see `build.gradle`).\n**Toolchain:** Kotlin JVM target 17; building requires **JDK 21** (Metro compiler plugin).\n\nNotable libraries: Room, Retrofit/OkHttp/Moshi, Coroutines, Jetpack Compose (selective), WorkManager,\n`logcat` (Square), KSP for annotation processing. Versions are in `versions.properties` and the module\n`build.gradle` files.\n\n---\n\n## Privacy invariants for anything leaving the device\n\nThis is a privacy browser; these hold for every outbound value — pixels, wide events, logs, crash\nreports, debug output — not just telemetry you are deliberately designing.\n\n- **No PII.** Never emails, names, account IDs, usernames or phone numbers, in a name or a value.\n- **No URLs, domains or page titles.** Breakage reports are the one controlled exception, with explicit\n  user consent.\n- **No correlation IDs.** No session IDs, GUIDs or exact timestamps — anything that links events to one\n  user session.\n- **Bucket numeric values.** Exact durations, byte counts and item counts fingerprint users; send ranges.\n- **Bounded enums over free-form strings.** High-cardinality unbounded strings are both a privacy risk\n  and unanalysable.\n\nAdding or changing a pixel or the pixel registry also requires privacy triage — see\n`.claude/docs/pixels.md`.\n\n---\n\n## Read these when the situation applies\n\nThese files are not in context. Read the whole file before doing the work it covers — don't rely on\nwhat you remember of it.\n\n| Read | When |\n|---|---|\n| `.claude/docs/architecture.md` | **planning or implementing** any new code, refactoring, or module changes — `-api`/`-impl` structure, Dagger/Anvil scopes, navigation, coroutine job patterns |\n| `.claude/docs/android-design-system.md` | **planning or implementing** any UI — which ADS component to use, colors, spacing; raw Android/Material widgets are lint-blocked |\n| `.claude/docs/contributions.md` | **planning** a change that touches a `-api` surface or spans multiple modules — it needs an approved API Proposal and/or Tech Design, and that has to be raised before implementation, not at PR time. Also read it when naming a branch, writing a commit, or opening a PR |\n| `.claude/docs/lateinit-hazards.md` | writing or reviewing any `lateinit var`, especially `@Inject lateinit var` in a View |\n| `.claude/docs/plugin-system.md` | declaring a plugin point or contributing a plugin (`PluginPoint` / `ActivePluginPoint`) |\n| `.claude/docs/pixels.md` | adding or changing pixel telemetry — including whether a pixel or a wide event is the right instrument |\n| `.claude/docs/dagger-scopes.md` | an injection fails at runtime (\"could not find dagger component\"), or you're deciding which scope to pass to `@InjectWith` — has the component/subcomponent diagram and the `injectorFactoryMap` lookups |\n| `.claude/docs/navigation.md` | adding a screen or navigating to one — `ActivityParams`, `@ContributeToActivityStarter`, deeplinks, and which `GlobalActivityStarter` overload to use |\n| `.claude/docs/url-classification.md` | routing typed input to navigation vs search — use `QueryUrlPredictor`, not `UriString.isWebUrl()` |\n| `.claude/docs/icons.md` | the change needs an icon the project doesn't have yet — it must be fetched from the internal Icons repository, never invented |\n\n---\n\n## Build & Test Commands\n\n```bash\n# Unit tests (all modules)\n./gradlew jvm_tests\n\n# Unit tests for a single module\n./gradlew :my-feature-impl:testDebugUnitTest\n\n# Code quality (spotless + lint + unit tests)\n./gradlew jvm_checks\n\n# Lint only\n./gradlew lint_check\n\n# Code formatting check / fix\n./gradlew spotlessCheck\n./gradlew spotlessApply\n\n# Install app\n./gradlew installInternalRelease    # internal build (more testing features)\n./gradlew installPlayRelease        # play store build\n```\n\n`jvm_tests` and `jvm_checks` resolve to `testPlayDebugUnitTest` in `:app` and `testDebugUnitTest` in\nlibrary modules. To run a single test class, use `--tests`:\n\n```bash\n./gradlew :my-feature-impl:testDebugUnitTest --tests \"com.duckduckgo.my.feature.RealFooTest\"\n```\n\n### Build Variants\n\n| Dimension | Flavors |\n|---|---|\n| store | `internal`, `fdroid`, `play` |\n| Build types | `debug`, `release`, `upload` |\n\n### Proprietary Fonts\n\nThe app uses a proprietary DuckSans font from a private GitHub Packages repository. The build\nconditionally swaps between the proprietary `ddg-proprietary-fonts` AAR and a local `:fonts` fallback\nmodule (empty `<font-family/>` stubs) based on credential availability. See `build.gradle` for the\ncredential detection logic and `android-design-system/fonts/readme.md` for details.\n\n---\n\n## Code Formatting\n\n- **Spotless** with ktlint for Kotlin; Google Java Format in AOSP style for Java\n- Max line length: 150 characters\n- Ratchet from `origin/develop` — only changed code is enforced\n\n---\n\n## Code Comments\n\nComments explain the **why** (intent, assumptions, non-obvious decisions), not the **what** the code\nalready shows.\n\nDefault to no comment: prefer self-documenting code (clear names, small functions), and add one only\nwhere code alone can't carry the reasoning, and only if it still answers *\"why was this done this\nway?\"* for someone reading it two years from now. `-api` module declarations may carry KDoc describing\nthe contract — that's documentation for consumers, not narration.\n\nNever add:\n\n- **narration** of trivial code\n- **conversational / temporal residue**\n- **process / plan references**\n\n```kotlin\ncount++ // increment the counter -> narration of trivial code\nval user = repo.load() // fixes the bug from the previous task -> temporal residue\nval state = flow // changed from LiveData -> temporal residue\ncache.clear() // step 3 of the plan -> process / plan reference\nretryCount = 3 // per the Asana task -> process / plan reference\n```\n\nWhen reviewing a diff, flag any added comment that doesn't follow these rules.\n"},"files":{"CLAUDE.md":"# CLAUDE.md — DuckDuckGo Android Browser\n\nDuckDuckGo Android is a privacy-focused browser built as a large multi-module Gradle project,\nproviding built-in search, tracker blocking, HTTPS enforcement and other privacy features.\n\n**Versions** (SDK levels, Kotlin, Gradle, libraries) live in the build files — don't restate them here:\n`min_sdk` / `target_sdk` / `compile_sdk` in `build.gradle`, `version.kotlin` in `versions.properties`,\nand the Gradle version in `gradle/wrapper/gradle-wrapper.properties`.\n**Build:** AGP via refreshVersions. DI is Anvil/Dagger2 today, with a migration to **Metro** in flight\n(dual-build selected by the `ddg.di` Gradle property; see `build.gradle`).\n**Toolchain:** Kotlin JVM target 17; building requires **JDK 21** (Metro compiler plugin).\n\nNotable libraries: Room, Retrofit/OkHttp/Moshi, Coroutines, Jetpack Compose (selective), WorkManager,\n`logcat` (Square), KSP for annotation processing. Versions are in `versions.properties` and the module\n`build.gradle` files.\n\n---\n\n## Privacy invariants for anything leaving the device\n\nThis is a privacy browser; these hold for every outbound value — pixels, wide events, logs, crash\nreports, debug output — not just telemetry you are deliberately designing.\n\n- **No PII.** Never emails, names, account IDs, usernames or phone numbers, in a name or a value.\n- **No URLs, domains or page titles.** Breakage reports are the one controlled exception, with explicit\n  user consent.\n- **No correlation IDs.** No session IDs, GUIDs or exact timestamps — anything that links events to one\n  user session.\n- **Bucket numeric values.** Exact durations, byte counts and item counts fingerprint users; send ranges.\n- **Bounded enums over free-form strings.** High-cardinality unbounded strings are both a privacy risk\n  and unanalysable.\n\nAdding or changing a pixel or the pixel registry also requires privacy triage — see\n`.claude/docs/pixels.md`.\n\n---\n\n## Read these when the situation applies\n\nThese files are not in context. Read the whole file before doing the work it covers — don't rely on\nwhat you remember of it.\n\n| Read | When |\n|---|---|\n| `.claude/docs/architecture.md` | **planning or implementing** any new code, refactoring, or module changes — `-api`/`-impl` structure, Dagger/Anvil scopes, navigation, coroutine job patterns |\n| `.claude/docs/android-design-system.md` | **planning or implementing** any UI — which ADS component to use, colors, spacing; raw Android/Material widgets are lint-blocked |\n| `.claude/docs/contributions.md` | **planning** a change that touches a `-api` surface or spans multiple modules — it needs an approved API Proposal and/or Tech Design, and that has to be raised before implementation, not at PR time. Also read it when naming a branch, writing a commit, or opening a PR |\n| `.claude/docs/lateinit-hazards.md` | writing or reviewing any `lateinit var`, especially `@Inject lateinit var` in a View |\n| `.claude/docs/plugin-system.md` | declaring a plugin point or contributing a plugin (`PluginPoint` / `ActivePluginPoint`) |\n| `.claude/docs/pixels.md` | adding or changing pixel telemetry — including whether a pixel or a wide event is the right instrument |\n| `.claude/docs/dagger-scopes.md` | an injection fails at runtime (\"could not find dagger component\"), or you're deciding which scope to pass to `@InjectWith` — has the component/subcomponent diagram and the `injectorFactoryMap` lookups |\n| `.claude/docs/navigation.md` | adding a screen or navigating to one — `ActivityParams`, `@ContributeToActivityStarter`, deeplinks, and which `GlobalActivityStarter` overload to use |\n| `.claude/docs/url-classification.md` | routing typed input to navigation vs search — use `QueryUrlPredictor`, not `UriString.isWebUrl()` |\n| `.claude/docs/icons.md` | the change needs an icon the project doesn't have yet — it must be fetched from the internal Icons repository, never invented |\n\n---\n\n## Build & Test Commands\n\n```bash\n# Unit tests (all modules)\n./gradlew jvm_tests\n\n# Unit tests for a single module\n./gradlew :my-feature-impl:testDebugUnitTest\n\n# Code quality (spotless + lint + unit tests)\n./gradlew jvm_checks\n\n# Lint only\n./gradlew lint_check\n\n# Code formatting check / fix\n./gradlew spotlessCheck\n./gradlew spotlessApply\n\n# Install app\n./gradlew installInternalRelease    # internal build (more testing features)\n./gradlew installPlayRelease        # play store build\n```\n\n`jvm_tests` and `jvm_checks` resolve to `testPlayDebugUnitTest` in `:app` and `testDebugUnitTest` in\nlibrary modules. To run a single test class, use `--tests`:\n\n```bash\n./gradlew :my-feature-impl:testDebugUnitTest --tests \"com.duckduckgo.my.feature.RealFooTest\"\n```\n\n### Build Variants\n\n| Dimension | Flavors |\n|---|---|\n| store | `internal`, `fdroid`, `play` |\n| Build types | `debug`, `release`, `upload` |\n\n### Proprietary Fonts\n\nThe app uses a proprietary DuckSans font from a private GitHub Packages repository. The build\nconditionally swaps between the proprietary `ddg-proprietary-fonts` AAR and a local `:fonts` fallback\nmodule (empty `<font-family/>` stubs) based on credential availability. See `build.gradle` for the\ncredential detection logic and `android-design-system/fonts/readme.md` for details.\n\n---\n\n## Code Formatting\n\n- **Spotless** with ktlint for Kotlin; Google Java Format in AOSP style for Java\n- Max line length: 150 characters\n- Ratchet from `origin/develop` — only changed code is enforced\n\n---\n\n## Code Comments\n\nComments explain the **why** (intent, assumptions, non-obvious decisions), not the **what** the code\nalready shows.\n\nDefault to no comment: prefer self-documenting code (clear names, small functions), and add one only\nwhere code alone can't carry the reasoning, and only if it still answers *\"why was this done this\nway?\"* for someone reading it two years from now. `-api` module declarations may carry KDoc describing\nthe contract — that's documentation for consumers, not narration.\n\nNever add:\n\n- **narration** of trivial code\n- **conversational / temporal residue**\n- **process / plan references**\n\n```kotlin\ncount++ // increment the counter -> narration of trivial code\nval user = repo.load() // fixes the bug from the previous task -> temporal residue\nval state = flow // changed from LiveData -> temporal residue\ncache.clear() // step 3 of the plan -> process / plan reference\nretryCount = 3 // per the Asana task -> process / plan reference\n```\n\nWhen reviewing a diff, flag any added comment that doesn't follow these rules.\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"# CLAUDE.md — DuckDuckGo Android Browser\n\nDuckDuckGo Android is a privacy-focused browser built as a large multi-module Gradle project,\nproviding built-in search, tracker blocking, HTTPS enforcement and other privacy features.\n\n**Versions** (SDK levels, Kotlin, Gradle, libraries) live in the build files — don't restate them here:\n`min_sdk` / `target_sdk` / `compile_sdk` in `build.gradle`, `version.kotlin` in `versions.properties`,\nand the Gradle version in `gradle/wrapper/gradle-wrapper.properties`.\n**Build:** AGP via refreshVersions. DI is Anvil/Dagger2 today, with a migration to **Metro** in flight\n(dual-build selected by the `ddg.di` Gradle property; see `build.gradle`).\n**Toolchain:** Kotlin JVM target 17; building requires **JDK 21** (Metro compiler plugin).\n\nNotable libraries: Room, Retrofit/OkHttp/Moshi, Coroutines, Jetpack Compose (selective), WorkManager,\n`logcat` (Square), KSP for annotation processing. Versions are in `versions.properties` and the module\n`build.gradle` files.\n\n---\n\n## Privacy invariants for anything leaving the device\n\nThis is a privacy browser; these hold for every outbound value — pixels, wide events, logs, crash\nreports, debug output — not just telemetry you are deliberately designing.\n\n- **No PII.** Never emails, names, account IDs, usernames or phone numbers, in a name or a value.\n- **No URLs, domains or page titles.** Breakage reports are the one controlled exception, with explicit\n  user consent.\n- **No correlation IDs.** No session IDs, GUIDs or exact timestamps — anything that links events to one\n  user session.\n- **Bucket numeric values.** Exact durations, byte counts and item counts fingerprint users; send ranges.\n- **Bounded enums over free-form strings.** High-cardinality unbounded strings are both a privacy risk\n  and unanalysable.\n\nAdding or changing a pixel or the pixel registry also requires privacy triage — see\n`.claude/docs/pixels.md`.\n\n---\n\n## Read these when the situation applies\n\nThese files are not in context. Read the whole file before doing the work it covers — don't rely on\nwhat you remember of it.\n\n| Read | When |\n|---|---|\n| `.claude/docs/architecture.md` | **planning or implementing** any new code, refactoring, or module changes — `-api`/`-impl` structure, Dagger/Anvil scopes, navigation, coroutine job patterns |\n| `.claude/docs/android-design-system.md` | **planning or implementing** any UI — which ADS component to use, colors, spacing; raw Android/Material widgets are lint-blocked |\n| `.claude/docs/contributions.md` | **planning** a change that touches a `-api` surface or spans multiple modules — it needs an approved API Proposal and/or Tech Design, and that has to be raised before implementation, not at PR time. Also read it when naming a branch, writing a commit, or opening a PR |\n| `.claude/docs/lateinit-hazards.md` | writing or reviewing any `lateinit var`, especially `@Inject lateinit var` in a View |\n| `.claude/docs/plugin-system.md` | declaring a plugin point or contributing a plugin (`PluginPoint` / `ActivePluginPoint`) |\n| `.claude/docs/pixels.md` | adding or changing pixel telemetry — including whether a pixel or a wide event is the right instrument |\n| `.claude/docs/dagger-scopes.md` | an injection fails at runtime (\"could not find dagger component\"), or you're deciding which scope to pass to `@InjectWith` — has the component/subcomponent diagram and the `injectorFactoryMap` lookups |\n| `.claude/docs/navigation.md` | adding a screen or navigating to one — `ActivityParams`, `@ContributeToActivityStarter`, deeplinks, and which `GlobalActivityStarter` overload to use |\n| `.claude/docs/url-classification.md` | routing typed input to navigation vs search — use `QueryUrlPredictor`, not `UriString.isWebUrl()` |\n| `.claude/docs/icons.md` | the change needs an icon the project doesn't have yet — it must be fetched from the internal Icons repository, never invented |\n\n---\n\n## Build & Test Commands\n\n```bash\n# Unit tests (all modules)\n./gradlew jvm_tests\n\n# Unit tests for a single module\n./gradlew :my-feature-impl:testDebugUnitTest\n\n# Code quality (spotless + lint + unit tests)\n./gradlew jvm_checks\n\n# Lint only\n./gradlew lint_check\n\n# Code formatting check / fix\n./gradlew spotlessCheck\n./gradlew spotlessApply\n\n# Install app\n./gradlew installInternalRelease    # internal build (more testing features)\n./gradlew installPlayRelease        # play store build\n```\n\n`jvm_tests` and `jvm_checks` resolve to `testPlayDebugUnitTest` in `:app` and `testDebugUnitTest` in\nlibrary modules. To run a single test class, use `--tests`:\n\n```bash\n./gradlew :my-feature-impl:testDebugUnitTest --tests \"com.duckduckgo.my.feature.RealFooTest\"\n```\n\n### Build Variants\n\n| Dimension | Flavors |\n|---|---|\n| store | `internal`, `fdroid`, `play` |\n| Build types | `debug`, `release`, `upload` |\n\n### Proprietary Fonts\n\nThe app uses a proprietary DuckSans font from a private GitHub Packages repository. The build\nconditionally swaps between the proprietary `ddg-proprietary-fonts` AAR and a local `:fonts` fallback\nmodule (empty `<font-family/>` stubs) based on credential availability. See `build.gradle` for the\ncredential detection logic and `android-design-system/fonts/readme.md` for details.\n\n---\n\n## Code Formatting\n\n- **Spotless** with ktlint for Kotlin; Google Java Format in AOSP style for Java\n- Max line length: 150 characters\n- Ratchet from `origin/develop` — only changed code is enforced\n\n---\n\n## Code Comments\n\nComments explain the **why** (intent, assumptions, non-obvious decisions), not the **what** the code\nalready shows.\n\nDefault to no comment: prefer self-documenting code (clear names, small functions), and add one only\nwhere code alone can't carry the reasoning, and only if it still answers *\"why was this done this\nway?\"* for someone reading it two years from now. `-api` module declarations may carry KDoc describing\nthe contract — that's documentation for consumers, not narration.\n\nNever add:\n\n- **narration** of trivial code\n- **conversational / temporal residue**\n- **process / plan references**\n\n```kotlin\ncount++ // increment the counter -> narration of trivial code\nval user = repo.load() // fixes the bug from the previous task -> temporal residue\nval state = flow // changed from LiveData -> temporal residue\ncache.clear() // step 3 of the plan -> process / plan reference\nretryCount = 3 // per the Asana task -> process / plan reference\n```\n\nWhen reviewing a diff, flag any added comment that doesn't follow these rules.\n","category":"root","tokens":1618}]}