Skip to content

Commit

Permalink
feat(sdk)!: allow passing Activity for passive sitekeys (#146)
Browse files Browse the repository at this point in the history
  • Loading branch information
CAMOBAP authored Mar 5, 2024
1 parent 87fa961 commit 44f1447
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 31 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
38 changes: 24 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ See the code example below along with the possible customization to enable human

<img src="/assets/hcaptcha-invisible-example.gif" alt="invisible hcaptcha example" width="300px"/>


## Usage

There are multiple ways to run a hCaptcha human verification. See the below snippet for the overall flow.
Expand Down Expand Up @@ -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<HCaptchaTokenResponse> firstListener = new OnSuccessListener<HCaptchaTokenResponse>() {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Original file line number Diff line number Diff line change
@@ -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
}

Expand Down
4 changes: 2 additions & 2 deletions sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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}\""

Expand Down
16 changes: 10 additions & 6 deletions sdk/src/main/java/com/hcaptcha/sdk/HCaptcha.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,7 +15,7 @@ public final class HCaptcha extends Task<HCaptchaTokenResponse> 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;
Expand All @@ -25,22 +26,23 @@ public final class HCaptcha extends Task<HCaptchaTokenResponse> 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;
}

/**
* 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);
}
Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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) {
Expand All @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions sdk/src/main/java/com/hcaptcha/sdk/IHCaptchaVerifier.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.
Expand Down
15 changes: 15 additions & 0 deletions test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()));
}
}
9 changes: 9 additions & 0 deletions test/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.hcaptcha.sdk.test.TestNonFragmentActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="androidx.fragment.app.testing.EmptyFragmentActivity"
android:exported="true">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.hcaptcha.sdk.test;

public class TestNonFragmentActivity extends android.app.Activity {
}

0 comments on commit 44f1447

Please sign in to comment.