diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e4a0c62..7835fc1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "9.0.1" +agp = "9.1.0" kotlin = "2.3.10" coreKtx = "1.17.0" junit = "4.13.2" @@ -7,7 +7,7 @@ junitVersion = "1.3.0" espressoCore = "3.7.0" lifecycleRuntimeKtx = "2.10.0" activityCompose = "1.12.4" -composeBom = "2026.02.00" +composeBom = "2026.02.01" fragment = "1.8.9" webkit = "1.15.0" ausweis = "2.4.1" @@ -17,7 +17,7 @@ logback = "3.0.0" test-json = "20251224" coroutines = "1.10.2" androidxtest = "1.7.0" -ktlint = "14.0.1" +ktlint = "14.1.0" gps-ic = "16.0.0-alpha11" registryDigitalcredentialsMdoc = "1.0.0-alpha04" datastore = "1.2.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3ce9d04..189fedb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Fri Sep 20 11:23:24 CEST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/wrapper/src/debug/java/org/siros/wwwallet/bridging/DebugMenuHandler.kt b/wrapper/src/debug/java/org/siros/wwwallet/bridging/DebugMenuHandler.kt index 003d1cf..598a249 100644 --- a/wrapper/src/debug/java/org/siros/wwwallet/bridging/DebugMenuHandler.kt +++ b/wrapper/src/debug/java/org/siros/wwwallet/bridging/DebugMenuHandler.kt @@ -10,11 +10,11 @@ import androidx.core.net.toUri import androidx.core.text.parseAsHtml import androidx.credentials.CredentialManager import org.json.JSONArray -import org.siros.wwwallet.json.toList -import org.siros.wwwallet.logging.YOLOLogger import org.siros.wwwallet.BuildConfig import org.siros.wwwallet.R import org.siros.wwwallet.bridging.WalletJsBridge.Companion.JAVASCRIPT_BRIDGE_NAME +import org.siros.wwwallet.json.toList +import org.siros.wwwallet.logging.YOLOLogger import java.net.URLEncoder private const val USE_DEMO_BASE_URL = "Use Demo Base URL (default)" @@ -182,8 +182,7 @@ class DebugMenuHandler( val combinedLogs = JSONArray(logsJson) .toList() - .map { "$it" } + - YOLOLogger.messages() + .map { "$it" } + YOLOLogger.messages() return combinedLogs.sorted() } diff --git a/wrapper/src/main/java/org/siros/wwwallet/MainActivity.kt b/wrapper/src/main/java/org/siros/wwwallet/MainActivity.kt index 6da7df0..86c36b4 100644 --- a/wrapper/src/main/java/org/siros/wwwallet/MainActivity.kt +++ b/wrapper/src/main/java/org/siros/wwwallet/MainActivity.kt @@ -40,6 +40,9 @@ import androidx.lifecycle.lifecycleScope import androidx.webkit.WebSettingsCompat import androidx.webkit.WebViewFeature import ch.qos.logback.classic.android.BasicLogcatConfigurator +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.siros.wwwallet.bluetooth.BleClientHandler import org.siros.wwwallet.bluetooth.BleServerHandler import org.siros.wwwallet.bridging.DebugMenuHandler @@ -49,9 +52,6 @@ import org.siros.wwwallet.credentials.YubicoContainer import org.siros.wwwallet.logging.YOLOLogger import org.siros.wwwallet.webkit.WalletWebChromeClient import org.siros.wwwallet.webkit.WalletWebViewClient -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import ui.EnterBaseUrlDialog class MainActivity : ComponentActivity() { @@ -149,8 +149,15 @@ class MainActivity : ComponentActivity() { title = stringResource(R.string.shortcut_open_custom), hint = when (reason) { - is MainViewModel.UpdateReason.WebpageError -> stringResource(R.string.shortcut_open_custom_by_error, reason.errorMessage) - is MainViewModel.UpdateReason.DeeplinkRequest -> stringResource(R.string.shortcut_open_custom_from_deeplink) + is MainViewModel.UpdateReason.WebpageError -> + stringResource( + R.string.shortcut_open_custom_by_error, + reason.errorMessage, + ) + + is MainViewModel.UpdateReason.DeeplinkRequest -> + stringResource(R.string.shortcut_open_custom_from_deeplink) + is MainViewModel.UpdateReason.UserRequest -> stringResource(R.string.shortcut_open_custom_by_user) }, currentBaseUrl = runBlocking { vm.getBaseUrl() }, @@ -232,12 +239,16 @@ private fun createWebViewFactory( if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_AUTHENTICATION)) { WebSettingsCompat.setWebAuthenticationSupport( webView.settings, - WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_APP) + WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_APP, + ) - YOLOLogger.i(webView.tagForLog, - "Web authentication support enabled: ${WebSettingsCompat.getWebAuthenticationSupport(webView.settings)}") - } - else { + YOLOLogger.i( + webView.tagForLog, + "Web authentication support enabled: ${ + WebSettingsCompat.getWebAuthenticationSupport(webView.settings) + }", + ) + } else { YOLOLogger.e(webView.tagForLog, "WebView does not support passkeys.") } diff --git a/wrapper/src/main/java/org/siros/wwwallet/MainViewModel.kt b/wrapper/src/main/java/org/siros/wwwallet/MainViewModel.kt index 65add15..8803c5d 100644 --- a/wrapper/src/main/java/org/siros/wwwallet/MainViewModel.kt +++ b/wrapper/src/main/java/org/siros/wwwallet/MainViewModel.kt @@ -7,14 +7,14 @@ import android.content.Intent import android.net.Uri import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import org.siros.wwwallet.logging.YOLOLogger -import org.siros.wwwallet.storage.ProfileStorage import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.siros.wwwallet.logging.YOLOLogger +import org.siros.wwwallet.storage.ProfileStorage import java.net.URI import java.net.URISyntaxException diff --git a/wrapper/src/main/java/org/siros/wwwallet/WalletApplication.kt b/wrapper/src/main/java/org/siros/wwwallet/WalletApplication.kt index 29118aa..24f44c1 100644 --- a/wrapper/src/main/java/org/siros/wwwallet/WalletApplication.kt +++ b/wrapper/src/main/java/org/siros/wwwallet/WalletApplication.kt @@ -6,4 +6,4 @@ class WalletApplication : Application() { override fun onCreate() { super.onCreate() } -} \ No newline at end of file +} diff --git a/wrapper/src/main/java/org/siros/wwwallet/bluetooth/BleServerHandler.kt b/wrapper/src/main/java/org/siros/wwwallet/bluetooth/BleServerHandler.kt index 05ef175..c05befe 100644 --- a/wrapper/src/main/java/org/siros/wwwallet/bluetooth/BleServerHandler.kt +++ b/wrapper/src/main/java/org/siros/wwwallet/bluetooth/BleServerHandler.kt @@ -82,10 +82,7 @@ class BleServerHandler( failure: () -> Unit, ) { val serviceUuid = UUID.fromString(rawServiceUuid) - val gattServer = manager.openGattServer( - activity, - callback, - ) + val gattServer = manager.openGattServer(activity, callback) if (gattServer == null) { YOLOLogger.e(tagForLog, "Could not create gatt server.") diff --git a/wrapper/src/main/java/org/siros/wwwallet/bridging/WalletJsBridge.kt b/wrapper/src/main/java/org/siros/wwwallet/bridging/WalletJsBridge.kt index ba3f008..02db951 100644 --- a/wrapper/src/main/java/org/siros/wwwallet/bridging/WalletJsBridge.kt +++ b/wrapper/src/main/java/org/siros/wwwallet/bridging/WalletJsBridge.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers import org.json.JSONArray import org.json.JSONException import org.json.JSONObject +import org.siros.wwwallet.BuildConfig import org.siros.wwwallet.bluetooth.BleClientHandler import org.siros.wwwallet.bluetooth.BleServerHandler import org.siros.wwwallet.bluetooth.ServiceCharacteristic @@ -16,7 +17,6 @@ import org.siros.wwwallet.credentials.Container import org.siros.wwwallet.json.toList import org.siros.wwwallet.logging.YOLOLogger import org.siros.wwwallet.tagForLog -import org.siros.wwwallet.BuildConfig import kotlin.coroutines.EmptyCoroutineContext class WalletJsBridge( @@ -41,8 +41,7 @@ class WalletJsBridge( if (hints.contains("security-key")) { securityKeyCredentialsContainer - } - else { + } else { clientDeviceCredentialsContainer } } catch (jsonException: JSONException) { @@ -116,38 +115,40 @@ class WalletJsBridge( options: String, ) { val mappedOptions = JSONObject(options) - YOLOLogger.i(tagForLog, "$JAVASCRIPT_BRIDGE_NAME.create($promiseUuid, ${mappedOptions.toString(2)}) called.") - - credentialsContainerByOption(mappedOptions) - .create( - options = mappedOptions, - failureCallback = { th -> - YOLOLogger.e(tagForLog, "Creation failed.", th) - - dispatcher.dispatch(EmptyCoroutineContext) { - webView.evaluateJavascript( - """ - console.log('credential creation failed', JSON.stringify("$th")) - alert('Credential creation failed: ' + JSON.stringify("${th.localizedMessage}")) - $JAVASCRIPT_BRIDGE_NAME.__reject__("$promiseUuid", JSON.stringify("$th")); - """.trimIndent(), - ) {} - } - }, - successCallback = { response -> - YOLOLogger.i(tagForLog, "Creation succeeded with $response.") - - dispatcher.dispatch(EmptyCoroutineContext) { - webView.evaluateJavascript( - """ - var response = JSON.parse('$response') - console.log('credential created', response) - $JAVASCRIPT_BRIDGE_NAME.__resolve__("$promiseUuid", response); - """.trimIndent(), - ) {} - } - }, - ) + YOLOLogger.i( + tagForLog, + "$JAVASCRIPT_BRIDGE_NAME.create($promiseUuid, ${mappedOptions.toString(2)}) called.", + ) + + credentialsContainerByOption(mappedOptions).create( + options = mappedOptions, + failureCallback = { th -> + YOLOLogger.e(tagForLog, "Creation failed.", th) + + dispatcher.dispatch(EmptyCoroutineContext) { + webView.evaluateJavascript( + """ + console.log('credential creation failed', JSON.stringify("$th")) + alert('Credential creation failed: ' + JSON.stringify("${th.localizedMessage}")) + $JAVASCRIPT_BRIDGE_NAME.__reject__("$promiseUuid", JSON.stringify("$th")); + """.trimIndent(), + ) {} + } + }, + successCallback = { response -> + YOLOLogger.i(tagForLog, "Creation succeeded with $response.") + + dispatcher.dispatch(EmptyCoroutineContext) { + webView.evaluateJavascript( + """ + var response = JSON.parse('$response') + console.log('credential created', response) + $JAVASCRIPT_BRIDGE_NAME.__resolve__("$promiseUuid", response); + """.trimIndent(), + ) {} + } + }, + ) } @JavascriptInterface @@ -160,36 +161,35 @@ class WalletJsBridge( val mappedOptions = JSONObject(options) val container = credentialsContainerByOption(mappedOptions) - container - .get( - options = mappedOptions, - failureCallback = { th -> - YOLOLogger.e(tagForLog, "Get failed.", th) - - dispatcher.dispatch(EmptyCoroutineContext) { - webView.evaluateJavascript( - """ - console.log('credential getting failed', JSON.stringify("$th")) - alert('Credential getting failed: ' + JSON.stringify("${th.localizedMessage}")) - $JAVASCRIPT_BRIDGE_NAME.__reject__("$promiseUuid", JSON.stringify("$th")); - """.trimIndent(), - ) {} - } - }, - successCallback = { response -> - YOLOLogger.i(tagForLog, "Get succeeded with $response.") - - dispatcher.dispatch(EmptyCoroutineContext) { - webView.evaluateJavascript( - """ - var response = JSON.parse('$response') - console.log('credential getted', response) - $JAVASCRIPT_BRIDGE_NAME.__resolve__("$promiseUuid", response); - """.trimIndent(), - ) {} - } - }, - ) + container.get( + options = mappedOptions, + failureCallback = { th -> + YOLOLogger.e(tagForLog, "Get failed.", th) + + dispatcher.dispatch(EmptyCoroutineContext) { + webView.evaluateJavascript( + """ + console.log('credential getting failed', JSON.stringify("$th")) + alert('Credential getting failed: ' + JSON.stringify("${th.localizedMessage}")) + $JAVASCRIPT_BRIDGE_NAME.__reject__("$promiseUuid", JSON.stringify("$th")); + """.trimIndent(), + ) {} + } + }, + successCallback = { response -> + YOLOLogger.i(tagForLog, "Get succeeded with $response.") + + dispatcher.dispatch(EmptyCoroutineContext) { + webView.evaluateJavascript( + """ + var response = JSON.parse('$response') + console.log('credential getted', response) + $JAVASCRIPT_BRIDGE_NAME.__resolve__("$promiseUuid", response); + """.trimIndent(), + ) {} + } + }, + ) } @JavascriptInterface diff --git a/wrapper/src/main/java/org/siros/wwwallet/credentials/YubicoContainer.kt b/wrapper/src/main/java/org/siros/wwwallet/credentials/YubicoContainer.kt index c38d57c..a009d76 100644 --- a/wrapper/src/main/java/org/siros/wwwallet/credentials/YubicoContainer.kt +++ b/wrapper/src/main/java/org/siros/wwwallet/credentials/YubicoContainer.kt @@ -33,12 +33,12 @@ import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialCreationOptions import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialDescriptor import com.yubico.yubikit.fido.webauthn.PublicKeyCredentialRequestOptions import com.yubico.yubikit.fido.webauthn.SerializationType +import kotlinx.coroutines.Dispatchers +import org.json.JSONObject import org.siros.wwwallet.json.getOrNull import org.siros.wwwallet.json.toMap import org.siros.wwwallet.logging.YOLOLogger import org.siros.wwwallet.tagForLog -import kotlinx.coroutines.Dispatchers -import org.json.JSONObject import kotlin.coroutines.EmptyCoroutineContext sealed class Operation( @@ -188,17 +188,18 @@ class YubicoContainer( val publicKey = createOptions.publicKey!! val kitOptions = PublicKeyCredentialCreationOptions.fromMap(publicKey.toMap()) val domain = publicKey.rp?.id ?: "" - val provider = ClientDataProvider.fromClientDataJson( - getClientOptions( - type = "webauthn.create", - origin = domain, - challenge = - encodeToString( - kitOptions.challenge, - NO_PADDING or NO_WRAP or URL_SAFE, - ), + val provider = + ClientDataProvider.fromClientDataJson( + getClientOptions( + type = "webauthn.create", + origin = domain, + challenge = + encodeToString( + kitOptions.challenge, + NO_PADDING or NO_WRAP or URL_SAFE, + ), + ), ) - ) val enterprise = null val state = null @@ -216,7 +217,11 @@ class YubicoContainer( YOLOLogger.i(tagForLog, "Done, created $result.") operation.success(JSONObject(result.toMap())) } catch (ctap: CtapException) { - YOLOLogger.e(tagForLog, "Protocol exception: '${ctap.ctapError.toHumanReadable()}'.", ctap) + YOLOLogger.e( + tagForLog, + "Protocol exception: '${ctap.ctapError.toHumanReadable()}'.", + ctap, + ) operation.failure(ctap) } catch (th: Throwable) { YOLOLogger.e(tagForLog, "Unexpected error: '${th.message}'.", th) @@ -237,7 +242,7 @@ class YubicoContainer( HmacSecretExtension(), MinPinLengthExtension(), LargeBlobExtension(), - ) + ), ) private fun getWithDevice( @@ -251,17 +256,18 @@ class YubicoContainer( val publicKey = getOptions.publicKey!! val kitOptions = PublicKeyCredentialRequestOptions.fromMap(publicKey.toMap()) val domain = publicKey.rpId ?: "" - val provider = ClientDataProvider.fromClientDataJson( - getClientOptions( - type = "webauthn.get", - origin = domain, - challenge = - encodeToString( - kitOptions.challenge, - NO_PADDING or NO_WRAP or URL_SAFE, - ), + val provider = + ClientDataProvider.fromClientDataJson( + getClientOptions( + type = "webauthn.get", + origin = domain, + challenge = + encodeToString( + kitOptions.challenge, + NO_PADDING or NO_WRAP or URL_SAFE, + ), + ), ) - ) val enterprise = null try { val result = @@ -276,7 +282,11 @@ class YubicoContainer( YOLOLogger.i(tagForLog, "Done, got $result.") operation.success(JSONObject(result.toMap())) } catch (ctap: CtapException) { - YOLOLogger.e(tagForLog, "Protocol exception: '${ctap.ctapError.toHumanReadable()}'.", ctap) + YOLOLogger.e( + tagForLog, + "Protocol exception: '${ctap.ctapError.toHumanReadable()}'.", + ctap, + ) operation.failure(ctap) } catch (multiple: MultipleAssertionsAvailable) { YOLOLogger.i(tagForLog, "Found several assertions. User selection needed.") diff --git a/wrapper/src/main/java/org/siros/wwwallet/credentials/provider/PasskeyProviderActivity.kt b/wrapper/src/main/java/org/siros/wwwallet/credentials/provider/PasskeyProviderActivity.kt index f1462be..7e61df8 100644 --- a/wrapper/src/main/java/org/siros/wwwallet/credentials/provider/PasskeyProviderActivity.kt +++ b/wrapper/src/main/java/org/siros/wwwallet/credentials/provider/PasskeyProviderActivity.kt @@ -193,8 +193,11 @@ class PasskeyProviderActivity : ComponentActivity() { val publicKeyRequest = request.callingRequest as CreatePublicKeyCredentialRequest - val publicKey = JSONObject(publicKeyRequest.candidateQueryData.getString( - BUNDLE_KEY_REQUEST, "{}")) + val publicKey = + JSONObject( + publicKeyRequest.candidateQueryData.getString(BUNDLE_KEY_REQUEST, "{}"), + ) + val requestJson = JSONObject( mutableMapOf("publicKey" to publicKey), diff --git a/wrapper/src/main/java/org/siros/wwwallet/json/Extensions.kt b/wrapper/src/main/java/org/siros/wwwallet/json/Extensions.kt index b54f547..c2bbc6f 100644 --- a/wrapper/src/main/java/org/siros/wwwallet/json/Extensions.kt +++ b/wrapper/src/main/java/org/siros/wwwallet/json/Extensions.kt @@ -4,12 +4,11 @@ import android.util.Base64.NO_PADDING import android.util.Base64.NO_WRAP import android.util.Base64.URL_SAFE import android.util.Base64.encodeToString -import org.siros.wwwallet.logging.YOLOLogger -import org.siros.wwwallet.tagForLog import org.json.JSONArray import org.json.JSONException import org.json.JSONObject -import kotlin.collections.iterator +import org.siros.wwwallet.logging.YOLOLogger +import org.siros.wwwallet.tagForLog fun ByteArray.b64(): String = encodeToString(this, NO_WRAP or NO_PADDING or URL_SAFE) diff --git a/wrapper/src/main/java/org/siros/wwwallet/webkit/WalletWebViewClient.kt b/wrapper/src/main/java/org/siros/wwwallet/webkit/WalletWebViewClient.kt index b7057d8..a5d43ce 100644 --- a/wrapper/src/main/java/org/siros/wwwallet/webkit/WalletWebViewClient.kt +++ b/wrapper/src/main/java/org/siros/wwwallet/webkit/WalletWebViewClient.kt @@ -30,7 +30,10 @@ class WalletWebViewClient( return super.shouldOverrideUrlLoading(view, request) } - override fun onPageCommitVisible(view: WebView, url: String) { + override fun onPageCommitVisible( + view: WebView, + url: String, + ) { super.onPageCommitVisible(view, url) view.evaluateJavascript("${WalletJsBridge.JAVASCRIPT_BRIDGE_NAME}.inject()") {} diff --git a/wrapper/src/test/java/org/siros/wwwallet/bridging/JSCodeSnippetTest.kt b/wrapper/src/test/java/org/siros/wwwallet/bridging/JSCodeSnippetTest.kt index acac344..a3170bb 100644 --- a/wrapper/src/test/java/org/siros/wwwallet/bridging/JSCodeSnippetTest.kt +++ b/wrapper/src/test/java/org/siros/wwwallet/bridging/JSCodeSnippetTest.kt @@ -30,4 +30,4 @@ class JSCodeSnippetTest { CoreMatchers.containsString("foo bar"), ) } -} \ No newline at end of file +}