Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add perf improvements #67

Merged
merged 3 commits into from
Jun 14, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/engine/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -196,7 +196,7 @@ dependencies {
api("org.smartregister:common:0.1.0-alpha05-preview3-SNAPSHOT") { isTransitive = true }

// api("org.smartregister:engine:1.0.0-preview7.1-SNAPSHOT") {
api("org.smartregister:engine:1.0.0-preview8-PERF-TEST-SNAPSHOT") {
api("org.smartregister:engine:1.0.0-preview9-PERF-TEST-SNAPSHOT") {
// api("org.smartregister:engine:1.0.0-preview7.1-PERF-TEST5-SNAPSHOT") {
isTransitive = true
exclude(group = "com.google.android.fhir", module = "common")
Original file line number Diff line number Diff line change
@@ -42,9 +42,11 @@ import org.hl7.fhir.r4.model.Reference
import org.hl7.fhir.r4.model.RelatedPerson
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType
import org.hl7.fhir.r4.model.StructureMap
import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry
import org.smartregister.fhircore.engine.configuration.app.ConfigService
import org.smartregister.fhircore.engine.configuration.view.SearchFilter
import org.smartregister.fhircore.engine.ui.questionnaire.ContentCache
import org.smartregister.fhircore.engine.ui.questionnaire.QuestionnaireConfig
import org.smartregister.fhircore.engine.util.DispatcherProvider
import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
Original file line number Diff line number Diff line change
@@ -21,15 +21,18 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.android.fhir.FhirEngine
import dagger.hilt.android.lifecycle.HiltViewModel
import io.jsonwebtoken.Jwts
import java.net.UnknownHostException
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.Questionnaire
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType
import org.hl7.fhir.r4.model.StructureMap
import org.jetbrains.annotations.TestOnly
import org.smartregister.fhircore.engine.auth.AccountAuthenticator
import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry
@@ -49,11 +52,14 @@ import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid
import org.smartregister.fhircore.engine.util.extension.getActivity
import org.smartregister.fhircore.engine.util.extension.isDeviceOnline
import org.smartregister.fhircore.engine.util.extension.loadResource
import org.smartregister.fhircore.engine.util.extension.practitionerEndpointUrl
import org.smartregister.fhircore.engine.util.extension.referenceValue
import org.smartregister.fhircore.engine.util.extension.valueToString
import org.smartregister.model.practitioner.PractitionerDetails
import retrofit2.HttpException
import timber.log.Timber
import kotlin.system.measureTimeMillis

@HiltViewModel
class LoginViewModel
@@ -69,6 +75,7 @@ constructor(
val fhirResourceService: FhirResourceService,
val configurationRegistry: ConfigurationRegistry,
private val appConfigs: AppConfigService,
private val fhirEngine: FhirEngine,
) : ViewModel() {

private val _launchDialPad: MutableLiveData<String?> = MutableLiveData(null)
@@ -163,6 +170,9 @@ constructor(
val trimmedUsername = _username.value!!.trim()
val passwordAsCharArray = _password.value!!.toCharArray()
if (offline) {

warmCache()

verifyCredentials(trimmedUsername, passwordAsCharArray)
return
}
@@ -174,6 +184,8 @@ constructor(
val multiUserLoginAttempted =
existingCredentials?.username?.equals(trimmedUsername, true) == false

warmCache()

when {
multiUserLoginAttempted -> {
_showProgressBar.postValue(false)
@@ -207,10 +219,35 @@ constructor(
_loginErrorState.postValue(LoginErrorState.ERROR_FETCHING_USER)
}
_showProgressBar.postValue(false)
} finally {
ContentCache.invalidate()
}
}
private suspend fun warmCache() {
val timeInMillis = measureTimeMillis {

val registrationResourceId = "patient-demographic-registration"

val registrationQuestionnaire = fhirEngine.loadResource<Questionnaire>(registrationResourceId)?.
apply { this.url = this.url ?: this.referenceValue() }

val registrationQuestionnaireStructureMap = fhirEngine.loadResource<StructureMap>(registrationResourceId)?.
apply { this.url = this.url ?: this.referenceValue() }

registrationQuestionnaire?.let {
ContentCache.saveResource(it)

}

registrationQuestionnaireStructureMap?.let {
ContentCache.saveResource(it)

}

Timber.d("Cached Questionnaire ${registrationQuestionnaire?.idPart} and url ${registrationQuestionnaire?.url}")
}

Timber.d("Cache reset in $timeInMillis ms")

}

private fun verifyCredentials(username: String, password: CharArray) {
if (accountAuthenticator.validateLoginCredentials(username, password)) {
Original file line number Diff line number Diff line change
@@ -26,10 +26,10 @@ object ContentCache {
private val cacheSize: Int = maxMemory / 8
private val cache = LruCache<String, Resource>(cacheSize)

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

fun getResource(resourceId: String) = cache[resourceId]
fun getResource(resourceId: String) = cache[resourceId]?.copy()

suspend fun invalidate() = withContext(Dispatchers.IO) { cache.evictAll() }
}
Original file line number Diff line number Diff line change
@@ -67,6 +67,7 @@ import org.smartregister.fhircore.engine.util.extension.find
import org.smartregister.fhircore.engine.util.extension.generateMissingItems
import org.smartregister.fhircore.engine.util.extension.showToast
import timber.log.Timber
import kotlin.system.measureTimeMillis

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

populateInitialValues(questionnaire)
val t = measureTimeMillis {
populateInitialValues(questionnaire)
}
Timber.d("populateInitialValues took $t ms : cachedxxx")
}
.onFailure {
Timber.e(it)
Original file line number Diff line number Diff line change
@@ -140,7 +140,7 @@ constructor(
}

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

if (questionnaire == null) {
questionnaire =
@@ -159,8 +159,7 @@ constructor(
}
?.also {
ContentCache.saveResource(
id,
it.copy(),
it,
)
}
}
@@ -224,7 +223,7 @@ constructor(
structureMap =
structureMap
?: defaultRepository.loadResource<StructureMap>(this)?.also {
it.let { ContentCache.saveResource(this, it) }
it.let { ContentCache.saveResource( it) }
}
}
return structureMap as? StructureMap
14 changes: 14 additions & 0 deletions android/engine/src/main/res/drawable/ic_insights.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="192"
android:viewportHeight="192">

<path
android:fillColor="#919191"
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"
android:strokeWidth="8"
android:strokeColor="#919191"
android:fillType="evenOdd"/>

</vector>
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@

package org.smartregister.fhircore.quest.navigation

import androidx.compose.material.icons.Icons
import org.smartregister.fhircore.quest.R

sealed class MainNavigationScreen(
@@ -54,6 +55,13 @@ sealed class MainNavigationScreen(
"reportsRoute",
true,
)
data object Insights :
MainNavigationScreen(
R.string.insights,
org.smartregister.fhircore.engine.R.drawable.ic_insights,
"insightsRoute",
true,
)

data object Settings :
MainNavigationScreen(
@@ -109,6 +117,7 @@ sealed class MainNavigationScreen(
TracingHistory,
TracingOutcomes,
TracingHistoryDetails,
Insights,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright 2021 Ona Systems, Inc
*
* 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.
*/

package org.smartregister.fhircore.quest.ui.insights

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import org.smartregister.fhircore.quest.R

@Composable
fun InsightsScreen(
navController: NavHostController,
modifier: Modifier = Modifier,
insightsViewModel: InsightsViewModel = hiltViewModel(),
) {
Scaffold(
topBar = {
Column(
modifier = modifier.fillMaxWidth().background(MaterialTheme.colors.primary),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier.padding(vertical = 8.dp),
) {
IconButton(onClick = { navController.popBackStack() }) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back",
tint = Color.White,
)
}
Text(
text = stringResource(id = R.string.insights),
fontSize = 20.sp,
color = Color.White,
modifier = Modifier.weight(1f),
)
}
}
},
) { innerPadding ->
Box(modifier = modifier.padding(innerPadding)) {

val isRefreshing by insightsViewModel.isRefreshing.collectAsState()
val isRefreshingRamAvailabilityStats by insightsViewModel.isRefreshingRamAvailabilityStatsStateFlow.collectAsState()
val ramAvailabilityStats by insightsViewModel.ramAvailabilityStatsStateFlow.collectAsState()

SwipeRefresh(
state = rememberSwipeRefreshState(isRefreshing),
onRefresh = { insightsViewModel.refresh() },
// indicator = { _, _ -> }
) {
LazyColumn {
item {
Card(
modifier = Modifier.padding(8.dp).fillMaxWidth().height(IntrinsicSize.Min),
) {
Box(
modifier = Modifier.padding(8.dp),
) {
Column {
Text(
text = stringResource(R.string.ram_available),
style = MaterialTheme.typography.h4.copy(color = Color.Gray),
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "$ramAvailabilityStats",
style =
if (isRefreshingRamAvailabilityStats) {
MaterialTheme.typography.h2.copy(color = Color.Gray.copy(alpha = 0.5F))
} else MaterialTheme.typography.h2,
)
}
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2021 Ona Systems, Inc
*
* 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.
*/

package org.smartregister.fhircore.quest.ui.insights

import android.app.ActivityManager
import android.app.Application
import android.content.Context
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.smartregister.fhircore.engine.data.local.register.AppRegisterRepository
import org.smartregister.fhircore.engine.util.DispatcherProvider
import kotlin.concurrent.fixedRateTimer
import kotlin.math.pow
import kotlin.math.roundToInt

@HiltViewModel
class InsightsViewModel
@Inject
constructor(
val dispatcherProvider: DispatcherProvider,
val registerRepository: AppRegisterRepository,
application: Application
) : AndroidViewModel(application) {

private val _isRefreshingRamAvailabilityStats = MutableStateFlow(false)
val isRefreshingRamAvailabilityStatsStateFlow = _isRefreshingRamAvailabilityStats.asStateFlow()

val isRefreshing = _isRefreshingRamAvailabilityStats.stateIn(viewModelScope, SharingStarted.Lazily, initialValue = false)

val ramAvailabilityStatsStateFlow = MutableStateFlow("")

fun refresh() {
getMemoryInfo()
}

init{
fixedRateTimer("timer", false, 0L, 1000) {
refresh()
}
}

private fun getMemoryInfo() {
viewModelScope.launch {

val actManager = getApplication<Application>().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memInfo = ActivityManager.MemoryInfo()
actManager.getMemoryInfo(memInfo)

// Fetch the available and total memory in GB
val availMemory = memInfo.availMem.toDouble()/(1024*1024*1024)
val totalMemory= memInfo.totalMem.toDouble()/(1024*1024*1024)

// Update the RAM Availability stateflow
ramAvailabilityStatsStateFlow.emit("${(totalMemory - availMemory).roundTo(3)}G/${totalMemory.roundTo(3)}G")
}
}
private fun Double.roundTo(numFractionDigits: Int): Double {
val factor = 10.0.pow(numFractionDigits.toDouble())
return (this * factor).roundToInt() / factor
}
}
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ import org.smartregister.fhircore.quest.navigation.NavigationArg
import org.smartregister.fhircore.quest.ui.appointment.register.AppointmentRegisterScreen
import org.smartregister.fhircore.quest.ui.counters.CountersScreen
import org.smartregister.fhircore.quest.ui.family.profile.FamilyProfileScreen
import org.smartregister.fhircore.quest.ui.insights.InsightsScreen
import org.smartregister.fhircore.quest.ui.main.components.AppDrawer
import org.smartregister.fhircore.quest.ui.patient.profile.PatientProfileScreen
import org.smartregister.fhircore.quest.ui.patient.profile.childcontact.ChildContactsProfileScreen
@@ -183,6 +184,8 @@ private fun AppMainNavigationGraph(
MainNavigationScreen.Counters ->
composable(route = it.route) { CountersScreen(navController = navController) }
MainNavigationScreen.Tasks -> composable(MainNavigationScreen.Tasks.route) {}
MainNavigationScreen.Insights ->
composable(route = it.route) { InsightsScreen(navController = navController) }
MainNavigationScreen.Reports ->
measureReportNavigationGraph(navController, measureReportViewModel)
MainNavigationScreen.Settings ->
Original file line number Diff line number Diff line change
@@ -203,6 +203,16 @@ fun AppDrawer(
}
}
}

SideMenuItem(
iconResource = org.smartregister.fhircore.engine.R.drawable.ic_insights,
title = stringResource(R.string.insights, username),
showEndText = false,
onSideMenuClick = {
openDrawer(false)
navController.navigate(MainNavigationScreen.Insights.route)
},
)
SideMenuItem(
iconResource = R.drawable.ic_settings,
title = stringResource(org.smartregister.fhircore.engine.R.string.settings, username),
2 changes: 2 additions & 0 deletions android/quest/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -144,4 +144,6 @@
<string name="home_tracing_conter_label">Home Tracing</string>
<string name="phone_tracing_counter_label">Phone Tracing</string>
<string name="appointments_counter_label">Appointments</string>
<string name="insights" translatable="false">Insights</string>
<string name="ram_available" translatable="false">RAM Available</string>
</resources>