Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions example-app/src/main/java/com/hcaptcha/example/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@

public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String SITEKEY = "10000000-ffff-ffff-ffff-000000000001";
private static final String SITEKEY_VISUAL = "00000000-0000-0000-0000-000000000000";
private static final String SITEKEY_PASSIVE = "10000000-ffff-ffff-ffff-000000000001";
private static final int MAX_AUDIT_LOG_LINES = 100;
private static final int TAB_CONFIGURATION = 0;
private static final int TAB_CUSTOM = 1;
Expand All @@ -54,6 +55,9 @@ public class MainActivity extends AppCompatActivity {

private RadioGroup modeGroup;
private RadioGroup sizeGroup;
private RadioGroup sitekeyGroup;
private TextInputLayout sitekeyInputLayout;
private TextInputEditText sitekeyInput;
private CheckBox loading;
private CheckBox disableHardwareAccel;
private CheckBox themeDark;
Expand Down Expand Up @@ -99,6 +103,9 @@ protected void onCreate(final Bundle savedInstanceState) {

modeGroup = findViewById(R.id.challenge_mode_group);
sizeGroup = findViewById(R.id.challenge_size_group);
sitekeyGroup = findViewById(R.id.sitekey_group);
sitekeyInputLayout = findViewById(R.id.sitekeyInputLayout);
sitekeyInput = findViewById(R.id.sitekeyInput);
loading = findViewById(R.id.loading);
disableHardwareAccel = findViewById(R.id.hwAccel);
themeDark = findViewById(R.id.themeDark);
Expand Down Expand Up @@ -140,6 +147,11 @@ protected void onCreate(final Bundle savedInstanceState) {
updateCustomHostUiState();
});

sitekeyGroup.setOnCheckedChangeListener((group, checkedId) -> {
sitekeyInputLayout.setVisibility(checkedId == R.id.sitekey_custom ? View.VISIBLE : View.GONE);
addAuditLog("Sitekey switched to " + sitekeyLabel(checkedId));
});

setPhoneModeUi(phoneModeSwitch.isChecked());
phoneModeSwitch.setOnCheckedChangeListener((button, checked) -> {
setPhoneModeUi(checked);
Expand Down Expand Up @@ -316,6 +328,28 @@ private HCaptchaSize getSelectedSize() {
return HCaptchaSize.NORMAL;
}

private String getSelectedSitekey() {
final int checkedId = sitekeyGroup.getCheckedRadioButtonId();
if (checkedId == R.id.sitekey_passive) {
return SITEKEY_PASSIVE;
}
if (checkedId == R.id.sitekey_custom) {
final String custom = sitekeyInput.getText() != null ? sitekeyInput.getText().toString().trim() : "";
return custom.isEmpty() ? SITEKEY_VISUAL : custom;
}
return SITEKEY_VISUAL;
}

private String sitekeyLabel(final int checkedId) {
if (checkedId == R.id.sitekey_passive) {
return "Passive";
}
if (checkedId == R.id.sitekey_custom) {
return "Custom";
}
return "Challenge";
}

private HCaptcha getConfiguredClient() {
final HCaptcha client = HCaptcha.getClient(this);
client.setEmbeddedContainer(selectedMode == HCaptchaRenderMode.EMBEDDED ? embeddedChallengeContainer : null);
Expand All @@ -328,7 +362,7 @@ private HCaptchaConfig getConfig() {
: getSelectedSize();
final boolean isDark = themeDark.isChecked();
return HCaptchaConfig.builder()
.siteKey(SITEKEY)
.siteKey(getSelectedSitekey())
.size(size)
.renderMode(selectedMode)
.loading(selectedMode != HCaptchaRenderMode.EMBEDDED && loading.isChecked())
Expand Down
56 changes: 56 additions & 0 deletions example-app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,62 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/sitekey"
android:textColor="@color/hc_text_primary" />

<RadioGroup
android:id="@+id/sitekey_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:orientation="horizontal">

<RadioButton
android:id="@+id/sitekey_passive"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:text="@string/sitekey_passive" />

<RadioButton
android:id="@+id/sitekey_visual"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/sitekey_visual" />

<RadioButton
android:id="@+id/sitekey_custom"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/sitekey_custom" />
</RadioGroup>

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/sitekeyInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:visibility="gone"
app:boxBackgroundMode="outline"
app:boxStrokeColor="@color/colorPrimary"
app:hintTextColor="@color/hc_text_secondary"
app:placeholderText="@string/sitekey_custom_hint">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/sitekeyInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:textColor="@color/hc_text_primary" />
</com.google.android.material.textfield.TextInputLayout>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/render_mode"
android:textColor="@color/hc_text_primary" />

Expand Down
5 changes: 5 additions & 0 deletions example-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
<string name="phone_input">Phone input</string>
<string name="phone_prefix_hint">Country prefix (e.g. 44)</string>
<string name="phone_number_hint">Full phone number (e.g. +44123456789)</string>
<string name="sitekey">Sitekey</string>
<string name="sitekey_visual">Challenge</string>
<string name="sitekey_passive">Passive</string>
<string name="sitekey_custom">Custom</string>
<string name="sitekey_custom_hint">xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</string>
<string name="rqdata_label">Request data (rqdata)</string>
<string name="rqdata_hint">Optional rqdata value</string>
<string name="embedded_showcase">Custom render host showcase</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Refresh
Expand All @@ -36,6 +36,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
Expand All @@ -55,6 +56,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
Expand All @@ -71,8 +73,14 @@ import java.util.Date
import java.util.Locale

class ComposeActivity : ComponentActivity() {
private enum class ResultState { Idle, Verifying, Success, Error, Setup, Reset, Destroyed }
private enum class ResultState { Idle, Verifying, Success, Error, Reset, Destroyed }
private enum class TopTab { Configuration, CustomHost, Result, AuditLog }
private enum class SitekeyOption { Visual, Passive, Custom }

companion object {
private const val SITEKEY_VISUAL = "00000000-0000-0000-0000-000000000000"
private const val SITEKEY_PASSIVE = "10000000-ffff-ffff-ffff-000000000001"
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -102,6 +110,8 @@ class ComposeActivity : ComponentActivity() {
var selectedTab by remember { mutableStateOf(TopTab.Configuration) }
var selectedMode by remember { mutableStateOf(HCaptchaRenderMode.DIALOG) }
var selectedSize by remember { mutableStateOf(HCaptchaSize.NORMAL) }
var selectedSitekeyOption by remember { mutableStateOf(SitekeyOption.Passive) }
var customSitekey by remember { mutableStateOf("") }

var loadingEnabled by remember { mutableStateOf(true) }
var webDebugEnabled by remember { mutableStateOf(false) }
Expand All @@ -114,7 +124,6 @@ class ComposeActivity : ComponentActivity() {
val auditLog = remember { mutableStateListOf<String>() }

var captchaVisible by remember { mutableStateOf(false) }
var setupArmed by remember { mutableStateOf(false) }
var captchaRenderKey by remember { mutableIntStateOf(0) }
var customWaitingForOpen by remember { mutableStateOf(false) }
var customStatusMessage by remember { mutableStateOf<String?>(null) }
Expand Down Expand Up @@ -142,25 +151,30 @@ class ComposeActivity : ComponentActivity() {
fun resetCaptcha() {
captchaVisible = false
captchaRenderKey += 1
setupArmed = false
customWaitingForOpen = false
customStatusMessage = null
customStatusIsError = false
}

val loadingForConfig = if (selectedMode == HCaptchaRenderMode.EMBEDDED) false else loadingEnabled
val sizeForConfig = if (selectedMode == HCaptchaRenderMode.HEADLESS) HCaptchaSize.INVISIBLE else selectedSize
val activeSitekey = when (selectedSitekeyOption) {
SitekeyOption.Visual -> SITEKEY_VISUAL
SitekeyOption.Passive -> SITEKEY_PASSIVE
SitekeyOption.Custom -> customSitekey.ifBlank { SITEKEY_VISUAL }
}

val config = remember(
selectedMode,
sizeForConfig,
loadingForConfig,
disableHwAccel,
darkTheme,
activeSitekey,
captchaRenderKey
) {
HCaptchaConfig.builder()
.siteKey("10000000-ffff-ffff-ffff-000000000001")
.siteKey(activeSitekey)
.size(sizeForConfig)
.renderMode(selectedMode)
.loading(loadingForConfig)
Expand Down Expand Up @@ -201,18 +215,6 @@ class ComposeActivity : ComponentActivity() {
label = { Text("Reset") },
colors = navItemColors
)
NavigationBarItem(
selected = false,
onClick = {
setupArmed = true
resultState = ResultState.Setup
resultMessage = "setup armed"
addAudit("Setup armed")
},
icon = { Icon(Icons.Default.Build, contentDescription = null) },
label = { Text("Setup") },
colors = navItemColors
)
NavigationBarItem(
selected = false,
onClick = {
Expand All @@ -226,7 +228,7 @@ class ComposeActivity : ComponentActivity() {
customStatusMessage = null
customStatusIsError = false
}
if (!setupArmed) addAudit("Verify without setup") else addAudit("Verify")
addAudit("Verify")
},
icon = { Icon(Icons.Default.CheckCircle, contentDescription = null) },
label = { Text("Verify") },
Expand Down Expand Up @@ -310,6 +312,39 @@ class ComposeActivity : ComponentActivity() {
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text("Configuration", style = MaterialTheme.typography.titleMedium, color = hcTextPrimary)
Text("Sitekey", color = hcTextPrimary)
InlineRadioRow(
labels = listOf("Passive", "Challenge", "Custom"),
selectedIndex = when (selectedSitekeyOption) {
SitekeyOption.Passive -> 0
SitekeyOption.Visual -> 1
SitekeyOption.Custom -> 2
},
onSelected = {
selectedSitekeyOption = when (it) {
1 -> SitekeyOption.Visual
2 -> SitekeyOption.Custom
else -> SitekeyOption.Passive
}
val label = when (selectedSitekeyOption) {
SitekeyOption.Visual -> "Challenge"
SitekeyOption.Passive -> "Passive"
SitekeyOption.Custom -> "Custom"
}
addAudit("Sitekey switched to $label")
}
)
if (selectedSitekeyOption == SitekeyOption.Custom) {
OutlinedTextField(
value = customSitekey,
onValueChange = { customSitekey = it },
label = { Text("Custom sitekey") },
placeholder = { Text("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii)
)
}
Text("Render mode", color = hcTextPrimary)
InlineRadioRow(
labels = listOf("Dialog", "Headless", "Embedded"),
Expand Down Expand Up @@ -379,7 +414,6 @@ class ComposeActivity : ComponentActivity() {
HCaptchaCompose(config = config) { response ->
when (response) {
is HCaptchaResponse.Success -> {
setupArmed = false
captchaVisible = false
customWaitingForOpen = false
customStatusMessage = "Success. See Result tab for details."
Expand All @@ -391,7 +425,6 @@ class ComposeActivity : ComponentActivity() {
}

is HCaptchaResponse.Failure -> {
setupArmed = false
captchaVisible = false
customWaitingForOpen = false
customStatusMessage = "Error. See Result tab for details."
Expand Down Expand Up @@ -534,7 +567,6 @@ class ComposeActivity : ComponentActivity() {
HCaptchaCompose(config = config) { response ->
when (response) {
is HCaptchaResponse.Success -> {
setupArmed = false
captchaVisible = false
resultState = ResultState.Success
lastToken = response.token
Expand All @@ -543,7 +575,6 @@ class ComposeActivity : ComponentActivity() {
}

is HCaptchaResponse.Failure -> {
setupArmed = false
captchaVisible = false
resultState = ResultState.Error
lastToken = null
Expand Down
Loading
Loading