├── android ├── .gitignore ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ │ ├── values │ │ │ │ └── ids.xml │ │ │ ├── drawable │ │ │ │ ├── table_divider.xml │ │ │ │ ├── table_border.xml │ │ │ │ ├── quote_background.xml │ │ │ │ ├── table_cell_border.xml │ │ │ │ └── table_header_cell_border.xml │ │ │ ├── layout │ │ │ │ ├── rich_block_layout.xml │ │ │ │ ├── rich_horizontal_rule_layout.xml │ │ │ │ ├── rich_text_layout.xml │ │ │ │ ├── rich_indention_layout.xml │ │ │ │ ├── rich_quote_layout.xml │ │ │ │ ├── rich_table_layout.xml │ │ │ │ ├── rich_table_cell_layout.xml │ │ │ │ ├── rich_hyperlink_layout.xml │ │ │ │ ├── rich_list_layout.xml │ │ │ │ └── rich_embedded_layout.xml │ │ │ └── drawable-night │ │ │ │ └── table_header_cell_border.xml │ │ └── java │ │ │ └── com │ │ │ └── contentful │ │ │ └── rich │ │ │ └── android │ │ │ ├── renderer │ │ │ ├── listdecorator │ │ │ │ ├── Decorator.java │ │ │ │ ├── LowerCaseCharacterDecorator.java │ │ │ │ ├── LowerCaseRomanNumeralsDecorator.java │ │ │ │ ├── BulletDecorator.java │ │ │ │ ├── NumbersDecorator.java │ │ │ │ ├── UpperCaseCharacterDecorator.java │ │ │ │ └── UpperCaseRomanNumeralsDecorator.java │ │ │ ├── views │ │ │ │ ├── NativeViewsRendererProvider.java │ │ │ │ ├── HorizontalRuleRenderer.java │ │ │ │ ├── EmbeddedLinkRenderer.java │ │ │ │ ├── BlockRenderer.java │ │ │ │ ├── HyperLinkRenderer.java │ │ │ │ └── ListRenderer.java │ │ │ └── chars │ │ │ │ ├── CharSequenceRendererProvider.java │ │ │ │ ├── HorizontalRuleRenderer.java │ │ │ │ ├── QuoteRenderer.java │ │ │ │ ├── HeadingRenderer.java │ │ │ │ ├── span │ │ │ │ └── QuoteSpan.java │ │ │ │ ├── HyperLinkRenderer.java │ │ │ │ └── TextRenderer.java │ │ │ ├── AndroidRenderer.java │ │ │ ├── AndroidProcessor.java │ │ │ └── AndroidContext.java │ └── test │ │ └── java │ │ ├── chars │ │ ├── QuoteTest.java │ │ ├── HeadingTest.java │ │ ├── LinkTest.java │ │ └── AllTheThingsTest.java │ │ ├── views │ │ ├── HeadingTest.java │ │ ├── QuoteTest.java │ │ ├── LinkTest.java │ │ └── AllTheThingsTest.java │ │ └── lib │ │ └── ContentfulCreator.java ├── proguard-rules.pro └── build.gradle ├── android_sample ├── .gitignore ├── screenshots │ ├── links.png │ ├── lists.png │ ├── headings.png │ ├── quotes.png │ ├── textmarks.png │ ├── native-links.png │ ├── native-lists.png │ ├── native-quotes.png │ ├── native-headings.png │ └── native-textmarks.png ├── src │ └── main │ │ ├── ic_launcher-web.png │ │ ├── res │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ ├── ic_launcher_background.png │ │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values │ │ │ ├── dimens.xml │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── values-night │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── layout │ │ │ ├── dropdown_item.xml │ │ │ ├── sample_item.xml │ │ │ ├── appbar_item.xml │ │ │ ├── fragment_page.xml │ │ │ └── activity_main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── contentful │ │ └── rich │ │ └── android │ │ └── sample │ │ ├── PageAdapter.kt │ │ ├── PageFragment.kt │ │ ├── RichNativeViewAdapter.kt │ │ ├── RichCharSequenceAdapter.kt │ │ └── MainActivity.kt ├── README.md ├── settings.gradle ├── proguard-rules.pro └── build.gradle ├── .github ├── CODEOWNERS └── workflows │ └── codeql.yml ├── html ├── out │ └── production │ │ └── classes │ │ └── META-INF │ │ └── html_main.kotlin_module ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── contentful │ │ │ └── rich │ │ │ └── html │ │ │ ├── HtmlProcessor.java │ │ │ ├── renderer │ │ │ ├── DynamicTagRenderer.java │ │ │ ├── TagWithArgumentsRenderer.java │ │ │ ├── TextRenderer.java │ │ │ └── TagRenderer.java │ │ │ ├── HtmlContext.java │ │ │ └── HtmlRendererProvider.java │ └── test │ │ └── java │ │ ├── EncodedRichTextTest.java │ │ ├── lib │ │ └── ContentfulCreator.java │ │ ├── HeadingTest.java │ │ ├── CustomRendererTest.java │ │ ├── LinksTest.java │ │ ├── MarksTextTest.java │ │ ├── ListsTest.java │ │ └── AllTheThingsTest.java ├── build.gradle └── README.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .vscode └── settings.json ├── jitpack.yml ├── gradle.properties ├── .gitignore ├── catalog-info.yaml ├── core ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── contentful │ │ │ └── rich │ │ │ └── core │ │ │ ├── simple │ │ │ ├── Simplification.java │ │ │ ├── Simplifier.java │ │ │ ├── RemoveToDeepNesting.java │ │ │ └── RemoveEmpties.java │ │ │ ├── RenderabilityChecker.java │ │ │ ├── Renderer.java │ │ │ ├── Context.java │ │ │ └── Processor.java │ └── test │ │ └── java │ │ ├── lib │ │ ├── ContentfulCreator.java │ │ └── DescendingTextRendererProvider.java │ │ ├── RendererOrderTest.java │ │ ├── ProcessorTest.java │ │ ├── SimplifierTest.java │ │ └── ComplexProcessorTest.java └── build.gradle ├── settings.gradle.kts ├── gradlew.bat └── README.md /android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android_sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @contentful/team-developer-experience 2 | -------------------------------------------------------------------------------- /html/out/production/classes/META-INF/html_main.kotlin_module: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android_sample/screenshots/links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/links.png -------------------------------------------------------------------------------- /android_sample/screenshots/lists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/lists.png -------------------------------------------------------------------------------- /android_sample/screenshots/headings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/headings.png -------------------------------------------------------------------------------- /android_sample/screenshots/quotes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/quotes.png -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "interactive", 3 | "java.compile.nullAnalysis.mode": "automatic" 4 | } -------------------------------------------------------------------------------- /android_sample/screenshots/textmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/textmarks.png -------------------------------------------------------------------------------- /android/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android_sample/screenshots/native-links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/native-links.png -------------------------------------------------------------------------------- /android_sample/screenshots/native-lists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/native-lists.png -------------------------------------------------------------------------------- /android_sample/screenshots/native-quotes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/native-quotes.png -------------------------------------------------------------------------------- /android_sample/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /android_sample/screenshots/native-headings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/native-headings.png -------------------------------------------------------------------------------- /android_sample/screenshots/native-textmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/screenshots/native-textmarks.png -------------------------------------------------------------------------------- /android_sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful/rich-text-renderer-java/HEAD/android_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android_sample/README.md: -------------------------------------------------------------------------------- 1 | Rendering Rich Text Sample Android APP 2 | ============================================ 3 | 4 | This app demonstrates how to use a predefined Rich Text object into textviews and other native Android views. 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue May 27 19:53:15 CEST 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk17 3 | 4 | before_install: 5 | - ./gradlew wrapper 6 | 7 | install: 8 | - ./gradlew clean publishToMavenLocal -x test -xlint 9 | 10 | jvm: 11 | - openjdk17 12 | 13 | android: 14 | - compileSdk: 35 15 | - buildTools: "35.0.0" 16 | - targetSdk: 35 -------------------------------------------------------------------------------- /android/src/main/res/drawable/table_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android_sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_block_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android_sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/res/drawable/table_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /android_sample/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 8dp 7 | 8 | -------------------------------------------------------------------------------- /android/src/main/java/com/contentful/rich/android/renderer/listdecorator/Decorator.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.android.renderer.listdecorator; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class Decorator { 6 | @Nonnull public CharSequence getSymbol() { 7 | return ""; 8 | } 9 | 10 | @Nonnull public CharSequence decorate(int position) { 11 | return ""; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=false 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m 4 | android.suppressUnsupportedCompileSdk=35 5 | org.gradle.parallel=true 6 | org.gradle.caching=true 7 | android.enableR8.fullMode=true 8 | 9 | # Force Android SDK versions for JitPack 10 | android.compileSdk=35 11 | android.buildToolsVersion=35.0.0 12 | android.targetSdk=35 13 | -------------------------------------------------------------------------------- /android_sample/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #00BFA5 4 | #00897B 5 | #FF4081 6 | #121212 7 | #FFFFFF 8 | #B3FFFFFF 9 | -------------------------------------------------------------------------------- /android_sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | #FFFFFF 7 | #000000 8 | #757575 9 | 10 | -------------------------------------------------------------------------------- /android_sample/settings.gradle: -------------------------------------------------------------------------------- 1 | project.name = "sample" 2 | pluginManagement { 3 | repositories { 4 | gradlePluginPortal() 5 | jcenter() 6 | google() 7 | } 8 | resolutionStrategy { 9 | eachPlugin { 10 | if (requested.id.id == "com.android.application") { 11 | useModule("com.android.tools.build:gradle:${requested.version}") 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | .idea/ 26 | .gradle 27 | **.iml 28 | 29 | **build/ 30 | local.properties 31 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_horizontal_rule_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: rich-text-renderer-java 5 | description: Java library for Rich Text API. It helps in easily rendering rich text stored in Contentful using Java. 6 | annotations: 7 | github.com/project-slug: contentful/rich-text-renderer-java 8 | contentful.com/service-tier: '4' 9 | tags: 10 | - tier-4 11 | spec: 12 | type: library 13 | lifecycle: production 14 | owner: group:team-developer-experience 15 | -------------------------------------------------------------------------------- /android/src/main/java/com/contentful/rich/android/renderer/listdecorator/LowerCaseCharacterDecorator.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.android.renderer.listdecorator; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class LowerCaseCharacterDecorator extends UpperCaseCharacterDecorator { 6 | @Nonnull @Override public CharSequence getSymbol() { 7 | return "a"; 8 | } 9 | 10 | public @Nonnull CharSequence decorate(int position) { 11 | return super.decorate(position).toString().toLowerCase(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /html/src/main/java/com/contentful/rich/html/HtmlProcessor.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.html; 2 | 3 | import com.contentful.rich.core.Processor; 4 | 5 | /** 6 | * Main processor for processing all HTML renderer content. Contains default renderer. 7 | */ 8 | public class HtmlProcessor extends Processor { 9 | /** 10 | * Construct the processor, including all html renderer. 11 | */ 12 | public HtmlProcessor() { 13 | super(); 14 | 15 | new HtmlRendererProvider().provide(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /android/src/main/res/drawable/quote_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 14 | -------------------------------------------------------------------------------- /android/src/main/java/com/contentful/rich/android/renderer/listdecorator/LowerCaseRomanNumeralsDecorator.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.android.renderer.listdecorator; 2 | 3 | import javax.annotation.Nonnull; 4 | 5 | public class LowerCaseRomanNumeralsDecorator extends UpperCaseRomanNumeralsDecorator { 6 | @Nonnull @Override public CharSequence getSymbol() { 7 | return "i"; 8 | } 9 | 10 | @Nonnull @Override 11 | public CharSequence decorate(int position) { 12 | return super.decorate(position).toString().toLowerCase(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/com/contentful/rich/core/simple/Simplification.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.core.simple; 2 | 3 | import com.contentful.java.cda.rich.CDARichNode; 4 | 5 | /** 6 | * This interface specifies a simplification: An option to be done on the graph of rich text nodes. 7 | */ 8 | public interface Simplification { 9 | /** 10 | * Simplify the given graph pointed to by node. 11 | * @param node the graph root node to start simplification on. 12 | * @return a simplified node graph. 13 | */ 14 | CDARichNode simplify(CDARichNode node); 15 | } 16 | -------------------------------------------------------------------------------- /android_sample/src/main/res/layout/dropdown_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | -------------------------------------------------------------------------------- /android/src/main/res/drawable/table_cell_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_text_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_indention_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_quote_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | -------------------------------------------------------------------------------- /android/src/main/java/com/contentful/rich/android/renderer/listdecorator/BulletDecorator.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.android.renderer.listdecorator; 2 | 3 | import android.text.SpannableString; 4 | import android.text.Spanned; 5 | 6 | import javax.annotation.Nonnull; 7 | 8 | public class BulletDecorator extends Decorator{ 9 | @Nonnull @Override public CharSequence getSymbol() { 10 | return "*"; 11 | } 12 | 13 | public @Nonnull CharSequence decorate(int position) { 14 | final SpannableString spannable = new SpannableString("• "); 15 | spannable.setSpan(this, 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 16 | return spannable; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | mavenCentral() 5 | google() 6 | } 7 | resolutionStrategy { 8 | eachPlugin { 9 | if (requested.id.id.startsWith("com.android")) { 10 | useModule("com.android.tools.build:gradle:8.2.2") 11 | } 12 | if (requested.id.id.startsWith("org.jetbrains.kotlin")) { 13 | useVersion("1.9.22") 14 | } 15 | } 16 | } 17 | } 18 | 19 | rootProject.name = "rich-text-renderer" 20 | 21 | include ("android") 22 | include ("android_sample") 23 | include ("core") 24 | include ("html") 25 | 26 | -------------------------------------------------------------------------------- /android/src/main/java/com/contentful/rich/android/renderer/listdecorator/NumbersDecorator.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.android.renderer.listdecorator; 2 | 3 | import android.text.SpannableString; 4 | import android.text.Spanned; 5 | 6 | import javax.annotation.Nonnull; 7 | 8 | public class NumbersDecorator extends Decorator { 9 | @Nonnull @Override public CharSequence getSymbol() { 10 | return "1"; 11 | } 12 | 13 | public @Nonnull CharSequence decorate(int position) { 14 | final SpannableString spannable = new SpannableString(position + ". "); 15 | spannable.setSpan(this, 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 16 | return spannable; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android_sample/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /android_sample/src/main/res/layout/sample_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /android_sample/src/main/res/layout/appbar_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | -------------------------------------------------------------------------------- /android/src/main/res/drawable-night/table_header_cell_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /android/src/main/res/drawable/table_header_cell_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /android_sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Rich Text Renderer - Android Sample Application 3 | Settings 4 | Hello World from section: %1$d 5 | use native views 6 | ⚠ No view rendered. ⚠ 7 | Hyperlink to entry with id \'%s\'. 8 | Navigate to entry with id \'%s\'. 9 | Hyperlink to asset with id \'%s\'. 10 | Navigate to asset with id \'%s\'. 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "CodeQL Scan for GitHub Actions Workflows" 3 | 4 | on: 5 | push: 6 | branches: [master] 7 | paths: [".github/workflows/**"] 8 | pull_request: 9 | branches: [master] 10 | paths: [".github/workflows/**"] 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze GitHub Actions workflows 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Initialize CodeQL 25 | uses: github/codeql-action/init@v3 26 | with: 27 | languages: actions 28 | 29 | - name: Run CodeQL Analysis 30 | uses: github/codeql-action/analyze@v3 31 | with: 32 | category: actions 33 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_table_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /html/src/test/java/EncodedRichTextTest.java: -------------------------------------------------------------------------------- 1 | import com.contentful.java.cda.rich.CDARichText; 2 | import com.contentful.rich.html.HtmlContext; 3 | import com.contentful.rich.html.HtmlProcessor; 4 | 5 | import org.junit.Test; 6 | 7 | import java.util.ArrayList; 8 | 9 | import static com.google.common.truth.Truth.assertThat; 10 | 11 | public class EncodedRichTextTest { 12 | 13 | @Test 14 | public void renderRichTextTest() { 15 | final HtmlProcessor processor = new HtmlProcessor(); 16 | final HtmlContext context = new HtmlContext(); 17 | 18 | 19 | final CDARichText text = new CDARichText("Some rich text with html tags
", new ArrayList<>()); 20 | final String result = processor.process(context, text); 21 | 22 | assertThat(result).isEqualTo("Some rich text with html tags<br/>"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/com/contentful/rich/core/RenderabilityChecker.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.core; 2 | 3 | import com.contentful.java.cda.rich.CDARichNode; 4 | 5 | import javax.annotation.Nonnull; 6 | import javax.annotation.Nullable; 7 | 8 | /** 9 | * Implementer of this interface will be able to tell if a given node can be rendered in the given 10 | * context. 11 | * 12 | * @param Custom Context Class 13 | * @see Renderer 14 | */ 15 | public interface RenderabilityChecker { 16 | 17 | /** 18 | * Return true if the associated renderer can process the node. 19 | * 20 | * @param context context this check should be performed in 21 | * @param node node to be checked 22 | * @return false if the renderer cannot be used. 23 | */ 24 | boolean canRender(@Nullable C context, @Nonnull CDARichNode node); 25 | } 26 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /android_sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /core/src/main/java/com/contentful/rich/core/Renderer.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.core; 2 | 3 | import com.contentful.java.cda.rich.CDARichNode; 4 | 5 | import javax.annotation.Nonnull; 6 | import javax.annotation.Nullable; 7 | 8 | /** 9 | * Implementing classes of this interface will control how the given node will be rendered in the 10 | * given Context. 11 | * 12 | * @param the custom context class to passed 13 | * @param the result type of the rendering 14 | * @see RenderabilityChecker 15 | */ 16 | public interface Renderer { 17 | /** 18 | * Renders the given node in the given context. 19 | * 20 | * @param context the generic context this node should be rendered in. 21 | * @param node the node to be rendered. 22 | * @return the rendered node, unless this renderer encountered an error. 23 | */ 24 | @Nullable R render( 25 | @Nonnull C context, 26 | @Nonnull CDARichNode node 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_table_cell_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 23 | -------------------------------------------------------------------------------- /core/src/test/java/lib/ContentfulCreator.java: -------------------------------------------------------------------------------- 1 | package lib; 2 | 3 | import com.contentful.java.cda.CDAEntry; 4 | import com.contentful.java.cda.CDAResource; 5 | 6 | import java.lang.reflect.Field; 7 | 8 | import static com.google.common.collect.Maps.newHashMap; 9 | 10 | public class ContentfulCreator { 11 | public static CDAResource mockCDAEntry() { 12 | final CDAEntry entry = new CDAEntry(); 13 | Field attrs = null; 14 | for (final Field field : CDAResource.class.getDeclaredFields()) { 15 | if ("attrs".equals(field.getName())) { 16 | attrs = field; 17 | } 18 | } 19 | if (attrs != null) { 20 | attrs.setAccessible(true); 21 | try { 22 | attrs.set(entry, newHashMap()); 23 | } catch (IllegalAccessException e) { 24 | } 25 | attrs.setAccessible(false); 26 | } 27 | 28 | entry.attrs().put("id", "fake_id"); 29 | entry.attrs().put("contentType", "fake_type"); 30 | return entry; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /html/src/test/java/lib/ContentfulCreator.java: -------------------------------------------------------------------------------- 1 | package lib; 2 | 3 | import com.contentful.java.cda.CDAEntry; 4 | import com.contentful.java.cda.CDAResource; 5 | 6 | import java.lang.reflect.Field; 7 | 8 | import static com.google.common.collect.Maps.newHashMap; 9 | 10 | public class ContentfulCreator { 11 | public static CDAResource mockCDAEntry() { 12 | final CDAEntry entry = new CDAEntry(); 13 | Field attrs = null; 14 | for (final Field field : CDAResource.class.getDeclaredFields()) { 15 | if ("attrs".equals(field.getName())) { 16 | attrs = field; 17 | } 18 | } 19 | if (attrs != null) { 20 | attrs.setAccessible(true); 21 | try { 22 | attrs.set(entry, newHashMap()); 23 | } catch (IllegalAccessException e) { 24 | } 25 | attrs.setAccessible(false); 26 | } 27 | 28 | entry.attrs().put("id", "fake_id"); 29 | entry.attrs().put("contentType", "fake_type"); 30 | return entry; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /android_sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_hyperlink_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 27 | -------------------------------------------------------------------------------- /android/src/main/java/com/contentful/rich/android/renderer/listdecorator/UpperCaseCharacterDecorator.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.android.renderer.listdecorator; 2 | 3 | import android.text.SpannableString; 4 | import android.text.Spanned; 5 | 6 | import javax.annotation.Nonnull; 7 | 8 | public class UpperCaseCharacterDecorator extends Decorator { 9 | @Nonnull @Override public CharSequence getSymbol() { 10 | return "A"; 11 | } 12 | 13 | public @Nonnull CharSequence decorate(int position) { 14 | final SpannableString spannable = new SpannableString(getColumnDecoration(position - 1) + ". "); 15 | spannable.setSpan(this, 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 16 | return spannable; 17 | } 18 | 19 | private CharSequence getColumnDecoration(int index) { 20 | if (index < 26) { 21 | return Character.toString((char) ('A' + index)); 22 | } else { 23 | return getColumnDecoration((index / 26) - 1).toString() + getColumnDecoration(index % 26); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/test/java/lib/DescendingTextRendererProvider.java: -------------------------------------------------------------------------------- 1 | package lib; 2 | 3 | import com.contentful.java.cda.rich.CDARichParagraph; 4 | import com.contentful.java.cda.rich.CDARichText; 5 | import com.contentful.rich.core.Context; 6 | import com.contentful.rich.core.Processor; 7 | 8 | import java.util.stream.Collectors; 9 | 10 | import javax.annotation.Nonnull; 11 | 12 | public class DescendingTextRendererProvider { 13 | public void provide(@Nonnull Processor, CharSequence> processor) { 14 | processor.addRenderer( 15 | (context, node) -> node instanceof CDARichText, 16 | (context, node) -> ((CDARichText) node).getText() 17 | ); 18 | 19 | processor.addRenderer( 20 | (context, node) -> node instanceof CDARichParagraph, 21 | (context, node) -> { 22 | final CDARichParagraph block = (CDARichParagraph) node; 23 | return "--" + block.getContent().stream().map(innerBlock -> processor.process(context, innerBlock)).collect(Collectors.joining()); 24 | } 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_list_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | 28 | 29 | -------------------------------------------------------------------------------- /html/src/test/java/HeadingTest.java: -------------------------------------------------------------------------------- 1 | import com.contentful.java.cda.rich.CDARichHeading; 2 | import com.contentful.rich.html.HtmlContext; 3 | import com.contentful.rich.html.HtmlProcessor; 4 | 5 | import org.junit.Test; 6 | 7 | import static com.google.common.truth.Truth.assertThat; 8 | 9 | public class HeadingTest { 10 | @Test 11 | public void canCreateAllHeadings() { 12 | final HtmlProcessor processor = new HtmlProcessor(); 13 | final HtmlContext context = new HtmlContext(); 14 | 15 | assertThat(processor.process(context, new CDARichHeading(1))).isEqualTo("

"); 16 | assertThat(processor.process(context, new CDARichHeading(2))).isEqualTo("

"); 17 | assertThat(processor.process(context, new CDARichHeading(3))).isEqualTo("

"); 18 | assertThat(processor.process(context, new CDARichHeading(4))).isEqualTo("

"); 19 | assertThat(processor.process(context, new CDARichHeading(5))).isEqualTo("
"); 20 | assertThat(processor.process(context, new CDARichHeading(6))).isEqualTo("
"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /android/src/main/res/layout/rich_embedded_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 28 | 29 | -------------------------------------------------------------------------------- /core/src/main/java/com/contentful/rich/core/Context.java: -------------------------------------------------------------------------------- 1 | package com.contentful.rich.core; 2 | 3 | import com.contentful.java.cda.rich.CDARichBlock; 4 | 5 | import javax.annotation.Nonnull; 6 | import javax.annotation.Nullable; 7 | 8 | /** 9 | * Class for handling context switches while parsing the rich text. 10 | * 11 | * @param Parameter class to be used for the path. 12 | */ 13 | public class Context { 14 | 15 | /** 16 | * This method gets called by the {@see Processor} when a new block node was found and entered. 17 | * 18 | * @param block the block node entered 19 | */ 20 | public void onBlockEntered(@Nonnull CDARichBlock block) { 21 | } 22 | 23 | /** 24 | * This method gets called by the {@see Processor} when a block was exited. 25 | * 26 | * @param block the block node exited 27 | */ 28 | public void onBlockExited(@Nonnull CDARichBlock block) { 29 | } 30 | 31 | /** 32 | * See implementors and or test cases for usage of this method. 33 | * 34 | * @return the accumulated path of this context. 35 | */ 36 | @Nullable public T getPath() { 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android_sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 18 | 19 |