Skip to content

Commit

Permalink
feat: preload webview (#175)
Browse files Browse the repository at this point in the history
* feat: create WebView before xml inflation

* feat: compose-sdk wrap HCaptchaWebView into remember

* chore: upgrade com.android.tools.build:gradle:8.7.3

* chore: bump version 4.1.0

* chore: make log statements follow format "identifier: message"

Co-authored-by: Sergiu D. <danalachi.sergiu@gmail.com>

---------

Co-authored-by: Sergiu D. <danalachi.sergiu@gmail.com>
  • Loading branch information
CAMOBAP and DSergiu authored Feb 17, 2025
1 parent c83aad6 commit a173a1a
Show file tree
Hide file tree
Showing 22 changed files with 223 additions and 130 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: 'HTML ES5 test'
run: |
npm install -g jshint
java -cp sdk/build/intermediates/javac/release/classes com.hcaptcha.sdk.HCaptchaHtml > sdk/build/hcaptcha-form.html
java -cp sdk/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes com.hcaptcha.sdk.HCaptchaHtml > sdk/build/hcaptcha-form.html
jshint --extract=always sdk/build/hcaptcha-form.html
- name: 'JitPack Test'
run: ./gradlew publishReleasePublicationToMavenLocal
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

# 4.1.0

- Feat: preload WebView on `setup` call

# 4.0.5

- compose-sdk: set minSdk to 21
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import androidx.annotation.NonNull;

import com.hcaptcha.sdk.IHCaptchaHtmlProvider;

class TestHCaptchaHtml implements IHCaptchaHtmlProvider {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import android.app.Activity;

import androidx.annotation.NonNull;

public class TestHCaptchaVerifier implements IHCaptchaVerifier {

@Override
public void startVerification(Activity activity) {
public void startVerification(@NonNull Activity activity) {
// no implementation need for performance measurement
}

Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.4'
classpath 'androidx.benchmark:benchmark-gradle-plugin:1.2.4'
classpath 'com.android.tools.build:gradle:8.7.3'
classpath 'androidx.benchmark:benchmark-gradle-plugin:1.3.3'
classpath 'com.slack.keeper:keeper:0.16.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
Expand Down
4 changes: 2 additions & 2 deletions compose-sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ android {
// See https://developer.android.com/studio/publish/versioning
// versionCode must be integer and be incremented by one for every new update
// android system uses this to prevent downgrades
versionCode 44
versionCode 45

// version number visible to the user
// should follow semantic versioning (See https://semver.org)
versionName "4.0.5"
versionName "4.1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
Expand Down
74 changes: 16 additions & 58 deletions compose-sdk/src/main/java/com/hcaptcha/sdk/HCaptchaCompose.kt
Original file line number Diff line number Diff line change
@@ -1,74 +1,43 @@
package com.hcaptcha.sdk

import android.app.Activity
import android.os.Handler
import android.os.Looper
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties

@Composable
public fun HCaptchaCompose(config: HCaptchaConfig, onResult: (HCaptchaResponse) -> Unit) {
val handler = Handler(Looper.getMainLooper())
var helper: HCaptchaWebViewHelper? = null
val verifier = object : IHCaptchaVerifier {
override fun onLoaded() {
onResult(HCaptchaResponse.Event(HCaptchaEvent.Loaded))
if (config.hideDialog) {
helper?.let {
it.resetAndExecute()
} ?: run {
HCaptchaLog.w("HCaptchaWebViewHelper wasn't created, report but to developer")
onResult(HCaptchaResponse.Failure(HCaptchaError.INTERNAL_ERROR))
}
}
}

override fun onOpen() {
onResult(HCaptchaResponse.Event(HCaptchaEvent.Opened))
}

override fun onSuccess(result: String) {
onResult(HCaptchaResponse.Success(result))
}

override fun onFailure(exception: HCaptchaException) {
onResult(HCaptchaResponse.Failure(exception.hCaptchaError))
}
HCaptchaLog.sDiagnosticsLogEnabled = config.diagnosticLog

override fun startVerification(activity: Activity) {
error("startVerification should never be reached")
}
val context = LocalContext.current
val handler = Handler(Looper.getMainLooper())
val internalConfig = HCaptchaInternalConfig(com.hcaptcha.sdk.HCaptchaHtml())

override fun reset() {
error("reset should never be reached")
val helper = remember { mutableStateOf<HCaptchaWebViewHelper?>(null) }
val verifier = remember { HCaptchaComposeVerifier(config, onResult, helper) }
val preloadedWebView = remember {
HCaptchaWebView(context).apply {
helper.value = HCaptchaWebViewHelper(
handler, context, config, internalConfig, verifier, this
)
}
}
val internalConfig = HCaptchaInternalConfig(com.hcaptcha.sdk.HCaptchaHtml())
HCaptchaLog.sDiagnosticsLogEnabled = config.diagnosticLog

HCaptchaLog.d("HCaptchaCompose($config)")

if (config.hideDialog) {
AndroidView(
modifier = Modifier.size(0.dp),
factory = { context ->
HCaptchaWebView(context).apply {
helper = HCaptchaWebViewHelper(
handler,
context,
config,
internalConfig,
verifier,
this
)
}
}
factory = { preloadedWebView }
)
} else {
Dialog(
Expand All @@ -77,18 +46,7 @@ public fun HCaptchaCompose(config: HCaptchaConfig, onResult: (HCaptchaResponse)
) {
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
HCaptchaWebView(context).apply {
helper = HCaptchaWebViewHelper(
handler,
context,
config,
internalConfig,
verifier,
this
)
}
}
factory = { preloadedWebView }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.hcaptcha.sdk

import android.app.Activity
import androidx.compose.runtime.State

internal class HCaptchaComposeVerifier(
private val config: HCaptchaConfig,
private val onResult: (HCaptchaResponse) -> Unit,
private val helperState: State<HCaptchaWebViewHelper?>
) : IHCaptchaVerifier {

override fun onLoaded() {
onResult(HCaptchaResponse.Event(HCaptchaEvent.Loaded))
if (config.hideDialog) {
helperState.value?.resetAndExecute() ?: run {
HCaptchaLog.w("HCaptchaWebViewHelper wasn't created, report but to developer")
onResult(HCaptchaResponse.Failure(HCaptchaError.INTERNAL_ERROR))
}
}
}

override fun onOpen() {
onResult(HCaptchaResponse.Event(HCaptchaEvent.Opened))
}

override fun onSuccess(result: String) {
onResult(HCaptchaResponse.Success(result))
}

override fun onFailure(exception: HCaptchaException) {
onResult(HCaptchaResponse.Failure(exception.hCaptchaError))
}

override fun startVerification(activity: Activity) {
error("startVerification should never be reached")
}

override fun reset() {
error("reset should never be reached")
}
}
2 changes: 1 addition & 1 deletion example-compose-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ android {
namespace 'com.hcaptcha.example.compose'

defaultConfig {
minSdkVersion 23
minSdkVersion 21
targetSdkVersion intProp("exampleTargetSdkVersion", 35)
versionCode 1
versionName "0.0.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.hcaptcha.sdk.HCaptchaCompose
import com.hcaptcha.sdk.HCaptchaConfig
import com.hcaptcha.sdk.HCaptchaEvent
import com.hcaptcha.sdk.HCaptchaResponse
import com.hcaptcha.sdk.HCaptchaSize

class ComposeActivity : ComponentActivity() {

Expand All @@ -33,7 +34,9 @@ class ComposeActivity : ComponentActivity() {
var text by remember { mutableStateOf("") }

Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
modifier = Modifier.fillMaxSize()
.padding(WindowInsets.systemBars.asPaddingValues())
.padding(16.dp),
verticalArrangement = Arrangement.Bottom
) {
TextField(
Expand Down Expand Up @@ -91,6 +94,7 @@ class ComposeActivity : ComponentActivity() {
HCaptchaCompose(HCaptchaConfig
.builder()
.siteKey("10000000-ffff-ffff-ffff-000000000001")
.size(if (hideDialog) HCaptchaSize.INVISIBLE else HCaptchaSize.NORMAL)
.hideDialog(hideDialog)
.diagnosticLog(true)
.build()) { result ->
Expand Down
23 changes: 13 additions & 10 deletions gradle/shared/code-quality.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ checkstyle {
toolVersion = '8.45.1'
}

task checkstyle(type: Checkstyle) {
tasks.register('checkstyle', Checkstyle) {
description 'Check code standard'
group 'verification'
configFile file("${rootDir}/gradle/config/checkstyle.xml")
Expand All @@ -19,7 +19,7 @@ pmd {
toolVersion = "6.51.0"
}

task pmd(type: Pmd) {
tasks.register('pmd', Pmd) {
ruleSetFiles = files("${project.rootDir}/gradle/config/pmd.xml")
ignoreFailures = false
ruleSets = []
Expand Down Expand Up @@ -56,7 +56,8 @@ gradle.taskGraph.beforeTask { task ->
}

// https://www.rallyhealth.com/coding/code-coverage-for-android-testing
task jacocoUnitTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
tasks.register('jacocoUnitTestReport', JacocoReport) {
dependsOn['testDebugUnitTest']
def coverageSourceDirs = [
"src/main/java"
]
Expand All @@ -79,6 +80,8 @@ task jacocoUnitTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest'])
xml.required = true
html.required = true
}

inputs.files(tasks.named("testDebugUnitTest").get().outputs)
}

check.dependsOn('checkstyle', 'pmd', 'jacocoUnitTestReport')
Expand All @@ -93,16 +96,16 @@ sonarqube {
property "sonar.sourceEncoding", "utf-8"

property "sonar.sources", "src/main"
property "sonar.java.binaries", "${project.buildDir}/intermediates/javac/debug/classes"
property "sonar.java.binaries", layout.buildDirectory.dir("intermediates/javac/debug/compileDebugJavaWithJavac/classes").get().asFile.absolutePath
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"
property "sonar.android.lint.report", layout.buildDirectory.dir("outputs/lint-results.xml").get().asFile.absolutePath
property "sonar.java.spotbugs.reportPaths", ["debug", "release"].collect { layout.buildDirectory.dir("reports/spotbugs/${it}.xml").get().asFile.absolutePath }
property "sonar.java.pmd.reportPaths", layout.buildDirectory.dir("reports/pmd/pmd.xml").get().asFile.absolutePath
property "sonar.java.checkstyle.reportPaths", layout.buildDirectory.dir("reports/checkstyle/checkstyle.xml").get().asFile.absolutePath
property "sonar.coverage.jacoco.xmlReportPaths", layout.buildDirectory.dir("reports/jacoco/jacocoUnitTestReport.xml").get().asFile.absolutePath
}
}

project.tasks["sonarqube"].dependsOn "check"
project.tasks.named("sonarqube").configure { dependsOn "check" }

6 changes: 3 additions & 3 deletions gradle/shared/html-java-gen.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
android.libraryVariants.all { variant ->
def packageName = android.namespace
def variantName = variant.name.capitalize()
def outputDir = file("${project.buildDir}/generated/source/hcaptcha/${variant.name}/${packageName.replaceAll('\\.', '/')}")
def generateTask = project.task("generate${variantName}JavaClassFromStaticHtml") {
def outputDir = layout.buildDirectory.dir("generated/source/hcaptcha/${variant.name}/${packageName.replaceAll('\\.', '/')}").get().asFile
def generateTask = tasks.register("generate${variantName}JavaClassFromStaticHtml") {
group 'Generate'
description "Generate HTML java class"

Expand All @@ -27,5 +27,5 @@ android.libraryVariants.all { variant ->
}

// preBuild.dependsOn generateTask
variant.registerJavaGeneratingTask(generateTask, outputDir)
variant.registerJavaGeneratingTask(generateTask.get(), outputDir)
}
8 changes: 5 additions & 3 deletions gradle/shared/size-check.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
android.libraryVariants.all { variant ->
def variantName = variant.name.capitalize()
project.task("report${variantName}AarSize") {
tasks.register("report${variantName}AarSize") {
group 'Help'
description "Report ${variant.name} AAR size"
dependsOn variant.packageLibraryProvider
Expand All @@ -12,7 +12,7 @@ android.libraryVariants.all { variant ->
}
}

project.tasks.findByName("check").dependsOn(project.task("check${variantName}AarSize") {
tasks.register("check${variantName}AarSize") {
group 'Verification'
description "Checks ${variant.name} AAR size doesn't exceed ${project.ext}Kb"
dependsOn variant.packageLibraryProvider
Expand All @@ -24,5 +24,7 @@ android.libraryVariants.all { variant ->
throw new GradleException("${aarPath} size exceeded! ${aarSizeKb}Kbyte > ${MAX_AAR_SIZE_KB}Kbyte")
}
}
})
}

tasks.named("check").configure { dependsOn "check${variantName}AarSize" }
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
4 changes: 2 additions & 2 deletions sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ android {
// See https://developer.android.com/studio/publish/versioning
// versionCode must be integer and be incremented by one for every new update
// android system uses this to prevent downgrades
versionCode 44
versionCode 45

// version number visible to the user
// should follow semantic versioning (See https://semver.org)
versionName "4.0.5"
versionName "4.1.0"

buildConfigField 'String', 'VERSION_NAME', "\"${defaultConfig.versionName}_${defaultConfig.versionCode}\""

Expand Down
2 changes: 1 addition & 1 deletion sdk/src/main/java/com/hcaptcha/sdk/HCaptcha.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ void onFailure(final HCaptchaException exception) {
.build();
captchaVerifier = new HCaptchaHeadlessWebView(activity, this.config, internalConfig, listener);
} else if (this.activity instanceof FragmentActivity) {
captchaVerifier = HCaptchaDialogFragment.newInstance(inputConfig, internalConfig, listener);
captchaVerifier = HCaptchaDialogFragment.newInstance(activity, inputConfig, internalConfig, listener);
this.config = inputConfig;
} else {
throw new IllegalStateException("Visual hCaptcha challenge verification requires FragmentActivity.");
Expand Down
Loading

0 comments on commit a173a1a

Please sign in to comment.