Skip to content

Commit

Permalink
Feature/claim cert (#17)
Browse files Browse the repository at this point in the history
- 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;
  • Loading branch information
MykhailoNester authored May 12, 2021
1 parent a2110b3 commit 9de9878
Show file tree
Hide file tree
Showing 30 changed files with 723 additions and 81 deletions.
2 changes: 2 additions & 0 deletions .idea/dictionaries/mykhailo_nester.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,6 @@ dependencies {
implementation Deps.gson_converter

implementation Deps.timber

testImplementation Deps.test_junit
}
6 changes: 4 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dgca.wallet.app.android">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

<application
android:name=".DgcaWalletApplication"
android:allowBackup="false"
Expand All @@ -11,8 +14,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.DgcaWalletAppAndroid">

<activity
android:name=".MainActivity">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
4 changes: 1 addition & 3 deletions app/src/main/java/dgca/wallet/app/android/AuthFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ class CodeReaderFragment : Fragment(), NavController.OnDestinationChangedListene
requestCameraPermission()

val formats: Collection<BarcodeFormat> = 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())
}
Expand All @@ -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)
}

Expand Down
27 changes: 27 additions & 0 deletions app/src/main/java/dgca/wallet/app/android/Event.kt
Original file line number Diff line number Diff line change
@@ -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<out T>(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
}
12 changes: 6 additions & 6 deletions app/src/main/java/dgca/wallet/app/android/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand All @@ -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<CertificateCard>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,14 @@ class CertificatesViewModel @Inject constructor(
private val certificateDecoder: CertificateDecoder
) : ViewModel() {


private val _certificates = MutableLiveData<List<CertificateCard>>()
val certificates: LiveData<List<CertificateCard>> = _certificates

private val _inProgress = MutableLiveData<Boolean>()
val inProgress: LiveData<Boolean> = _inProgress

fun fetchCertificates() {
_inProgress.value = true
viewModelScope.launch {
var certificateCards: List<CertificateCard>? = null
withContext(Dispatchers.IO) {
Expand All @@ -62,6 +65,7 @@ class CertificatesViewModel @Inject constructor(
)
}
}
_inProgress.value = false
_certificates.value = certificateCards ?: emptyList()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ClaimCertificateFragmentArgs>()
private val viewModel by viewModels<ClaimCertificateViewModel>()
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()
}
}
}
}
}
Loading

0 comments on commit 9de9878

Please sign in to comment.