Skip to content

Commit

Permalink
updated and fixed phone validation
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisjenx committed Oct 8, 2024
1 parent 8ba9266 commit ee412c1
Show file tree
Hide file tree
Showing 21 changed files with 152 additions and 33 deletions.
19 changes: 10 additions & 9 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,26 @@ junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.6"
composeBom = "2024.09.03"
startupRuntime = "1.2.0"

[libraries]
androidx-activityCompose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
androidx-testManifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-uiTest" }
androidx-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-uiTest" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
libphonenumber-jvm = { module = "com.googlecode.libphonenumber:libphonenumber", version.ref = "libphonenumberJvm" }
libphonenumber-android = { module = "io.michaelrocks:libphonenumber-android", version.ref = "libphonenumberAndroid" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-uiTest" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-startup-runtime = { module = "androidx.startup:startup-runtime", version.ref = "startupRuntime" }
androidx-testManifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-uiTest" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
libphonenumber-jvm = { module = "com.googlecode.libphonenumber:libphonenumber", version.ref = "libphonenumberJvm" }
libphonenumber-android = { module = "io.michaelrocks:libphonenumber-android", version.ref = "libphonenumberAndroid" }

[plugins]
cocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" }
Expand Down
10 changes: 9 additions & 1 deletion library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ plugins {
}

kotlin {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
freeCompilerArgs.add("-Xexpect-actual-classes")
}
// cocoapods {
// version = "1.0.0"
// summary = "Yet Another Kotlin COmpose Validation library"
Expand Down Expand Up @@ -106,7 +110,8 @@ kotlin {
androidMain.dependencies {
compileOnly(compose.uiTooling)
implementation(libs.androidx.activityCompose)
api(libs.libphonenumber.android)
implementation(libs.libphonenumber.android)
implementation(libs.androidx.startup.runtime)
}

jvmMain.dependencies {
Expand Down Expand Up @@ -154,6 +159,9 @@ android {
systemImageSource = "aosp"
}
}
unitTests {
isIncludeAndroidResources = true
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.chrisjenx.yakcov

actual fun initPhoneNumberUtil() = Unit
17 changes: 16 additions & 1 deletion library/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest></manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.chrisjenx.yakcov.PhoneNumberUtilInitializer"
android:value="androidx.startup" />
</provider>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.chrisjenx.yakcov

import android.content.Context
import androidx.startup.Initializer
import io.michaelrocks.libphonenumber.android.PhoneNumberUtil
import io.michaelrocks.libphonenumber.android.metadata.init.ClassPathResourceMetadataLoader
import io.michaelrocks.libphonenumber.android.metadata.source.AssetsMetadataLoader
import org.jetbrains.annotations.VisibleForTesting


private val phoneUtil = PhoneNumberUtil.createInstance(ClassPathResourceMetadataLoader())
@VisibleForTesting
internal lateinit var phoneUtil: PhoneNumberUtil


/**
* Check if is phone number to best ability of each platform.
Expand All @@ -19,3 +24,20 @@ actual fun String?.isPhoneNumber(defaultRegion: String?): Boolean {
false
}
}

/**
* Will init the Android phone number util at app startup using androidx.startup, see manifest.
*/
class PhoneNumberUtilInitializer : Initializer<PhoneNumberUtil> {
override fun create(context: Context): PhoneNumberUtil {
val assetManager = AssetsMetadataLoader(context.applicationContext.assets)
val instance = PhoneNumberUtil.createInstance(assetManager)
phoneUtil = instance // Set global
return instance
}

override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.chrisjenx.yakcov

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
actual annotation class IOSIgnore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.chrisjenx.yakcov

import io.michaelrocks.libphonenumber.android.PhoneNumberUtil
import io.michaelrocks.libphonenumber.android.metadata.init.ClassPathResourceMetadataLoader

actual fun initPhoneNumberUtil() {
phoneUtil = PhoneNumberUtil.createInstance(ClassPathResourceMetadataLoader())
}
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ fun String?.isEmail(): Boolean {
* @param defaultRegion The default region to use if the number is not in international format.
* it's two digits country code. e.g. "US", "GB", "ES"
*/
expect fun String?.isPhoneNumber(defaultRegion: String? = null): Boolean
expect fun String?.isPhoneNumber(defaultRegion: String? = "US"): Boolean
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.chrisjenx.yakcov

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
expect annotation class IOSIgnore()
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.chrisjenx.yakcov

import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class PhoneNumberTest {

@BeforeTest
fun setUp() {
initPhoneNumberUtil()
}

@Test
fun isPhoneNumber_fail() {
assertFalse("43435".isPhoneNumber())
Expand All @@ -21,5 +27,6 @@ class PhoneNumberTest {
assertTrue("6508991234".isPhoneNumber())
}


}

expect fun initPhoneNumberUtil()
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package com.chrisjenx.yakcov.strings

import com.chrisjenx.yakcov.IOSIgnore
import com.chrisjenx.yakcov.ValidationResult.Outcome
import com.chrisjenx.yakcov.initPhoneNumberUtil
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals

class PhoneRuleTest {

@BeforeTest
fun setUp() {
initPhoneNumberUtil()
}

@Test
fun phoneNumber_invalid() {
assertEquals(Outcome.ERROR, Phone().validate("43435").outcome())
Expand All @@ -17,6 +25,7 @@ class PhoneRuleTest {
}

@Test
@IOSIgnore
fun phoneNumber_wrongRegion() {
// This is a UK number should error for US
assertEquals(Outcome.ERROR, Phone("US").validate("07745973912").outcome())
Expand All @@ -27,5 +36,6 @@ class PhoneRuleTest {
fun phoneNumber_withRegion() {
assertEquals(Outcome.SUCCESS, Phone().validate("+16508991234").outcome())
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.chrisjenx.yakcov

actual typealias IOSIgnore = kotlin.test.Ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.chrisjenx.yakcov

actual fun initPhoneNumberUtil() = Unit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.chrisjenx.yakcov

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
actual annotation class IOSIgnore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.chrisjenx.yakcov

actual fun initPhoneNumberUtil() = Unit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.chrisjenx.yakcov

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
actual annotation class IOSIgnore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.chrisjenx.yakcov

actual fun initPhoneNumberUtil() = Unit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.chrisjenx.yakcov

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
actual annotation class IOSIgnore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.chrisjenx.yakcov

actual fun initPhoneNumberUtil() = Unit
46 changes: 28 additions & 18 deletions sample/src/main/java/com/chrisjenx/yakcov/sample/SampleActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button
Expand All @@ -27,14 +29,12 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.chrisjenx.yakcov.generic.IsChecked
import com.chrisjenx.yakcov.generic.ListNotEmpty
Expand All @@ -43,6 +43,7 @@ import com.chrisjenx.yakcov.sample.ui.theme.YakcovTheme
import com.chrisjenx.yakcov.strings.Email
import com.chrisjenx.yakcov.strings.MinLength
import com.chrisjenx.yakcov.strings.PasswordMatches
import com.chrisjenx.yakcov.strings.Phone
import com.chrisjenx.yakcov.strings.Required
import com.chrisjenx.yakcov.strings.rememberTextFieldValueValidator
import com.chrisjenx.yakcov.validate
Expand All @@ -57,6 +58,7 @@ class SampleActivity : ComponentActivity() {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(innerPadding)
.padding(16.dp)
) {
Expand Down Expand Up @@ -87,6 +89,29 @@ class SampleActivity : ComponentActivity() {

Spacer(modifier = Modifier.height(16.dp))

val phoneValidator = rememberTextFieldValueValidator(
rules = listOf(Required, Phone()),
)
with(phoneValidator) {
OutlinedTextField(
value = value,
label = { Text(text = "Phone") },
modifier = Modifier
.validationConfig(validateOnFocusLost = true)
.fillMaxWidth(),
onValueChange = ::onValueChange,
isError = isError(),
keyboardOptions = KeyboardOptions(
autoCorrectEnabled = false,
keyboardType = KeyboardType.Phone,
),
singleLine = true,
supportingText = supportingText()
)
}

Spacer(modifier = Modifier.height(16.dp))

Text(text = "Password", style = MaterialTheme.typography.headlineSmall)
// Password example
val passwordValidator = rememberTextFieldValueValidator(
Expand Down Expand Up @@ -255,6 +280,7 @@ class SampleActivity : ComponentActivity() {
onClick = {
listOf(
emailValidator,
phoneValidator,
passwordValidator,
passwordMatchesValidator,
requiredValidator,
Expand All @@ -274,19 +300,3 @@ class SampleActivity : ComponentActivity() {
}
}
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
YakcovTheme {
Greeting("Android")
}
}

0 comments on commit ee412c1

Please sign in to comment.