Skip to content

Commit 9c870f9

Browse files
authored
Merge pull request #30 from FreshworksStudio/main
Signed off code for v1.0.1
2 parents c616006 + a07fc97 commit 9c870f9

30 files changed

Lines changed: 727 additions & 322 deletions

README.md

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,108 @@
1-
# BC Vaccine Card-Android
1+
# BC Vaccine Card Verifier-Android
2+
3+
#### BC Vaccine Card Verifier App is used to scan the QR generated from HealthGateway app and shows the vaccination status of a person. This is for Businesses from the province of British Columbia.
4+
5+
## Table of Contents
6+
7+
- [Project Resources](#markdown-header-project-resources)
8+
- [Architecture](#markdown-header-architecture)
9+
- [Folder Structure](#markdown-header-folder-structure)
10+
- [Libraries](#markdown-header-libraries)
11+
- [Configuration](#markdown-header-configuration)
12+
- [Build Variants](#markdown-header-build-variants)
13+
- [Environment Variables](#markdownpheader-environment-variables)
14+
- [APIs](#markdown-header-apis)
15+
- [Deployment](#markdown-header-deployment)
16+
- [Versioning](#markdown-header-versioning)
17+
- [Internal](#markdown-header-internal)
18+
- [External](#markdown-header-external)
19+
- [Contributors](#markdown-header-contributors)
20+
21+
## Project Resources
22+
---
23+
24+
- [Google Play](https://play.google.com/store/apps/details?id=ca.bc.gov.vaxcheck)
25+
26+
## Architecture
27+
---
28+
29+
Language: __Kotlin__
30+
31+
Architecture: __MVVM__
32+
33+
- Architecture used for app: MVVM
34+
- One activity with multiple fragments is used. Business logic is present in classes under utils package. View models associated with fragments seperate the UI from business logic.
35+
- Does it differ from standard architecture? No.
36+
- Include UML diagram if needed. NA
37+
38+
Dependency Injection: __Hilt__
39+
40+
Concurrency: __Coroutines, Kotlin Flow__
41+
42+
### Folder Structure
43+
barcodeanalyser, DI, model, UI, utils, viewModel are placed under project package. barcodeanalyser and utils contains the business logic.
44+
45+
### Libraries
46+
47+
- [GSON](https://github.com/google/gson): For serialization/deserialization, and converting Java Objects into JSON, and back
48+
- [Material design](https://material.io/develop/android/docs/getting-started): Material Components for Android
49+
- [Jetpack navigation](https://developer.android.com/guide/navigation): For screen navigation
50+
- [Lifecycle-aware components](https://developer.android.com/jetpack/androidx/releases/lifecycle): For usage of Lifecycle-aware components
51+
- [Hilt](https://developer.android.com/training/dependency-injection/hilt-android): For dependency injection
52+
- [Google barcode scanner](https://developers.google.com/ml-kit/vision/barcode-scanning/android): For scanning QR
53+
- [CameraX](https://developer.android.com/jetpack/androidx/releases/camera): For camera funtionalities
54+
- [JWT](https://mvnrepository.com/artifact/io.jsonwebtoken): For verification of signature embedded in QR
55+
- [BouncyCastle](https://mvnrepository.com/artifact/org.bouncycastle): For crptography
56+
- [DataStore](https://developer.android.com/topic/libraries/architecture/datastore?gclid=CjwKCAjwhOyJBhA4EiwAEcJdcQu0nsVtVucNurmZ9Mr__luXN1zzVucwANlm07DqcpzHWqOL4T0SRRoCbbMQAvD_BwE&gclsrc=aw.ds): For caching key value pairs
57+
58+
## Configuration
59+
---
60+
61+
### Build Variants
62+
63+
Build variants used in the app
64+
65+
- demoDebug
66+
- Uses dev jwks.json configuration
67+
- demoRelease
68+
- Uses dev jwks.json configuration
69+
- prodDebug
70+
- Uses PROD jwks.json configuration
71+
- prodRelease
72+
- Uses PROD jwks.json configuration
73+
- stageDebug
74+
- Uses STAGE jwks.json configuration
75+
- stageRelease
76+
- Uses STAGE jwks.json configuration
77+
78+
### Environment Variables
79+
80+
- Not applicable
81+
82+
### APIs
83+
84+
- No external services were used
85+
86+
## Deployment
87+
---
88+
89+
### Versioning
90+
91+
In version name 1.2, 1 stands for major change and 2 stands of minor change or patch fix.
92+
93+
### Internal
94+
95+
How is the app deployed and where?
96+
97+
Builds generated manually, distributed through Google Play. CI/CD implementation is in progress
98+
99+
### External
100+
101+
How is the app deployed and where?
102+
103+
Builds generated manually, distributed through Google Play.
104+
105+
## Contributors
106+
---
107+
108+
List past and present contributors. Will S, Pinakin Kansara, Amit Metri

app/build.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ android {
1414
applicationId "ca.bc.gov.vaxcheck"
1515
minSdk 27
1616
targetSdk 31
17-
versionCode 11
18-
versionName "1.0"
17+
versionCode 12
18+
versionName "1.0.1"
1919

2020
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
2121
}
@@ -72,6 +72,8 @@ dependencies {
7272

7373
implementation "androidx.constraintlayout:constraintlayout:$versions.constraint_layout"
7474

75+
implementation "androidx.swiperefreshlayout:swiperefreshlayout:$versions.swipe_refresh_layout"
76+
7577
//Navigation
7678
implementation "androidx.navigation:navigation-fragment-ktx:$versions.navigation"
7779

app/src/main/AndroidManifest.xml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
<uses-permission android:name="android.permission.CAMERA" />
1010

1111
<application
12-
android:name="ca.bc.gov.vaxcheck.BcVaxApp"
12+
android:name=".BcVaxApp"
1313
android:icon="@mipmap/ic_launcher"
1414
android:label="@string/app_name"
1515
android:roundIcon="@mipmap/ic_launcher_round"
1616
android:supportsRtl="true"
1717
android:theme="@style/Theme.BC_VAX.Splash">
1818
<activity
19-
android:name="ca.bc.gov.vaxcheck.MainActivity"
19+
android:name=".SplashActivity"
2020
android:exported="true"
2121
android:screenOrientation="portrait">
2222
<intent-filter>
@@ -25,6 +25,11 @@
2525
<category android:name="android.intent.category.LAUNCHER" />
2626
</intent-filter>
2727
</activity>
28+
<activity
29+
android:name=".MainActivity"
30+
android:exported="true"
31+
android:screenOrientation="portrait"
32+
android:theme="@style/Theme.BC_VAX"/>
2833
</application>
2934

3035
</manifest>

app/src/main/java/ca/bc/gov/vaxcheck/MainActivity.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package ca.bc.gov.vaxcheck
22

33
import android.os.Bundle
4+
import android.os.Handler
5+
import android.os.Looper
46
import androidx.appcompat.app.AppCompatActivity
57
import dagger.hilt.android.AndroidEntryPoint
68

@@ -15,7 +17,6 @@ class MainActivity : AppCompatActivity() {
1517

1618
override fun onCreate(savedInstanceState: Bundle?) {
1719
super.onCreate(savedInstanceState)
18-
setTheme(R.style.Theme_BC_VAX)
1920
setContentView(R.layout.activity_main)
2021
}
2122
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package ca.bc.gov.vaxcheck
2+
3+
import android.annotation.SuppressLint
4+
import android.content.Intent
5+
import android.os.Bundle
6+
import android.os.Handler
7+
import android.os.Looper
8+
import androidx.appcompat.app.AppCompatActivity
9+
import ca.bc.gov.vaxcheck.ui.onboarding.OnBoardingFragment
10+
11+
/**
12+
* [SplashActivity]
13+
*
14+
* @author Amit Metri
15+
*/
16+
@SuppressLint("CustomSplashScreen")
17+
class SplashActivity : AppCompatActivity() {
18+
19+
override fun onCreate(savedInstanceState: Bundle?) {
20+
super.onCreate(savedInstanceState)
21+
Handler(Looper.getMainLooper()).postDelayed({
22+
startActivity(Intent(this, MainActivity::class.java))
23+
finish()
24+
}, 3000)
25+
}
26+
}

app/src/main/java/ca/bc/gov/vaxcheck/barcodeanalyzer/BarcodeAnalyzer.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package ca.bc.gov.vaxcheck.barcodeanalyzer
22

3-
import android.util.Log
43
import androidx.camera.core.ExperimentalGetImage
54
import androidx.camera.core.ImageAnalysis
65
import androidx.camera.core.ImageProxy
@@ -37,7 +36,6 @@ class BarcodeAnalyzer(private val listener: ScanningResultListener) : ImageAnaly
3736
}
3837
val rawValue = barcode?.rawValue
3938
rawValue?.let {
40-
Log.d("Barcode", it)
4139
listener.onScanned(it)
4240
isScanning = false
4341
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package ca.bc.gov.vaxcheck.data.local
2+
3+
import android.content.Context
4+
import androidx.datastore.preferences.core.booleanPreferencesKey
5+
import androidx.datastore.preferences.core.edit
6+
import androidx.datastore.preferences.preferencesDataStore
7+
import ca.bc.gov.vaxcheck.BuildConfig
8+
import kotlinx.coroutines.flow.Flow
9+
import kotlinx.coroutines.flow.map
10+
import javax.inject.Inject
11+
12+
13+
/**
14+
* [DataStoreRepo]
15+
*
16+
* @author amit metri
17+
*/
18+
private val Context.dataStore by preferencesDataStore(BuildConfig.APPLICATION_ID + "_preferences")
19+
20+
class DataStoreRepo @Inject constructor(
21+
private val context: Context
22+
) {
23+
24+
companion object {
25+
val ON_BOARDING_SHOWN = booleanPreferencesKey("ON_BOARDING_SHOWN")
26+
}
27+
28+
val isOnBoardingShown: Flow<Boolean> = context.dataStore.data.map { preference ->
29+
preference[ON_BOARDING_SHOWN] ?: false
30+
}
31+
32+
suspend fun setOnBoardingShown(shown: Boolean = true) = context.dataStore.edit { preference ->
33+
preference[ON_BOARDING_SHOWN] = shown
34+
}
35+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package ca.bc.gov.vaxcheck.di
2+
3+
import android.content.Context
4+
import ca.bc.gov.vaxcheck.data.local.DataStoreRepo
5+
import dagger.Module
6+
import dagger.Provides
7+
import dagger.hilt.InstallIn
8+
import dagger.hilt.android.qualifiers.ApplicationContext
9+
import dagger.hilt.components.SingletonComponent
10+
import javax.inject.Singleton
11+
12+
13+
/**
14+
* [DataStoreModule]
15+
*
16+
* @author amit metri
17+
*/
18+
@Module
19+
@InstallIn(SingletonComponent::class)
20+
class DataStoreModule {
21+
22+
@Provides
23+
@Singleton
24+
fun providesDataStore(@ApplicationContext context: Context) = DataStoreRepo(context)
25+
}
Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
package ca.bc.gov.vaxcheck.ui.onboarding
22

3+
34
import android.content.Intent
45
import android.net.Uri
56
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
117
import android.view.View
12-
import android.widget.TextView
8+
import android.widget.Toast
139
import androidx.fragment.app.Fragment
1410
import androidx.fragment.app.activityViewModels
11+
import androidx.lifecycle.Lifecycle
12+
import androidx.lifecycle.lifecycleScope
13+
import androidx.lifecycle.repeatOnLifecycle
1514
import androidx.navigation.NavOptions
1615
import androidx.navigation.fragment.findNavController
1716
import ca.bc.gov.vaxcheck.R
1817
import ca.bc.gov.vaxcheck.databinding.FragmentOnboardingBinding
19-
import ca.bc.gov.vaxcheck.ui.scanner.BarcodeScannerFragment
2018
import ca.bc.gov.vaxcheck.utils.setSpannableLink
19+
import ca.bc.gov.vaxcheck.utils.toast
2120
import ca.bc.gov.vaxcheck.utils.viewBindings
2221
import ca.bc.gov.vaxcheck.viewmodel.SharedViewModel
2322
import dagger.hilt.android.AndroidEntryPoint
23+
import kotlinx.coroutines.flow.collect
24+
import kotlinx.coroutines.launch
2425

2526
/**
2627
* [OnBoardingFragment]
@@ -38,24 +39,43 @@ class OnBoardingFragment : Fragment(R.layout.fragment_onboarding) {
3839
super.onViewCreated(view, savedInstanceState)
3940

4041
binding.btnAllowCameraPermission.setOnClickListener {
41-
sharedViewModel.writeFirstLaunch(BarcodeScannerFragment.ON_BOARDING_SHOWN, true)
42+
sharedViewModel.setOnBoardingShown(true)
4243
}
4344

45+
viewLifecycleOwner.lifecycleScope.launch {
4446

45-
sharedViewModel.isOnBoardingShown(BarcodeScannerFragment.ON_BOARDING_SHOWN)
46-
.observe(viewLifecycleOwner, { isOnBoardingShown ->
47-
if (isOnBoardingShown) {
48-
val navOptions = NavOptions.Builder()
49-
.setPopUpTo(R.id.onBoardingFragment, true)
50-
.build()
51-
findNavController().navigate(R.id.barcodeScannerFragment, null, navOptions)
47+
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
48+
sharedViewModel.isOnBoardingShown.collect { shown ->
49+
when (shown) {
50+
true -> {
51+
val navOptions = NavOptions.Builder()
52+
.setPopUpTo(R.id.onBoardingFragment, true)
53+
.build()
54+
findNavController().navigate(
55+
R.id.barcodeScannerFragment,
56+
null,
57+
navOptions
58+
)
59+
}
60+
}
5261
}
53-
})
62+
63+
}
64+
65+
}
5466

5567
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)
68+
showPrivacyPolicy()
69+
}
70+
}
71+
72+
private fun showPrivacyPolicy() {
73+
val webpage: Uri = Uri.parse(getString(R.string.url_privacy_policy))
74+
val intent = Intent(Intent.ACTION_VIEW, webpage)
75+
try {
76+
startActivity(intent)
77+
} catch (e: Exception) {
78+
context?.toast(getString(R.string.no_app_found))
5979
}
6080
}
6181
}

0 commit comments

Comments
 (0)