Skip to content
Open
8 changes: 8 additions & 0 deletions webapp/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'org.jetbrains.kotlin.plugin.compose'

android {
namespace "com.yoti.mobile.android.sdk.yotidocscan.websample"
Expand All @@ -24,6 +25,7 @@ android {
buildFeatures {
viewBinding true
buildConfig true
compose true
}

buildTypes {
Expand All @@ -46,4 +48,10 @@ dependencies {
// Multi-module projects: Add this dependency because of Android Studio Issue
// and androix.navigation dependencies management https://issuetracker.google.com/issues/152245564
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'

def composeBom = platform("androidx.compose:compose-bom:2025.04.00")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we declare the versions in a dedicated file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done here: 74bddca 🚀

implementation composeBom
implementation 'androidx.compose.material3:material3'
implementation 'androidx.activity:activity-compose'
debugImplementation 'androidx.compose.ui:ui-tooling'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.yoti.mobile.android.sdk.yotidocscan.websample

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.yoti.mobile.android.sdk.yotidocscan.websample.ui.YotiDocScanWebSampleAppTheme

@Composable
fun MainScreen(
sessionUrl: String,
onSessionUrlChanged: (String) -> Unit,
onStartSessionClicked: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
OutlinedTextField(
value = sessionUrl,
onValueChange = onSessionUrlChanged,
label = {
Text(text = stringResource(id = R.string.session_url_hint))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No color or font size/style ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, just use the default text style, should suffice given it's simply a demo app for YDS.

},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done
),
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.size(16.dp))
Button(
onClick = onStartSessionClicked,
enabled = sessionUrl.isNotBlank()
) {
Text(text = stringResource(id = R.string.start_session_button))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No color or font size/style ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see comment above.

}
}
}

@Preview(showBackground = true)
@Composable
fun PreviewEmptyMainScreen() {
YotiDocScanWebSampleAppTheme {
MainScreen(
sessionUrl = "",
onSessionUrlChanged = {},
onStartSessionClicked = {}
)
}
}

@Preview(showBackground = true)
@Composable
fun PreviewMainScreen() {
YotiDocScanWebSampleAppTheme {
MainScreen(
sessionUrl = "https://example.com/session",
onSessionUrlChanged = {},
onStartSessionClicked = {}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.yoti.mobile.android.sdk.yotidocscan.websample

import android.annotation.SuppressLint
import android.net.Uri
import android.webkit.PermissionRequest
import android.webkit.ValueCallback
import android.webkit.WebChromeClient
import android.webkit.WebChromeClient.FileChooserParams
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView

@Composable
fun WebScreen(
sessionUrl: String,
onPageCommitVisible: (String?) -> Unit,
onFilePathCallbackReady: (ValueCallback<Array<Uri>>?) -> Unit,
onShowCameraAndFilePickerChooser: (FileChooserParams) -> Unit,
modifier: Modifier = Modifier
) {
AndroidView(
modifier = modifier.fillMaxSize(),
factory = { context ->
WebView(context).apply {
configureSettings(settings)
configureWebViewClient(this, onPageCommitVisible)
configureWebChromeClient(
this,
onFilePathCallbackReady,
onShowCameraAndFilePickerChooser
)
}
},
update = { webView ->
sessionUrl.takeIf { it.isNotBlank() }?.let { webView.loadUrl(it) }
}
)
}

@SuppressLint("SetJavaScriptEnabled")
private fun configureSettings(settings: WebSettings) {
with(settings) {
javaScriptEnabled = true
allowFileAccess = true
allowUniversalAccessFromFileURLs = true
domStorageEnabled = true
javaScriptCanOpenWindowsAutomatically = true
mediaPlaybackRequiresUserGesture = false
}
}

private fun configureWebViewClient(webView: WebView, onPageCommitVisible: (String?) -> Unit) {
webView.webViewClient = object : WebViewClient() {
override fun onPageCommitVisible(view: WebView?, url: String?) {
super.onPageCommitVisible(view, url)
onPageCommitVisible(url)
}
}
}

private fun configureWebChromeClient(
webView: WebView,
onFilePathCallbackReady: (ValueCallback<Array<Uri>>?) -> Unit,
onShowCameraAndFilePickerChooser: (FileChooserParams) -> Unit
) {
webView.webChromeClient = object : WebChromeClient() {
// Launch file picker or camera intent and set the
// filePathCallback to set the capture results to the webview
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams?
): Boolean {
onFilePathCallbackReady(filePathCallback)
return if (fileChooserParams?.mode == FileChooserParams.MODE_OPEN) {
onShowCameraAndFilePickerChooser(fileChooserParams)
true
} else {
false
}
}

// Grant permission requested
override fun onPermissionRequest(request: PermissionRequest?) {
request?.grant(request.resources)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.yoti.mobile.android.sdk.yotidocscan.websample.ui

import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable

@Composable
fun YotiDocScanWebSampleAppTheme(content: @Composable () -> Unit) {
MaterialTheme(
colorScheme = lightColorScheme(),
content = content
)
}
2 changes: 2 additions & 0 deletions webapp/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<resources>
<string name="app_name">webapp</string>
<string name="session_url_hint">Session URL</string>
<string name="start_session_button">Start session</string>
</resources>