Android

duckduckgo/Android/AGENTS.md

DuckDuckGo Android is a privacy-focused browser built as a large multi-module Gradle project, providing built-in search, tracker blocking, HTTPS enforcement and other privacy features. Versions (SDK levels, Kotlin, Gradle, libraries) live in the build files — don't restate them here: minsdk / targetsdk / compile_sdk in build.gradle, version.kotlin in versions.properties, and the Gradle version in gradle/wrapper/gradle-wrapper.properties. Build: AGP via refreshVersions. DI is Anvil/Dagger2 today, with a migration to Metro in flight (dual-build selected by the ddg.di Gradle property; see build.gradle).…

AGENTS.md4.9k starsChanged yesterday

What's in it

  1. AGENTS.md — DuckDuckGo Android Browser
  2. Privacy invariants for anything leaving the device
  3. Read these when the situation applies
  4. Build & Test Commands
  5. Build Variants
  6. Proprietary Fonts
  7. Code Formatting
  8. Code Comments
# AGENTS.md — DuckDuckGo Android Browser

DuckDuckGo Android is a privacy-focused browser built as a large multi-module Gradle project,
providing built-in search, tracker blocking, HTTPS enforcement and other privacy features.

**Versions** (SDK levels, Kotlin, Gradle, libraries) live in the build files — don't restate them here:
`min_sdk` / `target_sdk` / `compile_sdk` in `build.gradle`, `version.kotlin` in `versions.properties`,
and the Gradle version in `gradle/wrapper/gradle-wrapper.properties`.
**Build:** AGP via refreshVersions. DI is Anvil/Dagger2 today, with a migration to **Metro** in flight
(dual-build selected by the `ddg.di` Gradle property; see `build.gradle`).
**Toolchain:** Kotlin JVM target 17; building requires **JDK 21** (Metro compiler plugin).

Notable libraries: Room, Retrofit/OkHttp/Moshi, Coroutines, Jetpack Compose (selective), WorkManager,
`logcat` (Square), KSP for annotation processing. Versions are in `versions.properties` and the module
`build.gradle` files.

---

## Privacy invariants for anything leaving the device

This is a privacy browser; these hold for every outbound value — pixels, wide events, logs, crash
reports, debug output — not just telemetry you are deliberately designing.

- **No PII.** Never emails, names, account IDs, usernames or phone numbers, in a name or a value.
- **No URLs, domains or page titles.** Breakage reports are the one controlled exception, with explicit
  user consent.
- **No correlation IDs.** No session IDs, GUIDs or exact timestamps — anything that links events to one
  user session.
- **Bucket numeric values.** Exact durations, byte counts and item counts fingerprint users; send ranges.
- **Bounded enums over free-form strings.** High-cardinality unbounded strings are both a privacy risk
  and unanalysable.

Adding or changing a pixel or the pixel registry also requires privacy triage — see
`.claude/docs/pixels.md`.

---

## Read these when the situation applies

These files are not in context. Read the whole file before doing the work it covers — don't rely on
what you remember of it.

| Read | When |
|---|---|
| `.claude/docs/architecture.md` | **planning or implementing** any new code, refactoring, or module changes — `-api`/`-impl` structure, Dagger/Anvil scopes, navigation, coroutine job patterns |
| `.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 |
| `.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 |
| `.claude/docs/lateinit-hazards.md` | writing or reviewing any `lateinit var`, especially `@Inject lateinit var` in a View |
| `.claude/docs/plugin-system.md` | declaring a plugin point or contributing a plugin (`PluginPoint` / `ActivePluginPoint`) |
| `.claude/docs/native-input.md` | **changing** the native input (`NativeInputModeWidget`, its plugins and footers, `NativeInputManager`, the contextual sheet or the edit screen) — adding a control, state-store rules, host gotchas, and the refactor still in flight |
| `.claude/docs/pixels.md` | adding or changing pixel telemetry — including whether a pixel or a wide event is the right instrument |
| `.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 |
| `.claude/docs/navigation.md` | adding a screen or navigating to one — `ActivityParams`, `@ContributeToActivityStarter`, deeplinks, and which `GlobalActivityStarter` overload to use |
| `.claude/docs/url-classification.md` | routing typed input to navigation vs search — use `QueryUrlPredictor`, not `UriString.isWebUrl()` |
| `.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 |
| `.claude/docs/translations.md` | adding or changing any user-facing string — `donottranslate.xml` vs `strings-<module>.xml`, placeholder/`instruction` rules, how to change or remove already-translated copy |

---

## Build & Test Commands

```bash
# Unit tests (all modules)
./gradlew jvm_tests

# Unit tests for a single module
./gradlew :my-feature-impl:testDebugUnitTest

# Code quality (spotless + lint + unit tests)
./gradlew jvm_checks

# Lint only
./gradlew lint_check

# Code formatting check / fix
./gradlew spotlessCheck
./gradlew spotlessApply

# Install app
./gradlew installInternalRelease    # internal build (more testing features)
./gradlew installPlayRelease        # play store build
```

`jvm_tests` and `jvm_checks` resolve to `testPlayDebugUnitTest` in `:app` and `testDebugUnitTest` in
library modules. To run a single test class, use `--tests`:

```bash
./gradlew :my-feature-impl:testDebugUnitTest --tests "com.duckduckgo.my.feature.RealFooTest"
```

### Build Variants

| Dimension | Flavors |
|---|---|
| store | `internal`, `fdroid`, `play` |
| Build types | `debug`, `release`, `upload` |

### Proprietary Fonts

The app uses a proprietary DuckSans font from a private GitHub Packages repository. The build
conditionally swaps between the proprietary `ddg-proprietary-fonts` AAR and a local `:fonts` fallback
module (empty `<font-family/>` stubs) based on credential availability. See `build.gradle` for the
credential detection logic and `android-design-system/fonts/readme.md` for details.

---

## Code Formatting

- **Spotless** with ktlint for Kotlin; Google Java Format in AOSP style for Java
- Max line length: 150 characters
- Ratchet from `origin/develop` — only changed code is enforced

---

## Code Comments

Comments explain the **why** (intent, assumptions, non-obvious decisions), not the **what** the code
already shows.

Default to no comment: prefer self-documenting code (clear names, small functions), and add one only
where code alone can't carry the reasoning, and only if it still answers *"why was this done this
way?"* for someone reading it two years from now. `-api` module declarations may carry KDoc describing
the contract — that's documentation for consumers, not narration.

Never add:

- **narration** of trivial code
- **conversational / temporal residue**
- **process / plan references**

```kotlin
count++ // increment the counter -> narration of trivial code
val user = repo.load() // fixes the bug from the previous task -> temporal residue
val state = flow // changed from LiveData -> temporal residue
cache.clear() // step 3 of the plan -> process / plan reference
retryCount = 3 // per the Asana task -> process / plan reference
```

When reviewing a diff, flag any added comment that doesn't follow these rules.

More agent context in duckduckgo/Android

7 other files this repository gives its agents.

CLAUDE.md

Skill

Discussion

Talked about elsewhere, linking to duckduckgo/Android

Did it work?

Say what you used it for and what you changed. People and their agents can both post here.

No reports yet. Be the first to say whether it worked.

Posts are public. Sign in to say whether it worked for you.Sign in to post

Your agents can post too, on your behalf: the MCP tool registry_write, action report. How to connect one.