Skip to content

Commit

Permalink
Migrate provisioning from using legacy APIs to the new apis (#344)
Browse files Browse the repository at this point in the history
  • Loading branch information
mitrejcevski authored Aug 24, 2023
1 parent 9798871 commit dd2fcf7
Show file tree
Hide file tree
Showing 78 changed files with 2,994 additions and 4,110 deletions.
6 changes: 2 additions & 4 deletions appholder/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
alias libs.plugins.android.application
alias libs.plugins.kotlin.android
alias libs.plugins.kotlinx.serialization.plugin
alias libs.plugins.navigation.safe.args
alias libs.plugins.parcelable
alias libs.plugins.kapt
Expand Down Expand Up @@ -68,16 +69,13 @@ dependencies {
implementation libs.bundles.androidx.core
implementation libs.bundles.androidx.lifecycle
implementation libs.bundles.androidx.navigation
implementation libs.bundles.androidx.room
implementation libs.bundles.androidx.crypto
implementation libs.bundles.bouncy.castle
implementation libs.bundles.compose
implementation libs.volley
implementation libs.cbor
implementation libs.exifinterface
implementation libs.code.scanner

kapt libs.androidx.room.kapt
implementation libs.kotlinx.serialization

androidTestImplementation libs.bundles.ui.testing

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.android.mdl.app.R
import com.android.mdl.app.databinding.ListItemDocumentBinding
import com.android.mdl.app.document.Document
import com.android.mdl.app.document.DocumentInformation
import com.android.mdl.app.wallet.SelectDocumentFragmentDirections

/**
* Adapter for the [RecyclerView].
*/
class DocumentAdapter :
ListAdapter<Document, RecyclerView.ViewHolder>(DocumentDiffCallback()) {
class DocumentAdapter : ListAdapter<DocumentInformation, RecyclerView.ViewHolder>(DocumentDiffCallback()) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return DocumentViewHolder(
Expand All @@ -51,6 +50,7 @@ class DocumentAdapter :
class DocumentViewHolder(
private val binding: ListItemDocumentBinding
) : RecyclerView.ViewHolder(binding.root) {

init {
binding.setClickDetailListener {
binding.document?.let { doc ->
Expand All @@ -59,16 +59,16 @@ class DocumentAdapter :
}
}

private fun navigateToDetail(document: Document, view: View) {
val direction = SelectDocumentFragmentDirections.toDocumentDetail(document)
private fun navigateToDetail(document: DocumentInformation, view: View) {
val direction = SelectDocumentFragmentDirections.toDocumentDetail(document.userVisibleName)
if (view.findNavController().currentDestination?.id == R.id.wallet) {
view.findNavController().navigate(direction)
}
}

fun bind(item: Document) {
fun bind(item: DocumentInformation) {
binding.apply {
val cardArt = cardArtFor(item.cardArt)
val cardArt = cardArtFor(item.documentColor)
binding.llItemContainer.setBackgroundResource(cardArt)
document = item
executePendingBindings()
Expand All @@ -77,22 +77,23 @@ class DocumentAdapter :

@DrawableRes
private fun cardArtFor(cardArt: Int): Int {
return when(cardArt) {
return when (cardArt) {
1 -> R.drawable.yellow_gradient
2 -> R.drawable.blue_gradient
3 -> R.drawable.gradient_red
else -> R.drawable.green_gradient
}
}
}
}

private class DocumentDiffCallback : DiffUtil.ItemCallback<Document>() {
private class DocumentDiffCallback : DiffUtil.ItemCallback<DocumentInformation>() {

override fun areItemsTheSame(oldItem: Document, newItem: Document): Boolean {
return oldItem.identityCredentialName == newItem.identityCredentialName
override fun areItemsTheSame(oldItem: DocumentInformation, newItem: DocumentInformation): Boolean {
return oldItem.userVisibleName == newItem.userVisibleName
}

override fun areContentsTheSame(oldItem: Document, newItem: Document): Boolean {
override fun areContentsTheSame(oldItem: DocumentInformation, newItem: DocumentInformation): Boolean {
return oldItem == newItem
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,25 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.android.mdl.app.R
import com.android.mdl.app.authprompt.UserAuthPromptBuilder
import com.android.mdl.app.theme.HolderAppTheme
import com.android.mdl.app.transfer.AddDocumentToResponseResult
import com.android.mdl.app.util.DocumentData
import com.android.mdl.app.util.log
import com.android.mdl.app.viewmodel.TransferDocumentViewModel
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.launch

class AuthConfirmationFragment : BottomSheetDialogFragment() {

private val viewModel: TransferDocumentViewModel by activityViewModels()
private val passphraseViewModel: PassphrasePromptViewModel by activityViewModels()
private val arguments by navArgs<AuthConfirmationFragmentArgs>()
private var isSendingInProgress = mutableStateOf(false)

Expand Down Expand Up @@ -59,6 +65,19 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
passphraseViewModel.authorizationState.collect { value ->
if (value is PassphraseAuthResult.Success) {
authenticationSucceeded(value.userPassphrase)
passphraseViewModel.reset()
}
}
}
}
}

override fun onCancel(dialog: DialogInterface) {
cancelAuthorization()
}
Expand Down Expand Up @@ -93,11 +112,10 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {

private fun sendResponse() {
isSendingInProgress.value = true
// Will return false if authentication is needed
if (!viewModel.sendResponseForSelection()) {
requestUserAuth(false)
} else {
findNavController().navigateUp()
when (viewModel.sendResponseForSelection()) {
is AddDocumentToResponseResult.UserAuthRequired -> requestUserAuth(false)
is AddDocumentToResponseResult.PassphraseRequired -> requestPassphrase()
else -> findNavController().navigateUp()
}
}

Expand Down Expand Up @@ -128,9 +146,14 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
}
}

private fun authenticationSucceeded(passphrase: String) {
viewModel.sendResponseForSelection(true, passphrase)
findNavController().navigateUp()
}

private fun authenticationSucceeded() {
try {
viewModel.sendResponseForSelection()
viewModel.sendResponseForSelection(true)
findNavController().navigateUp()
} catch (e: Exception) {
val message = "Send response error: ${e.message}"
Expand All @@ -148,4 +171,10 @@ class AuthConfirmationFragment : BottomSheetDialogFragment() {
private fun authenticationFailed() {
viewModel.closeConnection()
}

private fun requestPassphrase() {
val destination = AuthConfirmationFragmentDirections
.openPassphrasePrompt()
findNavController().navigate(destination)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.android.mdl.app.authconfirmation

sealed class PassphraseAuthResult {
object Idle: PassphraseAuthResult()
data class Success(val userPassphrase: String): PassphraseAuthResult()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.android.mdl.app.authconfirmation

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
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.platform.ComposeView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.android.mdl.app.R
import com.android.mdl.app.theme.HolderAppTheme

class PassphrasePrompt : DialogFragment() {

private val viewModel by activityViewModels<PassphrasePromptViewModel>()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
HolderAppTheme {
PassphrasePromptUI(
onDone = { passphrase ->
viewModel.authorize(userPassphrase = passphrase)
findNavController().navigateUp()
}
)
}
}
}
}
}

@Composable
private fun PassphrasePromptUI(
onDone: (passphrase: String) -> Unit
) {
var value by remember { mutableStateOf("") }
Card(
modifier = Modifier.fillMaxWidth(),
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = stringResource(id = R.string.passphrase_prompt_title),
style = MaterialTheme.typography.displaySmall
)
Text(
text = stringResource(id = R.string.passphrase_prompt_message),
style = MaterialTheme.typography.titleSmall
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = value,
onValueChange = { value = it },
textStyle = MaterialTheme.typography.bodyMedium
)
TextButton(
modifier = Modifier
.align(Alignment.End),
onClick = { onDone(value) }) {
Text(text = stringResource(id = R.string.bt_ok))
}
}
}
}

@Composable
@Preview
private fun PreviewPassphrasePrompt() {
HolderAppTheme {
PassphrasePromptUI(onDone = {})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.android.mdl.app.authconfirmation

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update

class PassphrasePromptViewModel : ViewModel() {

private val _authorizationState =
MutableStateFlow<PassphraseAuthResult>(PassphraseAuthResult.Idle)
val authorizationState: StateFlow<PassphraseAuthResult> = _authorizationState

fun authorize(userPassphrase: String) {
_authorizationState.update { PassphraseAuthResult.Success(userPassphrase) }
}

fun reset() {
_authorizationState.update { PassphraseAuthResult.Idle }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SignedElementsCollection {
val document = requestedDocuments.getValue(namespace)
SignedDocumentData(
signedElements,
document.identityCredentialName,
document.userReadableName,
document.requestedDocument.docType,
document.requestedDocument.readerAuth,
document.requestedDocument.itemsRequest
Expand Down
Loading

0 comments on commit dd2fcf7

Please sign in to comment.