Skip to content

Commit d144499

Browse files
Implemented trigerring of public keys sync (eu-digital-green-certificates#43)
1 parent aac7154 commit d144499

File tree

5 files changed

+124
-21
lines changed

5 files changed

+124
-21
lines changed

app/src/main/java/dgca/verifier/app/android/data/VerifierRepositoryImpl.kt

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,37 +29,43 @@ import dgca.verifier.app.android.data.remote.ApiService
2929
import dgca.verifier.app.android.security.KeyStoreCryptor
3030
import dgca.verifier.app.decoder.base64ToX509Certificate
3131
import dgca.verifier.app.decoder.toBase64
32+
import kotlinx.coroutines.sync.Mutex
33+
import kotlinx.coroutines.sync.withLock
3234
import timber.log.Timber
3335
import java.net.HttpURLConnection
3436
import java.security.MessageDigest
3537
import java.security.cert.Certificate
3638
import javax.inject.Inject
3739

3840
class VerifierRepositoryImpl @Inject constructor(
39-
private val apiService: ApiService,
40-
private val preferences: Preferences,
41-
private val db: AppDatabase,
42-
private val keyStoreCryptor: KeyStoreCryptor
41+
private val apiService: ApiService,
42+
private val preferences: Preferences,
43+
private val db: AppDatabase,
44+
private val keyStoreCryptor: KeyStoreCryptor
4345
) : BaseRepository(), VerifierRepository {
4446

4547
private val validCertList = mutableListOf<String>()
48+
private val mutex = Mutex()
4649

4750
override suspend fun fetchCertificates(): Boolean? {
48-
return execute {
49-
val response = apiService.getCertStatus()
50-
val body = response.body() ?: return@execute false
51-
validCertList.clear()
52-
validCertList.addAll(body)
53-
54-
val resumeToken = preferences.resumeToken
55-
fetchCertificate(resumeToken)
56-
db.keyDao().deleteAllExcept(validCertList.toTypedArray())
57-
return@execute true
51+
mutex.withLock {
52+
return execute {
53+
val response = apiService.getCertStatus()
54+
val body = response.body() ?: return@execute false
55+
validCertList.clear()
56+
validCertList.addAll(body)
57+
58+
val resumeToken = preferences.resumeToken
59+
fetchCertificate(resumeToken)
60+
db.keyDao().deleteAllExcept(validCertList.toTypedArray())
61+
return@execute true
62+
}
5863
}
5964
}
6065

6166
override suspend fun getCertificatesBy(kid: String): List<Certificate> {
62-
return db.keyDao().getByKid(kid).map { keyStoreCryptor.decrypt(it.key)?.base64ToX509Certificate()!! }
67+
return db.keyDao().getByKid(kid)
68+
.map { keyStoreCryptor.decrypt(it.key)?.base64ToX509Certificate()!! }
6369
}
6470

6571
private suspend fun fetchCertificate(resumeToken: Long) {
@@ -94,9 +100,9 @@ class VerifierRepositoryImpl @Inject constructor(
94100

95101
val cert = responseStr.base64ToX509Certificate() ?: return false
96102
val certKid = MessageDigest.getInstance("SHA-256")
97-
.digest(cert.encoded)
98-
.copyOf(8)
99-
.toBase64()
103+
.digest(cert.encoded)
104+
.copyOf(8)
105+
.toBase64()
100106

101107
return responseKid == certKid
102108
}

app/src/main/java/dgca/verifier/app/android/settings/SettingsFragment.kt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import android.view.MenuItem
3030
import android.view.View
3131
import android.view.ViewGroup
3232
import androidx.fragment.app.Fragment
33+
import androidx.fragment.app.viewModels
3334
import androidx.navigation.fragment.findNavController
3435
import dagger.hilt.android.AndroidEntryPoint
3536
import dgca.verifier.app.android.BuildConfig
@@ -44,10 +45,17 @@ class SettingsFragment : Fragment() {
4445
private val binding get() = _binding!!
4546

4647
companion object {
47-
const val PRIVACY_POLICY = "https://op.europa.eu/en/web/about-us/legal-notices/eu-mobile-apps"
48+
const val PRIVACY_POLICY =
49+
"https://op.europa.eu/en/web/about-us/legal-notices/eu-mobile-apps"
4850
}
4951

50-
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
52+
private val viewModel by viewModels<SettingsViewModel>()
53+
54+
override fun onCreateView(
55+
inflater: LayoutInflater,
56+
container: ViewGroup?,
57+
savedInstanceState: Bundle?
58+
): View {
5159
_binding = FragmentSettingsBinding.inflate(inflater, container, false)
5260
return binding.root
5361
}
@@ -57,7 +65,14 @@ class SettingsFragment : Fragment() {
5765
setHasOptionsMenu(true)
5866
(requireActivity() as MainActivity).setSupportActionBar(binding.toolbar)
5967
binding.privacyPolicy.setOnClickListener { launchWebIntent() }
68+
binding.syncPublicKeys.setOnClickListener { viewModel.syncPublicKeys() }
6069
binding.version.text = getString(R.string.version, BuildConfig.VERSION_NAME)
70+
71+
viewModel.inProgress.observe(viewLifecycleOwner, {
72+
binding.privacyPolicy.isClickable = it != true
73+
binding.syncPublicKeys.isClickable = it != true
74+
binding.progressBar.visibility = if (it == true) View.VISIBLE else View.GONE
75+
})
6176
}
6277

6378
override fun onOptionsItemSelected(item: MenuItem): Boolean {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* ---license-start
3+
* eu-digital-green-certificates / dgca-verifier-app-android
4+
* ---
5+
* Copyright (C) 2021 T-Systems International GmbH and all other contributors
6+
* ---
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* ---license-end
19+
*
20+
* Created by mykhailo.nester on 4/24/21 2:54 PM
21+
*/
22+
23+
package dgca.verifier.app.android.settings
24+
25+
import androidx.lifecycle.LiveData
26+
import androidx.lifecycle.MutableLiveData
27+
import androidx.lifecycle.ViewModel
28+
import androidx.lifecycle.viewModelScope
29+
import dagger.hilt.android.lifecycle.HiltViewModel
30+
import dgca.verifier.app.android.data.VerifierRepository
31+
import kotlinx.coroutines.Dispatchers
32+
import kotlinx.coroutines.launch
33+
import kotlinx.coroutines.withContext
34+
import javax.inject.Inject
35+
36+
@HiltViewModel
37+
class SettingsViewModel @Inject constructor(private val verifierRepository: VerifierRepository) :
38+
ViewModel() {
39+
40+
private val _inProgress = MutableLiveData<Boolean>()
41+
val inProgress: LiveData<Boolean> = _inProgress
42+
43+
fun syncPublicKeys() {
44+
viewModelScope.launch {
45+
_inProgress.value = true
46+
withContext(Dispatchers.IO) {
47+
verifierRepository.fetchCertificates()
48+
}
49+
_inProgress.value = false
50+
}
51+
}
52+
}

app/src/main/res/layout/fragment_settings.xml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,24 @@
7070
android:textSize="18sp"
7171
app:layout_constraintTop_toBottomOf="@+id/about_title" />
7272

73+
<com.google.android.material.button.MaterialButton
74+
android:id="@+id/syncPublicKeys"
75+
style="@style/Widget.MaterialComponents.Button.TextButton"
76+
android:layout_width="match_parent"
77+
android:layout_height="wrap_content"
78+
android:drawableEnd="@drawable/ic_baseline_arrow_forward_24"
79+
android:gravity="start|center"
80+
android:insetTop="0dp"
81+
android:insetBottom="0dp"
82+
android:letterSpacing="0"
83+
android:minHeight="48dp"
84+
android:paddingHorizontal="@dimen/default_padding"
85+
android:text="@string/sync_public_keys"
86+
android:textAllCaps="false"
87+
android:textColor="@color/black"
88+
android:textSize="18sp"
89+
app:layout_constraintTop_toBottomOf="@+id/privacyPolicy" />
90+
7391
<com.google.android.material.textview.MaterialTextView
7492
android:id="@+id/materialTextView"
7593
android:layout_width="match_parent"
@@ -78,7 +96,7 @@
7896
android:text="@string/settings_version_title"
7997
android:textColor="@color/black"
8098
android:textSize="18sp"
81-
app:layout_constraintTop_toBottomOf="@+id/privacyPolicy"
99+
app:layout_constraintTop_toBottomOf="@+id/syncPublicKeys"
82100
tools:layout_editor_absoluteX="0dp" />
83101

84102
<com.google.android.material.textview.MaterialTextView
@@ -103,4 +121,15 @@
103121
android:src="@drawable/eu_logo_big"
104122
app:layout_constraintBottom_toBottomOf="parent" />
105123

124+
<ProgressBar
125+
android:id="@+id/progress_bar"
126+
android:layout_width="wrap_content"
127+
android:layout_height="wrap_content"
128+
android:indeterminateTint="@color/blue"
129+
android:visibility="gone"
130+
app:layout_constraintBottom_toBottomOf="parent"
131+
app:layout_constraintEnd_toEndOf="parent"
132+
app:layout_constraintStart_toStartOf="parent"
133+
app:layout_constraintTop_toTopOf="parent" />
134+
106135
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848

4949
<string name="settings">Settings</string>
5050
<string name="privacy_policy">Privacy</string>
51+
<string name="sync_public_keys">Sync Public Keys</string>
5152
<string name="version">Version: %1$s</string>
5253
<string name="about">About</string>
5354
<string name="settings_version_title">Verifier App</string>

0 commit comments

Comments
 (0)