Skip to content

refactor(android): align the Compose API with Compose conventions - #872

Merged
jnowakow merged 8 commits into
mainfrom
refactor/android-compose-api-conventions
Sep 23, 2026
Merged

jnowakow merged 8 commits into
mainfrom
refactor/android-compose-api-conventions

Conversation

@jnowakow

@jnowakow jnowakow commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Aligns the public API of the compose module with the Jetpack Compose API guidelines. The module's structure already followed them, so this is a naming and typing pass.

The Compose API has no/little users yet and ships in the next bigger release, so the old names are removed outright rather than kept as deprecated aliases.

Renames

Was Now Where
onLinkPress, onLinkLongPress onLinkClick, onLinkLongClick EnrichedMarkdownText
onTaskListItemPress, TaskListItemPressEvent onTaskListItemToggle, TaskListItemToggle EnrichedMarkdownText
enableTaskListItemToggle taskListToggleEnabled EnrichedMarkdownText
Md4cFlags.DEFAULT Md4cFlags.Default Md4cFlags
MarkdownStyle.copy { } merge { }, plus merge(other) and operator plus MarkdownStyle
borderRadius cornerRadius blockquote, image
checkboxBorderRadius checkboxCornerRadius taskList
marginLeft marginStart list

Type changes

Was Now Where
textAlign: TextAlignment textAlign: TextAlign paragraph, h1…h6
align: TableAlignment alignment: Alignment.Horizontal table
underline: Boolean textDecoration: TextDecoration link
padding: Dp padding: PaddingValues codeBlock, blockquote

The ui module keeps its own TextAlignment and TableAlignment enums. It is the View-based layer and is published without a Compose dependency, so the Compose scopes take Compose types and map onto those enums in StyleUnits.kt.

Callbacks are non-null with a {} default. The 18 style scopes now have internal constructors, so the markdownStyle { } DSL is the only way to build them.

Test plan

  • :compose:testDebugUnitTest and :ui:testDebugUnitTest pass, including a new MarkdownStyleComposeTypesTest for each type mapping and for the uniform-padding error
  • ktlintCheck is clean across :parser, :ui and :compose
  • apps/android-example is updated to the new API and builds
  • Debug APK built and installed on an API 36 emulator

jnowakow and others added 3 commits September 22, 2026 15:12
Applies the twelve rows of the "Deviations from Compose conventions"
table from the Compose API review of the `compose` module.

Renames (old name kept for one release with @Deprecated/ReplaceWith
wherever Kotlin allows it):

- `onLinkPress`/`onLinkLongPress`/`onTaskListItemPress` ->
  `onLinkClick`/`onLinkLongClick`/`onTaskListItemToggle`, and
  `TaskListItemPressEvent` -> `TaskListItemToggle`
- `enableTaskListItemToggle` -> `taskListToggleEnabled`
- `Md4cFlags.DEFAULT` -> `Md4cFlags.Default`
- `MarkdownStyle.copy { }` -> `merge { }`, plus `merge(other)` and `+`
- `borderRadius` -> `cornerRadius` (blockquote, image),
  `checkboxBorderRadius` -> `checkboxCornerRadius`,
  `marginLeft` -> `marginStart`

Type changes; the `ui` module keeps its own enums and the Compose
scopes map onto them:

- `textAlign` takes `TextAlign`
- `table { alignment }` takes `Alignment.Horizontal`
- `link { textDecoration }` takes `TextDecoration`
- `codeBlock`/`blockquote` `padding` takes `PaddingValues`, which must
  be uniform because the renderer draws one inset per side

Callbacks are non-null with a `{}` default, and the 18 style scopes
now have internal constructors, so the DSL is the only way in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Compose API has no users yet and ships in the next bigger release,
so the renames don't need a migration path. Remove the deprecated
aliases and the README migration table.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@jnowakow
jnowakow marked this pull request as ready for review September 23, 2026 09:15
when (this) {
Alignment.Start -> TableAlignment.AUTO
Alignment.CenterHorizontally -> TableAlignment.CENTER
Alignment.End -> TableAlignment.RIGHT

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In RTL this puts End on the right. TableContainerView places LEFT/RIGHT absolutely and only AUTO follows reading direction, so with a forced RTL config a narrow table landed at x=599 for both Start and End (should be 0 for End). Adding TableAlignment.END with if (isRtl) 0f else freeSpace and mapping Alignment.End to it would fix it.

markdownStyle { table { alignment = AbsoluteAlignment.Left } }.resolve(context).tableStyle.align,
)
assertEquals(
TableAlignment.RIGHT,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would become END once it exists.

val top = value.calculateTopPadding()
val end = value.calculateEndPadding(LayoutDirection.Ltr)
val bottom = value.calculateBottomPadding()
require(start == top && start == end && start == bottom) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throws inside produceState, so a style with PaddingValues(horizontal = 12.dp, vertical = 4.dp) crashes at composition (reproduced under createComposeRule). Since the spans only draw one inset, I'd go back to padding: Dp for now rather than accept a type whose common values are invalid.

*/
internal fun TextAlign.toStyleTextAlignment(): TextAlignment =
when (this) {
TextAlign.Start, TextAlign.Left -> TextAlignment.LEFT

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LEFT is ALIGN_NORMAL, so TextAlign.Left behaves like Start and right-aligns Arabic text. Either drop Left/Right until the text layer can pin a side, or note it in the README next to textAlign.

TextAlign.Center -> TextAlignment.CENTER
TextAlign.End, TextAlign.Right -> TextAlignment.RIGHT
TextAlign.Justify -> TextAlignment.JUSTIFY
else -> TextAlignment.AUTO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KDoc says Unspecified leaves the value untouched, but this returns AUTO, which overrides the base via ?:. Return null there, or adjust the doc.

* Only [TextDecoration.Underline] reaches the rendered link; the native link span cannot draw a
* line-through, so [TextDecoration.LineThrough] is ignored.
*/
var textDecoration: TextDecoration? = null

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Across this PR unsupported values now behave three ways: padding throws, Left silently becomes Start, LineThrough is silently dropped. Worth picking one; my vote is accept only what renders, document the rest, never throw.

typealias Md4cFlags = Md4cFlagsInternal

typealias TaskListItemPressEvent = TaskListItemPressEventInternal
typealias TaskListItemToggle = TaskListItemToggleInternal

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still points at TaskListItemPressEvent in ui, so stack traces show the old name. Renaming the class itself is a one-liner.

Creates a style that tracks `MaterialTheme.colorScheme` changes. Use inside `MaterialTheme { }`.


- `padding` must be uniform. A `PaddingValues` with differing sides throws, since the renderer draws

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Goes away if padding returns to Dp; if not, worth saying it throws at composition.

jnowakow and others added 4 commits September 23, 2026 12:03
Unsupported style values are now ignored and documented, never thrown:

- `codeBlock`/`blockquote` `padding` goes back to `Dp`; non-uniform
  `PaddingValues` crashed at composition
- `TextAlign.Unspecified` keeps the inherited alignment instead of
  overriding it with `AUTO`
- README lists `TextAlign.Left`/`Right` (reading-direction relative)
  and `LineThrough` (ignored on links) as unsupported

`table { alignment = Alignment.End }` maps to a new `TableAlignment.END`
so it lands on the left in RTL. The `ui` class `TaskListItemPressEvent`
is renamed to `TaskListItemToggle` to match the Compose alias.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ose-api-conventions

# Conflicts:
#	packages/enriched-markdown-android/ui/src/test/java/com/swmansion/enriched/markdown/TaskListInteractionTest.kt
`link { textDecoration }` silently dropped `TextDecoration.LineThrough`.
Instead of narrowing the type, the renderer now draws it, so every value
the Compose type can hold is honoured:

- `LinkStyle` gains `strikethrough` (default `false`)
- `LinkSpan` turns on the paint's strike-thru flag when it is set, never
  clearing one from an enclosing `~~…~~`
- copied HTML emits the matching `text-decoration`

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
TextAlign.Left and TextAlign.Right used to behave like Start and End.
Each paragraph's direction is now resolved from its first strong
character, the way the text view resolves it, so Left and Right pin a
physical side.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@hryhoriiK97 hryhoriiK97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Two small nits, neither blocking:

  • compose/build.gradle still declares androidx.compose.foundation:foundation-layout, which was only there for PaddingValues and is unused now.
  • The PR description still mentions padding: PaddingValues, the foundation-layout dependency and the uniform-padding error in the test plan.

@jnowakow
jnowakow merged commit 772ba0d into main Sep 23, 2026
12 checks passed
@jnowakow
jnowakow deleted the refactor/android-compose-api-conventions branch September 23, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants