diff --git a/benchmark/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java b/benchmark/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java
index b58cc9bc..af813916 100644
--- a/benchmark/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java
+++ b/benchmark/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java
@@ -56,7 +56,6 @@ public void onLoaded() {
latch.countDown();
}
},
- new TestHCaptchaStateListener(),
webView
);
});
diff --git a/compose/build.gradle b/compose/build.gradle
index 8fe7d599..cfc3718e 100644
--- a/compose/build.gradle
+++ b/compose/build.gradle
@@ -2,14 +2,20 @@ plugins {
id 'maven-publish'
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
+ id "pmd"
+ id "jacoco"
+ id "checkstyle"
+ id "com.github.spotbugs" version "5.2.3"
+ id "org.owasp.dependencycheck" version "7.1.1"
+ id "org.sonarqube" version "3.4.0.2513"
}
android {
namespace 'com.hcaptcha.compose'
- compileSdk 33
+ compileSdk 34
defaultConfig {
- minSdk 16
+ minSdk 23
// See https://developer.android.com/studio/publish/versioning
// versionCode must be integer and be incremented by one for every new update
@@ -57,11 +63,17 @@ android {
dependencies {
implementation project(':sdk')
- implementation 'androidx.activity:activity-compose:1.8.2'
- implementation 'androidx.compose.ui:ui-tooling:1.6.1'
+ implementation "androidx.activity:activity-compose:$compose_version"
+ implementation "androidx.compose.ui:ui-tooling:$compose_version"
+ implementation 'androidx.compose.material3:material3:1.2.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-intents:3.5.1'
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4-android:$compose_version"
+ androidTestImplementation "androidx.compose.ui:ui:$compose_version"
+ androidTestImplementation "androidx.activity:activity-ktx:$compose_version"
+ androidTestImplementation "androidx.compose.foundation:foundation-layout-android:$compose_version"
}
project.afterEvaluate {
@@ -80,11 +92,11 @@ project.afterEvaluate {
pom {
name = 'Android Jetpack Compose SDK hCaptcha'
description = 'This SDK provides a wrapper for hCaptcha and ready to use Jetpack Compose Component'
- url = 'https://github.com/hCaptcha/hcaptcha-android-sdk'
+ url = 'https://github.com/hCaptcha/hcaptcha-jetpack-compose'
licenses {
license {
name = 'MIT License'
- url = 'https://github.com/hCaptcha/hcaptcha-android-sdk/blob/main/LICENSE'
+ url = 'https://github.com/hCaptcha/hcaptcha-jetpack-compose-sdk/blob/main/LICENSE'
}
}
developers {
@@ -105,4 +117,6 @@ project.afterEvaluate {
}
}
}
-}
\ No newline at end of file
+}
+
+apply from: "$rootProject.projectDir/gradle/shared/code-quality.gradle"
\ No newline at end of file
diff --git a/compose/src/androidTest/AndroidManifest.xml b/compose/src/androidTest/AndroidManifest.xml
new file mode 100644
index 00000000..1a3fd523
--- /dev/null
+++ b/compose/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/compose/src/androidTest/java/com/hcaptcha/sdk/compose/ExampleInstrumentedTest.kt b/compose/src/androidTest/java/com/hcaptcha/sdk/compose/ExampleInstrumentedTest.kt
deleted file mode 100644
index d2f4d684..00000000
--- a/compose/src/androidTest/java/com/hcaptcha/sdk/compose/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.hcaptcha.sdk.compose
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.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.getInstrumentation().targetContext
- assertEquals("com.hcaptcha.sdk.compose.test", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/compose/src/androidTest/java/com/hcaptcha/sdk/compose/HCaptchaComposeTest.kt b/compose/src/androidTest/java/com/hcaptcha/sdk/compose/HCaptchaComposeTest.kt
new file mode 100644
index 00000000..66ab4d4a
--- /dev/null
+++ b/compose/src/androidTest/java/com/hcaptcha/sdk/compose/HCaptchaComposeTest.kt
@@ -0,0 +1,77 @@
+package com.hcaptcha.sdk.compose
+
+import androidx.compose.ui.test.*
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.Text
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.hcaptcha.sdk.HCaptchaCompose
+import com.hcaptcha.sdk.HCaptchaConfig
+import com.hcaptcha.sdk.HCaptchaError
+import com.hcaptcha.sdk.HCaptchaResponse
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
+
+@RunWith(AndroidJUnit4::class)
+class HCaptchaComposeTest {
+ private val resultContentDescription = "HCaptchaResultString"
+ private val timeout = TimeUnit.SECONDS.toMillis(4)
+
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ fun setContent(token: String = "10000000-ffff-ffff-ffff-000000000001") {
+ composeTestRule.setContent {
+ var text by remember { mutableStateOf("") }
+ Column {
+ Text(text = text, modifier = Modifier.semantics { contentDescription = resultContentDescription })
+
+ HCaptchaCompose(HCaptchaConfig
+ .builder()
+ .siteKey(token)
+ .build()) { result ->
+ when (result) {
+ is HCaptchaResponse.Success -> {
+ text = result.token
+ }
+ is HCaptchaResponse.Failure -> {
+ text = result.error.name
+ }
+ else -> {}
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun validToken() {
+ setContent()
+
+ runBlocking { delay(timeout) }
+
+ composeTestRule.onNodeWithContentDescription(resultContentDescription)
+ .assertTextEquals("10000000-aaaa-bbbb-cccc-000000000001")
+ }
+
+ @Test
+ fun invalidToken() {
+ setContent("bad-baad-token")
+
+ runBlocking { delay(timeout) }
+
+ composeTestRule.onNodeWithContentDescription(resultContentDescription)
+ .assertTextEquals(HCaptchaError.INVALID_DATA.name)
+ }
+}
\ No newline at end of file
diff --git a/example-app/build.gradle b/example-app/build.gradle
index 1ee2bec2..9a8a1883 100644
--- a/example-app/build.gradle
+++ b/example-app/build.gradle
@@ -16,7 +16,7 @@ android {
namespace 'com.hcaptcha.example'
defaultConfig {
- minSdkVersion 21
+ minSdkVersion 23
targetSdkVersion intProp("exampleTargetSdkVersion", 34)
versionCode 1
versionName "0.0.1"
@@ -61,12 +61,12 @@ dependencies {
implementation "com.google.android.flexbox:flexbox:3.0.0"
implementation project(path: ':sdk')
- implementation 'androidx.compose.ui:ui:1.6.1'
- implementation 'androidx.compose.material3:material3:1.2.0'
+ implementation "androidx.compose.ui:ui:$compose_version"
+ implementation 'androidx.compose.material3:material3:1.2.1'
implementation 'androidx.activity:activity-ktx:1.8.2'
implementation 'androidx.activity:activity-compose:1.8.2'
implementation project(path: ':compose')
- implementation 'androidx.compose.foundation:foundation-layout-android:1.6.0'
+ implementation "androidx.compose.foundation:foundation-layout-android:$compose_version"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
diff --git a/example-app/src/main/java/com/hcaptcha/example/ComposeActivity.kt b/example-app/src/main/java/com/hcaptcha/example/ComposeActivity.kt
index 83d2214a..73c05234 100644
--- a/example-app/src/main/java/com/hcaptcha/example/ComposeActivity.kt
+++ b/example-app/src/main/java/com/hcaptcha/example/ComposeActivity.kt
@@ -56,6 +56,7 @@ class ComposeActivity : ComponentActivity() {
if (hCaptchaVisible) {
HCaptchaCompose(HCaptchaConfig
.builder()
+ .siteKey("10000000-ffff-ffff-ffff-000000000001")
.build()) { result ->
when (result) {
is HCaptchaResponse.Success -> {
diff --git a/gradle.properties b/gradle.properties
index 19cfd6f6..2201abbc 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,5 +20,5 @@ android.enableJetifier=true
# To test more aggressive optimizations
android.enableR8.fullMode=true
# Kotline version
-kotlin_version=1.9.0
-compose_version=1.5.2
\ No newline at end of file
+kotlin_version=1.9.10
+compose_version=1.5.3
\ No newline at end of file
diff --git a/gradle/shared/code-quality.gradle b/gradle/shared/code-quality.gradle
new file mode 100644
index 00000000..efe77442
--- /dev/null
+++ b/gradle/shared/code-quality.gradle
@@ -0,0 +1,108 @@
+checkstyle {
+ toolVersion = '8.45.1'
+}
+
+task checkstyle(type: Checkstyle) {
+ description 'Check code standard'
+ group 'verification'
+ configFile file("${rootDir}/gradle/config/checkstyle.xml")
+ source 'src'
+ include '**/*.java'
+ exclude '**/gen/**'
+ classpath = files()
+ ignoreFailures = false
+ maxWarnings = 0
+}
+
+pmd {
+ consoleOutput = true
+ toolVersion = "6.51.0"
+}
+
+task pmd(type: Pmd) {
+ ruleSetFiles = files("${project.rootDir}/gradle/config/pmd.xml")
+ ignoreFailures = false
+ ruleSets = []
+ source 'src'
+ include '**/*.java'
+ exclude '**/gen/**'
+ reports {
+ xml.required = false
+ xml.outputLocation = file("${project.buildDir}/reports/pmd/pmd.xml")
+ html.required = true
+ html.outputLocation = file("$project.buildDir/outputs/pmd/pmd.html")
+ }
+}
+
+spotbugs {
+ ignoreFailures = false
+ showStackTraces = true
+ showProgress = false
+ reportLevel = 'high'
+ excludeFilter = file("${project.rootDir}/gradle/config/findbugs-exclude.xml")
+ onlyAnalyze = ['com.hcaptcha.sdk.*']
+ projectName = name
+ release = version
+}
+
+// enable html report
+gradle.taskGraph.beforeTask { task ->
+ if (task.name.toLowerCase().contains('spotbugs')) {
+ task.reports {
+ html.enabled true
+ xml.enabled true
+ }
+ }
+}
+
+// https://www.rallyhealth.com/coding/code-coverage-for-android-testing
+task jacocoUnitTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
+ def coverageSourceDirs = [
+ "src/main/java"
+ ]
+ def javaClasses = fileTree(
+ dir: "${project.buildDir}/intermediates/javac/debug/classes",
+ excludes: [
+ '**/R.class',
+ '**/R$*.class',
+ '**/BuildConfig.*',
+ '**/Manifest*.*'
+ ]
+ )
+
+ classDirectories.from files([javaClasses])
+ additionalSourceDirs.from files(coverageSourceDirs)
+ sourceDirectories.from files(coverageSourceDirs)
+ executionData.from = "${project.buildDir}/jacoco/testDebugUnitTest.exec"
+
+ reports {
+ xml.required = true
+ html.required = true
+ }
+}
+
+check.dependsOn('checkstyle', 'pmd', 'jacocoUnitTestReport')
+
+sonarqube {
+ properties {
+ property "sonar.projectKey", "hCaptcha_hcaptcha-android-sdk"
+ property "sonar.organization", "hcaptcha"
+ property "sonar.host.url", "https://sonarcloud.io"
+
+ property "sonar.language", "java"
+ property "sonar.sourceEncoding", "utf-8"
+
+ property "sonar.sources", "src/main"
+ property "sonar.java.binaries", "${project.buildDir}/intermediates/javac/debug/classes"
+ property "sonar.tests", ["src/test/", "../test/src/androidTest/"]
+
+ property "sonar.android.lint.report", "${project.buildDir}/outputs/lint-results.xml"
+ property "sonar.java.spotbugs.reportPaths", ["${project.buildDir}/reports/spotbugs/debug.xml", "${project.buildDir}/reports/spotbugs/release.xml"]
+ property "sonar.java.pmd.reportPaths", "${project.buildDir}/reports/pmd/pmd.xml"
+ property "sonar.java.checkstyle.reportPaths", "${project.buildDir}/reports/checkstyle/checkstyle.xml"
+ property "sonar.coverage.jacoco.xmlReportPaths", "${project.buildDir}/reports/jacoco/jacocoUnitTestReport.xml"
+ }
+}
+
+project.tasks["sonarqube"].dependsOn "check"
+
diff --git a/sdk/build.gradle b/sdk/build.gradle
index 95229952..c8f9f4ca 100644
--- a/sdk/build.gradle
+++ b/sdk/build.gradle
@@ -1,9 +1,9 @@
plugins {
id "com.android.library"
+ id "maven-publish"
id "pmd"
id "jacoco"
id "checkstyle"
- id "maven-publish"
id "com.github.spotbugs" version "5.2.3"
id "org.owasp.dependencycheck" version "7.1.1"
id "org.sonarqube" version "3.4.0.2513"
@@ -180,111 +180,4 @@ android.libraryVariants.all { variant ->
variant.registerJavaGeneratingTask(generateTask, outputDir)
}
-checkstyle {
- toolVersion = '8.45.1'
-}
-
-task checkstyle(type: Checkstyle) {
- description 'Check code standard'
- group 'verification'
- configFile file("${rootDir}/gradle/config/checkstyle.xml")
- source 'src'
- include '**/*.java'
- exclude '**/gen/**'
- classpath = files()
- ignoreFailures = false
- maxWarnings = 0
-}
-
-pmd {
- consoleOutput = true
- toolVersion = "6.51.0"
-}
-
-task pmd(type: Pmd) {
- ruleSetFiles = files("${project.rootDir}/gradle/config/pmd.xml")
- ignoreFailures = false
- ruleSets = []
- source 'src'
- include '**/*.java'
- exclude '**/gen/**'
- reports {
- xml.required = false
- xml.outputLocation = file("${project.buildDir}/reports/pmd/pmd.xml")
- html.required = true
- html.outputLocation = file("$project.buildDir/outputs/pmd/pmd.html")
- }
-}
-
-spotbugs {
- ignoreFailures = false
- showStackTraces = true
- showProgress = false
- reportLevel = 'high'
- excludeFilter = file("${project.rootDir}/gradle/config/findbugs-exclude.xml")
- onlyAnalyze = ['com.hcaptcha.sdk.*']
- projectName = name
- release = version
-}
-
-// enable html report
-gradle.taskGraph.beforeTask { task ->
- if (task.name.toLowerCase().contains('spotbugs')) {
- task.reports {
- html.enabled true
- xml.enabled true
- }
- }
-}
-
-// https://www.rallyhealth.com/coding/code-coverage-for-android-testing
-task jacocoUnitTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
- def coverageSourceDirs = [
- "src/main/java"
- ]
- def javaClasses = fileTree(
- dir: "${project.buildDir}/intermediates/javac/debug/classes",
- excludes: [
- '**/R.class',
- '**/R$*.class',
- '**/BuildConfig.*',
- '**/Manifest*.*'
- ]
- )
-
- classDirectories.from files([javaClasses])
- additionalSourceDirs.from files(coverageSourceDirs)
- sourceDirectories.from files(coverageSourceDirs)
- executionData.from = "${project.buildDir}/jacoco/testDebugUnitTest.exec"
-
- reports {
- xml.required = true
- html.required = true
- }
-}
-
-check.dependsOn('checkstyle', 'pmd', 'jacocoUnitTestReport')
-
-sonarqube {
- properties {
- property "sonar.projectKey", "hCaptcha_hcaptcha-android-sdk"
- property "sonar.organization", "hcaptcha"
- property "sonar.host.url", "https://sonarcloud.io"
-
- property "sonar.language", "java"
- property "sonar.sourceEncoding", "utf-8"
-
- property "sonar.sources", "src/main"
- property "sonar.java.binaries", "${project.buildDir}/intermediates/javac/debug/classes"
- property "sonar.tests", ["src/test/", "../test/src/androidTest/"]
-
- property "sonar.android.lint.report", "${project.buildDir}/outputs/lint-results.xml"
- property "sonar.java.spotbugs.reportPaths", ["${project.buildDir}/reports/spotbugs/debug.xml", "${project.buildDir}/reports/spotbugs/release.xml"]
- property "sonar.java.pmd.reportPaths", "${project.buildDir}/reports/pmd/pmd.xml"
- property "sonar.java.checkstyle.reportPaths", "${project.buildDir}/reports/checkstyle/checkstyle.xml"
- property "sonar.coverage.jacoco.xmlReportPaths", "${project.buildDir}/reports/jacoco/jacocoUnitTestReport.xml"
- }
-}
-
-project.tasks["sonarqube"].dependsOn "check"
-
+apply from: "$rootProject.projectDir/gradle/shared/code-quality.gradle"
\ No newline at end of file
diff --git a/sdk/src/test/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java b/sdk/src/test/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java
index dd7458b9..18b2c1f2 100644
--- a/sdk/src/test/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java
+++ b/sdk/src/test/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java
@@ -41,9 +41,6 @@ public class HCaptchaWebViewHelperTest {
@Mock
IHCaptchaVerifier captchaVerifier;
- @Mock
- HCaptchaStateListener stateListener;
-
@Mock
HCaptchaWebView webView;
@@ -62,7 +59,6 @@ public class HCaptchaWebViewHelperTest {
public void init() {
MockitoAnnotations.openMocks(this);
androidLogMock = mockStatic(Log.class);
- stateListener = mock(HCaptchaStateListener.class);
webView = mock(HCaptchaWebView.class);
webSettings = mock(WebSettings.class);
htmlProvider = mock(IHCaptchaHtmlProvider.class);
@@ -79,7 +75,7 @@ public void release() {
@Test
public void test_constructor() {
new HCaptchaWebViewHelper(handler, context, config, internalConfig, captchaVerifier,
- stateListener, webView);
+ webView);
verify(webView).loadDataWithBaseURL(null, MOCK_HTML, "text/html", "UTF-8", null);
verify(webView, times(2)).addJavascriptInterface(any(), anyString());
}
@@ -87,7 +83,7 @@ public void test_constructor() {
@Test
public void test_destroy() {
final HCaptchaWebViewHelper webViewHelper = new HCaptchaWebViewHelper(handler, context, config,
- internalConfig, captchaVerifier, stateListener, webView);
+ internalConfig, captchaVerifier, webView);
final ViewGroup viewParent = mock(ViewGroup.class, withSettings().extraInterfaces(ViewParent.class));
when(webView.getParent()).thenReturn(viewParent);
webViewHelper.destroy();
@@ -98,7 +94,7 @@ public void test_destroy() {
@Test
public void test_destroy_webview_parent_null() {
final HCaptchaWebViewHelper webViewHelper = new HCaptchaWebViewHelper(handler, context, config,
- internalConfig, captchaVerifier, stateListener, webView);
+ internalConfig, captchaVerifier, webView);
webViewHelper.destroy();
}
@@ -106,8 +102,7 @@ public void test_destroy_webview_parent_null() {
public void test_config_host_pased() {
final String host = "https://my.awesome.host";
when(config.getHost()).thenReturn(host);
- new HCaptchaWebViewHelper(handler, context, config, internalConfig, captchaVerifier,
- stateListener, webView);
+ new HCaptchaWebViewHelper(handler, context, config, internalConfig, captchaVerifier, webView);
verify(webView).loadDataWithBaseURL(host, MOCK_HTML, "text/html", "UTF-8", null);
}
}
diff --git a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java
index fb7b1017..c22d4bb3 100644
--- a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java
+++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaWebViewHelperTest.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
@@ -43,17 +44,36 @@ public void testInsecureHttpRequestErrorHandling() throws Exception {
final Handler handler = new Handler(Looper.getMainLooper());
final CountDownLatch failureLatch = new CountDownLatch(1);
final HCaptchaConfig config = baseConfig.toBuilder().host("http://localhost").build();
- final IHCaptchaVerifier verifier = mock(IHCaptchaVerifier.class);
- final HCaptchaStateListener listener = new HCaptchaStateTestAdapter() {
+ final IHCaptchaVerifier verifier = new IHCaptchaVerifier() {
@Override
- void onSuccess(String token) {
+ public void onOpen() {
failAsNonReachable();
}
@Override
- void onFailure(HCaptchaException e) {
+ public void onLoaded() {
+ failAsNonReachable();
+ }
+
+ @Override
+ public void startVerification(Activity activity) {
+ failAsNonReachable();
+ }
+
+ @Override
+ public void reset() {
+ failAsNonReachable();
+ }
+
+ @Override
+ public void onSuccess(String token) {
+ failAsNonReachable();
+ }
+
+ @Override
+ public void onFailure(HCaptchaException e) {
assertEquals(HCaptchaError.INSECURE_HTTP_REQUEST_ERROR, e.getHCaptchaError());
assertEquals("Insecure resource http://localhost/favicon.ico requested", e.getMessage());
failureLatch.countDown();
@@ -64,7 +84,7 @@ void onFailure(HCaptchaException e) {
scenario.onActivity(activity -> {
HCaptchaWebView webView = new HCaptchaWebView(activity);
final HCaptchaWebViewHelper helper = new HCaptchaWebViewHelper(
- handler, activity, config, internalConfig, verifier, listener, webView);
+ handler, activity, config, internalConfig, verifier, webView);
});
assertTrue(failureLatch.await(AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS));