diff --git a/app/build.gradle b/app/build.gradle index 5718094..1e3ea35 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'org.jetbrains.kotlin.plugin.compose' version '2.1.0' } android { @@ -55,6 +56,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.compose.material3:material3:1.4.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' @@ -80,4 +82,17 @@ dependencies { changing = true } + def composeBom = platform('androidx.compose:compose-bom:2025.11.00') + implementation composeBom + androidTestImplementation composeBom + + // Compose Core Dependencies + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-graphics' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.material3:material3' + implementation 'androidx.activity:activity-compose:1.9.3' + implementation 'androidx.compose.ui:ui-tooling-preview' + debugImplementation 'androidx.compose.ui:ui-tooling' + } \ No newline at end of file diff --git a/app/src/main/java/de/webidsolutions/metaplugindemo/MainActivity.kt b/app/src/main/java/de/webidsolutions/metaplugindemo/MainActivity.kt index 6c2cff6..15c67d3 100644 --- a/app/src/main/java/de/webidsolutions/metaplugindemo/MainActivity.kt +++ b/app/src/main/java/de/webidsolutions/metaplugindemo/MainActivity.kt @@ -7,16 +7,20 @@ package de.webidsolutions.metaplugindemo import android.annotation.SuppressLint import android.content.Intent import android.os.Bundle -import android.widget.Button -import android.widget.CheckBox -import android.widget.TextView +import androidx.activity.compose.setContent import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import de.webidsolutions.auto_ident_on_server_product_plugin.AutoIdentOnServerProductPlugin import de.webidsolutions.eid_on_server_product_plugin.EIdOnServerProductPlugin import de.webidsolutions.meta_plugin.EMetaPluginFailReason import de.webidsolutions.meta_plugin.WebIdMetaPlugin +import de.webidsolutions.metaplugindemo.scenes.MetaPluginDemoScreen +import de.webidsolutions.metaplugindemo.scenes.ThemingChoice import de.webidsolutions.metaplugindemo.tasks.EApiResult import de.webidsolutions.metaplugindemo.tasks.MetaPluginVerifyTask import de.webidsolutions.mobile_app.sdk.WebIdMobileAppSdkException @@ -56,32 +60,32 @@ private val chosenEnvironment: EWebIDEnv = EWebIDEnv.TEST */ internal class MainActivity : AppCompatActivity() { - private lateinit var eidOnServerPluginCb: CheckBox - private lateinit var payOnServerPluginCb: CheckBox - private lateinit var autoIdOnServerPluginCb: CheckBox - private lateinit var videoPluginCb: CheckBox - private lateinit var textLog: TextView - private lateinit var startButton: Button + private var logText by mutableStateOf("") + // private lateinit var coreSdk: IWebIdMobileAppSdk override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - textLog = findViewById(R.id.log) - - autoIdOnServerPluginCb = findViewById(R.id.cb_plugin_auto_id_on_server) - payOnServerPluginCb = findViewById(R.id.cb_plugin_pay_on_server) - eidOnServerPluginCb = findViewById(R.id.cb_plugin_eid_on_server) - videoPluginCb = findViewById(R.id.cb_plugin_video) - - startButton = findViewById(R.id.startButton) - startButton.setOnClickListener { - clearLog() - writeLog(getString(R.string.starting)) - createCoreSdk() + setContent { + MaterialTheme { + MetaPluginDemoScreen( + logText = logText, + onStartClicked = { useAutoIdent, usePayOnServer, useEidOnServer, useVideo, themingChoice -> + clearLog() + writeLog(getString(R.string.starting)) + createCoreSdk( + useAutoIdent = useAutoIdent, + usePayOnServer = usePayOnServer, + useEidOnServer = useEidOnServer, + useVideo = useVideo, + themingChoice = themingChoice + ) + } + ) + } } + } /* CORE SDK */ @@ -89,7 +93,13 @@ internal class MainActivity : AppCompatActivity() { /** * Creates the Core SDK from the existing credentials. */ - private fun createCoreSdk() { + private fun createCoreSdk( + useAutoIdent: Boolean, + usePayOnServer: Boolean, + useEidOnServer: Boolean, + useVideo: Boolean, + themingChoice: ThemingChoice + ) { writeLog(getString(R.string.creating_core_sdk)) // should be provided to you -> currently just using demo credentials @@ -106,7 +116,12 @@ internal class MainActivity : AppCompatActivity() { writeLog(getString(R.string.core_sdk_creation_successful)) validateActionId() - val selectProductPlugins = getSelectedPlugins() + val selectProductPlugins = getSelectedPlugins( + useAutoIdent = useAutoIdent, + usePayOnServer = usePayOnServer, + useEidOnServer = useEidOnServer, + useVideo = useVideo + ) val metaPlugin = WebIdMetaPlugin( environment, @@ -142,13 +157,18 @@ internal class MainActivity : AppCompatActivity() { this::onPluginResultCallback ) - private fun getSelectedPlugins(): ArrayList { + private fun getSelectedPlugins( + useAutoIdent: Boolean, + usePayOnServer: Boolean, + useEidOnServer: Boolean, + useVideo: Boolean + ): ArrayList { return ArrayList( listOfNotNull( - if (autoIdOnServerPluginCb.isChecked) AutoIdentOnServerProductPlugin() else null, - if (payOnServerPluginCb.isChecked) PayOnServerProductPlugin() else null, - if (eidOnServerPluginCb.isChecked) EIdOnServerProductPlugin() else null, - if (videoPluginCb.isChecked) VideoIdentProductPlugin(config) else null, + if (useAutoIdent) AutoIdentOnServerProductPlugin() else null, + if (usePayOnServer) PayOnServerProductPlugin() else null, + if (useEidOnServer) EIdOnServerProductPlugin() else null, + if (useVideo) VideoIdentProductPlugin(config) else null, ) ) } @@ -156,7 +176,6 @@ internal class MainActivity : AppCompatActivity() { private fun metaPluginVerifyCallback(metaPlugin: WebIdMetaPlugin): (AsyncTaskResultGeneric) -> Unit { return { result -> if (result.errorResult == EApiResult.SUCCESS) { - var verifyActionIdResult = result.result try { metaPlugin.startPlugin( this, @@ -175,7 +194,6 @@ internal class MainActivity : AppCompatActivity() { } private fun onPluginResultCallback(activityResult: ActivityResult) { - var metaPluginActivityResult = activityResult var result: String? try { // Get data and handle potential null case @@ -194,6 +212,7 @@ internal class MainActivity : AppCompatActivity() { result += " $resultInfo" // your code to handle the successful plugin execution } + writeLog(result) } else { // failure case if (failReason.specificResult != null) { @@ -203,30 +222,36 @@ internal class MainActivity : AppCompatActivity() { if (specificResult is EMetaPluginFailReason) { if (specificResult == EMetaPluginFailReason.EID_PENDING_AUTHADA) { // handle EID_PENDING_AUTHADA as described in code documentation + writeLog("EID_PENDING_AUTHADA") } else { // handle all other EMetaPluginFailReason as described in code documentation + writeLog("MetaPluginFailReason: $specificResult") } } else { // handle EVideoIdentProductPluginFailReasons if (specificResult is EVideoIdentProductPluginFailReasons) { if (specificResult == EVideoIdentProductPluginFailReasons.CALL_CENTER_CLOSED) { // handle CALL_CENTER_CLOSED as described in code documentation + writeLog("CALL_CENTER_CLOSED") } else { // handle all other EVideoIdentProductPluginFailReasons errors as described in the code documentation + writeLog("VideoIdentFailReason: $specificResult") } } } } else { if (failReason.genericResult == EProductPluginErrors.UNKNOWN) { // handle UNKNOWN as described in code documentation + writeLog("Unknown Error") } else { // handle all other EProductPluginErrors errors as described in the code documentation + writeLog("Error: ${failReason.genericResult}") } } } } catch (e: WebIdPluginInterruptedException) { // Handle interruption in your preferred way - writeLog(e.toString()) + writeLog("Plugin interrupted: ${e.message}") } } @@ -234,18 +259,17 @@ internal class MainActivity : AppCompatActivity() { /** * Add an entry to the log. - * - * @param */ @SuppressLint("SetTextI18n") private fun writeLog(entry: String) { - textLog.text = "${textLog.text} $entry\n" + logText += "$entry\n" } /** * Resets the text log of this activity. */ private fun clearLog() { - textLog.text = "" + logText = "" } + } \ No newline at end of file diff --git a/app/src/main/java/de/webidsolutions/metaplugindemo/scenes/MetaPluginDemoScene.kt b/app/src/main/java/de/webidsolutions/metaplugindemo/scenes/MetaPluginDemoScene.kt new file mode 100644 index 0000000..faafa7c --- /dev/null +++ b/app/src/main/java/de/webidsolutions/metaplugindemo/scenes/MetaPluginDemoScene.kt @@ -0,0 +1,224 @@ +package de.webidsolutions.metaplugindemo.scenes + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Checkbox +import androidx.compose.material3.CheckboxDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import de.webidsolutions.metaplugindemo.R + +enum class ThemingChoice { + CODE, + XML +} + +@Composable +fun MetaPluginDemoScreen( + logText: String, + onStartClicked: ( + useAutoIdent: Boolean, + usePayOnServer: Boolean, + useEidOnServer: Boolean, + useVideo: Boolean, + themingChoice: ThemingChoice + ) -> Unit +) { + var useAutoIdent by remember { mutableStateOf(false) } + var usePayOnServer by remember { mutableStateOf(false) } + var useEidOnServer by remember { mutableStateOf(false) } + var useVideo by remember { mutableStateOf(false) } + var themingChoice by remember { mutableStateOf(ThemingChoice.CODE) } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(start = 10.dp, top = 10.dp, end = 10.dp) + .systemBarsPadding() + ) { + Text( + text = stringResource(R.string.choose_plugins), + style = MaterialTheme.typography.bodyLarge + ) + + Spacer(modifier = Modifier.height(10.dp)) + + Column(modifier = Modifier.padding(start = 10.dp)) { + CheckboxWithLabel( + checked = useAutoIdent, + onCheckedChange = { useAutoIdent = it }, + label = stringResource(R.string.autoid) + ) + + CheckboxWithLabel( + checked = usePayOnServer, + onCheckedChange = { usePayOnServer = it }, + label = stringResource(R.string.accountid) + ) + + CheckboxWithLabel( + checked = useEidOnServer, + onCheckedChange = { useEidOnServer = it }, + label = stringResource(R.string.eid) + ) + + CheckboxWithLabel( + checked = useVideo, + onCheckedChange = { useVideo = it }, + label = stringResource(R.string.videoid) + ) + } + + Spacer(modifier = Modifier.height(10.dp)) + + Text( + text = stringResource(R.string.choose_theming), + style = MaterialTheme.typography.bodyLarge, + ) + + Spacer(modifier = Modifier.height(10.dp)) + + Column ( + modifier = Modifier.padding(start = 10.dp), + ) { + CheckboxWithLabel( + checked = themingChoice == ThemingChoice.CODE, + onCheckedChange = { themingChoice = ThemingChoice.CODE }, + label = "Code-Theming" + ) + + CheckboxWithLabel( + checked = themingChoice == ThemingChoice.XML, + onCheckedChange = { themingChoice = ThemingChoice.XML }, + label = "XML-Theming" + ) + } + + Spacer(modifier = Modifier.height(10.dp)) + + Text( + text = stringResource(R.string.log_headline), + style = MaterialTheme.typography.bodyLarge, + ) + + Spacer(modifier = Modifier.height(10.dp)) + + Surface( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + .padding(horizontal = 10.dp), + color = MaterialTheme.colorScheme.surfaceVariant, + shape = MaterialTheme.shapes.small + ) { + val scrollState = rememberScrollState() + + LaunchedEffect(logText) { + scrollState.animateScrollTo(scrollState.maxValue) + } + + Text( + text = logText.ifEmpty { stringResource(R.string.log_placeholder) }, + modifier = Modifier + .fillMaxSize() + .verticalScroll(scrollState) + .padding(16.dp), + style = MaterialTheme.typography.bodyMedium + ) + } + + Spacer(modifier = Modifier.height(10.dp)) + + Button( + onClick = { + onStartClicked( + useAutoIdent, + usePayOnServer, + useEidOnServer, + useVideo, + themingChoice + ) + }, + modifier = Modifier + .fillMaxWidth() + .height(60.dp) + .padding(horizontal = 10.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFF05B1FB) + ) + ) { + Text( + text = stringResource(R.string.start_button), + color = Color.White + ) + } + } +} + +@Composable +private fun CheckboxWithLabel( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + label: String +) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Checkbox( + checked = checked, + onCheckedChange = onCheckedChange, + colors = CheckboxDefaults.colors( + checkedColor = Color(0xFF05B1FB), + checkmarkColor = Color.White + ) + ) + + Spacer(modifier = Modifier.width(2.dp)) + Text(text = label) + } +} + +@Preview( + name = "MetaPluginDemoScreen - Long Log", + showBackground = true, + showSystemUi = true +) +@Composable +private fun MetaPluginDemoScreenLongLogPreview() { + MaterialTheme { + MetaPluginDemoScreen( + logText = buildString { + repeat(20) { index -> + appendLine("Log entry ${index + 1}: Processing...") + } + }, + onStartClicked = { _, _, _, _, _ -> } + ) + } +} +