From 9de98785c4fb819a9f843511aabc813fd9fc201f Mon Sep 17 00:00:00 2001 From: Mykhailo Nester Date: Wed, 12 May 2021 09:41:44 +0300 Subject: [PATCH] Feature/claim cert (#17) - add endpoint; - add UI for claim certificate screen; - add viewmodel with QR decoder; - add new resources icons, strings etc; - add user permissions; - add new data models; - add progressview on cert list fragment; - handle navigation from claim cert to cert list; - update fragment animation; - add rsa support; --- .idea/dictionaries/mykhailo_nester.xml | 2 + app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 6 +- .../dgca/wallet/app/android/AuthFragment.kt | 4 +- .../wallet/app/android/CodeReaderFragment.kt | 8 +- .../java/dgca/wallet/app/android/Event.kt | 27 ++++ .../dgca/wallet/app/android/MainActivity.kt | 12 +- .../certificate/CertificatesFragment.kt | 8 +- .../certificate/CertificatesViewModel.kt | 6 +- .../claim/ClaimCertificateFragment.kt | 88 ++++++++++++ .../claim/ClaimCertificateViewModel.kt | 134 ++++++++++++++++++ .../wallet/app/android/data/BaseRepository.kt | 12 +- .../app/android/data/WalletRepository.kt | 4 +- .../app/android/data/WalletRepositoryImpl.kt | 42 ++++++ .../app/android/data/remote/ApiService.kt | 11 +- .../wallet/app/android/di/DecoderModule.kt | 42 ++++++ .../wallet/app/android/di/NetworkModule.kt | 2 +- .../wallet/app/android/di/RepositoryModule.kt | 40 ++++++ .../wallet/app/android/model/ClaimRequest.kt | 41 ++++++ .../PublicKeyData.kt} | 11 +- app/src/main/res/drawable/white_gradient.xml | 29 ++++ .../main/res/layout/certificate_card_view.xml | 3 +- .../res/layout/fragment_certificate_claim.xml | 108 +++++++++++++- .../res/layout/fragment_certificate_view.xml | 3 +- .../main/res/layout/fragment_certificates.xml | 83 +++++++---- .../main/res/layout/fragment_code_reader.xml | 17 ++- app/src/main/res/navigation/nav_graph.xml | 40 ++++-- app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/dimen.xml | 9 ++ app/src/main/res/values/strings.xml | 8 ++ 30 files changed, 723 insertions(+), 81 deletions(-) create mode 100644 app/src/main/java/dgca/wallet/app/android/Event.kt create mode 100644 app/src/main/java/dgca/wallet/app/android/certificate/claim/ClaimCertificateFragment.kt create mode 100644 app/src/main/java/dgca/wallet/app/android/certificate/claim/ClaimCertificateViewModel.kt create mode 100644 app/src/main/java/dgca/wallet/app/android/data/WalletRepositoryImpl.kt create mode 100644 app/src/main/java/dgca/wallet/app/android/di/RepositoryModule.kt create mode 100644 app/src/main/java/dgca/wallet/app/android/model/ClaimRequest.kt rename app/src/main/java/dgca/wallet/app/android/{certificate/ClaimCertificateFragment.kt => model/PublicKeyData.kt} (81%) create mode 100644 app/src/main/res/drawable/white_gradient.xml create mode 100644 app/src/main/res/values/dimen.xml diff --git a/.idea/dictionaries/mykhailo_nester.xml b/.idea/dictionaries/mykhailo_nester.xml index eddd97ca..69e78819 100644 --- a/.idea/dictionaries/mykhailo_nester.xml +++ b/.idea/dictionaries/mykhailo_nester.xml @@ -1,6 +1,8 @@ + cose + dgci promp diff --git a/app/build.gradle b/app/build.gradle index 8e80304e..4bc4ab98 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -74,6 +74,6 @@ dependencies { implementation Deps.gson_converter implementation Deps.timber - + testImplementation Deps.test_junit } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8ee55303..48f4f505 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,9 @@ + + + - + diff --git a/app/src/main/java/dgca/wallet/app/android/AuthFragment.kt b/app/src/main/java/dgca/wallet/app/android/AuthFragment.kt index 1c59fc13..4b6f6c74 100644 --- a/app/src/main/java/dgca/wallet/app/android/AuthFragment.kt +++ b/app/src/main/java/dgca/wallet/app/android/AuthFragment.kt @@ -60,6 +60,7 @@ class AuthFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { + (activity as MainActivity).disableBackButton() _binding = FragmentAuthBinding.inflate(inflater, container, false) return binding.root } @@ -81,9 +82,6 @@ class AuthFragment : Fragment() { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { super.onAuthenticationSucceeded(result) - Toast.makeText(requireContext(), "Authentication success", Toast.LENGTH_SHORT) - .show() - encryptSecretInformation(result.cryptoObject) navigateToCodeReader() } diff --git a/app/src/main/java/dgca/wallet/app/android/CodeReaderFragment.kt b/app/src/main/java/dgca/wallet/app/android/CodeReaderFragment.kt index 7691a385..09096914 100644 --- a/app/src/main/java/dgca/wallet/app/android/CodeReaderFragment.kt +++ b/app/src/main/java/dgca/wallet/app/android/CodeReaderFragment.kt @@ -68,8 +68,7 @@ class CodeReaderFragment : Fragment(), NavController.OnDestinationChangedListene requestCameraPermission() val formats: Collection = listOf(BarcodeFormat.AZTEC, BarcodeFormat.QR_CODE) - binding.barcodeScanner.barcodeView.decoderFactory = DefaultDecoderFactory(formats) - binding.barcodeScanner.initializeFromIntent(requireActivity().intent) + binding.barcodeScanner.decoderFactory = DefaultDecoderFactory(formats) binding.barcodeScanner.decodeContinuous(callback) beepManager = BeepManager(requireActivity()) } @@ -95,10 +94,9 @@ class CodeReaderFragment : Fragment(), NavController.OnDestinationChangedListene findNavController().currentDestination // TODO temporar changes, remove after implementing QR code data validation and saving. - Thread {db.certificateDao().insert(Certificate(qrCodeText = qrCodeText))}.start() + Thread { db.certificateDao().insert(Certificate(qrCodeText = qrCodeText)) }.start() - val action = - CodeReaderFragmentDirections.actionCodeReaderFragmentToClaimCertificateFragment(qrCodeText) + val action = CodeReaderFragmentDirections.actionCodeReaderFragmentToClaimCertificateFragment(qrCodeText) findNavController().navigate(action) } diff --git a/app/src/main/java/dgca/wallet/app/android/Event.kt b/app/src/main/java/dgca/wallet/app/android/Event.kt new file mode 100644 index 00000000..98a2c2e6 --- /dev/null +++ b/app/src/main/java/dgca/wallet/app/android/Event.kt @@ -0,0 +1,27 @@ +package dgca.wallet.app.android + +/** + * Used as a wrapper for data that is exposed via a LiveData that represents an event. + */ +open class Event(private val content: T) { + + var hasBeenHandled = false + private set // Allow external read but not write + + /** + * Returns the content and prevents its use again. + */ + fun getContentIfNotHandled(): T? { + return if (hasBeenHandled) { + null + } else { + hasBeenHandled = true + content + } + } + + /** + * Returns the content, even if it's already been handled. + */ + fun peekContent(): T = content +} diff --git a/app/src/main/java/dgca/wallet/app/android/MainActivity.kt b/app/src/main/java/dgca/wallet/app/android/MainActivity.kt index 3c452fd9..361894e2 100644 --- a/app/src/main/java/dgca/wallet/app/android/MainActivity.kt +++ b/app/src/main/java/dgca/wallet/app/android/MainActivity.kt @@ -43,20 +43,20 @@ class MainActivity : AppCompatActivity() { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - setSupportActionBar(binding.toolbar) - - val navHostFragment = - supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment + val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController val appBarConfiguration = AppBarConfiguration( topLevelDestinationIds = setOf(), fallbackOnNavigateUpListener = ::onSupportNavigateUp ) - binding.toolbar - .setupWithNavController(navController, appBarConfiguration) + binding.toolbar.setupWithNavController(navController, appBarConfiguration) } fun clearBackground() { window.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.white)) } + + fun disableBackButton() { + binding.toolbar.navigationIcon = null + } } \ No newline at end of file diff --git a/app/src/main/java/dgca/wallet/app/android/certificate/CertificatesFragment.kt b/app/src/main/java/dgca/wallet/app/android/certificate/CertificatesFragment.kt index 36e9f2f7..1a4c0599 100644 --- a/app/src/main/java/dgca/wallet/app/android/certificate/CertificatesFragment.kt +++ b/app/src/main/java/dgca/wallet/app/android/certificate/CertificatesFragment.kt @@ -27,6 +27,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.activity.addCallback +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -47,12 +48,13 @@ class CertificatesFragment : Fragment(), CertificateCardsAdapter.CertificateCard } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + (activity as MainActivity).disableBackButton() _binding = FragmentCertificatesBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - viewModel.fetchCertificates() + super.onViewCreated(view, savedInstanceState) requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { requireActivity().finish() } binding.scanCode.setOnClickListener { val action = CertificatesFragmentDirections.actionCertificatesFragmentToCodeReaderFragment() @@ -62,6 +64,10 @@ class CertificatesFragment : Fragment(), CertificateCardsAdapter.CertificateCard viewModel.certificates.observe(viewLifecycleOwner, { setCertificateCards(it) }) + viewModel.inProgress.observe(viewLifecycleOwner, { + binding.progressView.isVisible = it + }) + viewModel.fetchCertificates() } private fun setCertificateCards(certificateCards: List) { diff --git a/app/src/main/java/dgca/wallet/app/android/certificate/CertificatesViewModel.kt b/app/src/main/java/dgca/wallet/app/android/certificate/CertificatesViewModel.kt index 30e27234..7efd0ccb 100644 --- a/app/src/main/java/dgca/wallet/app/android/certificate/CertificatesViewModel.kt +++ b/app/src/main/java/dgca/wallet/app/android/certificate/CertificatesViewModel.kt @@ -43,11 +43,14 @@ class CertificatesViewModel @Inject constructor( private val certificateDecoder: CertificateDecoder ) : ViewModel() { - private val _certificates = MutableLiveData>() val certificates: LiveData> = _certificates + private val _inProgress = MutableLiveData() + val inProgress: LiveData = _inProgress + fun fetchCertificates() { + _inProgress.value = true viewModelScope.launch { var certificateCards: List? = null withContext(Dispatchers.IO) { @@ -62,6 +65,7 @@ class CertificatesViewModel @Inject constructor( ) } } + _inProgress.value = false _certificates.value = certificateCards ?: emptyList() } } diff --git a/app/src/main/java/dgca/wallet/app/android/certificate/claim/ClaimCertificateFragment.kt b/app/src/main/java/dgca/wallet/app/android/certificate/claim/ClaimCertificateFragment.kt new file mode 100644 index 00000000..a932d812 --- /dev/null +++ b/app/src/main/java/dgca/wallet/app/android/certificate/claim/ClaimCertificateFragment.kt @@ -0,0 +1,88 @@ +/* + * ---license-start + * eu-digital-green-certificates / dgca-verifier-app-android + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + * + * Created by mykhailo.nester on 5/12/21 12:27 AM + */ + +package dgca.wallet.app.android.certificate.claim + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import dagger.hilt.android.AndroidEntryPoint +import dgca.wallet.app.android.databinding.FragmentCertificateClaimBinding + +@AndroidEntryPoint +class ClaimCertificateFragment : Fragment() { + + private val args by navArgs() + private val viewModel by viewModels() + private var _binding: FragmentCertificateClaimBinding? = null + private val binding get() = _binding!! + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = FragmentCertificateClaimBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.backBtn.setOnClickListener { + findNavController().popBackStack() + } + binding.saveBtn.setOnClickListener { + viewModel.save(args.qrCodeText, binding.tanTextField.editText?.text.toString()) + } + + viewModel.inProgress.observe(viewLifecycleOwner, { + binding.progressView.isVisible = it + }) + viewModel.event.observe(viewLifecycleOwner) { event -> + event.getContentIfNotHandled()?.let { + onViewModelEvent(it) + } + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + private fun onViewModelEvent(event: ClaimCertificateViewModel.ClaimCertEvent) { + when (event) { + is ClaimCertificateViewModel.ClaimCertEvent.OnCertClaimed -> { + if (event.result) { + Toast.makeText(requireContext(), "Certificate claimed", Toast.LENGTH_SHORT).show() + val action = ClaimCertificateFragmentDirections.actionClaimCertificateFragmentToCertificatesFragment() + findNavController().navigate(action) + } else { + Toast.makeText(requireContext(), "Certificate not claimed", Toast.LENGTH_SHORT).show() + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/dgca/wallet/app/android/certificate/claim/ClaimCertificateViewModel.kt b/app/src/main/java/dgca/wallet/app/android/certificate/claim/ClaimCertificateViewModel.kt new file mode 100644 index 00000000..2d358c02 --- /dev/null +++ b/app/src/main/java/dgca/wallet/app/android/certificate/claim/ClaimCertificateViewModel.kt @@ -0,0 +1,134 @@ +/* + * ---license-start + * eu-digital-green-certificates / dgca-verifier-app-android + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + * + * Created by mykhailo.nester on 5/12/21 12:27 AM + */ + +package dgca.wallet.app.android.certificate.claim + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import dgca.verifier.app.decoder.* +import dgca.verifier.app.decoder.base45.Base45Service +import dgca.verifier.app.decoder.cbor.CborService +import dgca.verifier.app.decoder.compression.CompressorService +import dgca.verifier.app.decoder.cose.CoseService +import dgca.verifier.app.decoder.model.GreenCertificate +import dgca.verifier.app.decoder.model.VerificationResult +import dgca.verifier.app.decoder.prefixvalidation.PrefixValidationService +import dgca.verifier.app.decoder.schema.SchemaValidator +import dgca.wallet.app.android.Event +import dgca.wallet.app.android.data.CertificateModel +import dgca.wallet.app.android.data.WalletRepository +import dgca.wallet.app.android.model.ClaimRequest +import dgca.wallet.app.android.model.PublicKeyData +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import timber.log.Timber +import java.security.* +import java.util.* +import javax.inject.Inject + +@HiltViewModel +class ClaimCertificateViewModel @Inject constructor( + private val prefixValidationService: PrefixValidationService, + private val base45Service: Base45Service, + private val compressorService: CompressorService, + private val coseService: CoseService, + private val schemaValidator: SchemaValidator, + private val cborService: CborService, + private val walletRepository: WalletRepository +) : ViewModel() { + + private val _certificate = MutableLiveData() + val certificate: LiveData = _certificate + + private val _inProgress = MutableLiveData() + val inProgress: LiveData = _inProgress + + private val _event = MutableLiveData>() + val event: LiveData> = _event + + fun save(qrCode: String, tan: String) { + viewModelScope.launch { + _inProgress.value = true + var greenCertificate: GreenCertificate? = null + val verificationResult = VerificationResult() + var result = false + + withContext(Dispatchers.IO) { + val plainInput = prefixValidationService.decode(qrCode, verificationResult) + val compressedCose = base45Service.decode(plainInput, verificationResult) + val cose = compressorService.decode(compressedCose, verificationResult) + + val coseData = coseService.decode(cose, verificationResult) + if (coseData == null) { + Timber.d("Verification failed: COSE not decoded") + return@withContext + } + + schemaValidator.validate(coseData.cbor, verificationResult) + greenCertificate = cborService.decode(coseData.cbor, verificationResult) + + // Claim Cert + val dgci = greenCertificate?.getDgci() + val certHash = cose.getValidationDataFromCOSE().toHash() + val tanHash = tan.toByteArray().toHash() + + val keyPairData = cose.generateKeyPair() + val keyPair: KeyPair? = keyPairData?.keyPair + val sigAlg = keyPairData?.algo + + if (keyPair == null || sigAlg == null) { + Timber.d("Key not generated") + return@withContext + } + + val keyData = PublicKeyData( + keyPair.public.algorithm, + Base64.getEncoder().encodeToString(keyPair.public.encoded) + ) + + val signature = generateClaimSignature(tanHash, certHash, keyData.value, keyPair.private, sigAlg) + val request = ClaimRequest( + dgci, + certHash, + tanHash, + keyData, + sigAlg, + signature + ) + +// TODO: handle response + result = walletRepository.claimCertificate(request) + + } + _inProgress.value = false + _event.value = Event(ClaimCertEvent.OnCertClaimed(result)) + } + } + + sealed class ClaimCertEvent { + data class OnCertClaimed(val result: Boolean) : ClaimCertEvent() + } +} \ No newline at end of file diff --git a/app/src/main/java/dgca/wallet/app/android/data/BaseRepository.kt b/app/src/main/java/dgca/wallet/app/android/data/BaseRepository.kt index e5d8c6b2..14d7f1a8 100644 --- a/app/src/main/java/dgca/wallet/app/android/data/BaseRepository.kt +++ b/app/src/main/java/dgca/wallet/app/android/data/BaseRepository.kt @@ -22,30 +22,28 @@ package dgca.wallet.app.android.data -import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.ResponseBody +import timber.log.Timber import java.net.SocketTimeoutException import java.net.UnknownHostException -private const val TAG = "BaseRepository" - abstract class BaseRepository : Repository { suspend fun

execute(doOnAsyncBlock: suspend () -> P): P? { return withContext(Dispatchers.IO) { return@withContext try { - Log.v(TAG, "Do network coroutine work") + Timber.d("Do network coroutine work") doOnAsyncBlock.invoke() } catch (e: UnknownHostException) { - Log.w(TAG, "UnknownHostException", e) + Timber.w(e, "UnknownHostException") null } catch (e: SocketTimeoutException) { - Log.w(TAG, "SocketTimeoutException", e) + Timber.w(e, "SocketTimeoutException") null } catch (throwable: Throwable) { - Log.w(TAG, "Throwable", throwable) + Timber.w(throwable, "Throwable") null } } diff --git a/app/src/main/java/dgca/wallet/app/android/data/WalletRepository.kt b/app/src/main/java/dgca/wallet/app/android/data/WalletRepository.kt index 01cff84c..a4663e60 100644 --- a/app/src/main/java/dgca/wallet/app/android/data/WalletRepository.kt +++ b/app/src/main/java/dgca/wallet/app/android/data/WalletRepository.kt @@ -22,7 +22,9 @@ package dgca.wallet.app.android.data +import dgca.wallet.app.android.model.ClaimRequest + interface WalletRepository { - suspend fun claimCertificate(): Boolean? + suspend fun claimCertificate(request: ClaimRequest): Boolean } \ No newline at end of file diff --git a/app/src/main/java/dgca/wallet/app/android/data/WalletRepositoryImpl.kt b/app/src/main/java/dgca/wallet/app/android/data/WalletRepositoryImpl.kt new file mode 100644 index 00000000..be1d4ad5 --- /dev/null +++ b/app/src/main/java/dgca/wallet/app/android/data/WalletRepositoryImpl.kt @@ -0,0 +1,42 @@ +/* + * ---license-start + * eu-digital-green-certificates / dgca-verifier-app-android + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + * + * Created by mykhailo.nester on 5/11/21 9:36 PM + */ + +package dgca.wallet.app.android.data + +import dgca.wallet.app.android.data.remote.ApiService +import dgca.wallet.app.android.model.ClaimRequest +import javax.inject.Inject + +class WalletRepositoryImpl @Inject constructor( + private val apiService: ApiService, +) : BaseRepository(), WalletRepository { + + + override suspend fun claimCertificate(request: ClaimRequest): Boolean { + return execute { + val response = apiService.claimCertificate(request) + val body = response.body() ?: return@execute false + + return@execute true + } == true + } +} \ No newline at end of file diff --git a/app/src/main/java/dgca/wallet/app/android/data/remote/ApiService.kt b/app/src/main/java/dgca/wallet/app/android/data/remote/ApiService.kt index da30b9cc..0f51a251 100644 --- a/app/src/main/java/dgca/wallet/app/android/data/remote/ApiService.kt +++ b/app/src/main/java/dgca/wallet/app/android/data/remote/ApiService.kt @@ -22,13 +22,16 @@ package dgca.wallet.app.android.data.remote +import dgca.wallet.app.android.model.ClaimRequest import okhttp3.ResponseBody import retrofit2.Response -import retrofit2.http.GET +import retrofit2.http.Body +import retrofit2.http.POST interface ApiService { -// TODO: update - @GET("/cert") - suspend fun claimCertificate(): Response + @POST("/dgca-issuance-service/dgci/wallet/claim") + suspend fun claimCertificate( + @Body request: ClaimRequest + ): Response } \ No newline at end of file diff --git a/app/src/main/java/dgca/wallet/app/android/di/DecoderModule.kt b/app/src/main/java/dgca/wallet/app/android/di/DecoderModule.kt index 37702cbb..47fc09a0 100644 --- a/app/src/main/java/dgca/wallet/app/android/di/DecoderModule.kt +++ b/app/src/main/java/dgca/wallet/app/android/di/DecoderModule.kt @@ -29,6 +29,20 @@ import dagger.hilt.components.SingletonComponent import dgca.verifier.app.decoder.CertificateDecoder import dgca.verifier.app.decoder.DefaultCertificateDecoder import dgca.verifier.app.decoder.base45.Base45Decoder +import dgca.verifier.app.decoder.base45.Base45Service +import dgca.verifier.app.decoder.base45.DefaultBase45Service +import dgca.verifier.app.decoder.cbor.CborService +import dgca.verifier.app.decoder.cbor.DefaultCborService +import dgca.verifier.app.decoder.compression.CompressorService +import dgca.verifier.app.decoder.compression.DefaultCompressorService +import dgca.verifier.app.decoder.cose.CoseService +import dgca.verifier.app.decoder.cose.CryptoService +import dgca.verifier.app.decoder.cose.DefaultCoseService +import dgca.verifier.app.decoder.cose.VerificationCryptoService +import dgca.verifier.app.decoder.prefixvalidation.DefaultPrefixValidationService +import dgca.verifier.app.decoder.prefixvalidation.PrefixValidationService +import dgca.verifier.app.decoder.schema.DefaultSchemaValidator +import dgca.verifier.app.decoder.schema.SchemaValidator import javax.inject.Singleton /** @@ -46,4 +60,32 @@ object DecoderModule { @Singleton @Provides fun provideCertificateDecoder(base45Decoder: Base45Decoder): CertificateDecoder = DefaultCertificateDecoder(base45Decoder) + + @Singleton + @Provides + fun providePrefixValidationService(): PrefixValidationService = DefaultPrefixValidationService() + + @Singleton + @Provides + fun provideBase45ServiceDecoder(): Base45Service = DefaultBase45Service() + + @Singleton + @Provides + fun provideCompressorService(): CompressorService = DefaultCompressorService() + + @Singleton + @Provides + fun provideCoseService(): CoseService = DefaultCoseService() + + @Singleton + @Provides + fun provideSchemaValidator(): SchemaValidator = DefaultSchemaValidator() + + @Singleton + @Provides + fun provideCborService(): CborService = DefaultCborService() + + @Singleton + @Provides + fun provideCryptoService(): CryptoService = VerificationCryptoService() } \ No newline at end of file diff --git a/app/src/main/java/dgca/wallet/app/android/di/NetworkModule.kt b/app/src/main/java/dgca/wallet/app/android/di/NetworkModule.kt index 801ae72c..d98beff0 100644 --- a/app/src/main/java/dgca/wallet/app/android/di/NetworkModule.kt +++ b/app/src/main/java/dgca/wallet/app/android/di/NetworkModule.kt @@ -45,7 +45,7 @@ import javax.inject.Singleton private const val CONNECT_TIMEOUT = 30L -const val BASE_URL = "https://dgca-verifier-service.cfapps.eu10.hana.ondemand.com/" +const val BASE_URL = "https://dgca-issuance-web.cfapps.eu10.hana.ondemand.com/" @InstallIn(SingletonComponent::class) @Module diff --git a/app/src/main/java/dgca/wallet/app/android/di/RepositoryModule.kt b/app/src/main/java/dgca/wallet/app/android/di/RepositoryModule.kt new file mode 100644 index 00000000..ff5acc5a --- /dev/null +++ b/app/src/main/java/dgca/wallet/app/android/di/RepositoryModule.kt @@ -0,0 +1,40 @@ +/* + * ---license-start + * eu-digital-green-certificates / dgca-verifier-app-android + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + * + * Created by mykhailo.nester on 5/11/21 9:19 PM + */ + +package dgca.wallet.app.android.di + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dgca.wallet.app.android.data.WalletRepository +import dgca.wallet.app.android.data.WalletRepositoryImpl +import javax.inject.Singleton + +@InstallIn(SingletonComponent::class) +@Module +abstract class RepositoryModule { + + @Singleton + @Binds + abstract fun bindWalletRepository(repository: WalletRepositoryImpl): WalletRepository +} \ No newline at end of file diff --git a/app/src/main/java/dgca/wallet/app/android/model/ClaimRequest.kt b/app/src/main/java/dgca/wallet/app/android/model/ClaimRequest.kt new file mode 100644 index 00000000..722791dd --- /dev/null +++ b/app/src/main/java/dgca/wallet/app/android/model/ClaimRequest.kt @@ -0,0 +1,41 @@ +/* + * ---license-start + * eu-digital-green-certificates / dgca-verifier-app-android + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + * + * Created by mykhailo.nester on 5/11/21 11:13 PM + */ + +package dgca.wallet.app.android.model + +import com.google.gson.annotations.SerializedName + +data class ClaimRequest( + + @SerializedName("DGCI") + val dgci: String? = null, + + @SerializedName("certhash") + val certHash: String? = null, + + @SerializedName("TANHash") + val tanHash: String? = null, + + val publicKey: PublicKeyData? = null, + val sigAlg: String? = null, + val signature: String? = null +) \ No newline at end of file diff --git a/app/src/main/java/dgca/wallet/app/android/certificate/ClaimCertificateFragment.kt b/app/src/main/java/dgca/wallet/app/android/model/PublicKeyData.kt similarity index 81% rename from app/src/main/java/dgca/wallet/app/android/certificate/ClaimCertificateFragment.kt rename to app/src/main/java/dgca/wallet/app/android/model/PublicKeyData.kt index f709155b..8dbc0963 100644 --- a/app/src/main/java/dgca/wallet/app/android/certificate/ClaimCertificateFragment.kt +++ b/app/src/main/java/dgca/wallet/app/android/model/PublicKeyData.kt @@ -17,11 +17,12 @@ * limitations under the License. * ---license-end * - * Created by osarapulov on 5/7/21 9:33 AM + * Created by mykhailo.nester on 5/11/21 11:13 PM */ -package dgca.wallet.app.android.certificate +package dgca.wallet.app.android.model -import androidx.fragment.app.Fragment - -class ClaimCertificateFragment : Fragment() \ No newline at end of file +data class PublicKeyData( + val type: String, + val value: String +) \ No newline at end of file diff --git a/app/src/main/res/drawable/white_gradient.xml b/app/src/main/res/drawable/white_gradient.xml new file mode 100644 index 00000000..ba6be913 --- /dev/null +++ b/app/src/main/res/drawable/white_gradient.xml @@ -0,0 +1,29 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/certificate_card_view.xml b/app/src/main/res/layout/certificate_card_view.xml index 7905f231..51b240e8 100644 --- a/app/src/main/res/layout/certificate_card_view.xml +++ b/app/src/main/res/layout/certificate_card_view.xml @@ -27,7 +27,8 @@ android:layout_height="wrap_content" android:background="?attr/selectableItemBackground" android:clickable="true" - android:focusable="true"> + android:focusable="true" + tools:background="@color/white"> - - + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_certificate_view.xml b/app/src/main/res/layout/fragment_certificate_view.xml index de9afd76..391bd5cc 100644 --- a/app/src/main/res/layout/fragment_certificate_view.xml +++ b/app/src/main/res/layout/fragment_certificate_view.xml @@ -24,7 +24,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:background="@color/white"> + android:layout_height="match_parent" + tools:background="@color/white"> + app:layout_constraintTop_toTopOf="parent" /> -