Skip to content

Commit 68a54b6

Browse files
authored
Merge pull request #67 from d-tree-org/add-perf-improvements
Add perf improvements
2 parents 1ab21e4 + 32b765c commit 68a54b6

File tree

13 files changed

+297
-11
lines changed

13 files changed

+297
-11
lines changed

android/engine/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ dependencies {
196196
api("org.smartregister:common:0.1.0-alpha05-preview3-SNAPSHOT") { isTransitive = true }
197197

198198
// api("org.smartregister:engine:1.0.0-preview7.1-SNAPSHOT") {
199-
api("org.smartregister:engine:1.0.0-preview8-PERF-TEST-SNAPSHOT") {
199+
api("org.smartregister:engine:1.0.0-preview9-PERF-TEST-SNAPSHOT") {
200200
// api("org.smartregister:engine:1.0.0-preview7.1-PERF-TEST5-SNAPSHOT") {
201201
isTransitive = true
202202
exclude(group = "com.google.android.fhir", module = "common")

android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/DefaultRepository.kt

+2
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ import org.hl7.fhir.r4.model.Reference
4242
import org.hl7.fhir.r4.model.RelatedPerson
4343
import org.hl7.fhir.r4.model.Resource
4444
import org.hl7.fhir.r4.model.ResourceType
45+
import org.hl7.fhir.r4.model.StructureMap
4546
import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry
4647
import org.smartregister.fhircore.engine.configuration.app.ConfigService
4748
import org.smartregister.fhircore.engine.configuration.view.SearchFilter
49+
import org.smartregister.fhircore.engine.ui.questionnaire.ContentCache
4850
import org.smartregister.fhircore.engine.ui.questionnaire.QuestionnaireConfig
4951
import org.smartregister.fhircore.engine.util.DispatcherProvider
5052
import org.smartregister.fhircore.engine.util.SharedPreferencesHelper

android/engine/src/main/java/org/smartregister/fhircore/engine/ui/login/LoginViewModel.kt

+39-2
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@ import androidx.lifecycle.LiveData
2121
import androidx.lifecycle.MutableLiveData
2222
import androidx.lifecycle.ViewModel
2323
import androidx.lifecycle.viewModelScope
24+
import com.google.android.fhir.FhirEngine
2425
import dagger.hilt.android.lifecycle.HiltViewModel
2526
import io.jsonwebtoken.Jwts
2627
import java.net.UnknownHostException
2728
import javax.inject.Inject
2829
import kotlinx.coroutines.CoroutineScope
2930
import kotlinx.coroutines.launch
3031
import org.hl7.fhir.r4.model.Bundle
32+
import org.hl7.fhir.r4.model.Questionnaire
3133
import org.hl7.fhir.r4.model.Resource
3234
import org.hl7.fhir.r4.model.ResourceType
35+
import org.hl7.fhir.r4.model.StructureMap
3336
import org.jetbrains.annotations.TestOnly
3437
import org.smartregister.fhircore.engine.auth.AccountAuthenticator
3538
import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry
@@ -49,11 +52,14 @@ import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
4952
import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid
5053
import org.smartregister.fhircore.engine.util.extension.getActivity
5154
import org.smartregister.fhircore.engine.util.extension.isDeviceOnline
55+
import org.smartregister.fhircore.engine.util.extension.loadResource
5256
import org.smartregister.fhircore.engine.util.extension.practitionerEndpointUrl
57+
import org.smartregister.fhircore.engine.util.extension.referenceValue
5358
import org.smartregister.fhircore.engine.util.extension.valueToString
5459
import org.smartregister.model.practitioner.PractitionerDetails
5560
import retrofit2.HttpException
5661
import timber.log.Timber
62+
import kotlin.system.measureTimeMillis
5763

5864
@HiltViewModel
5965
class LoginViewModel
@@ -69,6 +75,7 @@ constructor(
6975
val fhirResourceService: FhirResourceService,
7076
val configurationRegistry: ConfigurationRegistry,
7177
private val appConfigs: AppConfigService,
78+
private val fhirEngine: FhirEngine,
7279
) : ViewModel() {
7380

7481
private val _launchDialPad: MutableLiveData<String?> = MutableLiveData(null)
@@ -163,6 +170,9 @@ constructor(
163170
val trimmedUsername = _username.value!!.trim()
164171
val passwordAsCharArray = _password.value!!.toCharArray()
165172
if (offline) {
173+
174+
warmCache()
175+
166176
verifyCredentials(trimmedUsername, passwordAsCharArray)
167177
return
168178
}
@@ -174,6 +184,8 @@ constructor(
174184
val multiUserLoginAttempted =
175185
existingCredentials?.username?.equals(trimmedUsername, true) == false
176186

187+
warmCache()
188+
177189
when {
178190
multiUserLoginAttempted -> {
179191
_showProgressBar.postValue(false)
@@ -207,10 +219,35 @@ constructor(
207219
_loginErrorState.postValue(LoginErrorState.ERROR_FETCHING_USER)
208220
}
209221
_showProgressBar.postValue(false)
210-
} finally {
211-
ContentCache.invalidate()
212222
}
213223
}
224+
private suspend fun warmCache() {
225+
val timeInMillis = measureTimeMillis {
226+
227+
val registrationResourceId = "patient-demographic-registration"
228+
229+
val registrationQuestionnaire = fhirEngine.loadResource<Questionnaire>(registrationResourceId)?.
230+
apply { this.url = this.url ?: this.referenceValue() }
231+
232+
val registrationQuestionnaireStructureMap = fhirEngine.loadResource<StructureMap>(registrationResourceId)?.
233+
apply { this.url = this.url ?: this.referenceValue() }
234+
235+
registrationQuestionnaire?.let {
236+
ContentCache.saveResource(it)
237+
238+
}
239+
240+
registrationQuestionnaireStructureMap?.let {
241+
ContentCache.saveResource(it)
242+
243+
}
244+
245+
Timber.d("Cached Questionnaire ${registrationQuestionnaire?.idPart} and url ${registrationQuestionnaire?.url}")
246+
}
247+
248+
Timber.d("Cache reset in $timeInMillis ms")
249+
250+
}
214251

215252
private fun verifyCredentials(username: String, password: CharArray) {
216253
if (accountAuthenticator.validateLoginCredentials(username, password)) {

android/engine/src/main/java/org/smartregister/fhircore/engine/ui/questionnaire/ContentCache.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ object ContentCache {
2626
private val cacheSize: Int = maxMemory / 8
2727
private val cache = LruCache<String, Resource>(cacheSize)
2828

29-
suspend fun saveResource(resourceId: String, resource: Resource) =
30-
withContext(Dispatchers.IO) { cache.put("${resource.resourceType.name}/$resourceId", resource) }
29+
suspend fun saveResource(resource: Resource) =
30+
withContext(Dispatchers.IO) { cache.put("${resource.resourceType.name}/${resource.idPart}", resource.copy()) }
3131

32-
fun getResource(resourceId: String) = cache[resourceId]
32+
fun getResource(resourceId: String) = cache[resourceId]?.copy()
3333

3434
suspend fun invalidate() = withContext(Dispatchers.IO) { cache.evictAll() }
3535
}

android/engine/src/main/java/org/smartregister/fhircore/engine/ui/questionnaire/QuestionnaireActivity.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import org.smartregister.fhircore.engine.util.extension.find
6767
import org.smartregister.fhircore.engine.util.extension.generateMissingItems
6868
import org.smartregister.fhircore.engine.util.extension.showToast
6969
import timber.log.Timber
70+
import kotlin.system.measureTimeMillis
7071

7172
/**
7273
* Launches Questionnaire/ Implement a subclass of this [QuestionnaireActivity] to provide
@@ -297,7 +298,10 @@ open class QuestionnaireActivity : BaseMultiLanguageActivity(), View.OnClickList
297298
questionnaireConfig = resultPair.first
298299
questionnaire = resultPair.second
299300

300-
populateInitialValues(questionnaire)
301+
val t = measureTimeMillis {
302+
populateInitialValues(questionnaire)
303+
}
304+
Timber.d("populateInitialValues took $t ms : cachedxxx")
301305
}
302306
.onFailure {
303307
Timber.e(it)

android/engine/src/main/java/org/smartregister/fhircore/engine/ui/questionnaire/QuestionnaireViewModel.kt

+3-4
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ constructor(
140140
}
141141

142142
suspend fun loadQuestionnaire(id: String, type: QuestionnaireType): Questionnaire? {
143-
var questionnaire = ContentCache.getResource(ResourceType.Questionnaire.name + "/" + id)?.copy()
143+
var questionnaire = ContentCache.getResource(ResourceType.Questionnaire.name + "/" + id)
144144

145145
if (questionnaire == null) {
146146
questionnaire =
@@ -159,8 +159,7 @@ constructor(
159159
}
160160
?.also {
161161
ContentCache.saveResource(
162-
id,
163-
it.copy(),
162+
it,
164163
)
165164
}
166165
}
@@ -224,7 +223,7 @@ constructor(
224223
structureMap =
225224
structureMap
226225
?: defaultRepository.loadResource<StructureMap>(this)?.also {
227-
it.let { ContentCache.saveResource(this, it) }
226+
it.let { ContentCache.saveResource( it) }
228227
}
229228
}
230229
return structureMap as? StructureMap
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="192"
5+
android:viewportHeight="192">
6+
7+
<path
8+
android:fillColor="#919191"
9+
android:pathData="m42.3,120.2 l22.5,-30.7m17,-3.7 l27.9,19.1m39.9,-33.2 l-21.5,28.9m-86.6,20.8c-2,-1.5 -4.4,-2.3 -7.1,-2.3 -6.6,0 -12,5.4 -12,12s5.4,12 12,12 12,-5.4 12,-12c0,-4 -2,-7.5 -4.9,-9.7zM157.6,48.9c-6.6,0 -12,5.4 -12,12 0,3.9 1.9,7.4 4.8,9.6 2,1.5 4.5,2.4 7.2,2.4 6.6,0 12,-5.4 12,-12s-5.3,-12 -12,-12zM72.3,67.3c-6.6,0 -12,5.4 -12,12 0,4 1.9,7.5 4.9,9.7 2,1.5 4.4,2.3 7.1,2.3 4.1,0 7.7,-2.1 9.9,-5.2 1.3,-1.9 2.1,-4.2 2.1,-6.8 0,-6.6 -5.4,-12 -12,-12zM127,102.1c-2,-1.5 -4.5,-2.4 -7.2,-2.4 -4.1,0 -7.8,2.1 -9.9,5.2 -1.3,1.9 -2.1,4.3 -2.1,6.8 0,6.6 5.4,12 12,12s12,-5.4 12,-12c0.1,-3.9 -1.8,-7.4 -4.8,-9.6z"
10+
android:strokeWidth="8"
11+
android:strokeColor="#919191"
12+
android:fillType="evenOdd"/>
13+
14+
</vector>

android/quest/src/main/java/org/smartregister/fhircore/quest/navigation/MainNavigationScreen.kt

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.smartregister.fhircore.quest.navigation
1818

19+
import androidx.compose.material.icons.Icons
1920
import org.smartregister.fhircore.quest.R
2021

2122
sealed class MainNavigationScreen(
@@ -54,6 +55,13 @@ sealed class MainNavigationScreen(
5455
"reportsRoute",
5556
true,
5657
)
58+
data object Insights :
59+
MainNavigationScreen(
60+
R.string.insights,
61+
org.smartregister.fhircore.engine.R.drawable.ic_insights,
62+
"insightsRoute",
63+
true,
64+
)
5765

5866
data object Settings :
5967
MainNavigationScreen(
@@ -109,6 +117,7 @@ sealed class MainNavigationScreen(
109117
TracingHistory,
110118
TracingOutcomes,
111119
TracingHistoryDetails,
120+
Insights,
112121
)
113122
}
114123
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright 2021 Ona Systems, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.smartregister.fhircore.quest.ui.insights
18+
19+
import androidx.compose.foundation.background
20+
import androidx.compose.foundation.layout.Box
21+
import androidx.compose.foundation.layout.Column
22+
import androidx.compose.foundation.layout.IntrinsicSize
23+
import androidx.compose.foundation.layout.Row
24+
import androidx.compose.foundation.layout.Spacer
25+
import androidx.compose.foundation.layout.fillMaxWidth
26+
import androidx.compose.foundation.layout.height
27+
import androidx.compose.foundation.layout.padding
28+
import androidx.compose.foundation.lazy.LazyColumn
29+
import androidx.compose.material.Card
30+
import androidx.compose.material.Icon
31+
import androidx.compose.material.IconButton
32+
import androidx.compose.material.MaterialTheme
33+
import androidx.compose.material.Scaffold
34+
import androidx.compose.material.Text
35+
import androidx.compose.material.icons.Icons
36+
import androidx.compose.material.icons.automirrored.filled.ArrowBack
37+
import androidx.compose.runtime.Composable
38+
import androidx.compose.runtime.collectAsState
39+
import androidx.compose.runtime.getValue
40+
import androidx.compose.ui.Alignment
41+
import androidx.compose.ui.Modifier
42+
import androidx.compose.ui.graphics.Color
43+
import androidx.compose.ui.res.stringResource
44+
import androidx.compose.ui.unit.dp
45+
import androidx.compose.ui.unit.sp
46+
import androidx.hilt.navigation.compose.hiltViewModel
47+
import androidx.navigation.NavHostController
48+
import com.google.accompanist.swiperefresh.SwipeRefresh
49+
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
50+
import org.smartregister.fhircore.quest.R
51+
52+
@Composable
53+
fun InsightsScreen(
54+
navController: NavHostController,
55+
modifier: Modifier = Modifier,
56+
insightsViewModel: InsightsViewModel = hiltViewModel(),
57+
) {
58+
Scaffold(
59+
topBar = {
60+
Column(
61+
modifier = modifier.fillMaxWidth().background(MaterialTheme.colors.primary),
62+
) {
63+
Row(
64+
verticalAlignment = Alignment.CenterVertically,
65+
modifier = modifier.padding(vertical = 8.dp),
66+
) {
67+
IconButton(onClick = { navController.popBackStack() }) {
68+
Icon(
69+
Icons.AutoMirrored.Filled.ArrowBack,
70+
contentDescription = "Back",
71+
tint = Color.White,
72+
)
73+
}
74+
Text(
75+
text = stringResource(id = R.string.insights),
76+
fontSize = 20.sp,
77+
color = Color.White,
78+
modifier = Modifier.weight(1f),
79+
)
80+
}
81+
}
82+
},
83+
) { innerPadding ->
84+
Box(modifier = modifier.padding(innerPadding)) {
85+
86+
val isRefreshing by insightsViewModel.isRefreshing.collectAsState()
87+
val isRefreshingRamAvailabilityStats by insightsViewModel.isRefreshingRamAvailabilityStatsStateFlow.collectAsState()
88+
val ramAvailabilityStats by insightsViewModel.ramAvailabilityStatsStateFlow.collectAsState()
89+
90+
SwipeRefresh(
91+
state = rememberSwipeRefreshState(isRefreshing),
92+
onRefresh = { insightsViewModel.refresh() },
93+
// indicator = { _, _ -> }
94+
) {
95+
LazyColumn {
96+
item {
97+
Card(
98+
modifier = Modifier.padding(8.dp).fillMaxWidth().height(IntrinsicSize.Min),
99+
) {
100+
Box(
101+
modifier = Modifier.padding(8.dp),
102+
) {
103+
Column {
104+
Text(
105+
text = stringResource(R.string.ram_available),
106+
style = MaterialTheme.typography.h4.copy(color = Color.Gray),
107+
)
108+
Spacer(modifier = Modifier.height(8.dp))
109+
Text(
110+
text = "$ramAvailabilityStats",
111+
style =
112+
if (isRefreshingRamAvailabilityStats) {
113+
MaterialTheme.typography.h2.copy(color = Color.Gray.copy(alpha = 0.5F))
114+
} else MaterialTheme.typography.h2,
115+
)
116+
}
117+
}
118+
}
119+
}
120+
}
121+
}
122+
}
123+
}
124+
}

0 commit comments

Comments
 (0)