Skip to content

Commit

Permalink
code cleanup and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
MykhailoNester authored May 14, 2021
1 parent 6b70103 commit 413807a
Show file tree
Hide file tree
Showing 46 changed files with 213 additions and 283 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
package dgca.wallet.app.android

import android.app.Application
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber

@HiltAndroidApp
class DgcaWalletApplication : Application() {

override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Expand Down
5 changes: 0 additions & 5 deletions app/src/main/java/dgca/wallet/app/android/Event.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,4 @@ open class Event<out T>(private val content: T) {
content
}
}

/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
6 changes: 3 additions & 3 deletions app/src/main/java/dgca/wallet/app/android/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ class MainActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
if (item.itemId == R.id.settings) {
return if (item.itemId == R.id.settings) {
navController.navigate(R.id.settingsFragment)
return true
true
} else {
return navController.navigateUp()
navController.navigateUp()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import dagger.hilt.android.AndroidEntryPoint
import dgca.wallet.app.android.FORMATTED_YEAR_MONTH_DAY
import dgca.wallet.app.android.R
import dgca.wallet.app.android.YEAR_MONTH_DAY
import dgca.wallet.app.android.data.CertificateData
import dgca.wallet.app.android.data.CertificateModel
import dgca.wallet.app.android.data.getCertificateListData
import dgca.wallet.app.android.databinding.FragmentCertificateClaimBinding
Expand Down Expand Up @@ -93,21 +92,25 @@ class ClaimCertificateFragment : Fragment() {
binding.personFullName.text =
getString(R.string.person_full_name_placeholder, certificate.person.givenName, certificate.person.familyName)
binding.personStandardisedFamilyName.text = certificate.person.standardisedFamilyName
binding.personStandardisedFamilyNameTitle.isVisible = true
binding.personStandardisedGivenName.text = certificate.person.standardisedGivenName
binding.personStandardisedGivenNameTitle.isVisible = true
binding.dateOfBirth.text = certificate.dateOfBirth.parseFromTo(YEAR_MONTH_DAY, FORMATTED_YEAR_MONTH_DAY)
binding.dateOfBirthTitle.isVisible = true
}

private fun onViewModelEvent(event: ClaimCertificateViewModel.ClaimCertEvent) {
when (event) {
is ClaimCertificateViewModel.ClaimCertEvent.OnCertClaimed -> {
if (event.result) {
if (event.isClaimed) {
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()
}
}
is ClaimCertificateViewModel.ClaimCertEvent.OnCertNotClaimed -> {
Toast.makeText(requireContext(), event.error, Toast.LENGTH_SHORT).show()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ 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.data.local.toCertificateModel
import dgca.wallet.app.android.data.remote.ApiResult
import dgca.wallet.app.android.data.remote.ClaimResponse
import dgca.wallet.app.android.model.ClaimRequest
import dgca.wallet.app.android.model.PublicKeyData
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -99,7 +101,7 @@ class ClaimCertificateViewModel @Inject constructor(
fun save(qrCode: String, tan: String) {
viewModelScope.launch {
_inProgress.value = true
var result = false
var claimResult: ApiResult<ClaimResponse>? = null

withContext(Dispatchers.IO) {
if (greenCertificate == null || cose.isEmpty()) {
Expand Down Expand Up @@ -134,18 +136,16 @@ class ClaimCertificateViewModel @Inject constructor(
signature
)

// TODO: handle response
result = walletRepository.claimCertificate(qrCode, request)
if (result) {

}
claimResult = walletRepository.claimCertificate(qrCode, request)
}
_inProgress.value = false
_event.value = Event(ClaimCertEvent.OnCertClaimed(result))
claimResult?.success?.let { _event.value = Event(ClaimCertEvent.OnCertClaimed(true)) }
claimResult?.error?.let { _event.value = Event(ClaimCertEvent.OnCertNotClaimed(it.details)) }
}
}

sealed class ClaimCertEvent {
data class OnCertClaimed(val result: Boolean) : ClaimCertEvent()
data class OnCertClaimed(val isClaimed: Boolean) : ClaimCertEvent()
data class OnCertNotClaimed(val error: String) : ClaimCertEvent()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import dgca.wallet.app.android.R
import dgca.wallet.app.android.databinding.FragmentCertificateTanBinding

@AndroidEntryPoint
Expand All @@ -48,8 +49,12 @@ class TanFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
binding.nextBtn.setOnClickListener {
val tan = binding.tanTextField.editText?.text.toString()
val action = TanFragmentDirections.actionTanFragmentToClaimCertificateFragment(args.qrCodeText, tan)
findNavController().navigate(action)
if (tan.isEmpty()) {
binding.tanTextField.error = getString(R.string.tan_empty_error)
} else {
val action = TanFragmentDirections.actionTanFragmentToClaimCertificateFragment(args.qrCodeText, tan)
findNavController().navigate(action)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,14 @@ class ViewCertificateFragment : Fragment() {
}

private fun showUserData(certificate: CertificateModel) {
binding.nameTitle.isVisible = true
binding.personFullName.text =
getString(R.string.person_full_name_placeholder, certificate.person.givenName, certificate.person.familyName)
binding.personStandardisedFamilyName.text = certificate.person.standardisedFamilyName
binding.personStandardisedFamilyNameTitle.isVisible = true
binding.personStandardisedGivenName.text = certificate.person.standardisedGivenName
binding.personStandardisedGivenNameTitle.isVisible = true
binding.dateOfBirth.text = certificate.dateOfBirth.parseFromTo(YEAR_MONTH_DAY, FORMATTED_YEAR_MONTH_DAY)
binding.dateOfBirthTitle.isVisible = true
}
}
49 changes: 42 additions & 7 deletions app/src/main/java/dgca/wallet/app/android/data/BaseRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,69 @@

package dgca.wallet.app.android.data

import dgca.wallet.app.android.data.remote.ApiResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.ResponseBody
import retrofit2.Response
import timber.log.Timber
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import kotlin.coroutines.CoroutineContext

abstract class BaseRepository : Repository {

suspend fun <P> execute(doOnAsyncBlock: suspend () -> P): P? {
return withContext(Dispatchers.IO) {
return@withContext try {
Timber.d("Do network coroutine work")
doOnAsyncBlock.invoke()
} catch (throwable: Throwable) {
Timber.w(throwable, "Throwable")
null
}
}
}

suspend fun <P> doApiBackgroundWork(doOnAsyncBlock: suspend CoroutineScope.() -> Response<P>): ApiResult<P> =
doCoroutineWork(doOnAsyncBlock, Dispatchers.IO)

private suspend inline fun <P> doCoroutineWork(
crossinline doOnAsyncBlock: suspend CoroutineScope.() -> Response<P>,
context: CoroutineContext
): ApiResult<P> {
var error: ApiResult<P>? = null
val response = withContext(context) {
return@withContext try {
Timber.v("Do network coroutine work")
doOnAsyncBlock.invoke(this)
} catch (e: UnknownHostException) {
Timber.w(e, "UnknownHostException")
error = ApiResult.error(
error = e.toString(),
errorDescription = "Server is unreachable. Please, check network connection."
)
null
} catch (e: SocketTimeoutException) {
Timber.w(e, "SocketTimeoutException")
error = ApiResult.error(
error = e.toString(),
errorDescription = "No internet connection"
)
null
} catch (throwable: Throwable) {
Timber.w(throwable, "Throwable")
error = ApiResult.error(
error = throwable.toString(),
errorDescription = "Unknown exception. Please, try again."
)
null
}
}
}
}

@Suppress("BlockingMethodInNonBlockingContext")
suspend fun ResponseBody.stringSuspending() =
withContext(Dispatchers.IO) { string() }
return if (error != null) {
error!!
} else {
ApiResult(response)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
package dgca.wallet.app.android.data

import dgca.wallet.app.android.certificate.CertificateCard
import dgca.wallet.app.android.data.remote.ApiResult
import dgca.wallet.app.android.data.remote.ClaimResponse
import dgca.wallet.app.android.model.ClaimRequest

interface WalletRepository {

suspend fun claimCertificate(qrCode: String, request: ClaimRequest): Boolean
suspend fun claimCertificate(qrCode: String, request: ClaimRequest): ApiResult<ClaimResponse>

suspend fun getCertificates(): List<CertificateCard>?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ import dgca.wallet.app.android.certificate.CertificateCard
import dgca.wallet.app.android.data.local.CertificateDao
import dgca.wallet.app.android.data.local.CertificateEntity
import dgca.wallet.app.android.data.local.toCertificateModel
import dgca.wallet.app.android.data.remote.ApiResult
import dgca.wallet.app.android.data.remote.ApiService
import dgca.wallet.app.android.data.remote.ClaimResponse
import dgca.wallet.app.android.model.ClaimRequest
import dgca.wallet.app.android.security.KeyStoreCryptor
import javax.inject.Inject
Expand All @@ -40,33 +42,23 @@ class WalletRepositoryImpl @Inject constructor(
private val certificateDecoder: CertificateDecoder
) : BaseRepository(), WalletRepository {


override suspend fun claimCertificate(qrCode: String, request: ClaimRequest): Boolean {
return execute {
val response = apiService.claimCertificate(request)
if (response.isSuccessful) {
val tan = response.body()?.tan ?: ""
override suspend fun claimCertificate(qrCode: String, request: ClaimRequest): ApiResult<ClaimResponse> {
return doApiBackgroundWork { apiService.claimCertificate(request) }.also { result ->
result.success?.let {
val tan = result.rawResponse?.body()?.tan ?: ""
val codeEncrypted = keyStoreCryptor.encrypt(qrCode)
val tanEncrypted = keyStoreCryptor.encrypt(tan)

if (codeEncrypted == null || tanEncrypted == null) {
return@execute false
}

val result = certificateDao.insert(
CertificateEntity(
qrCodeText = codeEncrypted,
tan = tanEncrypted
if (codeEncrypted != null && tanEncrypted != null) {
certificateDao.insert(
CertificateEntity(
qrCodeText = codeEncrypted,
tan = tanEncrypted
)
)
)

if (result != -1L) {
return@execute true
}
}

return@execute false
} == true
}
}

override suspend fun getCertificates(): List<CertificateCard>? {
Expand Down
80 changes: 80 additions & 0 deletions app/src/main/java/dgca/wallet/app/android/data/remote/ApiResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* ---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/14/21 2:03 PM
*/

package dgca.wallet.app.android.data.remote

import com.google.gson.Gson
import okhttp3.ResponseBody
import retrofit2.Response

class ApiResult<T>(response: Response<T>? = null) {

var success: T? = response?.body()
var error: CommonError? = response?.errorBody().parseError(response)
var rawResponse: Response<T>? = response

companion object {

fun <T> error(
error: String = "Unknown",
errorDescription: String = "Unknown exception."
): ApiResult<T> {
val result = ApiResult<T>()
result.error =
CommonError(problem = error, details = errorDescription)
return result
}
}

private fun ResponseBody?.parseError(response: Response<T>?): CommonError? {
var error: CommonError? =
try {
var responseError: CommonError?
this?.charStream().use { reader ->
val json = reader?.readText()
val result: CommonError? = Gson().fromJson(json, CommonError::class.java)

responseError = if (result != null && result.details.isEmpty()) {
null
} else {
result
}
}
responseError
} catch (e: Exception) {
null
}

if (this != null && error == null) {
error = CommonError(problem = "unknown", details = response?.message() ?: "")
}

return error
}
}

data class CommonError(
val problem: String,
val details: String,
val code: String = "",
val sendValue: String = ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class SecurityKeyWrapper(private val keyPair: KeyPair) {
var counter = 0
val originalSum = ByteArray(chunksAmount * BLOCK_SIZE) { EMPTY_BYTE }
for (i in 0 until chunksAmount) {
val chunk: ByteArray = ByteArray(OUTPUT_BLOCK_SIZE)
val chunk = ByteArray(OUTPUT_BLOCK_SIZE)
for (j in 0 until OUTPUT_BLOCK_SIZE) {
chunk[j] = decoded[i * OUTPUT_BLOCK_SIZE + j]
}
Expand Down
Binary file removed app/src/main/res/drawable-hdpi/check.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-hdpi/error.png
Binary file not shown.
Binary file not shown.
Binary file removed app/src/main/res/drawable-hdpi/verifier_icon.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-ldpi/check.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-ldpi/error.png
Binary file not shown.
Binary file not shown.
Binary file removed app/src/main/res/drawable-ldpi/verifier_icon.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-mdpi/check.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-mdpi/error.png
Binary file not shown.
Binary file not shown.
Binary file removed app/src/main/res/drawable-mdpi/verifier_icon.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-xhdpi/check.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-xhdpi/error.png
Binary file not shown.
Binary file not shown.
Binary file removed app/src/main/res/drawable-xhdpi/verifier_icon.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-xxhdpi/check.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-xxhdpi/error.png
Binary file not shown.
Binary file not shown.
Binary file removed app/src/main/res/drawable-xxhdpi/verifier_icon.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-xxxhdpi/check.png
Binary file not shown.
Binary file removed app/src/main/res/drawable-xxxhdpi/error.png
Binary file not shown.
Binary file not shown.
Binary file removed app/src/main/res/drawable-xxxhdpi/verifier_icon.png
Binary file not shown.
Loading

0 comments on commit 413807a

Please sign in to comment.