Skip to content

Commit 44f1447

Browse files
authored
feat(sdk)!: allow passing Activity for passive sitekeys (#146)
1 parent 87fa961 commit 44f1447

File tree

11 files changed

+77
-31
lines changed

11 files changed

+77
-31
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
# 4.0.0
4+
5+
- Feat (breaking change): accept `HCaptcha.getClient(Activity)` for passive sitekeys. (#112)
6+
37
# 3.11.0
48

59
- Fix: handle null `internalConfig` in args for HCaptchaDialogFragment (#140)

README.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ See the code example below along with the possible customization to enable human
3939

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

42-
4342
## Usage
4443

4544
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
116115
117116
To remove all listeners you may use `HCaptcha.removeAllListener()`.
118117
118+
Note ⚠️: For any sitekey that can show visual challenges, `HCaptcha.getClient(Activity)` must be called with `FragmentActivity` instance.
119+
120+
`Activity` is allowed only when `hideDialog=true` and sitekey setting is Passive (Enterprise feature).
121+
122+
119123
```java
120124
...
121125
OnSuccessListener<HCaptchaTokenResponse> firstListener = new OnSuccessListener<HCaptchaTokenResponse>() {
@@ -265,19 +269,19 @@ You can add logic to gracefully handle the errors.
265269
266270
The following is a list of possible error codes:
267271
268-
| Name | Code | Description |
269-
|-------------------------------|------|----------------------------------------------------|
270-
| `NETWORK_ERROR` | 7 | There is no internet connection. |
271-
| `INVALID_DATA` | 8 | Invalid data is not accepted by endpoints. |
272-
| `CHALLENGE_ERROR` | 9 | JS client encountered an error on challenge setup. |
273-
| `INTERNAL_ERROR` | 10 | JS client encountered an internal error. |
274-
| `SESSION_TIMEOUT` | 15 | The challenge expired. |
275-
| `TOKEN_TIMEOUT` | 16 | The token expired. |
276-
| `CHALLENGE_CLOSED` | 30 | The challenge was closed by the user. |
277-
| `RATE_LIMITED` | 31 | Spam detected. |
278-
| `INVALID_CUSTOM_THEME` | 32 | Invalid custom theme. |
279-
| `INSECURE_HTTP_REQUEST_ERROR` | 33 | Insecure resource requested. |
280-
| `ERROR` | 29 | General failure. |
272+
| Name | Code | Description |
273+
|--------------------------------|------|----------------------------------------------------|
274+
| `NETWORK_ERROR` | 7 | There is no internet connection. |
275+
| `INVALID_DATA` | 8 | Invalid data is not accepted by endpoints. |
276+
| `CHALLENGE_ERROR` | 9 | JS client encountered an error on challenge setup. |
277+
| `INTERNAL_ERROR` | 10 | JS client encountered an internal error. |
278+
| `SESSION_TIMEOUT` | 15 | The challenge expired. |
279+
| `TOKEN_TIMEOUT` | 16 | The token expired. |
280+
| `CHALLENGE_CLOSED` | 30 | The challenge was closed by the user. |
281+
| `RATE_LIMITED` | 31 | Spam detected. |
282+
| `INVALID_CUSTOM_THEME` | 32 | Invalid custom theme. |
283+
| `INSECURE_HTTP_REQUEST_ERROR` | 33 | Insecure resource requested. |
284+
| `ERROR` | 29 | General failure. |
281285
282286
### Retry Failed Verification
283287
@@ -339,6 +343,12 @@ final HCaptchaConfig config = HCaptchaConfig.builder()
339343
.build();
340344
```
341345
346+
> HCaptcha constantly failing with IllegalStateException "Visual Challenge verification require FragmentActivity", how to fix it?
347+
348+
SDK expect to be initialized with `FragmentActivity` instance in regular scenario.
349+
350+
In case if you use passive `siteKey` make sure that you called `hideDialog(true)` on `HCaptchaCconfig.builder()`
351+
342352
## For maintainers
343353
344354
If you plan to contribute to the repo, please see [MAINTAINERS.md](./MAINTAINERS.md) for detailed build, test, and release instructions.

benchmark/src/androidTest/java/com/hcaptcha/sdk/TestHCaptchaVerifier.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package com.hcaptcha.sdk;
22

3-
import androidx.fragment.app.FragmentActivity;
3+
import android.app.Activity;
44

55
public class TestHCaptchaVerifier implements IHCaptchaVerifier {
66

77
@Override
8-
public void startVerification(FragmentActivity activity) {
8+
public void startVerification(Activity activity) {
99
// no implementation need for performance measurement
1010
}
1111

sdk/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ android {
2424
// See https://developer.android.com/studio/publish/versioning
2525
// versionCode must be integer and be incremented by one for every new update
2626
// android system uses this to prevent downgrades
27-
versionCode 38
27+
versionCode 39
2828

2929
// version number visible to the user
3030
// should follow semantic versioning (See https://semver.org)
31-
versionName "3.11.0"
31+
versionName "4.0.0"
3232

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

sdk/src/main/java/com/hcaptcha/sdk/HCaptcha.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.hcaptcha.sdk;
22

3+
import android.app.Activity;
34
import android.content.pm.ApplicationInfo;
45
import android.content.pm.PackageManager;
56
import android.os.Bundle;
@@ -14,7 +15,7 @@ public final class HCaptcha extends Task<HCaptchaTokenResponse> implements IHCap
1415
public static final String META_SITE_KEY = "com.hcaptcha.sdk.site-key";
1516

1617
@NonNull
17-
private final FragmentActivity activity;
18+
private final Activity activity;
1819

1920
@Nullable
2021
private IHCaptchaVerifier captchaVerifier;
@@ -25,22 +26,23 @@ public final class HCaptcha extends Task<HCaptchaTokenResponse> implements IHCap
2526
@NonNull
2627
private final HCaptchaInternalConfig internalConfig;
2728

28-
private HCaptcha(@NonNull final FragmentActivity activity, @NonNull final HCaptchaInternalConfig internalConfig) {
29+
private HCaptcha(@NonNull final Activity activity, @NonNull final HCaptchaInternalConfig internalConfig) {
2930
this.activity = activity;
3031
this.internalConfig = internalConfig;
3132
}
3233

3334
/**
3435
* Constructs a new client which allows to display a challenge dialog
3536
*
36-
* @param activity The current activity
37+
* @param activity FragmentActivity instance for a visual challenge verification,
38+
* or any Activity in case of passive siteKey
3739
* @return new {@link HCaptcha} object
3840
*/
39-
public static HCaptcha getClient(@NonNull final FragmentActivity activity) {
41+
public static HCaptcha getClient(@NonNull final Activity activity) {
4042
return new HCaptcha(activity, HCaptchaInternalConfig.builder().build());
4143
}
4244

43-
static HCaptcha getClient(@NonNull final FragmentActivity activity,
45+
static HCaptcha getClient(@NonNull final Activity activity,
4446
@NonNull HCaptchaInternalConfig internalConfig) {
4547
return new HCaptcha(activity, internalConfig);
4648
}
@@ -100,9 +102,11 @@ void onFailure(final HCaptchaException exception) {
100102
.loading(false)
101103
.build();
102104
captchaVerifier = new HCaptchaHeadlessWebView(activity, this.config, internalConfig, listener);
103-
} else {
105+
} else if (this.activity instanceof FragmentActivity) {
104106
captchaVerifier = HCaptchaDialogFragment.newInstance(inputConfig, internalConfig, listener);
105107
this.config = inputConfig;
108+
} else {
109+
throw new IllegalStateException("Visual hCaptcha challenge verification requires FragmentActivity.");
106110
}
107111
} catch (AndroidRuntimeException e) {
108112
listener.onFailure(new HCaptchaException(HCaptchaError.ERROR));

sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ public void onSuccess(final String token) {
239239
}
240240

241241
@Override
242-
public void startVerification(@NonNull FragmentActivity fragmentActivity) {
243-
final FragmentManager fragmentManager = fragmentActivity.getSupportFragmentManager();
242+
public void startVerification(@NonNull Activity fragmentActivity) {
243+
final FragmentManager fragmentManager = ((FragmentActivity) fragmentActivity).getSupportFragmentManager();
244244
final Fragment oldFragment = fragmentManager.findFragmentByTag(HCaptchaDialogFragment.TAG);
245245
if (oldFragment != null && oldFragment.isAdded()) {
246246
HCaptchaLog.w("DialogFragment was already added.");

sdk/src/main/java/com/hcaptcha/sdk/HCaptchaHeadlessWebView.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package com.hcaptcha.sdk;
22

3+
import android.app.Activity;
34
import android.os.Handler;
45
import android.os.Looper;
56
import android.view.View;
67
import android.view.ViewGroup;
78
import android.webkit.WebView;
8-
import androidx.fragment.app.FragmentActivity;
99

1010
import lombok.Getter;
1111
import lombok.NonNull;
@@ -25,7 +25,7 @@ final class HCaptchaHeadlessWebView implements IHCaptchaVerifier {
2525
private boolean shouldExecuteOnLoad;
2626
private boolean shouldResetOnLoad;
2727

28-
HCaptchaHeadlessWebView(@NonNull final FragmentActivity activity,
28+
HCaptchaHeadlessWebView(@NonNull final Activity activity,
2929
@NonNull final HCaptchaConfig config,
3030
@NonNull final HCaptchaInternalConfig internalConfig,
3131
@NonNull final HCaptchaStateListener listener) {
@@ -44,7 +44,7 @@ final class HCaptchaHeadlessWebView implements IHCaptchaVerifier {
4444
}
4545

4646
@Override
47-
public void startVerification(@NonNull FragmentActivity activity) {
47+
public void startVerification(@NonNull Activity activity) {
4848
if (webViewLoaded) {
4949
// Safe to execute
5050
webViewHelper.resetAndExecute();

sdk/src/main/java/com/hcaptcha/sdk/IHCaptchaVerifier.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.hcaptcha.sdk;
22

3-
import androidx.fragment.app.FragmentActivity;
3+
import android.app.Activity;
44

55
import com.hcaptcha.sdk.tasks.OnFailureListener;
66
import com.hcaptcha.sdk.tasks.OnLoadedListener;
@@ -17,7 +17,7 @@ interface IHCaptchaVerifier extends
1717
/**
1818
* Starts the human verification process.
1919
*/
20-
void startVerification(@NonNull FragmentActivity activity);
20+
void startVerification(@NonNull Activity activity);
2121

2222
/**
2323
* Force stop verification and release resources.

test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
import static org.junit.Assert.assertTrue;
66
import static org.junit.Assert.fail;
77

8+
import android.app.Activity;
9+
import android.os.Looper;
10+
811
import androidx.test.core.app.ActivityScenario;
912
import androidx.test.ext.junit.rules.ActivityScenarioRule;
1013

1114
import com.hcaptcha.sdk.tasks.OnSuccessListener;
1215
import com.hcaptcha.sdk.test.TestActivity;
16+
import com.hcaptcha.sdk.test.TestNonFragmentActivity;
1317

1418
import org.junit.Rule;
1519
import org.junit.Test;
@@ -121,4 +125,15 @@ public void e2eWithDebugTokenHeadlessWebView() throws Exception {
121125

122126
assertTrue(latch.await(E2E_AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS));
123127
}
128+
129+
@Test(expected = IllegalStateException.class)
130+
public void badActivity() {
131+
Looper.prepare();
132+
final Activity activity = new TestNonFragmentActivity();
133+
134+
HCaptcha.getClient(activity)
135+
.verifyWithHCaptcha(config.toBuilder().hideDialog(false).diagnosticLog(true).build())
136+
.addOnSuccessListener(response -> fail("No token expected"))
137+
.addOnFailureListener(e -> fail("Wrong failure reason: " + e.getHCaptchaError()));
138+
}
124139
}

test/src/main/AndroidManifest.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616
<category android:name="android.intent.category.LAUNCHER" />
1717
</intent-filter>
1818
</activity>
19+
<activity
20+
android:name="com.hcaptcha.sdk.test.TestNonFragmentActivity"
21+
android:exported="true">
22+
<intent-filter>
23+
<action android:name="android.intent.action.MAIN" />
24+
25+
<category android:name="android.intent.category.LAUNCHER" />
26+
</intent-filter>
27+
</activity>
1928
<activity
2029
android:name="androidx.fragment.app.testing.EmptyFragmentActivity"
2130
android:exported="true">
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.hcaptcha.sdk.test;
2+
3+
public class TestNonFragmentActivity extends android.app.Activity {
4+
}

0 commit comments

Comments
 (0)