Skip to content
Merged
Show file tree
Hide file tree
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: 2 additions & 0 deletions api/src/main/java/com/getcode/analytics/AnalyticsManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ class AnalyticsManager @Inject constructor(
Backup("Backup Screen"),
Withdraw("Withdraw Screen"),
Debug("Debug Screen"),
Share("Share Screen"),
AppSettings("App Settings"),
ForceUpgrade("Force Upgrade"),
BuyMoreKin("Buy More Kin Screen"),
SendKin("Send Kin Screen"),
Expand Down
26 changes: 17 additions & 9 deletions api/src/main/java/com/getcode/model/PrefBool.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,24 @@ data class PrefBool(
val value: Boolean
)


sealed interface InternalRouting
sealed interface AppSetting
sealed interface BetaFlag


sealed class PrefsBool(val value: String) {
// internal routing
data object IS_DEBUG_ACTIVE: PrefsBool("debug_menu_active")
data object IS_DEBUG_ALLOWED: PrefsBool("debug_menu_allowed")
data object IS_ELIGIBLE_GET_FIRST_KIN_AIRDROP: PrefsBool("is_eligible_get_first_kin_airdrop")
data object IS_ELIGIBLE_GIVE_FIRST_KIN_AIRDROP: PrefsBool("is_eligible_give_first_kin_airdrop")
data object HAS_REMOVED_LOCAL_CURRENCY: PrefsBool("removed_local_currency")
data object SEEN_TIP_CARD : PrefsBool("seen_tip_card")
data object IS_DEBUG_ACTIVE: PrefsBool("debug_menu_active"), InternalRouting
data object IS_DEBUG_ALLOWED: PrefsBool("debug_menu_allowed"), InternalRouting
data object IS_ELIGIBLE_GET_FIRST_KIN_AIRDROP: PrefsBool("is_eligible_get_first_kin_airdrop"), InternalRouting
data object IS_ELIGIBLE_GIVE_FIRST_KIN_AIRDROP: PrefsBool("is_eligible_give_first_kin_airdrop"), InternalRouting
data object HAS_REMOVED_LOCAL_CURRENCY: PrefsBool("removed_local_currency"), InternalRouting
data object SEEN_TIP_CARD : PrefsBool("seen_tip_card"), InternalRouting

data object BUY_MODULE_AVAILABLE : PrefsBool("buy_module_available")
data object BUY_MODULE_AVAILABLE : PrefsBool("buy_module_available"), InternalRouting

// app settings
data object CAMERA_START_BY_DEFAULT: PrefsBool("camera_start_default"), AppSetting

// beta flags
data object BUCKET_DEBUGGER_ENABLED: PrefsBool("debug_buckets"), BetaFlag
Expand All @@ -39,4 +45,6 @@ sealed class PrefsBool(val value: String) {
data object TIPS_CHAT_ENABLED: PrefsBool("tips_chat_enabled"), BetaFlag
data object TIPS_CHAT_CASH_ENABLED: PrefsBool("tips_chat_cash_enabled"), BetaFlag
data object BALANCE_CURRENCY_SELECTION_ENABLED: PrefsBool("balance_currency_enabled"), BetaFlag
}
}

val APP_SETTINGS: List<AppSetting> = listOf(PrefsBool.CAMERA_START_BY_DEFAULT)
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.getcode.network.repository

import com.getcode.model.AppSetting
import com.getcode.model.PrefsBool
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

data class AppSettings(
val cameraStartByDefault: Boolean
) {
companion object {
val Defaults = AppSettings(
cameraStartByDefault = true
)
}
}
class AppSettingsRepository @Inject constructor(
private val prefRepository: PrefRepository,
) {

fun observe(): Flow<AppSettings> = AppSettings.Defaults.let { defaults ->
prefRepository.observeOrDefault(PrefsBool.CAMERA_START_BY_DEFAULT, defaults.cameraStartByDefault)
.map { AppSettings(it) }
}

suspend fun get(setting: AppSetting): Boolean {
return when (setting) {
PrefsBool.CAMERA_START_BY_DEFAULT -> prefRepository.get(
PrefsBool.CAMERA_START_BY_DEFAULT,
AppSettings.Defaults.cameraStartByDefault
)
}
}

fun update(setting: AppSetting, value: Boolean) {
when (setting) {
PrefsBool.CAMERA_START_BY_DEFAULT -> {
prefRepository.set(PrefsBool.CAMERA_START_BY_DEFAULT, value)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ data object ShareDownloadLinkModal : MainGraph, ModalRoot {
) {
ShareDownloadScreen()
}

AnalyticsScreenWatcher(
lifecycleOwner = LocalLifecycleOwner.current,
event = AnalyticsManager.Screen.Share
)
}
}

Expand Down
22 changes: 22 additions & 0 deletions app/src/main/java/com/getcode/navigation/screens/ModalScreens.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.getcode.view.main.account.AccountDeposit
import com.getcode.view.main.account.AccountDetails
import com.getcode.view.main.account.AccountFaq
import com.getcode.view.main.account.AccountPhone
import com.getcode.view.main.account.AppSettingsScreen
import com.getcode.view.main.account.BackupKey
import com.getcode.view.main.account.BetaFlagsScreen
import com.getcode.view.main.account.ConfirmDeleteAccount
Expand Down Expand Up @@ -101,6 +102,27 @@ data object AccountDebugOptionsScreen : MainGraph, ModalContent {
}
}

@Parcelize
data object AppSettingsScreen : MainGraph, ModalContent {
@IgnoredOnParcel
override val key: ScreenKey = uniqueScreenKey

override val name: String
@Composable get() = stringResource(id = R.string.title_appSettings)

@Composable
override fun Content() {
ModalContainer(backButtonEnabled = { it is AppSettingsScreen }) {
AppSettingsScreen(getViewModel())
}

AnalyticsScreenWatcher(
lifecycleOwner = LocalLifecycleOwner.current,
event = AnalyticsManager.Screen.AppSettings
)
}
}

@Parcelize
data object AccountDetailsScreen : MainGraph, ModalContent {
@IgnoredOnParcel
Expand Down
14 changes: 10 additions & 4 deletions app/src/main/java/com/getcode/ui/components/CodeButton.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ fun CodeButton(
isLoading: Boolean = false,
isSuccess: Boolean = false,
enabled: Boolean = true,
contentPadding: PaddingValues = PaddingValues(
top = CodeTheme.dimens.grid.x3,
bottom = CodeTheme.dimens.grid.x3,
),
buttonState: ButtonState = ButtonState.Bordered,
textColor: Color = Color.Unspecified,
shape: Shape = CodeTheme.shapes.small,
Expand All @@ -55,6 +59,7 @@ fun CodeButton(
isSuccess = isSuccess,
enabled = enabled,
buttonState = buttonState,
contentPadding = contentPadding,
shape = shape,
contentColor = textColor,
) {
Expand All @@ -72,6 +77,10 @@ fun CodeButton(
enabled: Boolean = true,
buttonState: ButtonState = ButtonState.Bordered,
shape: Shape = CodeTheme.shapes.small,
contentPadding: PaddingValues = PaddingValues(
top = CodeTheme.dimens.grid.x3,
bottom = CodeTheme.dimens.grid.x3,
),
contentColor: Color = Color.Unspecified,
content: @Composable RowScope.() -> Unit,
) {
Expand Down Expand Up @@ -111,10 +120,7 @@ fun CodeButton(
pressedElevation = 0.dp
),
shape = shape,
contentPadding = ButtonDefaults.ContentPadding.plus(
top = CodeTheme.dimens.grid.x3,
bottom = CodeTheme.dimens.grid.x3,
)
contentPadding = ButtonDefaults.ContentPadding.plus(contentPadding)
) {
when {
isLoading -> {
Expand Down
94 changes: 71 additions & 23 deletions app/src/main/java/com/getcode/ui/components/CodeSwitch.kt
Original file line number Diff line number Diff line change
@@ -1,37 +1,85 @@
package com.getcode.ui.components

import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Switch
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.size
import androidx.compose.material.SwitchColors
import androidx.compose.material.SwitchDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import com.getcode.theme.CodeTheme
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import com.getcode.theme.SystemGreen
import com.getcode.theme.White
import com.getcode.theme.White50
import com.getcode.ui.utils.addIf

object CodeToggleSwitchDefaults {

val colors: SwitchColors
@Composable get() = SwitchDefaults.colors(
checkedThumbColor = White,
uncheckedThumbColor = White,
checkedTrackColor = SystemGreen,
checkedTrackAlpha = 1f,
uncheckedTrackAlpha = 1f,
uncheckedTrackColor = Color(0xFF201D2F)
)
}

@Composable
fun CodeSwitch(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
fun CodeToggleSwitch(
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)? = null,
) {
Switch(
checked = checked,
onCheckedChange = onCheckedChange,
modifier = modifier,
enabled = enabled,
interactionSource = interactionSource,
colors = SwitchDefaults.colors(
checkedThumbColor = White,
uncheckedThumbColor = White50,
checkedTrackColor = CodeTheme.colors.brandLight,
checkedTrackAlpha = 1f,
uncheckedTrackColor = CodeTheme.colors.brandLight
)
val width = 51.dp
val height = 31.dp
val gapBetweenThumbAndTrackEdge = 2.dp

val thumbRadius = (height / 2) - gapBetweenThumbAndTrackEdge
val animatePosition = animateFloatAsState(
targetValue = if (checked) {
with(LocalDensity.current) { (width - thumbRadius - gapBetweenThumbAndTrackEdge).toPx() }
} else {
with (LocalDensity.current) { (thumbRadius + gapBetweenThumbAndTrackEdge).toPx() }
}, label = "thumb position"
)

val colors = CodeToggleSwitchDefaults.colors
val trackColor by colors.trackColor(enabled = enabled, checked = checked)
val thumbColor by colors.thumbColor(enabled = enabled, checked = checked)
Canvas(
modifier = Modifier
.size(width = width, height = height)
.addIf(onCheckedChange != null) {
Modifier.pointerInput(Unit) {
detectTapGestures(
onTap = {
onCheckedChange?.invoke(!checked)
}
)
}
}.then(modifier)
) {
drawRoundRect(
color = trackColor,
cornerRadius = CornerRadius(x = 45.dp.toPx(), y = 45.dp.toPx())
)
drawCircle(
color = thumbColor,
radius = thumbRadius.toPx(),
center = Offset(
x = animatePosition.value,
y = size.height / 2
)
)
}
}
68 changes: 68 additions & 0 deletions app/src/main/java/com/getcode/ui/components/SettingsRow.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.getcode.ui.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import com.getcode.theme.CodeTheme
import com.getcode.ui.utils.rememberedClickable

@Composable
fun SettingsRow(
modifier: Modifier = Modifier,
title: String,
icon: Int? = null,
subtitle: String? = null,
checked: Boolean,
onClick: () -> Unit) {
Row(
modifier = modifier
.rememberedClickable { onClick() }
.padding(horizontal = CodeTheme.dimens.grid.x3)
.padding(end = CodeTheme.dimens.grid.x3),
verticalAlignment = Alignment.CenterVertically
) {
if (icon != null) {
Image(
modifier = Modifier
.padding(end = CodeTheme.dimens.inset)
.height(CodeTheme.dimens.staticGrid.x5)
.width(CodeTheme.dimens.staticGrid.x5),
painter = painterResource(id = icon),
contentDescription = ""
)
}
Column(
modifier = Modifier
.weight(1f)
.padding(vertical = CodeTheme.dimens.grid.x3)
) {
Text(
modifier = Modifier
.padding(vertical = CodeTheme.dimens.grid.x2),
text = title,
)
if (!subtitle.isNullOrEmpty()) {
Text(
modifier = Modifier
.padding(vertical = CodeTheme.dimens.grid.x1),
text = subtitle,
style = CodeTheme.typography.caption,
color = CodeTheme.colors.textSecondary
)
}
}

CodeToggleSwitch(
checked = checked,
onCheckedChange = null,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
Expand Down Expand Up @@ -92,9 +91,9 @@ private fun TopBarView(
when (topBarMessage.type) {
ERROR_NETWORK, ERROR -> CodeTheme.colors.error
WARNING -> Warning
NOTIFICATION -> topNotification
NEUTRAL -> topNeutral
SUCCESS -> topSuccess
NOTIFICATION -> TopNotification
NEUTRAL -> TopNeutral
SUCCESS -> TopSuccess

}
)
Expand Down
Loading