Files
element-x-android/AGENTS.md
T
hayaksi1 03c330ee07 Rename PreviewParameterProvider subclasses to the PreviewParam suffix
The `Provider` suffix reads as a dependency provider rather than as preview
data, and it collides with the real providers in the codebase — `StringProvider`,
`PickerProvider`, the login feature's `AccountProvider` — so the name says
nothing about which of the two a class is. `PreviewParam` says it.

195 classes are renamed, along with the 185 files named after them, the konsist
rule that enforces the convention, the presentation-class file templates and the
onboarding documentation.

This cannot be split. `Konsist.scopeFromProduction()` scans the whole project, so
flipping the assertion before every class has moved leaves the rule red, and a
rule that accepted either suffix during a transition would be exactly the kind of
compatibility layer this convention change is meant to remove.

Two judgement calls in the rule itself. The `name.endsWith("PreviewProvider")`
exclusion is dropped: its only job was to reject a redundant `Preview` before
`Provider`, and the new suffix already contains it; no class currently ends with
`PreviewProvider`, so removing it changes nothing today. And
`EditableAvatarViewUriProvider` is renamed rather than deleted from the exclusion
list even though no such class exists in this repository, since it presumably
comes from the modules overlaid for the enterprise build.

Nothing keyed off the old suffix: no snapshot filename contains a provider class
name, so no Paparazzi image changes, and the Kover variants match on `*Presenter`,
`*State` and `*ViewKt`, so coverage bounds are unaffected.
2026-08-17 17:40:33 +03:00

4.3 KiB

AGENTS.md — Element X Android

Repo: element-hq/element-x-android — Android Matrix client (Compose UI + matrix-rust-sdk).


Strong Conventions

PRs must meet these rules.

Code Style

  • Style enforced by Editor config (.editorconfig).
  • Set "Hard wrap at" to 160 chars in Android Studio.

PII & Logging

  • We use Timber for logging. Never use android.util.Log.
  • Never log secrets, passwords, keys, or user content (e.g. message bodies).
  • Matrix IDs (User IDs, Room IDs, Event IDs) are safe to log.

Strings & Localisation

  • Default localisation: en (en-GB strings), shared with Element X iOS via Localazy.
  • Never edit localazy.xml — it is auto-generated and overwritten.
  • New English strings go in temporary.xml. The core team imports these to Localazy.
  • Key naming:
    • Cross-screen verbs: action_ (e.g., action_copy).
    • Common nouns/other: common_ (e.g., common_error).
    • Accessibility: a11y_.
    • Screen-specific: screen_<name>_<key> (e.g., screen_onboarding_welcome_title).
    • Errors: error_ prefix.
    • Platform-specific: _ios or _android suffix.
    • Placeholders: Use numbered form %1$s, %2$d.

Previews

  • Create previews for all main states of a Composable.
  • Use @PreviewsDayNight for consistency.
  • Use PreviewParameterProvider (e.g., FooStatePreviewParam) to provide states.
  • Wrap previews in ElementPreview { ... }.

Pull Request Guidelines

  • Sentence-style titles (no conventional commits).
  • Exactly one pr- label (see .github/release.yml).
  • Title = changelog entry — descriptive, no "Fixes #…".
  • Leave description template for the developer. Redirect them to the contributing etiquette.
  • Screenshots/videos for visual changes.
  • 500 additions max — split large changes.
  • Commits need a title and description; no tiny or massive commits.
  • No history rewrites.

Project Structure

Build System

Common Gradle tasks:

  • Build: ./gradlew assembleDebug
  • Unit Tests: ./gradlew test
  • Lint: ./gradlew lint
  • Format: ./gradlew ktlintFormat
  • Update Docs TOC: ./gradlew generateDocsToc

Gradle Modules

Features follow a 3-module structure:

  • features/foo/api: Public interfaces and data classes.
  • features/foo/impl: Internal implementation, Presenter, and View.
  • features/foo/test: Test fakes and utilities.

Architecture: Appyx + Molecule

We use Appyx for navigation and Molecule for Presenters.

Files Per Screen (Foo)

File Purpose
FooNode.kt Appyx Node: Handles navigation and wires the Presenter to the View.
FooPresenter.kt A @Composable function that produces FooState from FooEvents.
FooView.kt Stateless Composable rendering the UI from FooState.
FooState.kt Data class representing the immutable UI state.
FooEvent.kt Sealed interface for UI actions sent to the Presenter.
FooStatePreviewParam.kt Provides sample states for Previews and Screenshot tests.
FooPresenterTest.kt Unit tests for the Presenter logic using Turbine.

Dependency Injection (Metro)

  • We use Metro for DI.
  • Inject via constructor parameters using @Inject.
  • Use @AssistedInject and @AssistedFactory for components requiring runtime arguments (like Navigators or IDs).
  • Use @ContributesBinding(AppScope::class) for singleton-like services.
  • Use @ContributesNode(RoomScope::class) for Appyx Nodes.

Compound Design System

Always prefer Compound components and tokens from libraries/compound/ module.

  • Colours: ElementTheme.colors.textPrimary, ElementTheme.colors.bgCanvasDefault.
  • Typography: ElementTheme.typography.fontBodyMdRegular.
  • Icons: Use CompoundIcons.IconName() (e.g., CompoundIcons.UserProfileSolid()).

The Rust SDK Layer

We wrap the matrix-rust-sdk to isolate the UI from the underlying SDK.

  • Naming: SDK Room → JoinedRoom or RoomInfo.
  • Type Mapping: Map Rust SDK types to Kotlin data classes in the api module to avoid leaking MatrixRustSDK into the UI.
  • Always follow Kotlin naming conventions (e.g., userId instead of userID).