Skip to content

Commit

Permalink
Merge pull request #91 from CrisisCleanup/status-colors-and-translations
Browse files Browse the repository at this point in the history
Status colors and translations
  • Loading branch information
hueachilles authored Sep 14, 2023
2 parents 39c9db4 + 5946b9f commit e5cd061
Show file tree
Hide file tree
Showing 55 changed files with 726 additions and 192 deletions.
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

0 comments on commit e5cd061

Please sign in to comment.