Skip to content

Commit

Permalink
Merge pull request #97 from CrisisCleanup/onboarding
Browse files Browse the repository at this point in the history
Onboarding
  • Loading branch information
hueachilles authored Dec 19, 2023
2 parents 9cb451d + 3e3ea38 commit c38b674
Show file tree
Hide file tree
Showing 76 changed files with 2,717 additions and 305 deletions.
4 changes: 3 additions & 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 = 170
val buildVersion = 173
applicationId = "com.crisiscleanup"
versionCode = buildVersion
versionName = "0.9.${buildVersion - 168}"
Expand Down Expand Up @@ -112,6 +112,7 @@ dependencies {
implementation(project(":feature:dashboard"))
implementation(project(":feature:menu"))
implementation(project(":feature:mediamanage"))
implementation(project(":feature:organizationmanage"))
implementation(project(":feature:syncinsights"))
implementation(project(":feature:team"))
implementation(project(":feature:userfeedback"))
Expand Down Expand Up @@ -157,6 +158,7 @@ dependencies {
implementation(libs.androidx.window.manager)
implementation(libs.androidx.profileinstaller)
implementation(libs.playservices.location)
implementation(libs.zxing)

implementation(libs.coil.kt)

Expand Down
4 changes: 4 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@

# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

# Android Studio Hedgehog require the following rules
-dontwarn io.grpc.internal.DnsNameResolverProvider
-dontwarn io.grpc.internal.PickFirstLoadBalancerProvider
7 changes: 5 additions & 2 deletions app/src/main/java/com/crisiscleanup/CrisisCleanupAppEnv.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.crisiscleanup

import com.crisiscleanup.core.common.AppEnv
import com.crisiscleanup.core.common.AppSettingsProvider
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class CrisisCleanupAppEnv @Inject constructor() : AppEnv {
class CrisisCleanupAppEnv @Inject constructor(
private val settingsProvider: AppSettingsProvider,
) : AppEnv {
override val isDebuggable = !(BuildConfig.IS_RELEASE_BUILD || BuildConfig.IS_PROD_BUILD)
override val isProduction = BuildConfig.IS_RELEASE_BUILD && BuildConfig.IS_PROD_BUILD
override val isNotProduction = !isProduction
Expand All @@ -14,7 +17,7 @@ class CrisisCleanupAppEnv @Inject constructor() : AppEnv {

override val apiEnvironment: String
get() {
val apiUrl = BuildConfig.API_BASE_URL
val apiUrl = settingsProvider.apiBaseUrl
return when {
apiUrl.startsWith("https://api.dev.crisiscleanup.io") -> "Dev"
apiUrl.startsWith("https://api.staging.crisiscleanup.io") -> "Staging"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package com.crisiscleanup

import android.content.Intent
import android.net.Uri
import com.crisiscleanup.core.common.event.AuthEventBus
import com.crisiscleanup.core.common.event.ExternalEventBus
import com.crisiscleanup.core.common.log.AppLogger
import com.crisiscleanup.core.common.log.CrisisCleanupLoggers
import com.crisiscleanup.core.common.log.Logger
import javax.inject.Inject

class ExternalIntentProcessor @Inject constructor(
private val authEventBus: AuthEventBus,
private val externalEventBus: ExternalEventBus,
@Logger(CrisisCleanupLoggers.App) private val logger: AppLogger,
) {
fun processMainIntent(intent: Intent): Boolean {
Expand All @@ -34,12 +34,12 @@ class ExternalIntentProcessor @Inject constructor(
if (urlPath.startsWith("/l/")) {
val code = urlPath.replace("/l/", "")
if (code.isNotBlank()) {
authEventBus.onEmailLoginLink(code)
externalEventBus.onEmailLoginLink(code)
}
} else if (urlPath.startsWith("/password/reset/")) {
val code = urlPath.replace("/password/reset/", "")
if (code.isNotBlank()) {
authEventBus.onResetPassword(code)
externalEventBus.onResetPassword(code)
}
} else {
return false
Expand Down
8 changes: 4 additions & 4 deletions app/src/main/java/com/crisiscleanup/MainActivityViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.crisiscleanup.core.appheader.AppHeaderUiState
import com.crisiscleanup.core.common.AppEnv
import com.crisiscleanup.core.common.AppVersionProvider
import com.crisiscleanup.core.common.KeyResourceTranslator
import com.crisiscleanup.core.common.event.AuthEventBus
import com.crisiscleanup.core.common.event.ExternalEventBus
import com.crisiscleanup.core.common.log.AppLogger
import com.crisiscleanup.core.common.log.CrisisCleanupLoggers
import com.crisiscleanup.core.common.log.Logger
Expand Down Expand Up @@ -68,7 +68,7 @@ class MainActivityViewModel @Inject constructor(
private val appVersionProvider: AppVersionProvider,
private val appEnv: AppEnv,
firebaseAnalytics: FirebaseAnalytics,
authEventBus: AuthEventBus,
externalEventBus: ExternalEventBus,
@Dispatcher(IO) ioDispatcher: CoroutineDispatcher,
@Logger(CrisisCleanupLoggers.App) private val logger: AppLogger,
) : ViewModel() {
Expand Down Expand Up @@ -164,8 +164,8 @@ class MainActivityViewModel @Inject constructor(
return null
}

val showPasswordReset = authEventBus.showResetPassword
val showMagicLinkLogin = authEventBus.showMagicLinkLogin
val showPasswordReset = externalEventBus.showResetPassword
val showMagicLinkLogin = externalEventBus.showMagicLinkLogin

val isSwitchingToProduction: StateFlow<Boolean>
val productionSwitchMessage: StateFlow<String>
Expand Down
36 changes: 36 additions & 0 deletions app/src/main/java/com/crisiscleanup/ZxingQrCodeGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.crisiscleanup

import android.graphics.Bitmap
import android.graphics.Color
import com.crisiscleanup.core.common.QrCodeGenerator
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.QRCodeWriter
import javax.inject.Inject

class ZxingQrCodeGenerator @Inject constructor() : QrCodeGenerator {
override fun generate(payload: String, size: Int): Bitmap? {
if (payload.isBlank()) {
return null
}

val writer = QRCodeWriter()
// Removing margin in image should add padding around image display
val hints = mapOf(
EncodeHintType.MARGIN to 0,
)
val bitMatrix = writer.encode(payload, BarcodeFormat.QR_CODE, size, size, hints)
val width = bitMatrix.width
val height = bitMatrix.height
val pixels = IntArray(width * height)
for (h in 0 until height) {
val offset = h * width
for (w in 0 until width) {
pixels[offset + w] = if (bitMatrix.get(w, h)) Color.BLACK else Color.TRANSPARENT
}
}
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0, width, 0, 0, width, height)
return bitmap
}
}
4 changes: 4 additions & 0 deletions app/src/main/java/com/crisiscleanup/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.crisiscleanup.AndroidLocationProvider
import com.crisiscleanup.AndroidPermissionManager
import com.crisiscleanup.AppVisualAlertManager
import com.crisiscleanup.CrisisCleanupAppEnv
import com.crisiscleanup.ZxingQrCodeGenerator
import com.crisiscleanup.core.appheader.AppHeaderUiState
import com.crisiscleanup.core.common.*
import com.crisiscleanup.core.common.log.TagLogger
Expand Down Expand Up @@ -65,6 +66,9 @@ interface AppModule {

@Binds
fun bindsVisualAlertManager(manager: AppVisualAlertManager): VisualAlertManager

@Binds
fun bindsQrCodeGenerator(generator: ZxingQrCodeGenerator): QrCodeGenerator
}

@Module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import com.crisiscleanup.feature.cases.ui.CasesAction
import com.crisiscleanup.feature.dashboard.navigation.dashboardScreen
import com.crisiscleanup.feature.mediamanage.navigation.viewSingleImageScreen
import com.crisiscleanup.feature.menu.navigation.menuScreen
import com.crisiscleanup.feature.organizationmanage.navigation.inviteTeammateScreen
import com.crisiscleanup.feature.organizationmanage.navigation.navigateToInviteTeammate
import com.crisiscleanup.feature.syncinsights.navigation.navigateToSyncInsights
import com.crisiscleanup.feature.syncinsights.navigation.syncInsightsScreen
import com.crisiscleanup.feature.team.navigation.teamScreen
Expand Down Expand Up @@ -88,21 +90,14 @@ fun CrisisCleanupNavHost(
{ ids: ExistingWorksiteIdentifier -> navController.rerouteToCaseChange(ids) }
}

val openFilterCases = remember(navController) {
{ navController.navigateToCasesFilter() }
}
val openFilterCases = remember(navController) { { navController.navigateToCasesFilter() } }

val openUserFeedback = remember(navController) {
{
navController.navigateToUserFeedback()
}
}
val openInviteTeammate =
remember(navController) { { navController.navigateToInviteTeammate() } }

val openSyncLogs = remember(navController) {
{
navController.navigateToSyncInsights()
}
}
val openUserFeedback = remember(navController) { { navController.navigateToUserFeedback() } }

val openSyncLogs = remember(navController) { { navController.navigateToSyncInsights() } }

val navToCaseAddFlagNonEditing =
remember(navController) { { navController.navigateToCaseAddFlag(false) } }
Expand Down Expand Up @@ -138,10 +133,12 @@ fun CrisisCleanupNavHost(
dashboardScreen()
teamScreen()
menuScreen(
openUserFeedback,
openSyncLogs,
openInviteTeammate = openInviteTeammate,
openUserFeedback = openUserFeedback,
openSyncLogs = openSyncLogs,
)
viewSingleImageScreen(onBack)
inviteTeammateScreen(onBack)
userFeedbackScreen(onBack)
syncInsightsScreen(viewCase)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class CrisisCleanupInterceptorProvider @Inject constructor(
var response: Response = chain.proceed(request)

getHeaderKey(request, RequestHeaderKey.WrapResponse)?.let { key ->
if (response.code == 200) {
if (response.code in 200..299) {
// TODO Write tests. Including expired tokens where tokens are used.
// Would be more elegant to deserialize, make new, and re-serialize.
// Data structure is simple so text operations are sufficient.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ internal fun Project.configureKotlinAndroid(
compileOptions {
// Up to Java 11 APIs are available through desugaring
// https://developer.android.com/studio/write/java11-minimal-support-table
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
}
}
Expand All @@ -64,8 +64,8 @@ internal fun Project.configureKotlinJvm() {
extensions.configure<JavaPluginExtension> {
// Up to Java 11 APIs are available through desugaring
// https://developer.android.com/studio/write/java11-minimal-support-table
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

configureKotlin()
Expand All @@ -79,7 +79,7 @@ private fun Project.configureKotlin() {
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
// Set JVM target to 11
jvmTarget = JavaVersion.VERSION_11.toString()
jvmTarget = JavaVersion.VERSION_17.toString()
// Treat all Kotlin warnings as errors (disabled by default)
// Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
val warningsAsErrors: String? by project
Expand Down
8 changes: 0 additions & 8 deletions core/addresssearch/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@ plugins {
id("nowinandroid.android.library")
id("nowinandroid.android.library.jacoco")
id("nowinandroid.android.hilt")
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
}

android {
buildFeatures {
buildConfig = true
}
namespace = "com.crisiscleanup.core.addresssearch"
}

secrets {
defaultPropertiesFileName = "secrets.defaults.properties"
}

dependencies {
implementation(project(":core:common"))
implementation(project(":core:model"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import android.location.Geocoder
import android.util.LruCache
import com.crisiscleanup.core.addresssearch.model.KeySearchAddress
import com.crisiscleanup.core.common.AppSettingsProvider
import com.crisiscleanup.core.common.combineTrimText
import com.crisiscleanup.core.common.log.AppLogger
import com.crisiscleanup.core.common.log.CrisisCleanupLoggers
Expand Down Expand Up @@ -36,6 +37,7 @@ import kotlin.time.Duration.Companion.hours
class GooglePlaceAddressSearchRepository @Inject constructor(
@ApplicationContext private val context: Context,
@Logger(CrisisCleanupLoggers.App) private val logger: AppLogger,
private val settingsProvider: AppSettingsProvider,
) : AddressSearchRepository {
private val geocoder = Geocoder(context)

Expand All @@ -44,7 +46,7 @@ class GooglePlaceAddressSearchRepository @Inject constructor(
private suspend fun placesClient(): PlacesClient {
placesClientMutex.withLock {
if (_placesClient == null) {
Places.initialize(context, BuildConfig.MAPS_API_KEY)
Places.initialize(context, settingsProvider.mapsApiKey)
_placesClient = Places.createClient(context)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ object RouteConstant {
const val teamRoute = "team_route"
val topLevelRoutes = setOf(casesRoute, menuRoute)

const val inviteTeammateRoute = "invite_teammate"
const val userFeedbackRoute = "user_feedback_route"

const val syncInsightsRoute = "sync_insights_route"
Expand Down
8 changes: 8 additions & 0 deletions core/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ plugins {
id("nowinandroid.android.library")
id("nowinandroid.android.library.jacoco")
id("nowinandroid.android.hilt")
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
}

android {
buildFeatures {
buildConfig = true
}
namespace = "com.crisiscleanup.core.common"
}

secrets {
defaultPropertiesFileName = "secrets.defaults.properties"
}

dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.kotlinx.coroutines.android)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.crisiscleanup.core.common

import javax.inject.Inject

interface AppSettingsProvider {
val apiBaseUrl: String
val baseUrl: String
val mapsApiKey: String

val debugEmail: String
val debugPassword: String
}

class SecretsAppSettingsProvider @Inject constructor() : AppSettingsProvider {
override val apiBaseUrl: String
get() = BuildConfig.API_BASE_URL
override val baseUrl: String
get() = BuildConfig.BASE_URL
override val mapsApiKey: String
get() = BuildConfig.MAPS_API_KEY

override val debugEmail: String
get() = BuildConfig.DEBUG_EMAIL_ADDRESS
override val debugPassword: String
get() = BuildConfig.DEBUG_ACCOUNT_PASSWORD
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.crisiscleanup.core.common

import android.graphics.Bitmap

interface QrCodeGenerator {
fun generate(payload: String, size: Int = 512): Bitmap?
}
Loading

0 comments on commit c38b674

Please sign in to comment.