diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b75303 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..ce889bd --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,119 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+ + +
+
\ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..ada92a5 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..b87ce18 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..af0bbdd --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..d751d52 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'com.android.application' + +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.dev452.ultimatetextviewsample" + minSdkVersion 19 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.core:core-ktx:1.0.2' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + implementation project(path: ':ultimatetextview') +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# 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/androidTest/java/com/dev452/ultimatetextviewsample/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/dev452/ultimatetextviewsample/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..2c6105c --- /dev/null +++ b/app/src/androidTest/java/com/dev452/ultimatetextviewsample/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.dev452.ultimatetextviewsample + +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getTargetContext() + assertEquals("com.dev452.ultimatetextviewsample", appContext.packageName) + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d1ae2f5 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/app/src/main/assets/fonts/abril_fatface_regular.ttf b/app/src/main/assets/fonts/abril_fatface_regular.ttf new file mode 100644 index 0000000..e761f7b Binary files /dev/null and b/app/src/main/assets/fonts/abril_fatface_regular.ttf differ diff --git a/app/src/main/assets/fonts/alexbrush_italic.ttf b/app/src/main/assets/fonts/alexbrush_italic.ttf new file mode 100644 index 0000000..204d71b Binary files /dev/null and b/app/src/main/assets/fonts/alexbrush_italic.ttf differ diff --git a/app/src/main/assets/fonts/exo_black.ttf b/app/src/main/assets/fonts/exo_black.ttf new file mode 100644 index 0000000..6d271ad Binary files /dev/null and b/app/src/main/assets/fonts/exo_black.ttf differ diff --git a/app/src/main/assets/fonts/exo_bold.ttf b/app/src/main/assets/fonts/exo_bold.ttf new file mode 100644 index 0000000..c31be94 Binary files /dev/null and b/app/src/main/assets/fonts/exo_bold.ttf differ diff --git a/app/src/main/assets/fonts/exo_light.ttf b/app/src/main/assets/fonts/exo_light.ttf new file mode 100644 index 0000000..3353c1e Binary files /dev/null and b/app/src/main/assets/fonts/exo_light.ttf differ diff --git a/app/src/main/assets/fonts/exo_medium.ttf b/app/src/main/assets/fonts/exo_medium.ttf new file mode 100644 index 0000000..1546447 Binary files /dev/null and b/app/src/main/assets/fonts/exo_medium.ttf differ diff --git a/app/src/main/assets/fonts/exo_regular.ttf b/app/src/main/assets/fonts/exo_regular.ttf new file mode 100644 index 0000000..7174f83 Binary files /dev/null and b/app/src/main/assets/fonts/exo_regular.ttf differ diff --git a/app/src/main/assets/fonts/exo_semibold.ttf b/app/src/main/assets/fonts/exo_semibold.ttf new file mode 100644 index 0000000..35f0300 Binary files /dev/null and b/app/src/main/assets/fonts/exo_semibold.ttf differ diff --git a/app/src/main/assets/fonts/exo_thin.ttf b/app/src/main/assets/fonts/exo_thin.ttf new file mode 100644 index 0000000..f2c2a95 Binary files /dev/null and b/app/src/main/assets/fonts/exo_thin.ttf differ diff --git a/app/src/main/assets/fonts/krub_bold.ttf b/app/src/main/assets/fonts/krub_bold.ttf new file mode 100644 index 0000000..8dc08f6 Binary files /dev/null and b/app/src/main/assets/fonts/krub_bold.ttf differ diff --git a/app/src/main/assets/fonts/krub_extralight.ttf b/app/src/main/assets/fonts/krub_extralight.ttf new file mode 100644 index 0000000..8582cd0 Binary files /dev/null and b/app/src/main/assets/fonts/krub_extralight.ttf differ diff --git a/app/src/main/assets/fonts/krub_light.ttf b/app/src/main/assets/fonts/krub_light.ttf new file mode 100644 index 0000000..3fb8d65 Binary files /dev/null and b/app/src/main/assets/fonts/krub_light.ttf differ diff --git a/app/src/main/assets/fonts/krub_medium.ttf b/app/src/main/assets/fonts/krub_medium.ttf new file mode 100644 index 0000000..17281fb Binary files /dev/null and b/app/src/main/assets/fonts/krub_medium.ttf differ diff --git a/app/src/main/assets/fonts/krub_regular.ttf b/app/src/main/assets/fonts/krub_regular.ttf new file mode 100644 index 0000000..9d05cb6 Binary files /dev/null and b/app/src/main/assets/fonts/krub_regular.ttf differ diff --git a/app/src/main/assets/fonts/krub_semibold.ttf b/app/src/main/assets/fonts/krub_semibold.ttf new file mode 100644 index 0000000..2e7715a Binary files /dev/null and b/app/src/main/assets/fonts/krub_semibold.ttf differ diff --git a/app/src/main/java/com/dev452/ultimatetextviewsample/MainActivity.kt b/app/src/main/java/com/dev452/ultimatetextviewsample/MainActivity.kt new file mode 100644 index 0000000..d6846ba --- /dev/null +++ b/app/src/main/java/com/dev452/ultimatetextviewsample/MainActivity.kt @@ -0,0 +1,44 @@ +package com.dev452.ultimatetextviewsample + +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.widget.EditText +import androidx.appcompat.app.AppCompatActivity +import com.dev452.ultimatetextview.UltimateTextView + +/** + * Created by Debojyoti Singha on 06,July,2019. + */ +class MainActivity : AppCompatActivity() { + + private var editextDemo: EditText? = null + private var textviewDemo: UltimateTextView? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + initViews() + + editextDemo!!.addTextChangedListener(object : TextWatcher { + override fun afterTextChanged(p0: Editable?) { + + } + + override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { + } + + override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { + if (p0!!.isNotEmpty()) { + textviewDemo!!.text = p0.toString() + } + } + + }) + } + + private fun initViews() { + editextDemo = findViewById(R.id.editTextDemo) + textviewDemo = findViewById(R.id.textViewDemo) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..6348baa --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..a0ad202 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..a9c3c52 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,30 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..bbd3e02 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..bbd3e02 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..898f3ed Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..dffca36 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..64ba76f Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..dae5e08 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..e5ed465 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..14ed0af Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b0907ca Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..d8ae031 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..2c18de9 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..beed3cd Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..69b2233 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #008577 + #00574B + #D81B60 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..44d7f80 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + UltimateTextViewSample + + //fonts + fonts/exo_regular.ttf + fonts/exo_semibold.ttf + fonts/alexbrush_italic.ttf + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..5885930 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/test/java/com/dev452/ultimatetextviewsample/ExampleUnitTest.kt b/app/src/test/java/com/dev452/ultimatetextviewsample/ExampleUnitTest.kt new file mode 100644 index 0000000..abc9735 --- /dev/null +++ b/app/src/test/java/com/dev452/ultimatetextviewsample/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.dev452.ultimatetextviewsample + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..9a055e8 --- /dev/null +++ b/build.gradle @@ -0,0 +1,29 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.3.41' + repositories { + google() + jcenter() + + } + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..23339e0 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..20ece8a --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Sep 07 22:32:52 IST 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..63c47fc --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app', ':ultimatetextview' diff --git a/ultimatetextview/.gitignore b/ultimatetextview/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/ultimatetextview/.gitignore @@ -0,0 +1 @@ +/build diff --git a/ultimatetextview/build.gradle b/ultimatetextview/build.gradle new file mode 100644 index 0000000..cded237 --- /dev/null +++ b/ultimatetextview/build.gradle @@ -0,0 +1,40 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 29 + + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'androidx.appcompat:appcompat:1.1.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + implementation "androidx.core:core-ktx:1.1.0" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} +repositories { + mavenCentral() +} diff --git a/ultimatetextview/proguard-rules.pro b/ultimatetextview/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/ultimatetextview/proguard-rules.pro @@ -0,0 +1,21 @@ +# 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/ultimatetextview/src/androidTest/java/com/dev452/ultimatetextview/ExampleInstrumentedTest.java b/ultimatetextview/src/androidTest/java/com/dev452/ultimatetextview/ExampleInstrumentedTest.java new file mode 100644 index 0000000..ec53f1a --- /dev/null +++ b/ultimatetextview/src/androidTest/java/com/dev452/ultimatetextview/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.dev452.ultimatetextview; + +import android.content.Context; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.dev452.ultimatetextview.test", appContext.getPackageName()); + } +} diff --git a/ultimatetextview/src/main/AndroidManifest.xml b/ultimatetextview/src/main/AndroidManifest.xml new file mode 100644 index 0000000..81c5efb --- /dev/null +++ b/ultimatetextview/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/ultimatetextview/src/main/java/com/dev452/ultimatetextview/UltimateTextView.kt b/ultimatetextview/src/main/java/com/dev452/ultimatetextview/UltimateTextView.kt new file mode 100644 index 0000000..41ed0d8 --- /dev/null +++ b/ultimatetextview/src/main/java/com/dev452/ultimatetextview/UltimateTextView.kt @@ -0,0 +1,108 @@ +package com.dev452.ultimatetextview + +import android.content.Context +import android.util.AttributeSet +import android.util.TypedValue + +import com.dev452.ultimatetextview.helper_class.HelperForAutoFit +import com.dev452.ultimatetextview.helper_class.BackBoneTextView + +/** + * Created by Debojyoti Singha on 06,July,2019. + */ + +class UltimateTextView : BackBoneTextView, HelperForAutoFit.OnTextSizeChangeListener { + var autofitHelper: HelperForAutoFit? = null + private set + + private var mainContext: Context? = null + + var isSizeToFit: Boolean + get() = autofitHelper!!.isEnabled() + set(sizeToFit) { + autofitHelper!!.setEnabled(sizeToFit) + } + + var maxTextSize: Float + get() = autofitHelper!!.getMaxTextSize() + set(size) { + autofitHelper!!.setMaxTextSize(size) + } + + val minTextSize: Float + get() = autofitHelper!!.getMinTextSize() + + var precision: Float + get() = autofitHelper!!.getPrecision() + set(precision) { + autofitHelper!!.setPrecision(precision) + } + + constructor(context: Context) : super(context) { + init(context, null, 0) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs, 0) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) { + init(context, attrs, defStyleAttr) + } + + private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int) { + mainContext = context + autofitHelper = HelperForAutoFit.cookStuffs(this, attrs, defStyleAttr) + .addOnTextSizeChangeListener(this) + } + + override fun setTextSize(unit: Int, size: Float) { + super.setTextSize(unit, size) + if (autofitHelper != null) { + autofitHelper!!.setTextSize(unit, size) + } + } + + override fun setLines(lines: Int) { + super.setLines(lines) + if (autofitHelper != null) { + autofitHelper!!.setMaxLines(lines) + } + } + + override fun setMaxLines(maxLines: Int) { + super.setMaxLines(maxLines) + if (autofitHelper != null) { + autofitHelper!!.setMaxLines(maxLines) + } + } + + fun setSizeToFit() { + isSizeToFit = true + } + + fun setMaxTextSize(unit: Int, size: Float) { + autofitHelper!!.setMaxTextSize(unit, size) + } + + fun setMinTextSize(minSize: Int) { + autofitHelper!!.setMinTextSize(TypedValue.COMPLEX_UNIT_SP, minSize.toFloat()) + } + + fun setMinTextSize(unit: Int, minSize: Float) { + autofitHelper!!.setMinTextSize(unit, minSize) + } + + override fun onTextSizeChange(textSize: Float, oldTextSize: Float) { + // do nothing + } + + companion object { + + private val TAG = UltimateTextView::class.java.simpleName + } +} diff --git a/ultimatetextview/src/main/java/com/dev452/ultimatetextview/helper_class/BackBoneTextView.kt b/ultimatetextview/src/main/java/com/dev452/ultimatetextview/helper_class/BackBoneTextView.kt new file mode 100644 index 0000000..451f768 --- /dev/null +++ b/ultimatetextview/src/main/java/com/dev452/ultimatetextview/helper_class/BackBoneTextView.kt @@ -0,0 +1,49 @@ +package com.dev452.ultimatetextview.helper_class + +import android.content.Context +import android.content.res.TypedArray +import android.graphics.Typeface +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatTextView + +import com.dev452.ultimatetextview.R + +/** + * Created by Debojyoti Singha on 06,July,2019. + */ +open class BackBoneTextView : AppCompatTextView { + + private var fontPath: String? = null + + constructor(context: Context) : super(context) { + init(context, null, 0) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs, 0) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) { + init(context, attrs, defStyleAttr) + } + + private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int) { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.BackBoneTextView) + if (typedArray != null) { + fontPath = typedArray.getString(R.styleable.BackBoneTextView_UTVFonts) + typedArray.recycle() + setTypeFace(fontPath) + } + } + + fun setTypeFace(fontPath: String?) { + if (fontPath != null) { + val myTypeFace = Typeface.createFromAsset(this.context.assets, fontPath) + this.typeface = myTypeFace + } + } +} diff --git a/ultimatetextview/src/main/java/com/dev452/ultimatetextview/helper_class/HelperForAutoFit.kt b/ultimatetextview/src/main/java/com/dev452/ultimatetextview/helper_class/HelperForAutoFit.kt new file mode 100644 index 0000000..c489d57 --- /dev/null +++ b/ultimatetextview/src/main/java/com/dev452/ultimatetextview/helper_class/HelperForAutoFit.kt @@ -0,0 +1,464 @@ +package com.dev452.ultimatetextview.helper_class + + +import android.content.Context +import android.content.res.Resources +import android.content.res.TypedArray +import android.os.Build +import android.text.Editable +import android.text.Layout +import android.text.StaticLayout +import android.text.TextPaint +import android.text.TextWatcher +import android.text.method.SingleLineTransformationMethod +import android.text.method.TransformationMethod +import android.util.AttributeSet +import android.util.DisplayMetrics +import android.util.Log +import android.util.TypedValue +import android.view.View + +import com.dev452.ultimatetextview.R + +import java.util.ArrayList + +/** + * Created by Debojyoti Singha on 30,May,2019. + */ +class HelperForAutoFit private constructor(private val mBackBoneTextView: BackBoneTextView) { + private val mPaint: TextPaint + + private var mTextSize: Float = 0.toFloat() + private var mMaxLines: Int = 0 + private var mMinTextSize: Float = 0.toFloat() + private var mMaxTextSize: Float = 0.toFloat() + private var mPrecision = DEFAULT_PRECISION + private var mEnabled: Boolean = false + private var mIsAutofitting: Boolean = false + private var mListeners: ArrayList? = null + private val mTextWatcher = AutofitTextWatcher() + private val mOnLayoutChangeListener = AutofitOnLayoutChangeListener() + + var textSize: Float + get() = mTextSize + set(size) = setTextSize(TypedValue.COMPLEX_UNIT_SP, size) + + init { + val context = mBackBoneTextView.context + val scaledDensity = context.resources.displayMetrics.scaledDensity + mPaint = TextPaint() + setRawTextSize(mBackBoneTextView.textSize) + + mMaxLines = getMaxLines(mBackBoneTextView) + mMinTextSize = scaledDensity * DEFAULT_MIN_TEXT_SIZE + mMaxTextSize = mTextSize + mPrecision = DEFAULT_PRECISION + } + + fun addOnTextSizeChangeListener(listener: OnTextSizeChangeListener): HelperForAutoFit { + if (mListeners == null) { + mListeners = ArrayList() + } + mListeners!!.add(listener) + return this + } + + fun removeOnTextSizeChangeListener(listener: OnTextSizeChangeListener): HelperForAutoFit { + if (mListeners != null) { + mListeners!!.remove(listener) + } + return this + } + + fun getPrecision(): Float { + return mPrecision + } + + fun setPrecision(precision: Float): HelperForAutoFit { + if (mPrecision != precision) { + mPrecision = precision + + AutoFitFunc() + } + return this + } + + fun getMinTextSize(): Float { + return mMinTextSize + } + + fun setMinTextSize(size: Float): HelperForAutoFit { + return setMinTextSize(TypedValue.COMPLEX_UNIT_SP, size) + } + + fun setMinTextSize(unit: Int, size: Float): HelperForAutoFit { + val context = mBackBoneTextView.context + var r = Resources.getSystem() + + if (context != null) { + r = context.resources + } + + setRawMinTextSize(TypedValue.applyDimension(unit, size, r.displayMetrics)) + return this + } + + private fun setRawMinTextSize(size: Float) { + if (size != mMinTextSize) { + mMinTextSize = size + + AutoFitFunc() + } + } + + fun getMaxTextSize(): Float { + return mMaxTextSize + } + + fun setMaxTextSize(size: Float): HelperForAutoFit { + return setMaxTextSize(TypedValue.COMPLEX_UNIT_SP, size) + } + + fun setMaxTextSize(unit: Int, size: Float): HelperForAutoFit { + val context = mBackBoneTextView.context + var r = Resources.getSystem() + + if (context != null) { + r = context.resources + } + + setRawMaxTextSize(TypedValue.applyDimension(unit, size, r.displayMetrics)) + return this + } + + private fun setRawMaxTextSize(size: Float) { + if (size != mMaxTextSize) { + mMaxTextSize = size + + AutoFitFunc() + } + } + + fun getMaxLines(): Int { + return mMaxLines + } + + fun setMaxLines(lines: Int): HelperForAutoFit { + if (mMaxLines != lines) { + mMaxLines = lines + + AutoFitFunc() + } + return this + } + + fun isEnabled(): Boolean { + return mEnabled + } + + fun setEnabled(enabled: Boolean): HelperForAutoFit { + if (mEnabled != enabled) { + mEnabled = enabled + + if (enabled) { + mBackBoneTextView.addTextChangedListener(mTextWatcher) + mBackBoneTextView.addOnLayoutChangeListener(mOnLayoutChangeListener) + + AutoFitFunc() + } else { + mBackBoneTextView.removeTextChangedListener(mTextWatcher) + mBackBoneTextView.removeOnLayoutChangeListener(mOnLayoutChangeListener) + + mBackBoneTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize) + } + } + return this + } + + fun setTextSize(unit: Int, size: Float) { + if (mIsAutofitting) { + // We don't want to update the BackBoneTextView's actual textSize while we're autofitting + // since it'd get set to the autofitTextSize + return + } + val context = mBackBoneTextView.context + var r = Resources.getSystem() + + if (context != null) { + r = context.resources + } + + setRawTextSize(TypedValue.applyDimension(unit, size, r.displayMetrics)) + } + + private fun setRawTextSize(size: Float) { + if (mTextSize != size) { + mTextSize = size + } + } + + private fun AutoFitFunc() { + val oldTextSize = mBackBoneTextView.textSize + val textSize: Float + + mIsAutofitting = true + AutoFitFunc(mBackBoneTextView, mPaint, mMinTextSize, mMaxTextSize, mMaxLines, mPrecision) + mIsAutofitting = false + + textSize = mBackBoneTextView.textSize + if (textSize != oldTextSize) { + sendTextSizeChange(textSize, oldTextSize) + } + } + + private fun sendTextSizeChange(textSize: Float, oldTextSize: Float) { + if (mListeners == null) { + return + } + + for (listener in mListeners!!) { + listener.onTextSizeChange(textSize, oldTextSize) + } + } + + interface OnTextSizeChangeListener { + fun onTextSizeChange(textSize: Float, oldTextSize: Float) + } + + private inner class AutofitTextWatcher : TextWatcher { + override fun beforeTextChanged( + charSequence: CharSequence, + start: Int, + count: Int, + after: Int + ) { + // do nothing + } + + override fun onTextChanged( + charSequence: CharSequence, + start: Int, + before: Int, + count: Int + ) { + AutoFitFunc() + } + + override fun afterTextChanged(editable: Editable) { + // do nothing + } + } + + private inner class AutofitOnLayoutChangeListener : View.OnLayoutChangeListener { + override fun onLayoutChange( + view: View, left: Int, top: Int, right: Int, bottom: Int, + oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int + ) { + AutoFitFunc() + } + } + + companion object { + + private val TAG = "AutoFitTextHelper" + private val SPEW = false + + private val DEFAULT_MIN_TEXT_SIZE = 8 //sp + private val DEFAULT_PRECISION = 0.5f + + @JvmOverloads + fun cookStuffs( + view: BackBoneTextView, + attrs: AttributeSet? = null, + defStyle: Int = 0 + ): HelperForAutoFit { + val helper = HelperForAutoFit(view) + var sizeToFit = true + if (attrs != null) { + val context = view.context + var minTextSize = helper.getMinTextSize().toInt() + val precision = helper.getPrecision() + + val ta = context.obtainStyledAttributes( + attrs, + R.styleable.UltimateTextView, + defStyle, + 0 + ) + sizeToFit = ta.getBoolean(R.styleable.UltimateTextView_UTVAutoFitEnabled, sizeToFit) + minTextSize = ta.getDimensionPixelSize( + R.styleable.UltimateTextView_UTVMinTextSize, + minTextSize + ) + ta.recycle() + + helper.setMinTextSize(TypedValue.COMPLEX_UNIT_PX, minTextSize.toFloat()) + .setPrecision(precision) + } + helper.setEnabled(sizeToFit) + + return helper + } + + private fun AutoFitFunc( + view: BackBoneTextView, paint: TextPaint, minTextSize: Float, maxTextSize: Float, + maxLines: Int, precision: Float + ) { + if (maxLines <= 0 || maxLines == Integer.MAX_VALUE) { + // Don't auto-size since there's no limit on lines. + return + } + + val targetWidth = view.width - view.paddingLeft - view.paddingRight + if (targetWidth <= 0) { + return + } + + var text = view.text + val method = view.transformationMethod + if (method != null) { + text = method.getTransformation(text, view) + } + + val context = view.context + var r = Resources.getSystem() + val displayMetrics: DisplayMetrics + + var size = maxTextSize + val high = size + val low = 0f + + if (context != null) { + r = context.resources + } + displayMetrics = r.displayMetrics + + paint.set(view.paint) + paint.textSize = size + + if (maxLines == 1 && paint.measureText( + text, + 0, + text.length + ) > targetWidth || getLineCount( + text, + paint, + size, + targetWidth.toFloat(), + displayMetrics + ) > maxLines + ) { + size = getAutoFitSize( + text, paint, targetWidth.toFloat(), maxLines, low, high, precision, + displayMetrics + ) + } + + if (size < minTextSize) { + size = minTextSize + } + + view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size) + } + + private fun getAutoFitSize( + text: CharSequence, paint: TextPaint, + targetWidth: Float, maxLines: Int, low: Float, high: Float, precision: Float, + displayMetrics: DisplayMetrics + ): Float { + val mid = (low + high) / 2.0f + var lineCount = 1 + var layout: StaticLayout? = null + + paint.textSize = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_PX, mid, + displayMetrics + ) + + if (maxLines != 1) { + layout = StaticLayout( + text, paint, targetWidth.toInt(), Layout.Alignment.ALIGN_NORMAL, + 1.0f, 0.0f, true + ) + lineCount = layout.lineCount + } + + if (SPEW) + Log.d( + TAG, "low=" + low + " high=" + high + " mid=" + mid + + " target=" + targetWidth + " maxLines=" + maxLines + " lineCount=" + lineCount + ) + + if (lineCount > maxLines) { + // For the case that `text` has more newline characters than `maxLines`. + return if (high - low < precision) { + low + } else getAutoFitSize( + text, paint, targetWidth, maxLines, low, mid, precision, + displayMetrics + ) + } else if (lineCount < maxLines) { + return getAutoFitSize( + text, paint, targetWidth, maxLines, mid, high, precision, + displayMetrics + ) + } else { + var maxLineWidth = 0f + if (maxLines == 1) { + maxLineWidth = paint.measureText(text, 0, text.length) + } else { + for (i in 0 until lineCount) { + if (layout!!.getLineWidth(i) > maxLineWidth) { + maxLineWidth = layout.getLineWidth(i) + } + } + } + + return if (high - low < precision) { + low + } else if (maxLineWidth > targetWidth) { + getAutoFitSize( + text, paint, targetWidth, maxLines, low, mid, precision, + displayMetrics + ) + } else if (maxLineWidth < targetWidth) { + getAutoFitSize( + text, paint, targetWidth, maxLines, mid, high, precision, + displayMetrics + ) + } else { + mid + } + } + } + + private fun getLineCount( + text: CharSequence, paint: TextPaint, size: Float, width: Float, + displayMetrics: DisplayMetrics + ): Int { + paint.textSize = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_PX, size, + displayMetrics + ) + val layout = StaticLayout( + text, paint, width.toInt(), + Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true + ) + return layout.lineCount + } + + private fun getMaxLines(view: BackBoneTextView): Int { + var maxLines = -1 // No limit (Integer.MAX_VALUE also means no limit) + + val method = view.transformationMethod + if (method != null && method is SingleLineTransformationMethod) { + maxLines = 1 + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + // setMaxLines() and getMaxLines() are only available on android-16+ + maxLines = view.maxLines + } + + return maxLines + } + } +} diff --git a/ultimatetextview/src/main/res/values/attrs.xml b/ultimatetextview/src/main/res/values/attrs.xml new file mode 100644 index 0000000..3ef9655 --- /dev/null +++ b/ultimatetextview/src/main/res/values/attrs.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ultimatetextview/src/main/res/values/strings.xml b/ultimatetextview/src/main/res/values/strings.xml new file mode 100644 index 0000000..0fb81b8 --- /dev/null +++ b/ultimatetextview/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + UltimateTextView + + + diff --git a/ultimatetextview/src/test/java/com/dev452/ultimatetextview/ExampleUnitTest.java b/ultimatetextview/src/test/java/com/dev452/ultimatetextview/ExampleUnitTest.java new file mode 100644 index 0000000..87a9670 --- /dev/null +++ b/ultimatetextview/src/test/java/com/dev452/ultimatetextview/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.dev452.ultimatetextview; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file