diff --git a/.ci/notify.sh b/.ci/notify.sh index a9edf69..0b8353e 100755 --- a/.ci/notify.sh +++ b/.ci/notify.sh @@ -19,7 +19,7 @@ fi if [[ -n "${GITHUB_HEAD_REF}" ]]; then # this is a PR - COMMITS_INVOLVED=$(git log --oneline ^"${GITHUB_BASE_REF}" HEAD) + COMMITS_INVOLVED=$(git log --oneline ^"origin/${GITHUB_BASE_REF}" HEAD) PR_LINK_TEXT=$(cat < - Thanks for opening your first issue here! :tada: - -# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome - -# Comment to be posted to on PRs from first time contributors in your repository -newPRWelcomeComment: > - Thanks for opening this pull request! :nerd_face: - -# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge - -# Comment to be posted to on pull requests merged by a first time user -firstPRMergeComment: > - Congrats on merging your first pull request here! You should be proud of yourself :1st_place_medal: - ![Congratulations](https://media.giphy.com/media/ehhuGD0nByYxO/giphy.gif) - -# It is recommend to include as many gifs and emojis as possible diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2504eb4..8406cd7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,11 +12,11 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: temurin cache: 'gradle' - name: Perform base checks - run: ./gradlew app:assembleDebug library:sourcesJar library:dokkaJar --stacktrace + run: ./gradlew app:assembleDebug --stacktrace ANDROID_UNIT_TESTS: name: Unit Tests runs-on: ubuntu-latest @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: temurin cache: 'gradle' - name: Execute unit tests @@ -34,11 +34,15 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + with: + # required for telegram notifications on PRs + fetch-depth: 0 - uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: temurin cache: 'gradle' + - name: Setup custom keystore file run: | mkdir -p $HOME/.android diff --git a/.jitpack.yml b/.jitpack.yml new file mode 100644 index 0000000..57db296 --- /dev/null +++ b/.jitpack.yml @@ -0,0 +1 @@ +jdk: openjdk17 \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..86bd471 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +.PHONY: build + +clean: + ./gradlew clean + +build: + ./gradlew assembleDebug +apk: build + +buildRelease: + ./gradlew assembleRelease + +install: build + adb install ./app/build/outputs/apk/**/*.apk + +installRelease: buildRelease + adb install ./app/build/outputs/apk/**/*.apk + +run: install open + +runRelease: installRelease openRelease + +open: + adb shell am start -n "de.markusressel.kodeeditor.debug/de.markusressel.kodeeditor.demo.ComposeMainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER + +openRelease: + adb shell am start -n "de.markusressel.kodeeditor/de.markusressel.kodeeditor.demo.ComposeMainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER diff --git a/README.md b/README.md index bc12c36..07213e0 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,10 @@ -# KodeEditor +# KodeEditor [![codebeat badge](https://codebeat.co/badges/f7fa8602-1d15-457e-904d-cb585e984952)](https://codebeat.co/projects/github-com-markusressel-kodeeditor-master) A simple code editor with syntax highlighting and pinch to zoom ![Editing](https://thumbs.gfycat.com/TalkativeGrandIchthyosaurs-size_restricted.gif) ![Scroll and zoom](https://thumbs.gfycat.com/BouncyLividBlackbear-size_restricted.gif) ![Minimap](https://thumbs.gfycat.com/VigorousDimFrog-size_restricted.gif) -# Build Status - -| Master | -|--------| -| [![codebeat badge](https://codebeat.co/badges/f7fa8602-1d15-457e-904d-cb585e984952)](https://codebeat.co/projects/github-com-markusressel-kodeeditor-master) | - # Features * [x] Pinch-To-Zoom * [x] Line numbers diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 574053a..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,66 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-parcelize' - -android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - - defaultConfig { - applicationId "de.markusressel.kodeeditor" - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode rootProject.ext.versionCode - versionName rootProject.ext.versionName - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - - setProperty("archivesBaseName", "KodeEditor_v${versionName}_(${versionCode})") - - multiDexEnabled true - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - buildFeatures { - viewBinding true - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - packagingOptions { - pickFirst 'META-INF/proguard/androidx-annotations.pro' - exclude 'LICENSE.txt' - exclude 'META-INF/DEPENDENCIES' - exclude 'META-INF/ASL2.0' - exclude 'META-INF/NOTICE' - exclude 'META-INF/LICENSE' - } -} - -dependencies { - implementation project(':library') - - // Syntax Highlighting - api("com.github.markusressel.KodeHighlighter:markdown:$kodeHighlighterVersion") - - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - - implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.5.0' - - def fuelVersion = "2.3.1" - implementation "com.github.kittinunf.fuel:fuel:$fuelVersion" - implementation "com.github.kittinunf.fuel:fuel-android:$fuelVersion" - - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..8b0fd05 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id("kodeeditor.android.application") + id("kodeeditor.android.application.compose") + id("kodeeditor.android.application.flavors") +} + +android { + namespace = "de.markusressel.kodeeditor.demo" + + defaultConfig { + applicationId = "de.markusressel.kodeeditor" + versionCode = 1 + versionName = "4.0.1" + + setProperty("archivesBaseName", "KodeEditor_v${versionName}_(${versionCode})") + } +} + +dependencies { + implementation(project(":library")) + + // Syntax Highlighting + implementation(libs.kodehighlighter.markdown) + + implementation(libs.kotlin.stdlib.jdk8) + + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.material) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + + implementation(libs.fuel) + implementation(libs.fuel.android) + + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.coroutines.android) + + debugImplementation(libs.androidx.compose.ui.tooling) + implementation(libs.androidx.compose.ui.tooling.preview) + + testImplementation(libs.junit4) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.androidx.test.espresso.core) +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro deleted file mode 100644 index f1b4245..0000000 --- a/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f20dd8d..12106e0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + @@ -13,14 +12,19 @@ android:theme="@style/AppTheme" android:windowSoftInputMode="adjustResize"> + android:name=".ComposeMainActivity" + android:exported="true" + android:theme="@style/AppTheme"> + + \ No newline at end of file diff --git a/app/src/main/java/de/markusressel/kodeeditor/demo/ComposeMainActivity.kt b/app/src/main/java/de/markusressel/kodeeditor/demo/ComposeMainActivity.kt new file mode 100644 index 0000000..b9c3108 --- /dev/null +++ b/app/src/main/java/de/markusressel/kodeeditor/demo/ComposeMainActivity.kt @@ -0,0 +1,139 @@ +package de.markusressel.kodeeditor.demo + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.annotation.RawRes +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import de.markusressel.kodeeditor.demo.ui.theme.KodeEditorTheme +import de.markusressel.kodeeditor.library.compose.KodeEditor +import de.markusressel.kodeeditor.library.compose.KodeEditorDefaults +import de.markusressel.kodehighlighter.language.markdown.MarkdownRuleBook +import de.markusressel.kodehighlighter.language.markdown.colorscheme.DarkBackgroundColorSchemeWithSpanStyle + +class ComposeMainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + KodeEditorTheme { + var currentFontSize by remember { + mutableStateOf(14) + } + + Column( + modifier = Modifier.background(MaterialTheme.colors.background), + ) { + KodeEditorConfigurationMenu( + currentFontSize = currentFontSize, + onIncreaseFontSize = { currentFontSize++ }, + onDecreaseFontSize = { currentFontSize-- }, + ) + + var text by rememberSaveable(stateSaver = TextFieldValue.Saver) { + val initialText = readResourceFileAsText(R.raw.short_sample) +// val initialText = readResourceFileAsText(R.raw.sample_text) + mutableStateOf(TextFieldValue(initialText)) + } + + KodeEditor( + modifier = Modifier + .fillMaxSize() + .border(BorderStroke(1.dp, MaterialTheme.colors.primary)), + languageRuleBook = MarkdownRuleBook(), + colorScheme = DarkBackgroundColorSchemeWithSpanStyle(), + text = text, + onValueChange = { text = it }, + textStyle = TextStyle(fontSize = currentFontSize.sp).copy( + color = MaterialTheme.colors.onSurface, + ), + colors = KodeEditorDefaults.editorColors() + ) + } + } + } + } + + @Composable + private fun KodeEditorConfigurationMenu( + currentFontSize: Int, + onIncreaseFontSize: () -> Unit, + onDecreaseFontSize: () -> Unit, + ) { + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + modifier = Modifier.weight(1f), + text = "Font Size: $currentFontSize", + color = MaterialTheme.colors.onSurface, + ) + + Button(onClick = onIncreaseFontSize) { + Text( + text = "+", + color = MaterialTheme.colors.onPrimary, + ) + } + + Spacer(modifier = Modifier.size(4.dp)) + + Button(onClick = onDecreaseFontSize) { + Text( + text = "-", + color = MaterialTheme.colors.onPrimary, + ) + } + } + } + } + + private fun readResourceFileAsText(@RawRes resourceId: Int) = + resources.openRawResource(resourceId).bufferedReader().readText() + +} + + +@Preview(showBackground = true) +@Composable +private fun DefaultPreview() { + KodeEditorTheme { + var text by rememberSaveable(stateSaver = TextFieldValue.Saver) { + val initialText = """ + # Heading + + Heading Test + + ## Subheading + + Subheading Text + """.trimIndent() + mutableStateOf(TextFieldValue(initialText)) + } + + KodeEditor( + languageRuleBook = MarkdownRuleBook(), + colorScheme = DarkBackgroundColorSchemeWithSpanStyle(), + text = text, + onValueChange = { text = it } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/de/markusressel/kodeeditor/MainActivity.kt b/app/src/main/java/de/markusressel/kodeeditor/demo/MainActivity.kt similarity index 89% rename from app/src/main/java/de/markusressel/kodeeditor/MainActivity.kt rename to app/src/main/java/de/markusressel/kodeeditor/demo/MainActivity.kt index c09e580..8321229 100644 --- a/app/src/main/java/de/markusressel/kodeeditor/MainActivity.kt +++ b/app/src/main/java/de/markusressel/kodeeditor/demo/MainActivity.kt @@ -1,4 +1,4 @@ -package de.markusressel.kodeeditor +package de.markusressel.kodeeditor.demo import android.graphics.Color import android.os.Bundle @@ -6,9 +6,10 @@ import android.view.Gravity import androidx.annotation.RawRes import androidx.appcompat.app.AppCompatActivity import com.github.kittinunf.fuel.Fuel -import de.markusressel.kodeeditor.databinding.ActivityMainBinding +import de.markusressel.kodeeditor.demo.databinding.ActivityMainBinding import de.markusressel.kodeeditor.library.extensions.dpToPx import de.markusressel.kodehighlighter.language.markdown.MarkdownRuleBook +import de.markusressel.kodehighlighter.language.markdown.colorscheme.DarkBackgroundColorScheme class MainActivity : AppCompatActivity() { @@ -22,6 +23,7 @@ class MainActivity : AppCompatActivity() { binding.codeEditorLayout.apply { languageRuleBook = MarkdownRuleBook() + colorScheme = DarkBackgroundColorScheme() lineNumberGenerator = { lines -> (1..lines).map { " $it " } } diff --git a/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Color.kt b/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Color.kt new file mode 100644 index 0000000..fb44652 --- /dev/null +++ b/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Color.kt @@ -0,0 +1,8 @@ +package de.markusressel.kodeeditor.demo.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple200 = Color(0xFFBB86FC) +val Purple500 = Color(0xFF6200EE) +val Purple700 = Color(0xFF3700B3) +val Teal200 = Color(0xFF03DAC5) \ No newline at end of file diff --git a/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Shape.kt b/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Shape.kt new file mode 100644 index 0000000..90768f9 --- /dev/null +++ b/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Shape.kt @@ -0,0 +1,11 @@ +package de.markusressel.kodeeditor.demo.ui.theme + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Shapes +import androidx.compose.ui.unit.dp + +val Shapes = Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(4.dp), + large = RoundedCornerShape(0.dp) +) \ No newline at end of file diff --git a/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Theme.kt b/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Theme.kt new file mode 100644 index 0000000..5d1c9ce --- /dev/null +++ b/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Theme.kt @@ -0,0 +1,44 @@ +package de.markusressel.kodeeditor.demo.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable + +private val DarkColorPalette = darkColors( + primary = Purple200, + primaryVariant = Purple700, + secondary = Teal200 +) + +private val LightColorPalette = lightColors( + primary = Purple500, + primaryVariant = Purple700, + secondary = Teal200 + + /* Other default colors to override + background = Color.White, + surface = Color.White, + onPrimary = Color.White, + onSecondary = Color.Black, + onBackground = Color.Black, + onSurface = Color.Black, + */ +) + +@Composable +fun KodeEditorTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { + val colors = if (darkTheme) { + DarkColorPalette + } else { + LightColorPalette + } + + MaterialTheme( + colors = colors, + typography = Typography, + shapes = Shapes, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Type.kt b/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Type.kt new file mode 100644 index 0000000..8a9dddd --- /dev/null +++ b/app/src/main/java/de/markusressel/kodeeditor/demo/ui/theme/Type.kt @@ -0,0 +1,28 @@ +package de.markusressel.kodeeditor.demo.ui.theme + +import androidx.compose.material.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + body1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ) + /* Other default text styles to override + button = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + fontSize = 14.sp + ), + caption = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 12.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/res/raw/short_sample b/app/src/main/res/raw/short_sample new file mode 100644 index 0000000..c605b18 --- /dev/null +++ b/app/src/main/res/raw/short_sample @@ -0,0 +1,2 @@ +# KodeEditor +A simple code editor with syntax highlighting and pinch to zoom diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..78554f9 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts new file mode 100644 index 0000000..36efc20 --- /dev/null +++ b/build-logic/convention/build.gradle.kts @@ -0,0 +1,60 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + `kotlin-dsl` +} + +group = "de.markusressel.kodeeditor.buildlogic" +version = "0.1.0" + +java { + // Up to Java 11 APIs are available through desugaring + // https://developer.android.com/studio/write/java11-minimal-support-table + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } +} + +dependencies { + compileOnly(libs.android.gradle.plugin) + compileOnly(libs.kotlin.gradle.plugin) + compileOnly(libs.ksp.gradle.plugin) +} + +gradlePlugin { + plugins { + register("androidApplicationCompose") { + id = "kodeeditor.android.application.compose" + implementationClass = "AndroidApplicationComposeConventionPlugin" + } + register("androidApplication") { + id = "kodeeditor.android.application" + implementationClass = "AndroidApplicationConventionPlugin" + } + register("androidLibraryCompose") { + id = "kodeeditor.android.library.compose" + implementationClass = "AndroidLibraryComposeConventionPlugin" + } + register("androidLibrary") { + id = "kodeeditor.android.library" + implementationClass = "AndroidLibraryConventionPlugin" + } + register("androidLibraryPublishing") { + id = "kodeeditor.android.library.publishing" + implementationClass = "AndroidLibraryPublishingConventionPlugin" + } + register("androidTest") { + id = "kodeeditor.android.test" + implementationClass = "AndroidTestConventionPlugin" + } + register("androidFlavors") { + id = "kodeeditor.android.application.flavors" + implementationClass = "AndroidApplicationFlavorsConventionPlugin" + } + } +} diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt new file mode 100644 index 0000000..13856db --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt @@ -0,0 +1,16 @@ +import com.android.build.api.dsl.ApplicationExtension +import de.markusressel.kodeeditor.configureAndroidCompose +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType + +class AndroidApplicationComposeConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply("com.android.application") + val extension = extensions.getByType() + configureAndroidCompose(extension) + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt new file mode 100644 index 0000000..c8e5c75 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt @@ -0,0 +1,52 @@ +import com.android.build.api.dsl.ApplicationExtension +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import de.markusressel.kodeeditor.TARGET_SDK +import de.markusressel.kodeeditor.configureKotlinAndroid +import de.markusressel.kodeeditor.configureKotlinAndroidToolchain +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class AndroidApplicationConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.application") + apply("org.jetbrains.kotlin.android") + apply("org.jetbrains.kotlin.plugin.parcelize") + } + + group = "de.markusressel.kodeeditor.demo" + + configureKotlinAndroidToolchain() + extensions.configure { + configureKotlinAndroid(this) + + defaultConfig { + targetSdk = TARGET_SDK + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + dataBinding { + enable = true + } + viewBinding { + enable = true + } + + packaging { + resources { + excludes.add("/META-INF/{AL2.0,LGPL2.1}") + excludes.addAll( + listOf("LICENSE.txt", "META-INF/DEPENDENCIES", "META-INF/ASL2.0", "META-INF/NOTICE", "META-INF/LICENSE") + ) + pickFirsts.add("META-INF/proguard/androidx-annotations.pro") + } + } + } + extensions.configure { + } + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt new file mode 100644 index 0000000..74bc7c9 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt @@ -0,0 +1,13 @@ +import com.android.build.api.dsl.ApplicationExtension +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class AndroidApplicationFlavorsConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + extensions.configure { + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt new file mode 100644 index 0000000..1b242c4 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt @@ -0,0 +1,16 @@ +import com.android.build.gradle.LibraryExtension +import de.markusressel.kodeeditor.configureAndroidCompose +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType + +class AndroidLibraryComposeConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply("com.android.library") + val extension = extensions.getByType() + configureAndroidCompose(extension) + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt new file mode 100644 index 0000000..840d065 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -0,0 +1,53 @@ +import com.android.build.api.variant.LibraryAndroidComponentsExtension +import com.android.build.gradle.LibraryExtension +import de.markusressel.kodeeditor.TARGET_SDK +import de.markusressel.kodeeditor.configureKotlinAndroid +import de.markusressel.kodeeditor.configureKotlinAndroidToolchain +import de.markusressel.kodeeditor.disableUnnecessaryAndroidTests +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.kotlin + +class AndroidLibraryConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.library") + apply("org.jetbrains.kotlin.android") + } + + group = "de.markusressel.kodeeditor.library" + + configureKotlinAndroidToolchain() + extensions.configure { + configureKotlinAndroid(this) + defaultConfig.targetSdk = TARGET_SDK + } + extensions.configure { + disableUnnecessaryAndroidTests(target) + } + val libs = extensions.getByType().named("libs") + configurations.configureEach { + resolutionStrategy { + // TODO: + //cacheChangingModulesFor(0, 'seconds') + + force(libs.findLibrary("kotlin-stdlib").get()) + force(libs.findLibrary("kotlin-stdlib-jdk8").get()) + + force(libs.findLibrary("junit4").get()) + // Temporary workaround for https://issuetracker.google.com/174733673 + force("org.objenesis:objenesis:2.6") + } + } + dependencies { + add("androidTestImplementation", kotlin("test")) + add("testImplementation", kotlin("test")) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryPublishingConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryPublishingConventionPlugin.kt new file mode 100644 index 0000000..7ca1926 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryPublishingConventionPlugin.kt @@ -0,0 +1,42 @@ +import com.android.build.gradle.LibraryExtension +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.create + +class AndroidLibraryPublishingConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + extensions.configure { + with(pluginManager) { + apply("maven-publish") + } + publishing { + singleVariant("release") { + group = "com.github.markusressel.KodeEditor" + withJavadocJar() + withSourcesJar() + } + } + configure { + publications { + create("maven", MavenPublication::class) { + groupId = "com.github.markusressel" + artifactId = "KodeEditor" + version = "${target.version}" + + artifact("${layout.buildDirectory.get().asFile}/outputs/aar/${target.name}-release.aar") { + builtBy(tasks.getByName("assemble")) + } + } + } + repositories { + mavenLocal() + } + } + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt new file mode 100644 index 0000000..ede625f --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt @@ -0,0 +1,23 @@ +import com.android.build.gradle.TestExtension +import de.markusressel.kodeeditor.TARGET_SDK +import de.markusressel.kodeeditor.configureKotlinAndroid +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class AndroidTestConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.test") + apply("org.jetbrains.kotlin.android") + } + + extensions.configure { + configureKotlinAndroid(this) + defaultConfig.targetSdk = TARGET_SDK + } + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/de/markusressel/kodeeditor/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/de/markusressel/kodeeditor/AndroidCompose.kt new file mode 100644 index 0000000..4e3aa5a --- /dev/null +++ b/build-logic/convention/src/main/kotlin/de/markusressel/kodeeditor/AndroidCompose.kt @@ -0,0 +1,62 @@ +package de.markusressel.kodeeditor + +import com.android.build.api.dsl.CommonExtension +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import java.io.File + +/** + * Configure Compose-specific options + */ +internal fun Project.configureAndroidCompose( + commonExtension: CommonExtension<*, *, *, *, *, *>, +) { + val libs = extensions.getByType().named("libs") + pluginManager.apply("org.jetbrains.kotlin.plugin.compose") + + commonExtension.apply { + buildFeatures { + compose = true + } + + dependencies { + val bom = libs.findLibrary("androidx-compose-bom").get() + add("implementation", platform(bom)) + add("androidTestImplementation", platform(bom)) + } + } + + tasks.withType().configureEach { + kotlinOptions { + freeCompilerArgs = freeCompilerArgs + buildComposeMetricsParameters() + } + } +} + +private fun Project.buildComposeMetricsParameters(): List { + val metricParameters = mutableListOf() + val enableMetricsProvider = project.providers.gradleProperty("enableComposeCompilerMetrics") + val enableMetrics = (enableMetricsProvider.orNull == "true") + if (enableMetrics) { + val metricsFolder = File(project.layout.buildDirectory.get().asFile, "compose-metrics") + metricParameters.add("-P") + metricParameters.add( + "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + metricsFolder.absolutePath + ) + } + + val enableReportsProvider = project.providers.gradleProperty("enableComposeCompilerReports") + val enableReports = (enableReportsProvider.orNull == "true") + if (enableReports) { + val reportsFolder = File(project.layout.buildDirectory.get().asFile, "compose-reports") + metricParameters.add("-P") + metricParameters.add( + "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + reportsFolder.absolutePath + ) + } + return metricParameters.toList() +} diff --git a/build-logic/convention/src/main/kotlin/de/markusressel/kodeeditor/AndroidInstrumentedTests.kt b/build-logic/convention/src/main/kotlin/de/markusressel/kodeeditor/AndroidInstrumentedTests.kt new file mode 100644 index 0000000..9bf14be --- /dev/null +++ b/build-logic/convention/src/main/kotlin/de/markusressel/kodeeditor/AndroidInstrumentedTests.kt @@ -0,0 +1,19 @@ +package de.markusressel.kodeeditor + +import com.android.build.api.variant.LibraryAndroidComponentsExtension +import org.gradle.api.Project + +/** + * Disable unnecessary Android instrumented tests for the [project] if there is no `androidTest` folder. + * Otherwise, these projects would be compiled, packaged, installed and ran only to end-up with the following message: + * + * > Starting 0 tests on AVD + * + * Note: this could be improved by checking other potential sourceSets based on buildTypes and flavors. + */ +internal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests( + project: Project, +) = beforeVariants { + it.enableAndroidTest = it.enableAndroidTest + && project.projectDir.resolve("src/androidTest").exists() +} diff --git a/build-logic/convention/src/main/kotlin/de/markusressel/kodeeditor/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/de/markusressel/kodeeditor/KotlinAndroid.kt new file mode 100644 index 0000000..ba21968 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/de/markusressel/kodeeditor/KotlinAndroid.kt @@ -0,0 +1,74 @@ +package de.markusressel.kodeeditor + +import com.android.build.api.dsl.CommonExtension +import org.gradle.api.JavaVersion +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.api.plugins.ExtensionAware +import org.gradle.kotlin.dsl.* +import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +const val COMPILE_SDK = 34 +const val TARGET_SDK = 34 + +/** + * Configure base Kotlin with Android options + */ +internal fun Project.configureKotlinAndroid( + commonExtension: CommonExtension<*, *, *, *, *, *>, +) { + commonExtension.apply { + compileSdk = COMPILE_SDK + + defaultConfig { + minSdk = 21 + } + + compileOptions { + // Up to Java 11 APIs are available through desugaring + // https://developer.android.com/studio/write/java11-minimal-support-table + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + isCoreLibraryDesugaringEnabled = true + } + } + + // Use withType to workaround https://youtrack.jetbrains.com/issue/KT-55947 + tasks.withType().configureEach { + kotlinOptions { + // Set JVM target to 11 + jvmTarget = JavaVersion.VERSION_17.toString() + // Treat all Kotlin warnings as errors (disabled by default) + // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties + val warningsAsErrors: String? by project + allWarningsAsErrors = warningsAsErrors.toBoolean() + freeCompilerArgs = freeCompilerArgs + listOf( + "-opt-in=kotlin.RequiresOptIn", + // Enable experimental coroutines APIs, including Flow + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlinx.coroutines.FlowPreview", + ) + } + } + + val libs = extensions.getByType().named("libs") + + dependencies { + add("coreLibraryDesugaring", libs.findLibrary("android.desugarJdkLibs").get()) + } +} + +fun CommonExtension<*, *, *, *, *, *>.kotlinOptions(block: KotlinJvmOptions.() -> Unit) { + (this as ExtensionAware).extensions.configure("kotlinOptions", block) +} + +/** + * Configure Kotlin's jvm toolchain for Android projects + */ +internal fun Project.configureKotlinAndroidToolchain() { + extensions.configure { + jvmToolchain(17) + } +} diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties new file mode 100644 index 0000000..1c9073e --- /dev/null +++ b/build-logic/gradle.properties @@ -0,0 +1,4 @@ +# Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534 +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configureondemand=true diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000..2907fbf --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,14 @@ +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + } + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +rootProject.name = "build-logic" +include(":convention") diff --git a/build.gradle b/build.gradle deleted file mode 100644 index b68f2b6..0000000 --- a/build.gradle +++ /dev/null @@ -1,66 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - ext.kotlin_version = '1.8.21' - ext.dokka_version = '1.6.10' - - ext { - group = 'com.github.markusressel' - - gradle_plugin_version = '7.0.4' - - minSdkVersion = 19 - versionName = "4.0.1" - versionCode = 1 - - compileSdkVersion = 31 - targetSdkVersion = 31 - buildToolsVersion = "30.0.3" - - // DEPENDENCIES - - kodeHighlighterVersion = "v3.0.0" -// kodeHighlighterVersion = "master-SNAPSHOT" - - javaxAnnotationVersion = "10.0-b28" - aboutlibrariesVersion = "8.2.0" - - flowBindingVersion = "1.2.0" - - timberKtVersion = "1.5.1" - } - - repositories { - google() - jcenter() - } - - dependencies { - classpath "com.android.tools.build:gradle:$gradle_plugin_version" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' - classpath("org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}") - } -} - -allprojects { - configurations.all() { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' - - resolutionStrategy.force "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - resolutionStrategy.force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - } -} - -apply plugin: 'kotlin' - -compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} -compileTestKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..0abaa27 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,15 @@ +buildscript { + repositories { + google() + mavenCentral() + maven(url = "https://jitpack.io") + } +} + +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.compose.compiler) apply false + alias(libs.plugins.kotlin.jvm) apply false + alias(libs.plugins.kotlin.serialization) apply false + alias(libs.plugins.ksp) apply false +} diff --git a/gradle.properties b/gradle.properties index dc251f5..336247a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,4 +12,6 @@ org.gradle.jvmargs=-Xmx1536m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..c740164 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,78 @@ +[versions] +activityCompose = "1.9.0" +androidDesugarJdkLibs = "2.0.4" +androidGradlePlugin = "8.3.2" +androidxAnnotation = "1.8.0" +androidxAppCompat = "1.7.0" +androidxComposeBom = "2024.06.00" +androidxComposeUi = "1.6.8" +androidxComposeUiText = "1.6.8" +androidxLifecycle = "2.8.3" +androidxTestExtJunit = "1.2.1" +androidxTestEspressoCore = "3.6.1" +dokkaGradlePlugin = "1.7.20" +fuel = "2.3.1" +flowbindingAndroid = "1.2.0" +junit4 = "4.13.2" +kotlin = "2.0.0" +kotlinCoroutinesCore = "1.8.0" +kotlinCoroutinesAndroid = "1.8.0" +kodehighlighter = "4.0.3" +ksp = "2.0.0-1.0.22" +material = "1.12.0" +zoomlayout = "1.9.0" + + +[libraries] +android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } +android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } + +androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } +androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidxAnnotation" } + +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } + +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } +androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "androidxComposeUi" } +androidx-compose-ui-text = { group = "androidx.compose.ui", name = "ui-text", version.ref = "androidxComposeUiText" } +androidx-compose-material = { group = "androidx.compose.material", name = "material", version.ref = "androidxComposeUiText" } +androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } + +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidxLifecycle" } + +dokka-gradle-plugin = { group = "org.jetbrains.dokka", name = "dokka-gradle-plugin", version.ref = "dokkaGradlePlugin" } + +fuel = { group = "com.github.kittinunf.fuel", name = "fuel", version.ref = "fuel" } +fuel-android = { group = "com.github.kittinunf.fuel", name = "fuel-android", version.ref = "fuel" } + +flowbinding-android = { group = "io.github.reactivecircus.flowbinding", name = "flowbinding-android", version.ref = "flowbindingAndroid" } + +kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } +kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } +kotlin-stdlib-jdk8 = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" } +kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinCoroutinesCore" } +kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinCoroutinesAndroid" } + +ksp-gradle-plugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } + +kodehighlighter-core = { group = "com.github.markusressel.KodeHighlighter", name = "core", version.ref = "kodehighlighter" } +kodehighlighter-markdown = { group = "com.github.markusressel.KodeHighlighter", name = "markdown", version.ref = "kodehighlighter" } + +material = { module = "com.google.android.material:material", version.ref = "material" } +zoomlayout = { group = "com.otaliastudios", name = "zoomlayout", version.ref = "zoomlayout" } + +junit4 = { group = "junit", name = "junit", version.ref = "junit4" } +androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxTestExtJunit" } +androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxTestEspressoCore" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } +android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } +android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } + +org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 49ca654..5eb588b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip diff --git a/library/build.gradle b/library/build.gradle deleted file mode 100644 index b3b065c..0000000 --- a/library/build.gradle +++ /dev/null @@ -1,83 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'org.jetbrains.dokka' - -group = rootProject.ext.group - -android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - - defaultConfig { - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -// api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.25.0' - - implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'androidx.annotation:annotation:1.3.0' - - // RxFlow - implementation "io.github.reactivecircus.flowbinding:flowbinding-android:$flowBindingVersion" - - // Lifecycle - def lifecycle_version = "2.4.1" - implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" - - // Syntax Highlighting - api("com.github.markusressel.KodeHighlighter:core:$kodeHighlighterVersion") - - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2" - - // Zoom Layout Container - api 'com.otaliastudios:zoomlayout:1.9.0' - - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} - -configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' -} - -// build a jar with source files -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -task dokkaJar(type: Jar, dependsOn: dokkaHtml) { - classifier = 'javadoc' - from dokkaHtml.outputDirectory -} - -dokkaHtml.configure { - dokkaSourceSets { - named("main") { - noAndroidSdkLink.set(false) - } - } -} - -artifacts { - archives sourcesJar - archives dokkaJar -} diff --git a/library/build.gradle.kts b/library/build.gradle.kts new file mode 100644 index 0000000..57faea3 --- /dev/null +++ b/library/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + id("kodeeditor.android.library") + id("kodeeditor.android.library.compose") + id("kodeeditor.android.library.publishing") +} + +android { + namespace = "de.markusressel.kodeeditor.library" +} + +dependencies { + implementation(libs.kotlin.stdlib.jdk8) +// api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.25.0' + + implementation(libs.androidx.appcompat) + implementation(libs.androidx.annotation) + + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.material) + + debugImplementation(libs.androidx.compose.ui.tooling) + implementation(libs.androidx.compose.ui.tooling.preview) + + // RxFlow + implementation(libs.flowbinding.android) + + // Lifecycle + implementation(libs.androidx.lifecycle.runtime.ktx) + + // Syntax Highlighting + api(libs.kodehighlighter.core) + + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.coroutines.android) + + // Zoom Layout Container + api(libs.zoomlayout) + + testImplementation(libs.junit4) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.androidx.test.espresso.core) +} diff --git a/library/proguard-rules.pro b/library/proguard-rules.pro deleted file mode 100644 index f1b4245..0000000 --- a/library/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml index 692fb76..cc947c5 100644 --- a/library/src/main/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -1 +1 @@ - + diff --git a/library/src/main/java/de/markusressel/kodeeditor/library/compose/KodeEditor.kt b/library/src/main/java/de/markusressel/kodeeditor/library/compose/KodeEditor.kt new file mode 100644 index 0000000..bfb7cf8 --- /dev/null +++ b/library/src/main/java/de/markusressel/kodeeditor/library/compose/KodeEditor.kt @@ -0,0 +1,211 @@ +package de.markusressel.kodeeditor.library.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material.LocalTextStyle +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.TransformOrigin +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import de.markusressel.kodehighlighter.core.LanguageRuleBook +import de.markusressel.kodehighlighter.core.StyleFactory +import de.markusressel.kodehighlighter.core.colorscheme.ColorScheme +import de.markusressel.kodehighlighter.core.rule.LanguageRule +import de.markusressel.kodehighlighter.core.rule.RuleHelper +import de.markusressel.kodehighlighter.core.rule.RuleMatch +import de.markusressel.kodehighlighter.core.ui.KodeTextField + +/** + * Compose version of the KodeEditorLayout + * + * @param modifier compose modifiers + * @param text the current text of the editor + * @param languageRuleBook the language rule book to use for highlighting + * @param colorScheme the color scheme to apply + * @param onValueChange callback for changes to the text and/or cursor selection + * @param colors the color scheme to use for highlighting + * @param textStyle the text style used for the editor text + * @param enabled whether the editor is enabled + * @param readOnly whether the contents of the editor can be changed by the user + */ +@Composable +fun KodeEditor( + modifier: Modifier = Modifier, + text: TextFieldValue, + languageRuleBook: LanguageRuleBook, + colorScheme: ColorScheme, + onValueChange: (TextFieldValue) -> Unit, + colors: KodeEditorColors = KodeEditorDefaults.editorColors(), + textStyle: TextStyle = LocalTextStyle.current, + enabled: Boolean = true, + readOnly: Boolean = enabled.not(), +) { + var offset by remember { mutableStateOf(Offset.Zero) } + var zoom by remember { mutableStateOf(1f) } + + Box(modifier = Modifier + .clipToBounds() + .then(modifier) + ) { + var lineNumberWidth by remember { + mutableStateOf(0) + } + LineNumbers( + modifier = Modifier + .zIndex(1f) + .wrapContentSize( + align = Alignment.TopStart, + unbounded = true + ) + .onGloballyPositioned { + lineNumberWidth = it.size.width + } + .graphicsLayer( + transformOrigin = TransformOrigin(0f, 0f), + scaleX = zoom, scaleY = zoom, + translationX = 0f, + translationY = -offset.y * zoom, + ), + text = text.text, + textStyle = textStyle, + textColor = colors.lineNumberTextColor().value, + backgroundColor = colors.lineNumberBackgroundColor().value, + ) + + val computedPadding = LocalDensity.current.run { + (lineNumberWidth).coerceAtLeast(0).toDp() + } + + // Text Editor + ZoomLayout( + modifier = Modifier + .zIndex(0f) + .padding( + start = computedPadding, + ) + .matchParentSize(), + offset = offset, + zoom = zoom, + onOffsetChanged = { +// val newOffset = Offset( +// x = (offset + it).x.coerceIn(0f, (size.width.toFloat() - (configuration.screenWidthDp.dp.toPx() / newScale)).coerceAtLeast(0f)), +// y = (offset + it).y.coerceIn(0f, (size.height.toFloat() - (configuration.screenHeightDp.dp.toPx() / newScale)).coerceAtLeast(0f)), +// ) + + val newOffset = offset + (it / zoom) + offset = newOffset.copy( + x = newOffset.x.coerceAtLeast(0f), + y = newOffset.y.coerceAtLeast(0f) + ) + }, + onZoomChanged = { + zoom *= it + }, + ) { + KodeTextField( + modifier = Modifier + .align(Alignment.TopStart) + .wrapContentSize( + align = Alignment.TopStart, + unbounded = true + ) + .matchParentSize() + .background(colors.textFieldBackgroundColor().value) + .padding( + start = 4.dp, + end = 4.dp + ), + value = text, + languageRuleBook = languageRuleBook, + colorScheme = colorScheme, + onValueChange = onValueChange, + colors = colors.textFieldColors(enabled = enabled).value, + textStyle = textStyle, + enabled = enabled, + readOnly = readOnly, + ) + } + } +} + + +private data class DummyData( + val headingRule: LanguageRule = object : LanguageRule { + override fun findMatches(text: CharSequence): List { + val PATTERN = "^\\s{0,3}#{1,6} .+".toRegex(RegexOption.MULTILINE) + return RuleHelper.findRegexMatches(text, PATTERN) + } + }, + + val dummyRuleBook: LanguageRuleBook = object : LanguageRuleBook { + override fun getRules() = listOf( + headingRule + ) + }, + + val colorScheme: ColorScheme = object : ColorScheme { + override fun getStyles(type: LanguageRule): Set> { + return setOf { SpanStyle(Color(0xFFFF6D00)) } + } + } +) + +private val dummyData = DummyData() + +@Preview +@Composable +private fun KodeEditorPreview() { + var text by remember { + val initialText = """ + # Hello World + Code: `readResourceFileAsText(R.raw.sample_text)` + + ## Secondary headline + + This is a listing: + + * 1 + * 2 + * 3 + + # Code Block + + ``` + This is a code block. + ``` + """.trimIndent() + mutableStateOf(TextFieldValue( + text = initialText + )) + } + + val languageRuleBook by remember { + mutableStateOf(dummyData.dummyRuleBook) + } + val colorScheme by remember { + mutableStateOf(dummyData.colorScheme) + } + + KodeEditor( + modifier = Modifier.fillMaxSize(), + text = text, + languageRuleBook = languageRuleBook, + colorScheme = colorScheme, + onValueChange = { text = it } + ) +} diff --git a/library/src/main/java/de/markusressel/kodeeditor/library/compose/KodeEditorColors.kt b/library/src/main/java/de/markusressel/kodeeditor/library/compose/KodeEditorColors.kt new file mode 100644 index 0000000..ea869cf --- /dev/null +++ b/library/src/main/java/de/markusressel/kodeeditor/library/compose/KodeEditorColors.kt @@ -0,0 +1,102 @@ +package de.markusressel.kodeeditor.library.compose + +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.* +import androidx.compose.ui.graphics.Color +import de.markusressel.kodehighlighter.core.ui.KodeTextField +import de.markusressel.kodehighlighter.core.ui.KodeTextFieldColors +import de.markusressel.kodehighlighter.core.ui.KodeTextFieldDefaults + + +/** + * Represents the colors of the code editor (without highlighting), line numbers, etc. used + * in a code editor in different states. + * + * See [KodeEditorDefaults.editorColors] for the default colors used in [KodeEditorDefaults]. + */ +@Stable +interface KodeEditorColors { + + /** + * [KodeTextField] specific set of colors + * + * @param enabled whether the text field is enabled + */ + @Composable + fun textFieldColors(enabled: Boolean): State + + /** + * Represents the color used for the background of the editor text. + */ + @Composable + fun textFieldBackgroundColor(): State + + /** + * Represents the color used for line numbers text. + */ + @Composable + fun lineNumberTextColor(): State + + /** + * Represents the color used for the background of the line numbers. + */ + @Composable + fun lineNumberBackgroundColor(): State + +} + + +/** + * Contains the default values used by [KodeTextField]. + */ +@Immutable +object KodeEditorDefaults { + + /** + * Creates a [KodeEditorColors] that represents the default input text, background and content + * (including label, placeholder, leading and trailing icons) colors used in a [KodeEditor]. + */ + @Composable + fun editorColors( + textFieldColors: KodeTextFieldColors = KodeTextFieldDefaults.textFieldColors(), + textFieldBackgroundColor: Color = MaterialTheme.colors.surface, + lineNumberTextColor: Color = MaterialTheme.colors.onSurface, + lineNumberBackgroundColor: Color = MaterialTheme.colors.surface, + ): KodeEditorColors = + DefaultKodeEditorColors( + textFieldColors = textFieldColors, + textFieldBackgroundColor = textFieldBackgroundColor, + lineNumberTextColor = lineNumberTextColor, + lineNumberBackgroundColor = lineNumberBackgroundColor, + ) +} + + +private data class DefaultKodeEditorColors( + private val textFieldColors: KodeTextFieldColors, + private val textFieldBackgroundColor: Color, + private val lineNumberTextColor: Color, + private val lineNumberBackgroundColor: Color, +) : KodeEditorColors { + + @Composable + override fun textFieldColors(enabled: Boolean): State { + return rememberUpdatedState(textFieldColors) + } + + @Composable + override fun textFieldBackgroundColor(): State { + return rememberUpdatedState(textFieldBackgroundColor) + } + + @Composable + override fun lineNumberTextColor(): State { + return rememberUpdatedState(lineNumberTextColor) + } + + @Composable + override fun lineNumberBackgroundColor(): State { + return rememberUpdatedState(lineNumberBackgroundColor) + } + +} diff --git a/library/src/main/java/de/markusressel/kodeeditor/library/compose/LineNumbers.kt b/library/src/main/java/de/markusressel/kodeeditor/library/compose/LineNumbers.kt new file mode 100644 index 0000000..43e1b30 --- /dev/null +++ b/library/src/main/java/de/markusressel/kodeeditor/library/compose/LineNumbers.kt @@ -0,0 +1,58 @@ +package de.markusressel.kodeeditor.library.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.material.LocalTextStyle +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +@Composable +fun LineNumbers( + modifier: Modifier = Modifier, + text: String, + textStyle: TextStyle = LocalTextStyle.current, + backgroundColor: Color, + textColor: Color = Color.Unspecified, +) { + val lineNumbers by remember(text) { + val lineCount = text.lines().size + val lineText = (1..lineCount).joinToString(separator = "\n") + mutableStateOf(lineText) + } + + Box(modifier = modifier) { + Text( + modifier = Modifier + .background(color = backgroundColor) + .padding(start = 4.dp, end = 4.dp), + text = lineNumbers, + fontSize = textStyle.fontSize, + color = textColor, + textAlign = TextAlign.End, + ) + } +} + + +@Preview(widthDp = 100, heightDp = 500, backgroundColor = 0xFF00FF) +@Composable +private fun LineNumbersPreview() { + val text = (1..10).joinToString(separator = "\n", prefix = "Line: ") + LineNumbers( + text = text, + backgroundColor = Color.White, + textColor = Color.Black, + ) +} + + diff --git a/library/src/main/java/de/markusressel/kodeeditor/library/compose/ZoomLayout.kt b/library/src/main/java/de/markusressel/kodeeditor/library/compose/ZoomLayout.kt new file mode 100644 index 0000000..77a1130 --- /dev/null +++ b/library/src/main/java/de/markusressel/kodeeditor/library/compose/ZoomLayout.kt @@ -0,0 +1,97 @@ +package de.markusressel.kodeeditor.library.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectTransformGestures +import androidx.compose.foundation.layout.* +import androidx.compose.material.Surface +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.TransformOrigin +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +@Preview +@Composable +private fun ZoomLayoutPreview() { + var offset by remember { mutableStateOf(Offset.Zero) } + var zoom by remember { mutableStateOf(2f) } + + ZoomLayout( + modifier = Modifier + .padding(16.dp) + .background(Color.White), + zoom = zoom, + offset = offset, + onOffsetChanged = { offset = it }, + onZoomChanged = { zoom = it }, + ) { + Column(modifier = Modifier) { + for (i in 1..10) { + Row(modifier = Modifier) { + for (j in 1..10) { + val k = (i + j) % 2 + Surface( + modifier = Modifier.size(20.dp), color = when (k) { + 1 -> Color.Black + else -> Color.White + }) { + } + } + } + } + } + } +} + +@Composable +fun ZoomLayout( + modifier: Modifier = Modifier, + zoom: Float = 1f, + minZoom: Float = 0.01f, + maxZoom: Float = 10f, + offset: Offset = Offset.Zero, + onOffsetChanged: (Offset) -> Unit, + onZoomChanged: (Float) -> Unit, + content: @Composable BoxScope.() -> Unit, +) { + Box( + modifier = Modifier + .pointerInput(Unit) { + detectTransformGestures { centroid, pan, gestureZoom, gestureRotate -> + + val oldScale = zoom + val newScale = zoom * gestureZoom + + // For natural zooming and rotating, the centroid of the gesture should + // be the fixed point where zooming and rotating occurs. + // We compute where the centroid was (in the pre-transformed coordinate + // space), and then compute where it will be after this delta. + // We then compute what the new offset should be to keep the centroid + // visually stationary for rotating and zooming, and also apply the pan. + val tOffset = (offset + centroid / oldScale) - (centroid / newScale + pan / oldScale) + + if (offset != tOffset) { + onOffsetChanged(tOffset) + } + + val newZoom = (newScale).coerceIn(minZoom, maxZoom) + if (zoom != newZoom) { + onZoomChanged(newZoom) + } + } + } + .graphicsLayer( + transformOrigin = TransformOrigin(0f, 0f), + scaleX = zoom, scaleY = zoom, + translationX = -offset.x * zoom, + translationY = -offset.y * zoom, + ) + .then(modifier), + ) { + content() + } +} \ No newline at end of file diff --git a/library/src/main/java/de/markusressel/kodeeditor/library/view/CodeEditorLayout.kt b/library/src/main/java/de/markusressel/kodeeditor/library/view/CodeEditorLayout.kt index e37efd4..1a02580 100644 --- a/library/src/main/java/de/markusressel/kodeeditor/library/view/CodeEditorLayout.kt +++ b/library/src/main/java/de/markusressel/kodeeditor/library/view/CodeEditorLayout.kt @@ -8,6 +8,7 @@ import android.graphics.Rect import android.graphics.drawable.GradientDrawable import android.os.Build import android.text.Layout +import android.text.style.CharacterStyle import android.util.AttributeSet import android.util.Log import android.util.TypedValue @@ -22,6 +23,7 @@ import de.markusressel.kodeeditor.library.extensions.createSnapshot import de.markusressel.kodeeditor.library.extensions.dpToPx import de.markusressel.kodeeditor.library.extensions.getColor import de.markusressel.kodehighlighter.core.LanguageRuleBook +import de.markusressel.kodehighlighter.core.colorscheme.ColorScheme import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -122,6 +124,15 @@ constructor( codeEditorView.languageRuleBook = value } + /** + * The currently active syntax highlighter (if any) + */ + var colorScheme: ColorScheme? + get() = codeEditorView.colorScheme + set(value) { + codeEditorView.colorScheme = value + } + /** * Set the text in the editor * diff --git a/library/src/main/java/de/markusressel/kodeeditor/library/view/CodeEditorView.kt b/library/src/main/java/de/markusressel/kodeeditor/library/view/CodeEditorView.kt index 98e83cd..e21ab83 100644 --- a/library/src/main/java/de/markusressel/kodeeditor/library/view/CodeEditorView.kt +++ b/library/src/main/java/de/markusressel/kodeeditor/library/view/CodeEditorView.kt @@ -2,6 +2,7 @@ package de.markusressel.kodeeditor.library.view import android.content.Context import android.graphics.Color +import android.text.style.CharacterStyle import android.util.AttributeSet import android.view.LayoutInflater import android.view.View @@ -14,6 +15,7 @@ import de.markusressel.kodeeditor.library.R import de.markusressel.kodeeditor.library.extensions.getColor import de.markusressel.kodeeditor.library.extensions.setViewBackgroundWithoutResettingPadding import de.markusressel.kodehighlighter.core.LanguageRuleBook +import de.markusressel.kodehighlighter.core.colorscheme.ColorScheme import de.markusressel.kodehighlighter.core.util.EditTextHighlighter import de.markusressel.kodehighlighter.core.util.StatefulSpannableHighlighter @@ -21,8 +23,8 @@ import de.markusressel.kodehighlighter.core.util.StatefulSpannableHighlighter * Code Editor that allows pinch-to-zoom */ open class CodeEditorView -@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) - : ZoomLayout(context, attrs, defStyleAttr), SelectionChangedListener { +@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : + ZoomLayout(context, attrs, defStyleAttr), SelectionChangedListener { /** * The actual text editor content @@ -37,18 +39,43 @@ open class CodeEditorView /** * The currently active syntax highlighter (if any) */ - var languageRuleBook: LanguageRuleBook? - get() = codeEditText.highlighter?.languageRuleBook + var languageRuleBook: LanguageRuleBook? = null + //get() = codeEditText.highlighter?.languageRuleBook set(value) { - if (value != null) { - codeEditText.highlighter = EditTextHighlighter(codeEditText, value) - codeTextView.highlighter = StatefulSpannableHighlighter(value, value.defaultColorScheme) - } else { - codeEditText.highlighter = null - codeTextView.highlighter = null - } + field = value + updateHighlighter(value, colorScheme) } + /** + * The color scheme to use for the currently active syntax highlighter (if any) + */ + var colorScheme: ColorScheme? = null + //get() = codeEditText.highlighter?.colorScheme + set(value) { + field = value + updateHighlighter(languageRuleBook, value) + } + + private fun updateHighlighter( + languageRuleBook: LanguageRuleBook?, + colorScheme: ColorScheme?, + ) { + if (languageRuleBook != null && colorScheme != null) { + codeEditText.highlighter = EditTextHighlighter( + target = codeEditText, + languageRuleBook = languageRuleBook, + colorScheme = colorScheme + ) + codeTextView.highlighter = StatefulSpannableHighlighter( + languageRuleBook = languageRuleBook, + colorScheme = colorScheme + ) + } else { + codeEditText.highlighter = null + codeTextView.highlighter = null + } + } + /** * Listener for selection changes */ @@ -124,11 +151,15 @@ open class CodeEditorView private fun readParameters(attrs: AttributeSet?, defStyleAttr: Int) { val a = context.obtainStyledAttributes(attrs, R.styleable.CodeEditorView, defStyleAttr, 0) - val editTextBackgroundColor = a.getColor(context, - defaultColor = Color.WHITE, - styleableRes = R.styleable.CodeEditorView_ke_editor_backgroundColor, - attr = intArrayOf(R.attr.ke_editor_backgroundColor, - android.R.attr.windowBackground)) + val editTextBackgroundColor = a.getColor( + context, + defaultColor = Color.WHITE, + styleableRes = R.styleable.CodeEditorView_ke_editor_backgroundColor, + attr = intArrayOf( + R.attr.ke_editor_backgroundColor, + android.R.attr.windowBackground + ) + ) codeEditText.setBackgroundColor(editTextBackgroundColor) val maxRealZoom = a.getFloat(R.styleable.CodeEditorView_ke_editor_maxZoom, DEFAULT_MAX_ZOOM) @@ -173,8 +204,10 @@ open class CodeEditorView val containerHeight = height - (paddingTop + paddingBottom) val codeEditTextLayoutParams = (codeEditText.layoutParams as MarginLayoutParams) - val minimumWidth = containerWidth + (codeEditTextLayoutParams.leftMargin + codeEditTextLayoutParams.rightMargin) - val minimumHeight = containerHeight - (codeEditTextLayoutParams.topMargin + codeEditTextLayoutParams.bottomMargin) + val minimumWidth = + containerWidth + (codeEditTextLayoutParams.leftMargin + codeEditTextLayoutParams.rightMargin) + val minimumHeight = + containerHeight - (codeEditTextLayoutParams.topMargin + codeEditTextLayoutParams.bottomMargin) codeEditText.minWidth = minimumWidth codeTextView.minWidth = minimumWidth diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 5243a55..0000000 --- a/settings.gradle +++ /dev/null @@ -1,12 +0,0 @@ -dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - google() - mavenCentral() - maven { url "https://jitpack.io" } - } -} - -rootProject.name = "KodeEditor" - -include ':app', ':library' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..9938e0a --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,23 @@ +pluginManagement { + includeBuild("build-logic") + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven(url = "https://jitpack.io") + } +} + +rootProject.name = "KodeEditor" +if (System.getenv("JITPACK").toBoolean().not()) + include(":app") +include(":library")