Skip to content

Commit

Permalink
add controller for managing app state (#984)
Browse files Browse the repository at this point in the history
  • Loading branch information
swaptr authored Sep 25, 2023
1 parent bfac035 commit b71327e
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 136 deletions.
9 changes: 2 additions & 7 deletions aries_vcx/tests/test_credential_retrieval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,13 +319,8 @@ async fn test_agency_pool_it_should_fail_to_select_credentials_for_predicate() {

let presentation_request = verifier.get_presentation_request_msg().unwrap();
let mut prover = create_prover_from_request(presentation_request.clone()).await;
let selected_credentials = prover_select_credentials(
&mut prover,
&mut consumer,
presentation_request.into(),
None,
)
.await;
let selected_credentials =
prover_select_credentials(&mut prover, &mut consumer, presentation_request, None).await;

assert!(selected_credentials.credential_for_referent.is_empty());
})
Expand Down
6 changes: 3 additions & 3 deletions uniffi_aries_vcx/core/src/core/unpack_message.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::sync::Arc;

use serde::{Deserialize, Serialize};

use super::profile::ProfileHolder;
use crate::{errors::error::VcxUniFFIResult, runtime::block_on};
use aries_vcx::errors::error::AriesVcxError;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct UnpackMessage {
Expand All @@ -20,7 +20,7 @@ pub fn unpack_message(
block_on(async {
let packed_bytes = packed_msg.as_bytes();
let wallet = profile_holder.inner.inject_wallet();
let unpacked_bytes = wallet.unpack_message(&packed_bytes).await?;
let unpacked_bytes = wallet.unpack_message(packed_bytes).await?;
let unpacked_string = String::from_utf8(unpacked_bytes)?;
let unpacked_message = serde_json::from_str::<UnpackMessage>(&unpacked_string)?;
Ok(unpacked_message)
Expand Down
13 changes: 8 additions & 5 deletions uniffi_aries_vcx/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ pub mod errors;
pub mod handlers;
pub mod runtime;

use crate::core::profile::*;
use crate::core::unpack_message::*;
use crate::errors::error::*;
use aries_vcx::aries_vcx_core::wallet::indy::WalletConfig;
use aries_vcx::protocols::connection::pairwise_info::PairwiseInfo;
use aries_vcx::{
aries_vcx_core::wallet::indy::WalletConfig, protocols::connection::pairwise_info::PairwiseInfo,
};
use handlers::connection::*;

use crate::{
core::{profile::*, unpack_message::*},
errors::error::*,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.hyperledger.ariesvcx

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
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 kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.hyperledger.ariesvcx.utils.await

data class AppUiState(
val profileReady: Boolean = false,
val connectionInvitationReceived: Boolean = false,
val connectionCompleted: Boolean = false
)

class AppDemoController : ViewModel() {
private val httpClient = OkHttpClient()

private var profile: ProfileHolder? = null
private var connection: Connection? = null

private var onConnectionComplete: (connection: Connection) -> Unit = {}

// Expose screen UI state
private val _state = MutableStateFlow(AppUiState())
val states: StateFlow<AppUiState> = _state.asStateFlow()

private val walletConfig = WalletConfig(
walletName = "test_create_wallet_add_uuid_here",
walletKey = "8dvfYSt5d1taSd6yJdpjq4emkwsPDDLYxkNFysFD2cZY",
walletKeyDerivation = "RAW",
walletType = null,
storageConfig = null,
storageCredentials = null,
rekey = null,
rekeyDerivationMethod = null
)

suspend fun setupProfile() {
withContext(Dispatchers.IO) {
val newProfile = newIndyProfile(walletConfig)
profile = newProfile
connection = createInvitee(newProfile)
}
_state.update { current ->
current.copy(profileReady = true)
}
}

suspend fun acceptConnectionInvitation(invitation: String) {
if (connection == null || profile == null) {
throw Exception("Connection or Profile is null")
}
withContext(Dispatchers.IO) {
connection!!.acceptInvitation(
profile = profile!!,
invitation = invitation
)
_state.update { it.copy(connectionInvitationReceived = true) }

connection!!.sendRequest(
profile!!,
"$BASE_RELAY_ENDPOINT/send_user_message/$RELAY_USER_ID",
emptyList()
)

// use viewmodel scope to finish off this work
viewModelScope.launch(Dispatchers.IO) {
awaitConnectionCompletion()
}
}
}

private suspend fun awaitConnectionCompletion() {
val pollRelayRequest = Request.Builder()
.url("$BASE_RELAY_ENDPOINT/pop_user_message/$RELAY_USER_ID")
.build()
while (true) {
delay(500)
val relayResponse = httpClient.newCall(pollRelayRequest).await()
if (relayResponse.code == 200) {
val message = relayResponse.body!!.string()

val unpackedMessage = unpackMessage(
profile!!,
message
)

Log.d("AppDemoController", unpackedMessage.message)
connection!!.handleResponse(profile!!, unpackedMessage.message)
connection!!.sendAck(profile!!)

Log.d("AppDemoController", "connection state: ${connection!!.getState()}")

_state.update { it.copy(connectionCompleted = true) }
onConnectionComplete(connection!!)
break
}
}
}

fun subscribeToConnectionComplete(onComplete: (connection: Connection) -> Unit) {
onConnectionComplete = onComplete
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package org.hyperledger.ariesvcx

// Set your public IP address here, this endpoint will be used while communicating with the peer(agent).
const val BASE_RELAY_ENDPOINT = "https://b199-27-57-116-96.ngrok-free.app";
const val RELAY_USER_ID = "demo-user-1";
const val RELAY_USER_ID = "demo-user-1";
Original file line number Diff line number Diff line change
Expand Up @@ -8,67 +8,36 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavHostController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.hyperledger.ariesvcx.utils.await


@Composable
fun HomeScreen(
demoController: AppDemoController,
navController: NavHostController,
setProfileHolder: (ProfileHolder) -> Unit,
profileHolder: ProfileHolder?,
connection: Connection?,
walletConfig: WalletConfig,
connectionRequestState: Boolean,
httpClient: OkHttpClient
) {

val demoState by demoController.states.collectAsState()

val scope = rememberCoroutineScope()
val context = LocalContext.current

var flagKeepFetching by remember {
mutableStateOf(true)
}

val request = Request.Builder()
.url("$BASE_RELAY_ENDPOINT/pop_user_message/$RELAY_USER_ID")
.build()


LaunchedEffect(true) {
scope.launch(Dispatchers.IO) {
while (flagKeepFetching && connectionRequestState) {
delay(500)
val response = httpClient.newCall(request).await()
if (response.code == 200) {
val message = response.body!!.string()

val unpackedMessage = unpackMessage(
profileHolder!!,
message
)

Log.d("HOMESCREEN", "HomeScreen: ${unpackedMessage.message}")
connection?.handleResponse(profileHolder, unpackedMessage.message)
flagKeepFetching = false
connection?.sendAck(profileHolder)
}
}
demoController.subscribeToConnectionComplete { newConn ->
scope.launch(Dispatchers.Main) {
Toast.makeText(
context,
"New Connection Created",
Toast.LENGTH_SHORT
).show()
}
}

Expand All @@ -78,27 +47,31 @@ fun HomeScreen(
verticalArrangement = Arrangement.Center
) {
Button(
enabled = (connection == null),
enabled = (!demoState.profileReady),
onClick = {
scope.launch(Dispatchers.IO) {
val profile = newIndyProfile(walletConfig)
setProfileHolder(profile)
scope.launch {
demoController.setupProfile()
withContext(Dispatchers.Main) {
Toast.makeText(
context,
"New Profile Created: $profile",
"New Profile Created",
Toast.LENGTH_SHORT
).show()
}
}
}) {
Text(text = "New Indy Profile")
}
Button(enabled = (profileHolder != null && connection != null),
Button(enabled = (demoState.profileReady && !demoState.connectionInvitationReceived),
onClick = {
navController.navigate(Destination.QRScan.route)
}) {
Text(text = "Scan QR Code")
}
Button(enabled = (demoState.connectionCompleted),
onClick = {
}) {
Text(text = "Receive a credential")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,6 @@ sealed class Destination(val route: String) {
}

class MainActivity : ComponentActivity() {
private var profile by mutableStateOf<ProfileHolder?>(null)
private var connection by mutableStateOf<Connection?>(null)
private var connectionRequestState by mutableStateOf(false)
private var httpClient = OkHttpClient()

private val walletConfig = WalletConfig(
walletName = "test_create_wallet_add_uuid_here",
walletKey = "8dvfYSt5d1taSd6yJdpjq4emkwsPDDLYxkNFysFD2cZY",
walletKeyDerivation = "RAW",
walletType = null,
storageConfig = null,
storageCredentials = null,
rekey = null,
rekeyDerivationMethod = null
)

private fun setProfileHolder(profileHolder: ProfileHolder) {
profile = profileHolder
connection = createInvitee(profileHolder)
}

private fun setConnectionRequestState() {
connectionRequestState = true
}


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Os.setenv("EXTERNAL_STORAGE", this.filesDir.absolutePath, true)
Expand All @@ -63,14 +37,8 @@ class MainActivity : ComponentActivity() {
) {
val navController = rememberNavController()
NavigationAppHost(
demoController = AppDemoController(),
navController = navController,
setProfileHolder = { setProfileHolder(it) },
connection = connection,
profileHolder = profile,
walletConfig = walletConfig,
connectionRequestState = connectionRequestState,
setConnectionRequestState = { setConnectionRequestState() },
httpClient = httpClient
)
}
}
Expand All @@ -80,35 +48,21 @@ class MainActivity : ComponentActivity() {

@Composable
fun NavigationAppHost(
demoController: AppDemoController,
navController: NavHostController,
setProfileHolder: (ProfileHolder) -> Unit,
connection: Connection?,
profileHolder: ProfileHolder?,
walletConfig: WalletConfig,
connectionRequestState: Boolean,
setConnectionRequestState: () -> Unit,
httpClient: OkHttpClient,
) {
NavHost(navController = navController, startDestination = "home") {
composable(Destination.Home.route) {
HomeScreen(
demoController = demoController,
navController = navController,
setProfileHolder = setProfileHolder,
profileHolder = profileHolder,
connection = connection,
walletConfig = walletConfig,
connectionRequestState = connectionRequestState,
httpClient = httpClient
)
}

composable(Destination.QRScan.route) {
ScanScreen(
connection = connection!!,
profileHolder = profileHolder!!,
demoController = demoController,
navController = navController,
walletConfig = walletConfig,
setConnectionRequestState = setConnectionRequestState
)
}
}
Expand Down
Loading

0 comments on commit b71327e

Please sign in to comment.