Skip to content

Commit c616006

Browse files
Merge pull request #24 from FreshworksStudio/main
BC Vaccine Card QR Scanner 1.0
2 parents 4fe6ee5 + c22fd69 commit c616006

12 files changed

Lines changed: 203 additions & 58 deletions

File tree

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ android {
1414
applicationId "ca.bc.gov.vaxcheck"
1515
minSdk 27
1616
targetSdk 31
17-
versionCode 10
17+
versionCode 11
1818
versionName "1.0"
1919

2020
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

app/src/main/java/ca/bc/gov/vaxcheck/ui/onboarding/OnBoardingFragment.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,33 @@
11
package ca.bc.gov.vaxcheck.ui.onboarding
22

3+
import android.content.Intent
4+
import android.net.Uri
35
import android.os.Bundle
6+
import android.text.SpannableString
7+
import android.text.Spanned
8+
import android.text.TextPaint
9+
import android.text.method.LinkMovementMethod
10+
import android.text.style.ClickableSpan
411
import android.view.View
12+
import android.widget.TextView
513
import androidx.fragment.app.Fragment
614
import androidx.fragment.app.activityViewModels
715
import androidx.navigation.NavOptions
816
import androidx.navigation.fragment.findNavController
917
import ca.bc.gov.vaxcheck.R
1018
import ca.bc.gov.vaxcheck.databinding.FragmentOnboardingBinding
1119
import ca.bc.gov.vaxcheck.ui.scanner.BarcodeScannerFragment
20+
import ca.bc.gov.vaxcheck.utils.setSpannableLink
1221
import ca.bc.gov.vaxcheck.utils.viewBindings
1322
import ca.bc.gov.vaxcheck.viewmodel.SharedViewModel
23+
import dagger.hilt.android.AndroidEntryPoint
1424

1525
/**
1626
* [OnBoardingFragment]
1727
*
1828
* @author Amit Metri
1929
*/
30+
@AndroidEntryPoint
2031
class OnBoardingFragment : Fragment(R.layout.fragment_onboarding) {
2132

2233
private val binding by viewBindings(FragmentOnboardingBinding::bind)
@@ -26,10 +37,11 @@ class OnBoardingFragment : Fragment(R.layout.fragment_onboarding) {
2637
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
2738
super.onViewCreated(view, savedInstanceState)
2839

29-
binding.btnStartScanning.setOnClickListener {
40+
binding.btnAllowCameraPermission.setOnClickListener {
3041
sharedViewModel.writeFirstLaunch(BarcodeScannerFragment.ON_BOARDING_SHOWN, true)
3142
}
3243

44+
3345
sharedViewModel.isOnBoardingShown(BarcodeScannerFragment.ON_BOARDING_SHOWN)
3446
.observe(viewLifecycleOwner, { isOnBoardingShown ->
3547
if (isOnBoardingShown) {
@@ -39,5 +51,11 @@ class OnBoardingFragment : Fragment(R.layout.fragment_onboarding) {
3951
findNavController().navigate(R.id.barcodeScannerFragment, null, navOptions)
4052
}
4153
})
54+
55+
binding.txtPrivacyPolicy.setSpannableLink {
56+
val uri = Uri.parse("https://www2.gov.bc.ca/gov/content/covid-19/vaccine/proof/businesses#app-privacy-policy")
57+
val intent = Intent(Intent.ACTION_VIEW, uri)
58+
requireContext().startActivity(intent)
59+
}
4260
}
4361
}

app/src/main/java/ca/bc/gov/vaxcheck/ui/scanner/BarcodeScannerFragment.kt

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package ca.bc.gov.vaxcheck.ui.scanner
22

33
import android.Manifest
44
import android.content.Context
5+
import android.content.Intent
56
import android.content.pm.PackageManager
7+
import android.net.Uri
68
import android.os.Bundle
79
import android.util.Size
810
import android.view.View
@@ -29,6 +31,7 @@ import ca.bc.gov.vaxcheck.barcodeanalyzer.ScanningResultListener
2931
import ca.bc.gov.vaxcheck.databinding.FragmentBarcodeScannerBinding
3032
import ca.bc.gov.vaxcheck.model.ImmunizationStatus
3133
import ca.bc.gov.vaxcheck.utils.readJsonFromAsset
34+
import ca.bc.gov.vaxcheck.utils.setSpannableLink
3235
import ca.bc.gov.vaxcheck.utils.viewBindings
3336
import ca.bc.gov.vaxcheck.viewmodel.BarcodeScanResultViewModel
3437
import ca.bc.gov.vaxcheck.viewmodel.SharedViewModel
@@ -72,9 +75,7 @@ class BarcodeScannerFragment : Fragment(R.layout.fragment_barcode_scanner), Scan
7275
if (isGranted) {
7376
setUpCamera()
7477
} else {
75-
//TODO: Add Optional screen that explain why your app require permission
76-
//For now closing the app
77-
findNavController().popBackStack()
78+
showRationalDialog()
7879
}
7980
}
8081
}
@@ -111,7 +112,9 @@ class BarcodeScannerFragment : Fragment(R.layout.fragment_barcode_scanner), Scan
111112
ImmunizationStatus.FULLY_IMMUNIZED,
112113
ImmunizationStatus.PARTIALLY_IMMUNIZED -> {
113114
sharedViewModel.setStatus(status)
114-
findNavController().navigate(R.id.action_barcodeScannerFragment_to_barcodeScanResultFragment)
115+
findNavController().navigate(
116+
R.id.action_barcodeScannerFragment_to_barcodeScanResultFragment
117+
)
115118
}
116119

117120
ImmunizationStatus.INVALID_QR_CODE -> {
@@ -123,6 +126,11 @@ class BarcodeScannerFragment : Fragment(R.layout.fragment_barcode_scanner), Scan
123126

124127
}
125128

129+
binding.txtPrivacyPolicy.setSpannableLink {
130+
val uri = Uri.parse("https://www2.gov.bc.ca/gov/content/covid-19/vaccine/proof/businesses#app-privacy-policy")
131+
val intent = Intent(Intent.ACTION_VIEW, uri)
132+
requireContext().startActivity(intent)
133+
}
126134
}
127135

128136
override fun onDestroyView() {
@@ -163,9 +171,12 @@ class BarcodeScannerFragment : Fragment(R.layout.fragment_barcode_scanner), Scan
163171
private fun showRationalDialog() {
164172
MaterialAlertDialogBuilder(requireContext())
165173
.setTitle(getString(R.string.bc_permission_required_title))
174+
.setCancelable(false)
166175
.setMessage(getString(R.string.bc_permission_message))
167176
.setNegativeButton(getString(R.string.exit)) { dialog, which ->
168-
findNavController().popBackStack()
177+
if (!findNavController().popBackStack() || !findNavController().navigateUp()) {
178+
requireActivity().finish()
179+
}
169180
dialog.dismiss()
170181
}
171182
.show()
@@ -193,31 +204,52 @@ class BarcodeScannerFragment : Fragment(R.layout.fragment_barcode_scanner), Scan
193204
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
194205
.build()
195206

196-
val resolution = Size(
197-
binding.scannerPreview.width,
198-
binding.scannerPreview.height
199-
)
200-
val preview = Preview.Builder()
201-
.apply {
202-
setTargetResolution(resolution)
203-
}.build()
204-
205-
imageAnalysis = ImageAnalysis.Builder()
206-
.setTargetResolution(resolution)
207-
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
208-
.build()
207+
val hasCamera = cameraProvider.hasCamera(cameraSelector)
208+
209+
if (hasCamera) {
210+
211+
val resolution = Size(
212+
binding.scannerPreview.width,
213+
binding.scannerPreview.height
214+
)
215+
val preview = Preview.Builder()
216+
.apply {
217+
setTargetResolution(resolution)
218+
}.build()
219+
220+
imageAnalysis = ImageAnalysis.Builder()
221+
.setTargetResolution(resolution)
222+
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
223+
.build()
224+
225+
imageAnalysis.setAnalyzer(cameraExecutor, BarcodeAnalyzer(this))
209226

210-
imageAnalysis.setAnalyzer(cameraExecutor, BarcodeAnalyzer(this))
211227

228+
cameraProvider.unbindAll()
212229

213-
cameraProvider.unbindAll()
230+
camera = cameraProvider.bindToLifecycle(
231+
viewLifecycleOwner, cameraSelector, preview, imageAnalysis
232+
)
214233

215-
camera = cameraProvider.bindToLifecycle(
216-
viewLifecycleOwner, cameraSelector, preview, imageAnalysis
217-
)
234+
preview.setSurfaceProvider(binding.scannerPreview.surfaceProvider)
218235

219-
preview.setSurfaceProvider(binding.scannerPreview.surfaceProvider)
236+
} else {
237+
showNoCameraAlertDialog()
238+
}
239+
}
220240

241+
private fun showNoCameraAlertDialog() {
242+
MaterialAlertDialogBuilder(requireContext())
243+
.setTitle(getString(R.string.bc_no_rear_camera_title))
244+
.setCancelable(false)
245+
.setMessage(getString(R.string.bc_nor_rear_camera_message))
246+
.setNegativeButton(getString(R.string.exit)) { dialog, which ->
247+
if (!findNavController().popBackStack() || !findNavController().navigateUp()) {
248+
requireActivity().finish()
249+
}
250+
dialog.dismiss()
251+
}
252+
.show()
221253
}
222254

223255
private fun enableFlashControl() {

app/src/main/java/ca/bc/gov/vaxcheck/utils/DataStoreRepo.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ const val PREFERENCE_NAME = BuildConfig.APPLICATION_ID + "_preferences"
1414

1515
class DataStoreRepo(var context: Context) {
1616

17-
companion object{
18-
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = PREFERENCE_NAME)
17+
companion object {
18+
private val Context.dataStore: DataStore<Preferences>
19+
by preferencesDataStore(name = PREFERENCE_NAME)
1920
}
2021

2122
suspend fun saveToDataStore(key: String, value: Boolean) {
Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,48 @@
11
package ca.bc.gov.vaxcheck.utils
22

33
import android.content.Context
4+
import android.text.SpannableString
5+
import android.text.Spanned
6+
import android.text.TextPaint
7+
import android.text.method.LinkMovementMethod
8+
import android.text.style.ClickableSpan
9+
import android.view.View
10+
import android.widget.TextView
11+
import ca.bc.gov.vaxcheck.R
412

513
/**
614
* Helper function to read file from asset
715
* and return String JSON.
816
*/
917
fun Context.readJsonFromAsset(fileName: String) =
10-
this.assets.open(fileName).bufferedReader().use { it.readText() }
18+
this.assets.open(fileName).bufferedReader().use { it.readText() }
19+
20+
fun TextView.setSpannableLink(text: String? = null, onClick: () -> Unit) {
21+
val spannableString = SpannableString(
22+
if (text.isNullOrBlank()) {
23+
this.text
24+
} else {
25+
text
26+
}
27+
)
28+
val clickableSpan = object : ClickableSpan() {
29+
override fun onClick(p0: View) {
30+
onClick()
31+
32+
}
33+
34+
override fun updateDrawState(ds: TextPaint) {
35+
super.updateDrawState(ds)
36+
ds.color = resources.getColor(R.color.white, null)
37+
}
38+
}
39+
40+
spannableString.setSpan(
41+
clickableSpan,
42+
0,
43+
spannableString.length,
44+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
45+
)
46+
this.setText(spannableString, TextView.BufferType.SPANNABLE)
47+
this.movementMethod = LinkMovementMethod.getInstance()
48+
}

app/src/main/java/ca/bc/gov/vaxcheck/utils/SHCDecoder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import java.security.KeyFactory
1717
import java.security.interfaces.ECPublicKey
1818
import java.security.spec.ECPoint
1919
import java.security.spec.ECPublicKeySpec
20-
import java.util.*
20+
import java.util.Base64
2121
import java.util.zip.DataFormatException
2222
import java.util.zip.Inflater
2323

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24"
6+
android:tint="?attr/colorControlNormal">
7+
<path
8+
android:fillColor="@android:color/white"
9+
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
10+
</vector>

app/src/main/res/drawable/ic_primary_button.xml

Lines changed: 0 additions & 15 deletions
This file was deleted.

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@
3030
app:layout_constraintEnd_toEndOf="parent"
3131
app:layout_constraintTop_toTopOf="parent" />
3232

33+
<com.google.android.material.textview.MaterialTextView
34+
android:id="@+id/txt_privacy_policy"
35+
android:layout_width="match_parent"
36+
android:layout_height="wrap_content"
37+
android:textAlignment="center"
38+
android:text="@string/btn_privacy_policy"
39+
android:background="@color/dark_blue"
40+
android:paddingTop="@dimen/medium"
41+
android:paddingBottom="@dimen/medium"
42+
app:layout_constraintBottom_toBottomOf="parent"
43+
app:layout_constraintEnd_toEndOf="parent"
44+
app:layout_constraintStart_toStartOf="parent" />
3345

3446
</androidx.constraintlayout.widget.ConstraintLayout>
3547

0 commit comments

Comments
 (0)