Skip to content

Commit

Permalink
Support for Android (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
wingio committed Nov 21, 2023
1 parent ed5790f commit 0e8c805
Show file tree
Hide file tree
Showing 22 changed files with 813 additions and 28 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,22 @@ fun SomeScreen() {
}
```

### Android
Artifact: `syntakts-android`

Syntakts uses SpannableStrings in order to display rendered text on Android

Example:
```kotlin
val syntakts = syntakts<Unit> { /* */ }

findViewById<TextView>(R.id.my_text_view).render("some input", syntakts)
```

#### Clickable
Syntakts for Compose includes a ClickableText component that is neccessary in order to handle clickable text. The `syntakts-compose-material3` includes this component as well but adds support for Material 3 theming

Syntakts for Android requires that the TextView have its movementMethod set to our ClickableMovementMethod

## Attribution
Syntakts was heavily inspired by [SimpleAST](https://github.com/discord/SimpleAST), an unfortunately abandoned library that was once used in Discords android app
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ buildscript {
dependencies {
classpath(libs.plugin.maven)
classpath(libs.plugin.multiplatform.compose)
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0")
}
}

Expand Down
4 changes: 4 additions & 0 deletions demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,8 @@ android {
dependencies {
implementation(libs.bundles.compose)
implementation(project(":syntakts-compose-material3"))
implementation(project(":syntakts-android"))
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.constraintlayout)
}
4 changes: 4 additions & 0 deletions demo/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
android:supportsRtl="true"
android:theme="@style/Theme.Syntakts"
tools:targetApi="31">
<activity
android:name=".AndroidTestActivity"
android:exported="true"
android:theme="@style/Theme.Syntakts" />
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
27 changes: 27 additions & 0 deletions demo/src/main/java/xyz/wingio/syntakts/demo/AndroidTestActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package xyz.wingio.syntakts.demo

import android.os.Bundle
import android.widget.TextView
import androidx.activity.ComponentActivity
import xyz.wingio.syntakts.android.render

class AndroidTestActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_android_test)

val testString = """
# Header
**Bold** *Italic* __Underline__ ~~Strikethrough~~ <@1234>
""".trimIndent()

findViewById<TextView>(R.id.test_text).render(
text = testString,
syntakts = TestSyntakts,
context = Context(this),
enableClickable = true
)
}

}
39 changes: 11 additions & 28 deletions demo/src/main/java/xyz/wingio/syntakts/demo/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package xyz.wingio.syntakts.demo

import android.content.Intent
import android.graphics.Typeface
import android.os.Bundle
import android.widget.Toast
Expand All @@ -12,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.ProvideTextStyle
Expand All @@ -25,6 +27,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -52,31 +55,10 @@ class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val atIntent = Intent(this, AndroidTestActivity::class.java)
setContent {
SyntaktsTheme {
val syntakts = rememberSyntakts<Context> {
rule("<@([0-9]+)>") { result, ctx ->
val username = ctx.userMap[result.groupValues[1]] ?: "Unknown"
appendClickable(
"@$username",
Style(
color = SyntaktsColor.YELLOW,
background = SyntaktsColor.YELLOW withOpacity 0.2f
),
onLongClick = {
Toast.makeText(this@MainActivity, "Mention long clicked", Toast.LENGTH_SHORT).show()
},
onClick = {
Toast.makeText(this@MainActivity, "Mention clicked", Toast.LENGTH_SHORT).show()
}
)
}

addMarkdownRules()
}

var text by remember { mutableStateOf("Test") }
var text by remember { mutableStateOf("**bold** *italic* __underline__ ~~strikethrough~~") }

// A surface container using the 'background' color from the theme
Surface(
Expand All @@ -91,7 +73,7 @@ class MainActivity : ComponentActivity() {
.fillMaxSize()
) {
ClickableText(
text = syntakts.rememberRendered(text, Context(MaterialTheme.colorScheme.primary)),
text = TestSyntakts.rememberRendered(text, Context(LocalContext.current)),
modifier = Modifier
.weight(1f)
.fillMaxWidth()
Expand All @@ -105,6 +87,11 @@ class MainActivity : ComponentActivity() {
},
modifier = Modifier.weight(1f).fillMaxWidth()
)
Button(
onClick = { startActivity(atIntent) }
) {
Text("Launch android test")
}
}
}
}
Expand All @@ -113,7 +100,3 @@ class MainActivity : ComponentActivity() {

}

data class Context(
val primaryColor: Color,
val userMap: Map<String, String> = mapOf("1234" to "Wing")
)
34 changes: 34 additions & 0 deletions demo/src/main/java/xyz/wingio/syntakts/demo/TestSyntakts.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package xyz.wingio.syntakts.demo

import android.content.Context as AndroidContext
import android.widget.Toast
import xyz.wingio.syntakts.markdown.addMarkdownRules
import xyz.wingio.syntakts.style.Color
import xyz.wingio.syntakts.style.Style
import xyz.wingio.syntakts.syntakts

val TestSyntakts = syntakts<Context> {
rule("<@([0-9]+)>") { result, ctx ->
val username = ctx.userMap[result.groupValues[1]] ?: "Unknown"
appendClickable(
"@$username",
Style(
color = Color.MAGENTA,
background = Color.MAGENTA withOpacity 0.2f
),
onLongClick = {
Toast.makeText(ctx.androidContext, "Mention long clicked", Toast.LENGTH_SHORT).show()
},
onClick = {
Toast.makeText(ctx.androidContext, "Mention clicked", Toast.LENGTH_SHORT).show()
}
)
}

addMarkdownRules()
}

data class Context(
val androidContext: AndroidContext,
val userMap: Map<String, String> = mapOf("1234" to "Wing")
)
20 changes: 20 additions & 0 deletions demo/src/main/res/layout/activity_android_test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AndroidTestActivity">

<TextView
android:id="@+id/test_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
7 changes: 7 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,26 @@ junit = "4.13.2"
kotlin = "1.9.10"
kotlin-binary-compatibility = "0.13.2"
uuid = "0.8.1"
appcompat = "1.6.1"
material = "1.9.0"
constraintlayout = "2.1.4"

[libraries]
plugin-android = { module = "com.android.tools.build:gradle", version.ref = "agp" }
plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
plugin-maven = { module = "com.vanniktech:gradle-maven-publish-plugin", version = "0.25.3" }
plugin-multiplatform-compose = { module = "org.jetbrains.compose:compose-gradle-plugin", version.ref = "compose-multiplatform" }

androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.12.0" }
compose-activity = { group = "androidx.activity", name = "activity-compose", version = "1.8.0" }
compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "compose-material3" }
compose-stable-marker = { group = "com.github.skydoves", name = "compose-stable-marker", version.ref = "compose-stable-marker" }
junit = { module = "junit:junit", version.ref = "junit" }
kotlin-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version = "1.7.3" }
uuid = { group = "com.benasher44", name = "uuid", version.ref = "uuid"}
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }

[plugins]
binary-compatibility = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "kotlin-binary-compatibility" }
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ rootProject.name = "Syntakts"
include(":demo")

include(":syntakts-core")

include(":syntakts-android")
include(":syntakts-compose")
include(":syntakts-compose-material3")
1 change: 1 addition & 0 deletions syntakts-android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
74 changes: 74 additions & 0 deletions syntakts-android/api/syntakts-android.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
public final class xyz/wingio/syntakts/android/ClickableMovementMethod : android/text/method/LinkMovementMethod {
public static final field Companion Lxyz/wingio/syntakts/android/ClickableMovementMethod$Companion;
public static final field LONG_TOUCH_DURATION J
public fun <init> ()V
public fun onTouchEvent (Landroid/widget/TextView;Landroid/text/Spannable;Landroid/view/MotionEvent;)Z
}

public final class xyz/wingio/syntakts/android/ClickableMovementMethod$Companion {
}

public final class xyz/wingio/syntakts/android/SyntaktsKt {
public static final fun render (Landroid/widget/TextView;Ljava/lang/CharSequence;Lxyz/wingio/syntakts/Syntakts;Ljava/lang/Object;Z)V
public static final fun render (Landroid/widget/TextView;Ljava/lang/CharSequence;Lxyz/wingio/syntakts/Syntakts;Z)V
public static final fun render (Lxyz/wingio/syntakts/Syntakts;Ljava/lang/CharSequence;Landroid/content/Context;)Ljava/lang/CharSequence;
public static final fun render (Lxyz/wingio/syntakts/Syntakts;Ljava/lang/CharSequence;Ljava/lang/Object;Landroid/content/Context;)Ljava/lang/CharSequence;
public static synthetic fun render$default (Landroid/widget/TextView;Ljava/lang/CharSequence;Lxyz/wingio/syntakts/Syntakts;Ljava/lang/Object;ZILjava/lang/Object;)V
public static synthetic fun render$default (Landroid/widget/TextView;Ljava/lang/CharSequence;Lxyz/wingio/syntakts/Syntakts;ZILjava/lang/Object;)V
}

public final class xyz/wingio/syntakts/android/markdown/MarkdownKt {
public static final fun renderBasicMarkdown (Landroid/widget/TextView;Ljava/lang/CharSequence;)V
public static final fun renderMarkdown (Landroid/widget/TextView;Ljava/lang/CharSequence;)V
}

public class xyz/wingio/syntakts/android/spans/ClickableSpan : android/text/style/ClickableSpan {
public fun <init> (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V
public fun onClick (Landroid/view/View;)V
public final fun onLongClick (Landroid/view/View;)V
public fun updateDrawState (Landroid/text/TextPaint;)V
}

public class xyz/wingio/syntakts/android/spans/SyntaktsStyleSpan : android/text/style/MetricAffectingSpan {
public static final field Companion Lxyz/wingio/syntakts/android/spans/SyntaktsStyleSpan$Companion;
public fun <init> (Lxyz/wingio/syntakts/style/Style;Landroid/content/Context;)V
public final fun getContext ()Landroid/content/Context;
public final fun getStyle ()Lxyz/wingio/syntakts/style/Style;
public fun updateDrawState (Landroid/text/TextPaint;)V
public fun updateMeasureState (Landroid/text/TextPaint;)V
}

public final class xyz/wingio/syntakts/android/spans/SyntaktsStyleSpan$Companion {
public final fun apply (Landroid/text/TextPaint;Lxyz/wingio/syntakts/style/Style;Landroid/content/Context;)V
}

public final class xyz/wingio/syntakts/android/style/ColorKt {
public static final fun fromAndroidColorLong (Lxyz/wingio/syntakts/style/Color$Companion;J)Lxyz/wingio/syntakts/style/Color;
public static final fun toAndroidColorInt (Lxyz/wingio/syntakts/style/Color;)I
public static final fun toSyntaktsColor (J)Lxyz/wingio/syntakts/style/Color;
}

public final class xyz/wingio/syntakts/android/style/SpannableStyledTextBuilder : xyz/wingio/syntakts/style/StyledTextBuilder {
public fun <init> (Landroid/content/Context;)V
public fun addClickable (IILkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Lxyz/wingio/syntakts/android/style/SpannableStyledTextBuilder;
public synthetic fun addClickable (IILkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Lxyz/wingio/syntakts/style/StyledTextBuilder;
public fun addClickable (Lkotlin/ranges/IntRange;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Lxyz/wingio/syntakts/style/StyledTextBuilder;
public fun addStyle (IILkotlin/jvm/functions/Function1;)Lxyz/wingio/syntakts/android/style/SpannableStyledTextBuilder;
public synthetic fun addStyle (IILkotlin/jvm/functions/Function1;)Lxyz/wingio/syntakts/style/StyledTextBuilder;
public fun addStyle (Lkotlin/ranges/IntRange;Lkotlin/jvm/functions/Function1;)Lxyz/wingio/syntakts/style/StyledTextBuilder;
public fun addStyle (Lxyz/wingio/syntakts/style/Style;II)Lxyz/wingio/syntakts/android/style/SpannableStyledTextBuilder;
public synthetic fun addStyle (Lxyz/wingio/syntakts/style/Style;II)Lxyz/wingio/syntakts/style/StyledTextBuilder;
public fun addStyle (Lxyz/wingio/syntakts/style/Style;Lkotlin/ranges/IntRange;)Lxyz/wingio/syntakts/style/StyledTextBuilder;
public fun append (Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function1;)Lxyz/wingio/syntakts/android/style/SpannableStyledTextBuilder;
public synthetic fun append (Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function1;)Lxyz/wingio/syntakts/style/StyledTextBuilder;
public fun append (Ljava/lang/CharSequence;Lxyz/wingio/syntakts/style/Style;)Lxyz/wingio/syntakts/android/style/SpannableStyledTextBuilder;
public synthetic fun append (Ljava/lang/CharSequence;Lxyz/wingio/syntakts/style/Style;)Lxyz/wingio/syntakts/style/StyledTextBuilder;
public fun appendClickable (Ljava/lang/CharSequence;Lxyz/wingio/syntakts/style/Style;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Lxyz/wingio/syntakts/android/style/SpannableStyledTextBuilder;
public synthetic fun appendClickable (Ljava/lang/CharSequence;Lxyz/wingio/syntakts/style/Style;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Lxyz/wingio/syntakts/style/StyledTextBuilder;
public fun build ()Ljava/lang/CharSequence;
public synthetic fun build ()Ljava/lang/Object;
public fun clear ()V
public final fun getContext ()Landroid/content/Context;
public fun getLength ()I
}

38 changes: 38 additions & 0 deletions syntakts-android/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
kotlin("multiplatform")
id("com.android.library")
id("com.vanniktech.maven.publish.base")
alias(libs.plugins.binary.compatibility)
}

setup(
libName = "Syntakts for Android",
moduleName = "syntakts-android",
moduleDescription = "Support for Syntakts rendering on Android"
)

kotlin {
androidTarget {
publishLibraryVariants("release")
}

jvmToolchain(17)
explicitApi()

sourceSets {
val androidMain by named("androidMain") {
dependencies {
api(project(":syntakts-core"))
implementation(libs.androidx.core.ktx)
implementation(libs.kotlin.coroutines.core)
}
}

val androidTest by named("androidUnitTest") {
dependencies {
implementation(kotlin("test"))
implementation(libs.junit)
}
}
}
}
Loading

0 comments on commit 0e8c805

Please sign in to comment.