├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── themes.xml
│ │ │ ├── drawable
│ │ │ │ ├── fill.png
│ │ │ │ ├── hand_indicator.jpg
│ │ │ │ └── ic_launcher_background.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
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── xml
│ │ │ │ ├── backup_rules.xml
│ │ │ │ └── data_extraction_rules.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── pk
│ │ │ └── farimarwat
│ │ │ └── wordgame
│ │ │ └── MainActivity.kt
│ ├── test
│ │ └── java
│ │ │ └── pk
│ │ │ └── farimarwat
│ │ │ └── wordgame
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── pk
│ │ └── farimarwat
│ │ └── wordgame
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── abckids
├── .gitignore
├── consumer-rules.pro
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── hand.png
│ │ │ │ └── letter.png
│ │ │ └── values
│ │ │ │ ├── colors.xml
│ │ │ │ └── attrs.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── pk
│ │ │ └── farimarwat
│ │ │ └── abckids
│ │ │ ├── models
│ │ │ ├── KPointF.kt
│ │ │ ├── LetterA.kt
│ │ │ ├── LetterC.kt
│ │ │ ├── LetterU.kt
│ │ │ ├── LetterD.kt
│ │ │ ├── LetterP.kt
│ │ │ ├── LetterF.kt
│ │ │ ├── LetterE.kt
│ │ │ ├── LetterR.kt
│ │ │ ├── LetterB.kt
│ │ │ └── KSegment.kt
│ │ │ ├── AbcdkidsListener.kt
│ │ │ └── TracingLetterView.kt
│ ├── test
│ │ └── java
│ │ │ └── pk
│ │ │ └── farimarwat
│ │ │ └── abckids
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── pk
│ │ └── farimarwat
│ │ └── abckids
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── .idea
├── .gitignore
├── compiler.xml
├── vcs.xml
├── misc.xml
└── gradle.xml
├── demo.gif
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/abckids/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/abckids/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/demo.gif
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WordGame
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/drawable/fill.png
--------------------------------------------------------------------------------
/abckids/src/main/res/drawable/hand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/abckids/src/main/res/drawable/hand.png
--------------------------------------------------------------------------------
/abckids/src/main/res/drawable/letter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/abckids/src/main/res/drawable/letter.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/hand_indicator.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/drawable/hand_indicator.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/abckids/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/farimarwat/android-word-tracing-sdk/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/KPointF.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.PointF
4 |
5 | data class KPointF(
6 | var isaccessed:Boolean,
7 | val point:PointF
8 | )
9 |
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/AbcdkidsListener.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids
2 |
3 | import android.graphics.PointF
4 |
5 | interface AbcdkidsListener {
6 |
7 | fun onDotTouched(progress:Float)
8 | fun onSegmentFinished()
9 | fun onTraceFinished()
10 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Apr 29 10:04:20 PKT 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/abckids/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #009688
4 | #AB47Bc
5 | #4CAF50
6 | #FAFAFA
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "WordGame"
16 | include ':app'
17 | include ':abckids'
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/abckids/src/test/java/pk/farimarwat/abckids/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids
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 | }
--------------------------------------------------------------------------------
/app/src/test/java/pk/farimarwat/wordgame/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.wordgame
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 | }
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/abckids/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/LetterA.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 |
5 | class LetterA {
6 |
7 | companion object {
8 | fun getSegments(width:Int,height:Int):Path{
9 |
10 | val path = Path()
11 | path.moveTo(width*0.19f,height*0.9f)
12 | path.lineTo(width*0.45f,height*0.08f)
13 |
14 | path.moveTo(width*0.46f,height*0.08f)
15 | path.lineTo(width*0.76f,height*0.9f)
16 |
17 | path.moveTo(width*0.32f,height*0.62f)
18 | path.lineTo(width*0.62f,height*0.62f)
19 |
20 | return path
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/LetterC.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 | import android.graphics.RectF
5 |
6 | class LetterC {
7 |
8 | companion object {
9 | fun getSegments(width:Int,height:Int):Path{
10 |
11 | val path = Path()
12 | val marginstart = width * 0.10f
13 | val marginEnd = width * 0.90f
14 | val marginTop = height * 0.1f
15 | val marginBottom = height * 0.9f
16 |
17 | val oval = RectF(marginstart,marginTop,marginEnd,marginBottom)
18 | path.addArc(oval,290f, -210f)
19 |
20 | return path
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/LetterU.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 |
5 | class LetterU {
6 | companion object {
7 | fun getSegments(width:Int,height:Int):Path{
8 | val path = Path()
9 | val marginstart = width * 0.25f
10 | val marginEnd = width * 0.80f
11 | val marginTop = height * 0.1f
12 | val marginBottom = height * 0.9f
13 | path.moveTo(marginstart,marginTop)
14 | path.arcTo(marginstart,height*0.3f,marginEnd,height*0.9f,-180f,-180f,false)
15 | path.lineTo(marginEnd,marginTop)
16 |
17 | return path
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/pk/farimarwat/wordgame/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.wordgame
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("pk.farimarwat.wordgame", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/abckids/src/androidTest/java/pk/farimarwat/abckids/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids
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("pk.farimarwat.abckids.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/abckids/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/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/LetterD.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 | import android.graphics.RectF
5 |
6 | class LetterD {
7 |
8 | companion object {
9 | fun getSegments(width:Int,height:Int):Path{
10 |
11 | val path = Path()
12 | val marginstart = width * 0.10f + 100f
13 | val marginEnd = width * 0.80f
14 | val marginTop = height * 0.1f
15 | val marginBottom = height * 0.9f
16 |
17 | path.moveTo(marginstart,marginTop)
18 | path.lineTo(marginstart ,marginBottom)
19 |
20 | path.moveTo(marginstart,marginTop)
21 | val oval = RectF(width/4f,marginTop,marginEnd,marginBottom)
22 | path.arcTo(oval,270f,180f)
23 | path.lineTo(marginstart,marginBottom)
24 |
25 | return path
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/LetterP.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 | import android.graphics.RectF
5 |
6 | class LetterP {
7 |
8 | companion object {
9 | fun getSegments(width:Int,height:Int):Path{
10 | val path = Path()
11 | val marginstart = width * 0.25f
12 | val marginEnd = width * 0.80f
13 | val marginTop = height * 0.1f
14 | val marginBottom = height * 0.9f
15 | path.moveTo(marginstart,marginTop)
16 | path.lineTo(marginstart,marginBottom)
17 |
18 | path.moveTo(marginstart,marginTop)
19 | val oval = RectF(marginstart+100f,marginTop,marginEnd,marginBottom/2f+40f)
20 | path.arcTo(oval,280f,175f)
21 | path.lineTo(marginstart,marginBottom/2f+40f)
22 |
23 |
24 | return path
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/LetterF.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 | import android.graphics.RectF
5 |
6 | class LetterF {
7 |
8 | companion object {
9 | fun getSegments(width:Int,height:Int):Path{
10 | val path = Path()
11 | val marginstart = width * 0.10f + 100f
12 | val marginEnd = width * 0.80f
13 | val marginTop = height * 0.1f
14 | val marginBottom = height * 0.9f
15 |
16 | path.moveTo(marginstart,marginTop)
17 | path.lineTo(marginstart ,marginBottom)
18 |
19 | path.moveTo(marginstart,marginTop)
20 | path.lineTo(marginEnd,marginTop)
21 |
22 | path.moveTo(marginstart,(marginBottom+marginTop)/2f)
23 | path.lineTo(marginEnd-60f,(marginBottom+marginTop)/2f)
24 |
25 | return path
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/LetterE.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 | import android.graphics.RectF
5 |
6 | class LetterE {
7 |
8 | companion object {
9 | fun getSegments(width:Int,height:Int):Path{
10 | val path = Path()
11 | val marginstart = width * 0.10f + 100f
12 | val marginEnd = width * 0.80f
13 | val marginTop = height * 0.1f
14 | val marginBottom = height * 0.9f
15 |
16 | path.moveTo(marginstart,marginTop)
17 | path.lineTo(marginstart ,marginBottom)
18 |
19 | path.moveTo(marginstart,marginTop)
20 | path.lineTo(marginEnd,marginTop)
21 |
22 | path.moveTo(marginstart,(marginBottom+marginTop)/2f)
23 | path.lineTo(marginEnd-60f,(marginBottom+marginTop)/2f)
24 |
25 | path.moveTo(marginstart,marginBottom)
26 | path.lineTo(marginEnd,marginBottom)
27 |
28 | return path
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/LetterR.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 | import android.graphics.RectF
5 |
6 | class LetterR {
7 |
8 | companion object {
9 | fun getSegments(width:Int,height:Int):Path{
10 |
11 | val path = Path()
12 | val marginstart = width * 0.25f
13 | val marginEnd = width * 0.80f
14 | val marginTop = height * 0.1f
15 | val marginBottom = height * 0.9f
16 | path.moveTo(marginstart,marginTop)
17 | path.lineTo(marginstart,marginBottom)
18 |
19 | path.moveTo(marginstart,marginTop)
20 | val oval = RectF(marginstart+100f,marginTop,marginEnd,marginBottom/2f+50f)
21 | path.arcTo(oval,280f,175f)
22 | path.lineTo(marginstart,marginBottom/2f+50f)
23 |
24 | path.moveTo(width/2f+100f,marginBottom/2f+90f)
25 | path.lineTo(marginEnd,marginBottom-10f)
26 |
27 | return path
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/LetterB.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 | import android.graphics.RectF
5 |
6 | class LetterB {
7 |
8 | companion object {
9 | fun getSegments(width:Int,height:Int):Path{
10 | val path = Path()
11 | val marginstart = width * 0.25f
12 | val marginEnd = width * 0.80f
13 | val marginTop = height * 0.1f
14 | val marginBottom = height * 0.9f
15 | path.moveTo(marginstart,marginTop)
16 | path.lineTo(marginstart,marginBottom)
17 |
18 | path.moveTo(marginstart,marginTop)
19 | var oval = RectF(marginstart+100f,marginTop,marginEnd,marginBottom/2f+20f)
20 | path.arcTo(oval,280f,175f)
21 | path.lineTo(marginstart,marginBottom/2f+20f)
22 |
23 | path.moveTo(marginstart,marginBottom/2f+20f)
24 | oval = RectF(marginstart+100f,marginBottom/2f+20f,marginEnd,marginBottom)
25 | path.arcTo(oval,280f,175f)
26 | path.lineTo(marginstart,marginBottom)
27 |
28 | return path
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
27 |
28 |
--------------------------------------------------------------------------------
/abckids/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 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles "consumer-rules.pro"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 | kotlinOptions {
28 | jvmTarget = '1.8'
29 | }
30 | namespace 'pk.farimarwat.abckids'
31 | }
32 |
33 | dependencies {
34 |
35 | implementation 'androidx.core:core-ktx:1.8.0'
36 | implementation 'androidx.appcompat:appcompat:1.5.0'
37 | implementation 'com.google.android.material:material:1.6.1'
38 | testImplementation 'junit:junit:4.13.2'
39 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
41 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1")
42 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | compileSdk 32
8 |
9 | defaultConfig {
10 | applicationId "pk.farimarwat.wordgame"
11 | minSdk 21
12 | targetSdk 32
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility JavaVersion.VERSION_1_8
27 | targetCompatibility JavaVersion.VERSION_1_8
28 | }
29 | kotlinOptions {
30 | jvmTarget = '1.8'
31 | }
32 | buildFeatures{
33 | viewBinding true
34 | }
35 | namespace 'pk.farimarwat.wordgame'
36 | }
37 |
38 | dependencies {
39 |
40 | implementation 'androidx.core:core-ktx:1.7.0'
41 | implementation 'androidx.appcompat:appcompat:1.4.2'
42 | implementation 'com.google.android.material:material:1.6.1'
43 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
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 | implementation project(path:':abckids')
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/pk/farimarwat/wordgame/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.wordgame
2 |
3 | import android.content.Context
4 | import android.graphics.Path
5 | import androidx.appcompat.app.AppCompatActivity
6 | import android.os.Bundle
7 | import android.util.Log
8 | import androidx.core.content.ContextCompat
9 | import pk.farimarwat.abckids.AbcdkidsListener
10 | import pk.farimarwat.abckids.TAG
11 | import pk.farimarwat.wordgame.databinding.ActivityMainBinding
12 |
13 | class MainActivity : AppCompatActivity() {
14 | lateinit var mContext:Context
15 | private val binding:ActivityMainBinding by lazy {
16 | ActivityMainBinding.inflate(layoutInflater)
17 | }
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | setContentView(binding.root)
21 | mContext = this
22 | val path = Path()
23 | val width = 420
24 | val height = 420
25 | path.moveTo(width*0.19f,height*0.9f)
26 | path.lineTo(width*0.45f,height*0.08f)
27 |
28 | path.moveTo(width*0.46f,height*0.08f)
29 | path.lineTo(width*0.76f,height*0.9f)
30 |
31 | path.moveTo(width*0.32f,height*0.62f)
32 | path.lineTo(width*0.62f,height*0.62f)
33 | binding.tlview.setLetter(path)
34 |
35 | // binding.tlview.setLetter("A",width,height)
36 | binding.tlview.addListener(object :AbcdkidsListener{
37 | override fun onDotTouched(progress: Float) {
38 | Log.e(TAG,"Progress: ${progress}")
39 | }
40 |
41 | override fun onSegmentFinished() {
42 | Log.e(TAG,"Segment Finished")
43 | }
44 |
45 | override fun onTraceFinished() {
46 | Log.e(TAG,"Tracing completed")
47 | }
48 |
49 | })
50 |
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/models/KSegment.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids.models
2 |
3 | import android.graphics.Path
4 | import android.graphics.PointF
5 |
6 | data class KSegment(
7 | var isaccessed: Boolean?,
8 | var points: MutableList?,
9 | var path:Path?,
10 | var startpoint:PointF?,
11 | var endpoint:PointF?
12 | ) {
13 |
14 | fun getPrevious(point: KPointF): KPointF? {
15 | var kpointf: KPointF? = null
16 | points?.let {
17 | val index = getIndex(point)
18 | if(index > 0){
19 | kpointf = it[index -1]
20 | }
21 | }
22 | return kpointf
23 | }
24 |
25 | fun getNext(point: KPointF): KPointF? {
26 | var kpointf: KPointF? = null
27 | points?.let {
28 | for (i in 0 until it.size - 1) {
29 | val item = it[i]
30 | if (item.point.x == point.point.x
31 | && item.point.y == point.point.y
32 | && i < it.size
33 | ) {
34 | kpointf = it[i + 1]
35 | break
36 | }
37 | }
38 | }
39 | return kpointf
40 | }
41 |
42 | fun setAccess(point: KPointF) {
43 | points?.let {
44 | for (p in it) {
45 | if (p.point.x == point.point.x
46 | && p.point.y == p.point.y
47 | ) {
48 | p.isaccessed = true
49 | }
50 | }
51 | }
52 | }
53 |
54 | fun getIndex(point: KPointF): Int {
55 | var index = 0
56 | points?.let {
57 | for (p in it) {
58 | if (p.point.x == point.point.x && p.point.y == point.point.y) {
59 | break
60 | }
61 | index++
62 | }
63 | }
64 | return index
65 | }
66 |
67 | fun getFirst():KPointF?{
68 | var pointf:KPointF? = null
69 | points?.let {
70 | pointf = it.first()
71 | }
72 | return pointf
73 | }
74 | fun getLast():KPointF?{
75 | var pointf:KPointF? = null
76 | points?.let {
77 | pointf = it.last()
78 | }
79 | return pointf
80 | }
81 | fun getTotal():Int{
82 | points?.let {
83 | return it.size
84 | }
85 | return 0
86 | }
87 |
88 | fun isSegmentAccessed(): Boolean {
89 | points?.let {
90 | for (p in it) {
91 | if (!p.isaccessed) {
92 | return false
93 | }
94 | }
95 | }
96 | return true
97 | }
98 |
99 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Android Word Tracing SDK
2 | Android Word/Letter Tracing SDK is a complet solution to build apps for kids learning in pure kotlin. It supports all kind of shapes and all language letters e.g. english, arabic, Urdu, hindi etc.
3 |
4 | 
5 | ### Getting Started
6 | Step 1: Include it in your settings.gradle
7 | ```
8 | allprojects {
9 | repositories {
10 | ...
11 | maven { url 'https://jitpack.io' }
12 | }
13 | }
14 | ```
15 | Step 2: Include it in app gradle
16 | ```
17 | implementation 'com.github.farimarwat:android-word-tracing-sdk:1.1'
18 | ```
19 |
20 | ### XML Example
21 | ```
22 |
31 | ```
32 |
33 | #### Custom Attributes
34 | | Attribute | Details |
35 | | ------------ | ------------ |
36 | | app:tlv_segmentfillimage | Drawable image for fill while moving finger |
37 | | app:tlv_segmentfillcolor | Fill color while moving finger (Note: Use either fill color or image) |
38 | | app:tlv_indicator | Drawable png image e.g. hand indication image |
39 | | app:tlv_segmentbordercolor | Border color for shape |
40 | | app:tlv_segmentbackgroundcolor | Background color for shape |
41 | | app:tlv_segmentdot | Dots color on shape |
42 | | app:tlv_segmentsize | Segment size for shape |
43 |
44 |
45 | ### Kotlin Code
46 | ```kotlin
47 | val width = 420
48 | val height = 420
49 |
50 | binding.tlview.setLetter("A",width,height)
51 |
52 | binding.tlview.addListener(object :AbcdkidsListener{
53 | override fun onDotTouched(progress: Float) {
54 | Log.e(TAG,"Progress: ${progress}")
55 | }
56 |
57 | override fun onSegmentFinished() {
58 | Log.e(TAG,"Segment Finished")
59 | }
60 |
61 | override fun onTraceFinished() {
62 | Log.e(TAG,"Tracing completed")
63 | }
64 |
65 | })
66 | ```
67 | **Supported letters at the moment**
68 | A, B, C, D, E, F,P, R,U
69 |
70 | *Note: Due to short time I have included limited letters. I will gradually update letters Or you have made some shapes/letters then share it with me so I include it in next release*
71 |
72 | ## Custom Shape/letters
73 | If you want to build your own custom shape/letter(of any language) then you have to familiar with canvas PATH in android.
74 | It is as simple as ABC. Just create a path and use as below. For instance I want to make letter 'A' which has 3 segments:
75 |
76 | ```kotlin
77 | val path = Path()
78 | path.moveTo(width*0.19f,height*0.9f)
79 | path.lineTo(width*0.45f,height*0.08f)
80 |
81 | path.moveTo(width*0.46f,height*0.08f)
82 | path.lineTo(width*0.76f,height*0.9f)
83 |
84 | path.moveTo(width*0.32f,height*0.62f)
85 | path.lineTo(width*0.62f,height*0.62f)
86 |
87 | binding.tlview.setLetter(path)
88 | ```
89 | ### Rules for custom shapes/letters
90 | I have built all the letters using width and height 420,420. If you change the size then you may face unexpacted result in your shapes/letters.
91 | Another point to note that if you move finger and the fill does not hide the end then your path is wrong. So every thing is the PATH creation and do not blam the library.
92 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/abckids/src/main/java/pk/farimarwat/abckids/TracingLetterView.kt:
--------------------------------------------------------------------------------
1 | package pk.farimarwat.abckids
2 |
3 | import android.animation.ValueAnimator
4 | import android.content.Context
5 | import android.content.res.TypedArray
6 | import android.graphics.*
7 | import android.graphics.drawable.Drawable
8 | import android.util.AttributeSet
9 | import android.util.Log
10 | import android.view.MotionEvent
11 | import android.view.View
12 | import androidx.core.content.ContextCompat
13 | import androidx.core.graphics.drawable.toBitmap
14 | import pk.farimarwat.abckids.models.*
15 |
16 |
17 | const val TAG = "abckids"
18 |
19 | class TracingLetterView(context: Context, attrs: AttributeSet) : View(context, attrs) {
20 | private var mListener: AbcdkidsListener? = null
21 | private var mTa: TypedArray
22 | private var mCanvasSize = 400
23 | private var mSizeSegment = 60f
24 | private val mPaint = Paint().apply {
25 | isAntiAlias = true
26 | }
27 | private val mPaintSpecific = Paint().apply {
28 | isAntiAlias = true
29 | }
30 |
31 | private var mSegBorderStrokeSize = 0f
32 | private var mSegBorderColor = 0
33 | private var mPathSegBorder = Path()
34 | lateinit var mPaintSegBackground:Paint
35 |
36 | private var mSegBackgroundStrokeSize = 0f
37 | private var mSegBackgroundColor = 0
38 | private var mPathSegBackground = Path()
39 |
40 | private var mSegFillStrokeSize = 0f
41 | private var mSegFillColor = 0
42 | private var mFillBitmapShader: BitmapShader? = null
43 | private var mPathSegFill = Path()
44 | lateinit var mPaintSegFill:Paint
45 | private val mPaintSegDot = Paint().apply {
46 | isAntiAlias = true
47 | style = Paint.Style.FILL
48 | }
49 | private val mSegDotRadius = 10f
50 | private val mSegDotDetectRadius = 50f
51 | private val mListSegments = mutableListOf()
52 | private var mTracingCompleted = false
53 | private var mActiveSegment: KSegment? = null
54 | private var mActiveSegmentIndex: Int = 0
55 | private var mCanMove = false
56 | private var mPorterDuff_SRC_ATOP = PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)
57 | private var mPorterDuff_DST_ATOP = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)
58 | private var mIndicatorBitmap: Bitmap? = null
59 | private var mShowIndicator = true
60 | private var mIndicatorPoint:PointF? = null
61 | private var mIndicatorValueAnimator:ValueAnimator? = null
62 |
63 | private var mLetter:Path? = null
64 |
65 | init {
66 | setLayerType(LAYER_TYPE_SOFTWARE, null)
67 | mTa = context.theme.obtainStyledAttributes(
68 | attrs,
69 | R.styleable.TracingLetterView, 0, 0
70 | )
71 | initSegment(context, mTa)
72 |
73 | }
74 |
75 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
76 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
77 | mCanvasSize = Math.min(measuredWidth, measuredHeight)
78 | setMeasuredDimension(mCanvasSize, mCanvasSize)
79 | }
80 |
81 | override fun onDraw(canvas: Canvas?) {
82 | super.onDraw(canvas)
83 | mPaint.xfermode = null
84 | mLetter?.let {
85 | prepareLetter(it)
86 | }
87 | canvas?.drawBitmap(createSegBackground(width, height), 0f, 0f, mPaint)
88 | mPaint.xfermode = mPorterDuff_SRC_ATOP
89 | canvas?.drawBitmap(createSegFill(width, height), 0f, 0f, mPaint)
90 | mPaint.xfermode = mPorterDuff_DST_ATOP
91 | canvas?.drawBitmap(createSegBorder(width, height), 0f, 0f, mPaint)
92 |
93 | drawUnaccessedSegment(canvas, mListSegments)
94 | if(mShowIndicator){
95 | mIndicatorBitmap?.let { ibitmap ->
96 | mIndicatorPoint?.let { ipoint ->
97 | canvas?.drawBitmap(ibitmap,ipoint.x,ipoint.y,null)
98 | }
99 | }
100 | }
101 | }
102 |
103 | private fun initSegment(context: Context, ta: TypedArray) {
104 | val colorSegBorder = ta.getColor(R.styleable.TracingLetterView_tlv_segmentbordercolor, 0)
105 | if (colorSegBorder != 0) {
106 | mSegBorderColor = colorSegBorder
107 | } else {
108 | mSegBorderColor = ContextCompat.getColor(context, R.color.segmentborder)
109 | }
110 |
111 | val colorSegBackground =
112 | ta.getColor(R.styleable.TracingLetterView_tlv_segmentbackgroundcolor, 0)
113 | if (colorSegBackground != 0) {
114 | mSegBackgroundColor = colorSegBackground
115 | } else {
116 | mSegBackgroundColor = ContextCompat.getColor(context, R.color.segmentbackground)
117 | }
118 |
119 | val colorSegFill = ta.getColor(R.styleable.TracingLetterView_tlv_segmentfillcolor, 0)
120 | if (colorSegFill != 0) {
121 | mSegFillColor = colorSegFill
122 | } else {
123 | mSegFillColor = ContextCompat.getColor(context, R.color.segmentfill)
124 | }
125 |
126 | val fillimagedrawable = ta.getDrawable(R.styleable.TracingLetterView_tlv_segmentfillimage)
127 | fillimagedrawable?.let {
128 | var bitmap = it.toBitmap()
129 | bitmap = Bitmap.createScaledBitmap(bitmap, 60, 60, false)
130 | mFillBitmapShader = BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
131 | }
132 | val colorSegDot = ta.getColor(R.styleable.TracingLetterView_tlv_segmentdot, 0)
133 | if (colorSegDot != 0) {
134 | mPaintSegDot.color = colorSegDot
135 | } else {
136 | mPaintSegDot.color = ContextCompat.getColor(context, R.color.segmentdot)
137 | }
138 |
139 | mSizeSegment = ta.getFloat(R.styleable.TracingLetterView_tlv_segmentsize, SEG_SIZE_DEFAULT)
140 | mSegBorderStrokeSize = mSizeSegment
141 | mSegBackgroundStrokeSize = mSizeSegment - 20f
142 | mSegFillStrokeSize = mSizeSegment
143 |
144 | val indicator = ta.getDrawable(R.styleable.TracingLetterView_tlv_indicator)
145 | if(indicator != null){
146 | val ind_bitmapt = indicator.toBitmap()
147 | mIndicatorBitmap = Bitmap.createScaledBitmap(ind_bitmapt,60,60,false)
148 | } else {
149 | mIndicatorBitmap = BitmapFactory.decodeResource(resources,R.drawable.hand)
150 | }
151 | ta.recycle()
152 | }
153 |
154 | private fun createSegBorder(width: Int, height: Int): Bitmap {
155 | val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
156 | val canvas = Canvas(bitmap)
157 | val paint = Paint().apply {
158 | isAntiAlias = true
159 | style = Paint.Style.STROKE
160 | strokeCap = Paint.Cap.ROUND
161 | color = mSegBorderColor
162 | strokeWidth = mSegBorderStrokeSize
163 | }
164 | canvas.drawPath(mPathSegBorder, paint)
165 | return bitmap
166 | }
167 |
168 | private fun createSegBackground(width: Int, height: Int): Bitmap {
169 | val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
170 | val canvas = Canvas(bitmap)
171 | mPaintSegBackground = Paint().apply {
172 | isAntiAlias = true
173 | style = Paint.Style.STROKE
174 | strokeCap = Paint.Cap.ROUND
175 | color = mSegBackgroundColor
176 | strokeWidth = mSegBackgroundStrokeSize
177 | }
178 | canvas.drawPath(mPathSegBackground, mPaintSegBackground)
179 | return bitmap
180 | }
181 |
182 | private fun createSegFill(width: Int, height: Int): Bitmap {
183 | val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
184 | val canvas = Canvas(bitmap)
185 | mPaintSegFill = Paint().apply {
186 | isAntiAlias = true
187 | style = Paint.Style.STROKE
188 | strokeCap = Paint.Cap.ROUND
189 | strokeWidth = mSegFillStrokeSize
190 | }
191 | if (mFillBitmapShader != null) {
192 | mPaintSegFill.shader = mFillBitmapShader
193 | } else {
194 | mPaintSegFill.color = mSegFillColor
195 | }
196 | canvas.drawPath(mPathSegFill, mPaintSegFill)
197 | return bitmap
198 | }
199 |
200 | fun setLetter(letter:String, width: Int, height: Int){
201 | when(letter) {
202 | "A"-> {
203 | mLetter = LetterA.getSegments(width,height)
204 | }
205 | "B"-> {
206 | mLetter = LetterB.getSegments(width,height)
207 | }
208 | "C"-> {
209 | mLetter = LetterC.getSegments(width,height)
210 | }
211 | "D"-> {
212 | mLetter = LetterD.getSegments(width,height)
213 | }
214 | "E"-> {
215 | mLetter = LetterE.getSegments(width,height)
216 | }
217 | "F"-> {
218 | mLetter = LetterF.getSegments(width,height)
219 | }
220 | }
221 | }
222 | fun setLetter(path:Path){
223 | mLetter = path
224 | }
225 | private fun prepareLetter(path: Path) {
226 | if (mListSegments.isEmpty()) {
227 | mPathSegBackground = path
228 | mPathSegBorder = path
229 | Log.e(TAG,"Setting Letter")
230 | val paths = pathsFromComplexPath(path)
231 | paths?.let { list_paths ->
232 | for (p in list_paths) {
233 | val pm = PathMeasure(path, false)
234 | val startpoint = getPoint(pm, 0f)
235 | val endPoint = getPoint(pm, pm.length)
236 | val points = getPoints(p)
237 | val kpoints = mutableListOf()
238 | for (point in points) {
239 | kpoints.add(
240 | KPointF(false, point)
241 | )
242 | }
243 | mListSegments.add(
244 | KSegment(false, kpoints,p, startpoint, endPoint)
245 | )
246 | }
247 | }
248 | }
249 | }
250 |
251 | private fun drawUnaccessedSegment(canvas: Canvas?, segments: MutableList) {
252 | if (segments.isNotEmpty()
253 | && (!mTracingCompleted)
254 | ) {
255 | if(mActiveSegment == null){
256 | var counter = 0
257 | for (seg in segments) {
258 | if (!seg.isaccessed!!) {
259 | mActiveSegment = seg
260 | mActiveSegmentIndex = counter
261 | mShowIndicator = true
262 | showIndicator()
263 | break
264 | }
265 | counter++
266 | }
267 |
268 | }
269 | mActiveSegment?.points?.let {
270 | for (d in it) {
271 | if(!d.isaccessed){
272 | canvas?.drawCircle(d.point.x, d.point.y, mSegDotRadius, mPaintSegDot)
273 | }
274 | }
275 | }
276 | }
277 | }
278 |
279 | override fun onTouchEvent(event: MotionEvent?): Boolean {
280 | event?.let {
281 | event
282 | when (event.action) {
283 | MotionEvent.ACTION_DOWN -> {
284 | mShowIndicator = false
285 | mIndicatorValueAnimator?.cancel()
286 | mActiveSegment?.points?.let { list ->
287 | for (point in list) {
288 | val istouched = isCircleTouched(
289 | event.x, event.y,
290 | point.point.x, point.point.y,
291 | mSegDotDetectRadius
292 | )
293 | if (istouched) {
294 | mShowIndicator = false
295 | //setting progress
296 | mActiveSegment?.let { seg ->
297 | val total = seg.getTotal()
298 | val current = seg.getIndex(point).plus(1)
299 | mListener?.onDotTouched(
300 | (current * total) / 100f
301 | )
302 | }
303 |
304 | if (mActiveSegment?.getPrevious(point)?.isaccessed == true
305 | || mActiveSegment?.getPrevious(point) == null
306 | ) {
307 | mCanMove = true
308 | mActiveSegment?.let {
309 | val first = it.getFirst()
310 | first?.let { kpoint ->
311 | mPathSegFill.moveTo(kpoint.point.x, kpoint.point.y)
312 | }
313 | }
314 | //
315 | mActiveSegment?.setAccess(point)
316 | invalidate()
317 | }
318 | break
319 | }
320 | }
321 | }
322 | }
323 | MotionEvent.ACTION_MOVE -> {
324 | mActiveSegment?.points?.let { list ->
325 | for (point in list) {
326 | val istouched = isCircleTouched(
327 | event.x, event.y, point.point.x, point.point.y, mSegDotDetectRadius
328 | )
329 | val previous = mActiveSegment?.getPrevious(point)
330 | if (istouched
331 | && mCanMove
332 | && previous?.isaccessed == true
333 | ) {
334 | //setting progress
335 | mActiveSegment?.let { seg ->
336 | val total = seg.getTotal().toFloat()
337 | val current = seg.getIndex(point).plus(1).toFloat()
338 | val progress = ((current / total) * 100f)
339 | mListener?.onDotTouched(
340 | progress
341 | )
342 | mPathSegFill.lineTo(point.point.x, point.point.y)
343 | if (progress >= 100.0f) {
344 | mListSegments[mActiveSegmentIndex].isaccessed = true
345 | mListener?.onSegmentFinished()
346 | mCanMove = false
347 | mActiveSegment = null
348 | val endpoint = seg.endpoint!!
349 | mPathSegFill.moveTo(endpoint.x,endpoint.y)
350 | if (mActiveSegmentIndex == mListSegments.size - 1) {
351 | mTracingCompleted = true
352 | mListener?.onTraceFinished()
353 | mActiveSegment = null
354 | }
355 | }
356 |
357 | }
358 |
359 | mPathSegFill.lineTo(point.point.x, point.point.y)
360 | mActiveSegment?.setAccess(point)
361 | //
362 | invalidate()
363 | break
364 | }
365 | }
366 | }
367 | }
368 | MotionEvent.ACTION_UP -> {
369 | mCanMove = false
370 | }
371 | else -> {}
372 | }
373 | }
374 | return true
375 | }
376 |
377 | //attributes
378 | fun setSegmentFillImage(image: Drawable?) {
379 | image?.let {
380 | var bitmap = it.toBitmap()
381 | bitmap = Bitmap.createScaledBitmap(bitmap, 60, 60, false)
382 | mFillBitmapShader = BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
383 | }
384 | }
385 |
386 | //
387 | fun addListener(listener: AbcdkidsListener) {
388 | mListener = listener
389 | }
390 |
391 | private fun isCircleTouched(
392 | touchX: Float,
393 | touchY: Float,
394 | centerX: Float,
395 | centerY: Float,
396 | r: Float
397 | ): Boolean {
398 | val distanceX = touchX - centerX
399 | val distanceY = touchY - centerY
400 | return (distanceX * distanceX) + (distanceY * distanceY) <= r * r;
401 | }
402 |
403 | private fun pathsFromComplexPath(p: Path?): List? {
404 | val pathList: MutableList = ArrayList()
405 | val pm = PathMeasure(p, false)
406 | var fin = false
407 | while (!fin) {
408 | val len = pm.length
409 | if (len > 0) {
410 | val np = Path()
411 | pm.getSegment(0f, len, np, true)
412 | pathList.add(np)
413 | }
414 | fin = !pm.nextContour()
415 | }
416 | return pathList
417 | }
418 |
419 | private fun getPoints(path: Path): MutableList {
420 | val list = mutableListOf()
421 | val pm = PathMeasure(path, false)
422 | val amoutofpoints = pm.length / 60f
423 | val aCoordinates = floatArrayOf(0f, 0f)
424 | var i = 0.0f
425 | while (i < 1.0) {
426 | pm.getPosTan(pm.length * i, aCoordinates, null)
427 | i += 1.0f / amoutofpoints
428 | val point = PointF()
429 | point.x = aCoordinates[0]
430 | point.y = aCoordinates[1]
431 | list.add(point)
432 | }
433 |
434 | return list
435 | }
436 |
437 | private fun getPoint(pm: PathMeasure, length: Float): PointF {
438 | val pointf = PointF(0f, 0f)
439 | val coordinates = floatArrayOf(0f, 0f)
440 |
441 | pm.getPosTan(length, coordinates, null)
442 | pointf.x = coordinates[0]
443 | pointf.y = coordinates[1]
444 | return pointf
445 | }
446 |
447 | private fun showIndicator() {
448 | if(mShowIndicator){
449 | mActiveSegment?.let { kSegment ->
450 | kSegment.points?.let { points ->
451 | mIndicatorValueAnimator = ValueAnimator.ofInt(0, points.size -1)
452 | mIndicatorValueAnimator?.apply {
453 | duration = 2000
454 | repeatCount = ValueAnimator.INFINITE
455 | }
456 | mIndicatorValueAnimator?.addUpdateListener {
457 | mIndicatorPoint = points[mIndicatorValueAnimator?.animatedValue as Int]
458 | .point
459 |
460 | invalidate()
461 | }
462 | mIndicatorValueAnimator?.start()
463 | }
464 | }
465 | } else {
466 | mIndicatorValueAnimator?.cancel()
467 | }
468 | }
469 |
470 | companion object {
471 | val SEG_SIZE_DEFAULT = 80f
472 | }
473 | }
--------------------------------------------------------------------------------