diff --git a/CHANGES.md b/CHANGES.md index 794f0e8e..46a78a95 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changelog +# 4.0.0 + +- Feat (breaking change): accept `HCaptcha.getClient(Activity)` for passive sitekeys. (#112) + # 3.11.0 - Fix: handle null `internalConfig` in args for HCaptchaDialogFragment (#140) diff --git a/README.md b/README.md index 9d91f857..6f3e4319 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,6 @@ See the code example below along with the possible customization to enable human invisible hcaptcha example - ## Usage There are multiple ways to run a hCaptcha human verification. See the below snippet for the overall flow. @@ -116,6 +115,11 @@ To remove a specific listener you may use `HCaptcha.removeOn[Success|Failure|Ope To remove all listeners you may use `HCaptcha.removeAllListener()`. +Note ⚠️: For any sitekey that can show visual challenges, `HCaptcha.getClient(Activity)` must be called with `FragmentActivity` instance. + +`Activity` is allowed only when `hideDialog=true` and sitekey setting is Passive (Enterprise feature). + + ```java ... OnSuccessListener firstListener = new OnSuccessListener() { @@ -265,19 +269,19 @@ You can add logic to gracefully handle the errors. The following is a list of possible error codes: -| Name | Code | Description | -|-------------------------------|------|----------------------------------------------------| -| `NETWORK_ERROR` | 7 | There is no internet connection. | -| `INVALID_DATA` | 8 | Invalid data is not accepted by endpoints. | -| `CHALLENGE_ERROR` | 9 | JS client encountered an error on challenge setup. | -| `INTERNAL_ERROR` | 10 | JS client encountered an internal error. | -| `SESSION_TIMEOUT` | 15 | The challenge expired. | -| `TOKEN_TIMEOUT` | 16 | The token expired. | -| `CHALLENGE_CLOSED` | 30 | The challenge was closed by the user. | -| `RATE_LIMITED` | 31 | Spam detected. | -| `INVALID_CUSTOM_THEME` | 32 | Invalid custom theme. | -| `INSECURE_HTTP_REQUEST_ERROR` | 33 | Insecure resource requested. | -| `ERROR` | 29 | General failure. | +| Name | Code | Description | +|--------------------------------|------|----------------------------------------------------| +| `NETWORK_ERROR` | 7 | There is no internet connection. | +| `INVALID_DATA` | 8 | Invalid data is not accepted by endpoints. | +| `CHALLENGE_ERROR` | 9 | JS client encountered an error on challenge setup. | +| `INTERNAL_ERROR` | 10 | JS client encountered an internal error. | +| `SESSION_TIMEOUT` | 15 | The challenge expired. | +| `TOKEN_TIMEOUT` | 16 | The token expired. | +| `CHALLENGE_CLOSED` | 30 | The challenge was closed by the user. | +| `RATE_LIMITED` | 31 | Spam detected. | +| `INVALID_CUSTOM_THEME` | 32 | Invalid custom theme. | +| `INSECURE_HTTP_REQUEST_ERROR` | 33 | Insecure resource requested. | +| `ERROR` | 29 | General failure. | ### Retry Failed Verification @@ -339,6 +343,12 @@ final HCaptchaConfig config = HCaptchaConfig.builder() .build(); ``` +> HCaptcha constantly failing with IllegalStateException "Visual Challenge verification require FragmentActivity", how to fix it? + +SDK expect to be initialized with `FragmentActivity` instance in regular scenario. + +In case if you use passive `siteKey` make sure that you called `hideDialog(true)` on `HCaptchaCconfig.builder()` + ## For maintainers If you plan to contribute to the repo, please see [MAINTAINERS.md](./MAINTAINERS.md) for detailed build, test, and release instructions. diff --git a/benchmark/src/androidTest/java/com/hcaptcha/sdk/TestHCaptchaVerifier.java b/benchmark/src/androidTest/java/com/hcaptcha/sdk/TestHCaptchaVerifier.java index a7559a18..2a4f4f6b 100644 --- a/benchmark/src/androidTest/java/com/hcaptcha/sdk/TestHCaptchaVerifier.java +++ b/benchmark/src/androidTest/java/com/hcaptcha/sdk/TestHCaptchaVerifier.java @@ -1,11 +1,11 @@ package com.hcaptcha.sdk; -import androidx.fragment.app.FragmentActivity; +import android.app.Activity; public class TestHCaptchaVerifier implements IHCaptchaVerifier { @Override - public void startVerification(FragmentActivity activity) { + public void startVerification(Activity activity) { // no implementation need for performance measurement } diff --git a/sdk/build.gradle b/sdk/build.gradle index 6e562061..95229952 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -24,11 +24,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 38 + versionCode 39 // version number visible to the user // should follow semantic versioning (See https://semver.org) - versionName "3.11.0" + versionName "4.0.0" buildConfigField 'String', 'VERSION_NAME', "\"${defaultConfig.versionName}_${defaultConfig.versionCode}\"" diff --git a/sdk/src/main/java/com/hcaptcha/sdk/HCaptcha.java b/sdk/src/main/java/com/hcaptcha/sdk/HCaptcha.java index 077516cf..8dc4ce02 100644 --- a/sdk/src/main/java/com/hcaptcha/sdk/HCaptcha.java +++ b/sdk/src/main/java/com/hcaptcha/sdk/HCaptcha.java @@ -1,5 +1,6 @@ package com.hcaptcha.sdk; +import android.app.Activity; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Bundle; @@ -14,7 +15,7 @@ public final class HCaptcha extends Task implements IHCap public static final String META_SITE_KEY = "com.hcaptcha.sdk.site-key"; @NonNull - private final FragmentActivity activity; + private final Activity activity; @Nullable private IHCaptchaVerifier captchaVerifier; @@ -25,7 +26,7 @@ public final class HCaptcha extends Task implements IHCap @NonNull private final HCaptchaInternalConfig internalConfig; - private HCaptcha(@NonNull final FragmentActivity activity, @NonNull final HCaptchaInternalConfig internalConfig) { + private HCaptcha(@NonNull final Activity activity, @NonNull final HCaptchaInternalConfig internalConfig) { this.activity = activity; this.internalConfig = internalConfig; } @@ -33,14 +34,15 @@ private HCaptcha(@NonNull final FragmentActivity activity, @NonNull final HCaptc /** * Constructs a new client which allows to display a challenge dialog * - * @param activity The current activity + * @param activity FragmentActivity instance for a visual challenge verification, + * or any Activity in case of passive siteKey * @return new {@link HCaptcha} object */ - public static HCaptcha getClient(@NonNull final FragmentActivity activity) { + public static HCaptcha getClient(@NonNull final Activity activity) { return new HCaptcha(activity, HCaptchaInternalConfig.builder().build()); } - static HCaptcha getClient(@NonNull final FragmentActivity activity, + static HCaptcha getClient(@NonNull final Activity activity, @NonNull HCaptchaInternalConfig internalConfig) { return new HCaptcha(activity, internalConfig); } @@ -100,9 +102,11 @@ void onFailure(final HCaptchaException exception) { .loading(false) .build(); captchaVerifier = new HCaptchaHeadlessWebView(activity, this.config, internalConfig, listener); - } else { + } else if (this.activity instanceof FragmentActivity) { captchaVerifier = HCaptchaDialogFragment.newInstance(inputConfig, internalConfig, listener); this.config = inputConfig; + } else { + throw new IllegalStateException("Visual hCaptcha challenge verification requires FragmentActivity."); } } catch (AndroidRuntimeException e) { listener.onFailure(new HCaptchaException(HCaptchaError.ERROR)); diff --git a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java index a9e137c6..18f8b518 100644 --- a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java +++ b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java @@ -239,8 +239,8 @@ public void onSuccess(final String token) { } @Override - public void startVerification(@NonNull FragmentActivity fragmentActivity) { - final FragmentManager fragmentManager = fragmentActivity.getSupportFragmentManager(); + public void startVerification(@NonNull Activity fragmentActivity) { + final FragmentManager fragmentManager = ((FragmentActivity) fragmentActivity).getSupportFragmentManager(); final Fragment oldFragment = fragmentManager.findFragmentByTag(HCaptchaDialogFragment.TAG); if (oldFragment != null && oldFragment.isAdded()) { HCaptchaLog.w("DialogFragment was already added."); diff --git a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaHeadlessWebView.java b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaHeadlessWebView.java index 98440458..4302754e 100644 --- a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaHeadlessWebView.java +++ b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaHeadlessWebView.java @@ -1,11 +1,11 @@ package com.hcaptcha.sdk; +import android.app.Activity; import android.os.Handler; import android.os.Looper; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; -import androidx.fragment.app.FragmentActivity; import lombok.Getter; import lombok.NonNull; @@ -25,7 +25,7 @@ final class HCaptchaHeadlessWebView implements IHCaptchaVerifier { private boolean shouldExecuteOnLoad; private boolean shouldResetOnLoad; - HCaptchaHeadlessWebView(@NonNull final FragmentActivity activity, + HCaptchaHeadlessWebView(@NonNull final Activity activity, @NonNull final HCaptchaConfig config, @NonNull final HCaptchaInternalConfig internalConfig, @NonNull final HCaptchaStateListener listener) { @@ -44,7 +44,7 @@ final class HCaptchaHeadlessWebView implements IHCaptchaVerifier { } @Override - public void startVerification(@NonNull FragmentActivity activity) { + public void startVerification(@NonNull Activity activity) { if (webViewLoaded) { // Safe to execute webViewHelper.resetAndExecute(); diff --git a/sdk/src/main/java/com/hcaptcha/sdk/IHCaptchaVerifier.java b/sdk/src/main/java/com/hcaptcha/sdk/IHCaptchaVerifier.java index 6b046f50..4370ed38 100644 --- a/sdk/src/main/java/com/hcaptcha/sdk/IHCaptchaVerifier.java +++ b/sdk/src/main/java/com/hcaptcha/sdk/IHCaptchaVerifier.java @@ -1,6 +1,6 @@ package com.hcaptcha.sdk; -import androidx.fragment.app.FragmentActivity; +import android.app.Activity; import com.hcaptcha.sdk.tasks.OnFailureListener; import com.hcaptcha.sdk.tasks.OnLoadedListener; @@ -17,7 +17,7 @@ interface IHCaptchaVerifier extends /** * Starts the human verification process. */ - void startVerification(@NonNull FragmentActivity activity); + void startVerification(@NonNull Activity activity); /** * Force stop verification and release resources. diff --git a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java index 1754eb8d..6c182af2 100644 --- a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java @@ -5,11 +5,15 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.app.Activity; +import android.os.Looper; + import androidx.test.core.app.ActivityScenario; import androidx.test.ext.junit.rules.ActivityScenarioRule; import com.hcaptcha.sdk.tasks.OnSuccessListener; import com.hcaptcha.sdk.test.TestActivity; +import com.hcaptcha.sdk.test.TestNonFragmentActivity; import org.junit.Rule; import org.junit.Test; @@ -121,4 +125,15 @@ public void e2eWithDebugTokenHeadlessWebView() throws Exception { assertTrue(latch.await(E2E_AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS)); } + + @Test(expected = IllegalStateException.class) + public void badActivity() { + Looper.prepare(); + final Activity activity = new TestNonFragmentActivity(); + + HCaptcha.getClient(activity) + .verifyWithHCaptcha(config.toBuilder().hideDialog(false).diagnosticLog(true).build()) + .addOnSuccessListener(response -> fail("No token expected")) + .addOnFailureListener(e -> fail("Wrong failure reason: " + e.getHCaptchaError())); + } } diff --git a/test/src/main/AndroidManifest.xml b/test/src/main/AndroidManifest.xml index d9151625..e6434d05 100644 --- a/test/src/main/AndroidManifest.xml +++ b/test/src/main/AndroidManifest.xml @@ -16,6 +16,15 @@ + + + + + + + diff --git a/test/src/main/java/com/hcaptcha/sdk/test/TestNonFragmentActivity.java b/test/src/main/java/com/hcaptcha/sdk/test/TestNonFragmentActivity.java new file mode 100644 index 00000000..44d596ee --- /dev/null +++ b/test/src/main/java/com/hcaptcha/sdk/test/TestNonFragmentActivity.java @@ -0,0 +1,4 @@ +package com.hcaptcha.sdk.test; + +public class TestNonFragmentActivity extends android.app.Activity { +} \ No newline at end of file