├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── InteractiveTextMaker ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── emirhankolver │ │ └── itm │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── emirhankolver │ │ │ └── itm │ │ │ ├── InteractiveTextMaker.kt │ │ │ └── InteractiveTextView.kt │ └── res │ │ └── values │ │ └── attrs.xml │ └── test │ └── java │ └── com │ └── emirhankolver │ └── itm │ └── ExampleUnitTest.kt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── myapplication │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── myapplication │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── font │ │ ├── bold.otf │ │ └── bold_italic.otf │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── example │ └── myapplication │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── sc1.png ├── sc2.png └── sc3.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | My Application -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /InteractiveTextMaker/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /InteractiveTextMaker/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk 33 8 | 9 | defaultConfig { 10 | minSdk 21 11 | targetSdk 33 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | consumerProguardFiles "consumer-rules.pro" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_8 24 | targetCompatibility JavaVersion.VERSION_1_8 25 | } 26 | kotlinOptions { 27 | jvmTarget = '1.8' 28 | } 29 | } 30 | 31 | dependencies { 32 | 33 | implementation 'androidx.core:core-ktx:1.9.0' 34 | implementation 'androidx.appcompat:appcompat:1.5.1' 35 | implementation 'com.google.android.material:material:1.6.1' 36 | testImplementation 'junit:junit:4.13.2' 37 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 39 | } -------------------------------------------------------------------------------- /InteractiveTextMaker/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emirhankolver/InteractiveTextMaker/96a436f26a7b31a5b813ca7b945576ff3c25e7c9/InteractiveTextMaker/consumer-rules.pro -------------------------------------------------------------------------------- /InteractiveTextMaker/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 -------------------------------------------------------------------------------- /InteractiveTextMaker/src/androidTest/java/com/emirhankolver/itm/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.interactivetextmaker 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.interactivetextmaker.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /InteractiveTextMaker/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /InteractiveTextMaker/src/main/java/com/emirhankolver/itm/InteractiveTextMaker.kt: -------------------------------------------------------------------------------- 1 | package com.emirhankolver.itm 2 | 3 | import android.content.Context 4 | import android.graphics.Typeface 5 | import android.text.SpannableString 6 | import android.text.TextPaint 7 | import android.text.method.LinkMovementMethod 8 | import android.text.style.ClickableSpan 9 | import android.util.Log 10 | import android.view.View 11 | import android.widget.TextView 12 | import androidx.annotation.ColorRes 13 | import androidx.annotation.FontRes 14 | import androidx.core.content.ContextCompat 15 | import androidx.core.content.res.ResourcesCompat 16 | import androidx.core.graphics.ColorUtils 17 | 18 | class InteractiveTextMaker private constructor( 19 | private val textView: TextView, 20 | private val context: Context, 21 | ) { 22 | 23 | private var specialWordSeparator: String = "__" 24 | private var onTextClicked: ((index: Int) -> Unit) = {} 25 | private var specialTextColor: Int = textView.currentTextColor 26 | private var specialTextHighLightColor: Int = ColorUtils.setAlphaComponent(specialTextColor, 50) 27 | private var specialTextFontFamily = textView.typeface 28 | private var specialTextSize = textView.textSize 29 | private var underlinedSpecialText: Boolean = false 30 | 31 | fun setSpecialTextSeparator(separator: String): InteractiveTextMaker { 32 | specialWordSeparator = separator 33 | return this 34 | } 35 | 36 | fun setSpecialTextSize(size: Float): InteractiveTextMaker { 37 | specialTextSize = size 38 | return this 39 | } 40 | 41 | fun setSpecialTextFontFamilyRes(@FontRes fontId: Int): InteractiveTextMaker { 42 | specialTextFontFamily = ResourcesCompat.getFont(context, fontId)!! 43 | return this 44 | } 45 | 46 | 47 | fun setSpecialTextFontFamily(font: Typeface): InteractiveTextMaker { 48 | specialTextFontFamily = font 49 | return this 50 | } 51 | 52 | fun setSpecialTextUnderLined(boolean: Boolean): InteractiveTextMaker { 53 | underlinedSpecialText = boolean 54 | return this 55 | } 56 | 57 | fun setSpecialTextColorRes(@ColorRes colorId: Int): InteractiveTextMaker { 58 | val color = ContextCompat.getColor(context, colorId) 59 | specialTextColor = color 60 | specialTextHighLightColor = ColorUtils.setAlphaComponent(color, 50) 61 | return this 62 | } 63 | 64 | fun setSpecialTextColor(colorInt: Int): InteractiveTextMaker { 65 | specialTextColor = colorInt 66 | specialTextHighLightColor = ColorUtils.setAlphaComponent(colorInt, 50) 67 | return this 68 | } 69 | 70 | fun setSpecialTextHighlightColor(color: Int): InteractiveTextMaker { 71 | specialTextHighLightColor = color 72 | return this 73 | } 74 | 75 | fun setSpecialTextHighlightColorRes(@ColorRes color: Int): InteractiveTextMaker { 76 | specialTextHighLightColor = ContextCompat.getColor(context, color) 77 | return this 78 | } 79 | 80 | fun setOnTextClickListener(func: (index: Int) -> Unit): InteractiveTextMaker { 81 | onTextClicked = func 82 | return this 83 | } 84 | 85 | fun initialize() { 86 | val regex = Regex("$specialWordSeparator(.*?)$specialWordSeparator") 87 | val words = regex.findAll(textView.text) 88 | val span = SpannableString(textView.text.replace(Regex(specialWordSeparator), "")) 89 | val actionTextLength = specialWordSeparator.length * 2// A word result has twice of it 90 | if (words.toList().isEmpty()) { 91 | Log.w( 92 | TAG, 93 | "initialize: WARNING No results has found with separator:'$specialWordSeparator'" 94 | ) 95 | return 96 | } 97 | words.forEachIndexed { index: Int, wordResult: MatchResult -> 98 | val startIndex = wordResult.range.first - (actionTextLength * index) 99 | val endIndex = wordResult.range.last - (actionTextLength * (index + 1)) + 1 100 | Log.i( 101 | TAG, 102 | "initialize: text:'${textView.text}' startIndex:$startIndex endIndex:$endIndex" 103 | ) 104 | span.setSpan( 105 | object : ClickableSpan() { 106 | override fun onClick(p0: View) { 107 | onTextClicked(index) 108 | } 109 | 110 | override fun updateDrawState(ds: TextPaint) { 111 | super.updateDrawState(ds) 112 | ds.isUnderlineText = underlinedSpecialText 113 | ds.textSize = specialTextSize 114 | ds.typeface = specialTextFontFamily 115 | ds.color = specialTextColor 116 | } 117 | }, 118 | startIndex, 119 | endIndex, 120 | 0 121 | ) 122 | textView.linksClickable = true 123 | textView.isClickable = true 124 | textView.movementMethod = LinkMovementMethod.getInstance() 125 | textView.highlightColor = specialTextHighLightColor 126 | textView.text = span 127 | } 128 | } 129 | 130 | 131 | companion object { 132 | fun of(textView: TextView): InteractiveTextMaker = 133 | InteractiveTextMaker(textView, textView.context) 134 | 135 | // Will be used at debugging. 136 | private const val TAG = "InteractiveTextMaker" 137 | } 138 | } -------------------------------------------------------------------------------- /InteractiveTextMaker/src/main/java/com/emirhankolver/itm/InteractiveTextView.kt: -------------------------------------------------------------------------------- 1 | package com.emirhankolver.itm 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.graphics.Typeface 6 | import android.util.AttributeSet 7 | import android.util.Log 8 | import android.util.TypedValue 9 | import androidx.appcompat.widget.AppCompatTextView 10 | import androidx.core.content.res.ResourcesCompat 11 | import androidx.core.content.withStyledAttributes 12 | 13 | class InteractiveTextView( 14 | context: Context, 15 | attrs: AttributeSet? 16 | ) : AppCompatTextView(context, attrs) { 17 | 18 | private val listeners = mutableListOf<(index: Int) -> Unit>() 19 | 20 | // Attributes. 21 | private var colorPrimary: Int? = 0 22 | private var specialTextColor: Int? = 0 23 | private var specialTextHighlightColor: Int? = 0 24 | private var specialTextUnderlined: Boolean? = false 25 | private var allTextUnderlined: Boolean? = false 26 | private var specialTextSize: Float? = 0f 27 | private var specialTextFontFamily: Typeface? = null 28 | private var specialTextSeparator: String? = null 29 | private lateinit var previousString: String 30 | 31 | 32 | init { 33 | Log.d(TAG, "null() called") 34 | context.withStyledAttributes(attrs, R.styleable.InteractiveTextView) { 35 | colorPrimary = TypedValue().let { 36 | getContext().theme.resolveAttribute(android.R.attr.colorPrimary, it, true) 37 | it.data 38 | } 39 | specialTextSeparator = 40 | getString(R.styleable.InteractiveTextView_specialTextSeparator) ?: "__" 41 | specialTextColor = 42 | getColor(R.styleable.InteractiveTextView_specialTextColor, colorPrimary!!) 43 | specialTextHighlightColor = 44 | getColor(R.styleable.InteractiveTextView_specialTextHighlightColor, -1) 45 | specialTextUnderlined = 46 | getBoolean(R.styleable.InteractiveTextView_specialTextUnderlined, false) 47 | allTextUnderlined = 48 | getBoolean(R.styleable.InteractiveTextView_allTextUnderlined, false) 49 | specialTextSize = getDimension( 50 | R.styleable.InteractiveTextView_specialTextSize, 51 | this@InteractiveTextView.textSize 52 | ) 53 | specialTextFontFamily = try { 54 | val fontId = 55 | getResourceId(R.styleable.InteractiveTextView_specialTextFontFamily, -1) 56 | ResourcesCompat.getFont(context, fontId)!! 57 | } catch (e: Exception) { 58 | this@InteractiveTextView.typeface!! 59 | } 60 | Log.d(TAG, "specialTextSeparator: $specialTextSeparator") 61 | } 62 | initialize() 63 | } 64 | 65 | 66 | /* 67 | * I don't how that's happening but I managed to fix the 68 | * ``` 69 | * Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter separator 70 | * ``` 71 | * crash with null controls at initialize function. In somehow the variables like "specialTextSeparator" become null after 72 | * the setText() method runs. 73 | * 74 | */ 75 | @SuppressLint("SetTextI18n") 76 | fun initialize() { 77 | Log.d(TAG, "initialize() called with: $specialTextSeparator") 78 | if (allTextUnderlined == null) return 79 | if (specialTextColor == null) return 80 | if (specialTextSeparator == null) return 81 | if (specialTextUnderlined == null) return 82 | if (specialTextSize == null) return 83 | if (specialTextFontFamily == null) return 84 | if (specialTextHighlightColor == null) return 85 | if (allTextUnderlined!!) text = "$specialTextSeparator${text}$specialTextSeparator" 86 | InteractiveTextMaker.of(this@InteractiveTextView) 87 | .also { 88 | it.setSpecialTextColor(specialTextColor!!) 89 | it.setSpecialTextSeparator(specialTextSeparator!!) 90 | it.setSpecialTextUnderLined(specialTextUnderlined!!) 91 | it.setSpecialTextSize(specialTextSize!!) 92 | it.setSpecialTextFontFamily(specialTextFontFamily!!) 93 | if (specialTextHighlightColor != -1) 94 | it.setSpecialTextHighlightColor(specialTextHighlightColor!!) 95 | } 96 | .setOnTextClickListener { index -> listeners.forEach { it(index) } } 97 | .initialize() 98 | } 99 | 100 | /* 101 | * InteractiveTextMaker uses .setText method to work. It'll gonna create a [setText] loop. 102 | * to block that I'm gonna use previousString variable to capture every string. and compare with 103 | * previous one. 104 | * 105 | * If anyone knows how to make this in right way please contact me :) 106 | */ 107 | override fun setText(text: CharSequence?, type: BufferType?) { 108 | if (this::previousString.isInitialized && previousString == text) return 109 | previousString = text.toString() 110 | super.setText(text, type) 111 | initialize() 112 | } 113 | 114 | 115 | /** 116 | * Whenever a special word has been clicked this [function] will be run with index 117 | * of the word. 118 | */ 119 | fun addOnSpecialWordClickListener(function: (index: Int) -> Unit) { 120 | listeners.add(function) 121 | } 122 | 123 | companion object { 124 | private const val TAG = "InteractiveTextView" 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /InteractiveTextMaker/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /InteractiveTextMaker/src/test/java/com/emirhankolver/itm/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.interactivetextmaker 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interactive Text Maker 2 | 3 | ![Header Image](https://github.com/emirhankolver/InteractiveTextMaker/blob/master/screenshots/sc2.png?raw=true) 4 | 5 | ## Overview 6 | Interactive Text Maker allows you to create clickable and translatable texts in a `TextView` with ease. By marking special words with separator characters like `"__"`, users can tap on them, triggering a callback function that returns the index of the clicked word. This ensures: 7 | - Seamless translation while maintaining the structure of tactile words across languages. 8 | - Easy implementation of interactive text features without complex span management. 9 | 10 | [Medium Article](https://medium.com/p/2b70e2072453) 11 | 12 | --- 13 | 14 | ## 🔄 Update Notice 15 | If you previously used my dependency under the username `alonew0lfxx`, please update your `build.gradle` file to reflect my new username `emirhankolver`: 16 | 17 | ```gradle 18 | dependencies { 19 | // OLD: implementation 'com.github.alonew0lfxx:InteractiveTextMaker:1.0.8' 20 | implementation 'com.github.emirhankolver:InteractiveTextMaker:1.0.8' 21 | } 22 | ``` 23 | 24 | --- 25 | 26 | ## 🚀 Installation 27 | ### Step 1: Add JitPack Repository 28 | Ensure you have JitPack in your `build.gradle` (Project-level): 29 | 30 | ```gradle 31 | allprojects { 32 | repositories { 33 | ... 34 | maven { url 'https://jitpack.io' } 35 | } 36 | } 37 | ``` 38 | 39 | ### Step 2: Add the Dependency 40 | Add the InteractiveTextMaker dependency in your `build.gradle` (App-level): 41 | 42 | ```gradle 43 | dependencies { 44 | implementation 'com.github.emirhankolver:InteractiveTextMaker:1.0.8' 45 | } 46 | ``` 47 | 48 | --- 49 | 50 | ## ✨ New Feature: InteractiveTextView 51 | ### 🖥️ Real-Time Preview of Your Special Words 52 | You can now use `InteractiveTextView` to directly display and preview clickable words in XML without additional setup. 53 | 54 | #### XML Usage: 55 | ```xml 56 | 63 | ``` 64 | 65 | ### InteractiveTextView Output: 66 | ![App Screenshot](https://github.com/emirhankolver/InteractiveTextMaker/blob/master/screenshots/sc3.png?raw=true) 67 | 68 | --- 69 | 70 | ## 🛠 Example Usage of InteractiveTextMaker 71 | You can implement InteractiveTextMaker in Kotlin to add interactivity to `TextView` dynamically. 72 | 73 | ```kotlin 74 | class MainActivity : AppCompatActivity() { 75 | private lateinit var binding: ActivityMainBinding 76 | 77 | override fun onCreate(savedInstanceState: Bundle?) { 78 | super.onCreate(savedInstanceState) 79 | binding = ActivityMainBinding.inflate(layoutInflater) 80 | 81 | binding.textView.text = "How To __Make__ Clickable __Text__ In A __TextView__?" 82 | 83 | InteractiveTextMaker.of(binding.textView) 84 | .setSpecialTextColor(R.color.purple_500) 85 | .setSpecialTextFontFamily(R.font.bold_italic) 86 | .setSpecialTextUnderLined(true) 87 | .setOnTextClickListener { 88 | when (it) { 89 | 0 -> Toast.makeText(this, "'Make' has been clicked", Toast.LENGTH_SHORT).show() 90 | 1 -> Toast.makeText(this, "'Text' has been clicked", Toast.LENGTH_SHORT).show() 91 | 2 -> Toast.makeText(this, "'TextView' has been clicked", Toast.LENGTH_SHORT).show() 92 | } 93 | } 94 | .initialize() 95 | 96 | setContentView(binding.root) 97 | } 98 | } 99 | ``` 100 | 101 | ### InteractiveTextMaker Output: 102 | ![App Screenshot](https://github.com/emirhankolver/InteractiveTextMaker/blob/master/screenshots/sc1.png?raw=true) 103 | 104 | --- 105 | 106 | ## 📜 License 107 | This project is licensed under the [MIT License](https://choosealicense.com/licenses/mit/). 108 | 109 | --- 110 | 111 | ### 💡 Contributions & Feedback 112 | Feel free to open an issue or contribute via pull requests! 113 | Happy coding! 🚀 114 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk 33 8 | 9 | defaultConfig { 10 | applicationId "com.example.myapplication" 11 | minSdk 23 12 | targetSdk 33 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | buildFeatures { 19 | viewBinding true 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | kotlinOptions { 33 | jvmTarget = '1.8' 34 | } 35 | } 36 | 37 | dependencies { 38 | 39 | implementation 'androidx.core:core-ktx:1.9.0' 40 | implementation 'androidx.appcompat:appcompat:1.5.1' 41 | implementation 'com.google.android.material:material:1.6.1' 42 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 43 | implementation project(path: ':InteractiveTextMaker') 44 | testImplementation 'junit:junit:4.13.2' 45 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 47 | } -------------------------------------------------------------------------------- /app/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 -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.myapplication 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.myapplication", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/myapplication/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.myapplication 2 | 3 | import android.os.Bundle 4 | import android.widget.Toast 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.emirhankolver.itm.InteractiveTextMaker 7 | import com.example.myapplication.databinding.ActivityMainBinding 8 | 9 | class MainActivity : AppCompatActivity() { 10 | 11 | private lateinit var binding: ActivityMainBinding 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | binding = ActivityMainBinding.inflate(layoutInflater) 16 | binding.textView.text = "How To __Make__ Clickable __Text__ In A __TextView__?" 17 | InteractiveTextMaker.of(binding.textView) 18 | .setSpecialTextColor(R.color.purple_500) 19 | .setSpecialTextFontFamilyRes(R.font.bold_italic) 20 | .setSpecialTextUnderLined(true) 21 | .setOnTextClickListener { 22 | when (it) { 23 | 0 -> Toast.makeText(this, "'Make' has been clicked", Toast.LENGTH_SHORT).show() 24 | 1 -> Toast.makeText(this, "'Text' has been clicked", Toast.LENGTH_SHORT).show() 25 | 2 -> Toast.makeText(this, "'TextView' has been clicked", Toast.LENGTH_SHORT) 26 | .show() 27 | } 28 | } 29 | .initialize() 30 | binding.bUpdate.setOnClickListener { 31 | binding.textView.text = ("Hello this is __updated__ text!") 32 | } 33 | setContentView(binding.root) 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/font/bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emirhankolver/InteractiveTextMaker/96a436f26a7b31a5b813ca7b945576ff3c25e7c9/app/src/main/res/font/bold.otf -------------------------------------------------------------------------------- /app/src/main/res/font/bold_italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emirhankolver/InteractiveTextMaker/96a436f26a7b31a5b813ca7b945576ff3c25e7c9/app/src/main/res/font/bold_italic.otf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | 23 |