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

Status colors and translations #91

Merged
merged 10 commits into from
Sep 14, 2023
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ plugins {

android {
defaultConfig {
val buildVersion = 149
val buildVersion = 154
applicationId = "com.crisiscleanup"
versionCode = buildVersion
versionName = "0.7.${buildVersion - 140}"
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/com/crisiscleanup/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ import com.crisiscleanup.core.common.log.Logger
import com.crisiscleanup.core.common.sync.SyncPuller
import com.crisiscleanup.core.data.repository.AppMetricsRepository
import com.crisiscleanup.core.data.repository.EndOfLifeRepository
import com.crisiscleanup.core.data.repository.LanguageTranslationsRepository
import com.crisiscleanup.core.designsystem.theme.CrisisCleanupTheme
import com.crisiscleanup.core.designsystem.theme.navigationContainerColor
import com.crisiscleanup.core.model.data.DarkThemeConfig
import com.crisiscleanup.sync.initializers.scheduleSyncWorksites
import com.crisiscleanup.ui.CrisisCleanupApp
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.android.gms.maps.MapsInitializer
Expand Down Expand Up @@ -91,6 +93,9 @@ class MainActivity : ComponentActivity() {
@Inject
internal lateinit var appMetricsRepository: AppMetricsRepository

@Inject
internal lateinit var languageTranslationsRepository: LanguageTranslationsRepository

private val lifecycleObservers = mutableListOf<LifecycleObserver>()

override fun onCreate(savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -176,6 +181,10 @@ class MainActivity : ComponentActivity() {

endOfLifeRepository.saveEndOfLifeData()
appMetricsRepository.saveAppSupportInfo()

languageTranslationsRepository.setLanguageFromSystem()

scheduleSyncWorksites(this)
}

override fun onPause() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import com.crisiscleanup.core.appnav.RouteConstant.casesGraphRoutePattern
import com.crisiscleanup.core.data.model.ExistingWorksiteIdentifier
import com.crisiscleanup.core.model.data.EmptyIncident
import com.crisiscleanup.core.model.data.EmptyWorksite
import com.crisiscleanup.feature.caseeditor.ExistingWorksiteIdentifier
import com.crisiscleanup.feature.caseeditor.navigation.caseAddFlagScreen
import com.crisiscleanup.feature.caseeditor.navigation.caseEditMoveLocationOnMapScreen
import com.crisiscleanup.feature.caseeditor.navigation.caseEditSearchAddressScreen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ interface SyncPusher {
suspend fun syncPushWorksitesAsync(): Deferred<SyncResult>
fun stopPushWorksites()
suspend fun syncPushMedia(): SyncResult
suspend fun syncPushWorksites(): SyncResult

fun scheduleSyncMedia()
fun scheduleSyncWorksites()
}

sealed interface SyncResult {
Expand Down
5 changes: 5 additions & 0 deletions core/commoncase/src/main/java/CommonCaseConstants.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.crisiscleanup.core.commoncase

import java.text.DecimalFormat

val oneDecimalFormat = DecimalFormat("#.#")
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.crisiscleanup.core.data

import kotlinx.datetime.Clock
import kotlinx.datetime.Instant

interface WorksiteInteractor {
fun onSelectCase(
incidentId: Long,
worksiteId: Long,
)

fun wasCaseSelected(
incidentId: Long,
worksiteId: Long,
reference: Instant = Clock.System.now(),
): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.crisiscleanup.core.data.model

import com.crisiscleanup.core.model.data.EmptyIncident
import com.crisiscleanup.core.model.data.EmptyWorksite

data class ExistingWorksiteIdentifier(
val incidentId: Long,
// This is the local (database) ID not network ID
val worksiteId: Long,
) {
val isDefined = incidentId != EmptyIncident.id &&
worksiteId != EmptyWorksite.id
}

val ExistingWorksiteIdentifierNone = ExistingWorksiteIdentifier(
EmptyIncident.id,
EmptyWorksite.id,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ fun NetworkLanguageDescription.asEntity() = LanguageTranslationEntity(
fun NetworkLanguageTranslation.asEntity(syncedAt: Instant) = LanguageTranslationEntity(
key = subtag,
name = name,
translationsJson = Json.encodeToString(translations),
translationsJson = Json.encodeToString(translations.filter { it.value != null }),
syncedAt = syncedAt,
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ fun NetworkPersonContact.asExternalModel() = PersonContact(
mobile = mobile,
)

fun NetworkPersonContact.asEntities(): PersonContactEntities {
fun NetworkPersonContact.asEntities() = organization?.let {
val organizationEntity = IncidentOrganizationEntity(
id = organization!!.id,
name = organization!!.name,
id = it.id,
name = it.name,
primaryLocation = null,
secondaryLocation = null,
)
val personContact = asEntity()
val personToOrganization = PersonOrganizationCrossRef(id, organization!!.id)
return PersonContactEntities(
val personToOrganization = PersonOrganizationCrossRef(id, it.id)
PersonContactEntities(
organization = organizationEntity,
organizationAffiliates = organization!!.affiliates,
organizationAffiliates = it.affiliates,
personContact = personContact,
personToOrganization = personToOrganization,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class OfflineFirstCaseHistoryRepository @Inject constructor(
private suspend fun queryUpdateUsers(userIds: Collection<Long>) {
try {
val networkUsers = networkDataSource.getUsers(userIds)
val entities = networkUsers.map(NetworkPersonContact::asEntities)
val entities = networkUsers.mapNotNull(NetworkPersonContact::asEntities)

val organizations = entities.map(PersonContactEntities::organization)
val affiliates = entities.map(PersonContactEntities::organizationAffiliates)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.crisiscleanup.core.data.repository

import com.crisiscleanup.core.common.AndroidResourceProvider
import com.crisiscleanup.core.common.KeyTranslator
import com.crisiscleanup.core.common.di.ApplicationScope
import com.crisiscleanup.core.common.log.AppLogger
Expand Down Expand Up @@ -33,6 +34,7 @@ import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.datetime.Clock
import java.util.Locale
import javax.inject.Inject
import javax.inject.Singleton

Expand All @@ -46,6 +48,8 @@ interface LanguageTranslationsRepository : KeyTranslator {
suspend fun loadLanguages(force: Boolean = false)

fun setLanguage(key: String = "")

fun setLanguageFromSystem()
}

@Singleton
Expand All @@ -55,6 +59,7 @@ class OfflineFirstLanguageTranslationsRepository @Inject constructor(
private val languageDao: LanguageDao,
private val languageDaoPlus: LanguageDaoPlus,
private val statusRepository: WorkTypeStatusRepository,
private val resourceProvider: AndroidResourceProvider,
@Logger(CrisisCleanupLoggers.Language) private val logger: AppLogger,
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
@ApplicationScope private val coroutineScope: CoroutineScope,
Expand Down Expand Up @@ -143,13 +148,20 @@ class OfflineFirstLanguageTranslationsRepository @Inject constructor(
} else {
pullUpdatedTranslations()
}

setLanguageFromSystem()
} catch (e: Exception) {
logger.logException(e)
} finally {
isLoadingLanguages.value = false
}
}

override fun setLanguageFromSystem() {
val systemLocale = Locale.getDefault().toLanguageTag()
setLanguage(systemLocale)
}

private suspend fun pullUpdatedTranslations() =
pullUpdatedTranslations(currentLanguage.value.key)

Expand All @@ -166,16 +178,17 @@ class OfflineFirstLanguageTranslationsRepository @Inject constructor(
override fun setLanguage(key: String) {
setLanguageJob?.cancel()
setLanguageJob = coroutineScope.launch(ioDispatcher) {
try {
// TODO Set the language if local translations exist.
// Pull does not need to succeed in this case.
// Consider possible race condition if ordering changes.
val languages = supportedLanguages.first()
.map(Language::key)
.toSet()
val languageKey = if (languages.contains(key)) key else EnglishLanguage.key

pullUpdatedTranslations(key)
try {
pullUpdatedTranslations(languageKey)

ensureActive()

appPreferences.setLanguageKey(key)
appPreferences.setLanguageKey(languageKey)
} catch (e: Exception) {
logger.logException(e)
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ val statusDoneByOthersNhwDiColor = Color(statusDoneByOthersNhwColorCode)
val statusOutOfScopeRejectedColor = Color(statusOutOfScopeRejectedColorCode)
val statusUnresponsiveColor = Color(statusUnresponsiveColorCode)

val visitedCaseMarkerColorCode = 0xFF681da8

val avatarAttentionColor = primaryRedColor
val avatarStandardColor = statusCompletedColor

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class InMemoryDotProvider @Inject constructor(
cacheKey.statusClaim,
cacheKey.isDuplicate,
cacheKey.isFilteredOut,
true,
isVisited = false,
isDot = true,
)
val bitmap = drawDot(colors, dotDrawProperties)
val bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap)
Expand All @@ -82,6 +83,7 @@ class InMemoryDotProvider @Inject constructor(
hasMultipleWorkTypes: Boolean,
isDuplicate: Boolean,
isFilteredOut: Boolean,
isVisited: Boolean,
): BitmapDescriptor? {
val cacheKey = DotCacheKey(statusClaim, isDuplicate, isFilteredOut)
synchronized(cache) {
Expand All @@ -99,6 +101,7 @@ class InMemoryDotProvider @Inject constructor(
hasMultipleWorkTypes: Boolean,
isDuplicate: Boolean,
isFilteredOut: Boolean,
isVisited: Boolean,
): Bitmap? {
val cacheKey = DotCacheKey(statusClaim, isDuplicate, isFilteredOut)
synchronized(cache) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface MapCaseIconProvider {
hasMultipleWorkTypes: Boolean,
isDuplicate: Boolean = false,
isFilteredOut: Boolean = false,
isVisited: Boolean = false,
): BitmapDescriptor?

fun getIconBitmap(
Expand All @@ -28,5 +29,6 @@ interface MapCaseIconProvider {
hasMultipleWorkTypes: Boolean,
isDuplicate: Boolean = false,
isFilteredOut: Boolean = false,
isVisited: Boolean = false,
): Bitmap?
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import com.crisiscleanup.core.designsystem.theme.statusOutOfScopeRejectedColorCo
import com.crisiscleanup.core.designsystem.theme.statusPartiallyCompletedColorCode
import com.crisiscleanup.core.designsystem.theme.statusUnclaimedColorCode
import com.crisiscleanup.core.designsystem.theme.statusUnknownColorCode
import com.crisiscleanup.core.designsystem.theme.visitedCaseMarkerColorCode
import com.crisiscleanup.core.model.data.CaseStatus.ClaimedNotStarted
import com.crisiscleanup.core.model.data.CaseStatus.Completed
import com.crisiscleanup.core.model.data.CaseStatus.DoneByOthersNhwPc
import com.crisiscleanup.core.model.data.CaseStatus.DoneByOthersNhw
import com.crisiscleanup.core.model.data.CaseStatus.InProgress
import com.crisiscleanup.core.model.data.CaseStatus.Incomplete
import com.crisiscleanup.core.model.data.CaseStatus.NeedsFollowUp
Expand Down Expand Up @@ -43,7 +44,7 @@ private val statusMapMarkerColors = mapOf(
PartiallyCompleted to MapMarkerColor(statusPartiallyCompletedColorCode),
NeedsFollowUp to MapMarkerColor(statusNeedsFollowUpColorCode),
Completed to MapMarkerColor(statusCompletedColorCode),
DoneByOthersNhwPc to MapMarkerColor(statusDoneByOthersNhwColorCode),
DoneByOthersNhw to MapMarkerColor(statusDoneByOthersNhwColorCode),
// Unresponsive
OutOfScopeDu to MapMarkerColor(statusOutOfScopeRejectedColorCode),
Incomplete to MapMarkerColor(statusDoneByOthersNhwColorCode),
Expand Down Expand Up @@ -75,6 +76,7 @@ internal fun getMapMarkerColors(
statusClaim: WorkTypeStatusClaim,
isDuplicate: Boolean,
isFilteredOut: Boolean,
isVisited: Boolean,
isDot: Boolean,
): MapMarkerColor {
var colors = statusClaimMapMarkerColors[statusClaim]
Expand All @@ -98,6 +100,11 @@ internal fun getMapMarkerColors(
stroke = it.stroke.copy(alpha = strokeAlpha),
)
}
} else if (isVisited) {
colors = MapMarkerColor(
colors.fillLong,
strokeLong = visitedCaseMarkerColorCode,
)
}

return colors
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
package com.crisiscleanup.core.mapmarker

import com.crisiscleanup.core.model.data.CaseStatus
import com.crisiscleanup.core.model.data.CaseStatus.*
import com.crisiscleanup.core.model.data.CaseStatus.ClaimedNotStarted
import com.crisiscleanup.core.model.data.CaseStatus.Completed
import com.crisiscleanup.core.model.data.CaseStatus.DoneByOthersNhw
import com.crisiscleanup.core.model.data.CaseStatus.InProgress
import com.crisiscleanup.core.model.data.CaseStatus.Incomplete
import com.crisiscleanup.core.model.data.CaseStatus.NeedsFollowUp
import com.crisiscleanup.core.model.data.CaseStatus.OutOfScopeDu
import com.crisiscleanup.core.model.data.CaseStatus.PartiallyCompleted
import com.crisiscleanup.core.model.data.CaseStatus.Unclaimed
import com.crisiscleanup.core.model.data.WorkTypeStatus
import com.crisiscleanup.core.model.data.WorkTypeStatus.*
import com.crisiscleanup.core.model.data.WorkTypeStatus.ClosedCompleted
import com.crisiscleanup.core.model.data.WorkTypeStatus.ClosedDoneByOthers
import com.crisiscleanup.core.model.data.WorkTypeStatus.ClosedDuplicate
import com.crisiscleanup.core.model.data.WorkTypeStatus.ClosedIncomplete
import com.crisiscleanup.core.model.data.WorkTypeStatus.ClosedNoHelpWanted
import com.crisiscleanup.core.model.data.WorkTypeStatus.ClosedOutOfScope
import com.crisiscleanup.core.model.data.WorkTypeStatus.OpenAssigned
import com.crisiscleanup.core.model.data.WorkTypeStatus.OpenNeedsFollowUp
import com.crisiscleanup.core.model.data.WorkTypeStatus.OpenPartiallyCompleted
import com.crisiscleanup.core.model.data.WorkTypeStatus.OpenUnassigned
import com.crisiscleanup.core.model.data.WorkTypeStatus.OpenUnresponsive
import com.crisiscleanup.core.model.data.WorkTypeStatusClaim

internal val statusClaimToStatus = mapOf(
Expand All @@ -17,7 +35,8 @@ internal val statusClaimToStatus = mapOf(
WorkTypeStatusClaim(ClosedIncomplete, true) to Incomplete,
WorkTypeStatusClaim(ClosedOutOfScope, true) to OutOfScopeDu,
WorkTypeStatusClaim(ClosedDuplicate, true) to OutOfScopeDu,
WorkTypeStatusClaim(ClosedDoneByOthers, true) to DoneByOthersNhwPc,
WorkTypeStatusClaim(ClosedDoneByOthers, true) to DoneByOthersNhw,
WorkTypeStatusClaim(ClosedNoHelpWanted, true) to DoneByOthersNhw,
WorkTypeStatusClaim(WorkTypeStatus.Unknown, false) to CaseStatus.Unknown,
WorkTypeStatusClaim(OpenAssigned, false) to Unclaimed,
WorkTypeStatusClaim(OpenUnassigned, false) to Unclaimed,
Expand All @@ -28,5 +47,6 @@ internal val statusClaimToStatus = mapOf(
WorkTypeStatusClaim(ClosedIncomplete, false) to Incomplete,
WorkTypeStatusClaim(ClosedOutOfScope, false) to OutOfScopeDu,
WorkTypeStatusClaim(ClosedDuplicate, false) to OutOfScopeDu,
WorkTypeStatusClaim(ClosedDoneByOthers, false) to DoneByOthersNhwPc,
WorkTypeStatusClaim(ClosedDoneByOthers, false) to DoneByOthersNhw,
WorkTypeStatusClaim(ClosedNoHelpWanted, false) to DoneByOthersNhw,
)
Loading
Loading