Skip to content

Commit 8d6ad97

Browse files
Detect user country and put it at the top
1 parent 4cbb782 commit 8d6ad97

File tree

6 files changed

+85
-41
lines changed

6 files changed

+85
-41
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.wisnu.kurniawan.wallee.features.onboarding.data
22

33
import com.wisnu.kurniawan.wallee.model.Currency
4+
import kotlinx.coroutines.flow.Flow
45

56
interface IOnboardingEnvironment {
7+
fun getCurrentCountryCode(): Flow<String>
68
suspend fun saveAccount(currency: Currency)
79
}

app/src/main/java/com/wisnu/kurniawan/wallee/features/onboarding/data/OnboardingEnvironment.kt

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
11
package com.wisnu.kurniawan.wallee.features.onboarding.data
22

3+
import android.content.Context
34
import com.google.firebase.crashlytics.ktx.crashlytics
45
import com.google.firebase.crashlytics.ktx.setCustomKeys
56
import com.google.firebase.ktx.Firebase
67
import com.wisnu.kurniawan.wallee.foundation.datasource.local.LocalManager
78
import com.wisnu.kurniawan.wallee.foundation.datasource.preference.PreferenceManager
89
import com.wisnu.kurniawan.wallee.foundation.extension.defaultAccount
10+
import com.wisnu.kurniawan.wallee.foundation.extension.getCountryCode
911
import com.wisnu.kurniawan.wallee.foundation.wrapper.DateTimeProvider
1012
import com.wisnu.kurniawan.wallee.model.Currency
13+
import dagger.hilt.android.qualifiers.ApplicationContext
1114
import javax.inject.Inject
15+
import kotlinx.coroutines.flow.Flow
16+
import kotlinx.coroutines.flow.flowOf
1217

1318
class OnboardingEnvironment @Inject constructor(
1419
private val localManager: LocalManager,
1520
private val preferenceManager: PreferenceManager,
16-
private val dateTimeProvider: DateTimeProvider
17-
) : IOnboardingEnvironment {
21+
private val dateTimeProvider: DateTimeProvider,
22+
@ApplicationContext private val context: Context
23+
) : IOnboardingEnvironment {
24+
25+
override fun getCurrentCountryCode(): Flow<String> {
26+
return flowOf(getCountryCode(context))
27+
}
1828

1929
override suspend fun saveAccount(currency: Currency) {
2030
val account = defaultAccount(currency, dateTimeProvider.now())

app/src/main/java/com/wisnu/kurniawan/wallee/features/onboarding/ui/OnboardingScreen.kt

+24-17
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import androidx.compose.runtime.Composable
2727
import androidx.compose.runtime.getValue
2828
import androidx.compose.ui.Alignment
2929
import androidx.compose.ui.Modifier
30+
import androidx.compose.ui.draw.clip
3031
import androidx.compose.ui.graphics.Color
3132
import androidx.compose.ui.graphics.Shape
3233
import androidx.compose.ui.res.stringResource
@@ -47,6 +48,7 @@ import com.wisnu.kurniawan.wallee.foundation.uicomponent.PgIcon
4748
import com.wisnu.kurniawan.wallee.foundation.uicomponent.PgIconButton
4849
import com.wisnu.kurniawan.wallee.foundation.uicomponent.PgPageLayout
4950
import com.wisnu.kurniawan.wallee.foundation.viewmodel.HandleEffect
51+
import com.wisnu.kurniawan.wallee.model.Currency
5052

5153
@OptIn(ExperimentalLifecycleComposeApi::class)
5254
@Composable
@@ -160,6 +162,7 @@ private fun Content(
160162

161163
CurrencyCell(
162164
data = items,
165+
selectedCurrency = state.selectedCurrency,
163166
onItemClick = onItemClick,
164167
)
165168

@@ -171,7 +174,8 @@ private fun Content(
171174

172175
@OptIn(ExperimentalFoundationApi::class)
173176
private inline fun LazyListScope.CurrencyCell(
174-
data: Map<Char, List<CurrencyItem>>,
177+
data: Map<String, List<CurrencyItem>>,
178+
selectedCurrency: Currency?,
175179
noinline onItemClick: (CurrencyItem) -> Unit,
176180
) {
177181
item {
@@ -187,31 +191,33 @@ private inline fun LazyListScope.CurrencyCell(
187191
SpacerHeadline2()
188192
}
189193

190-
data.forEach { (initial, currenciesForInitial) ->
191-
stickyHeader {
192-
Box(
193-
modifier = Modifier
194-
.fillMaxWidth()
195-
.background(color = MaterialTheme.colorScheme.surface)
196-
) {
197-
Text(
198-
style = MaterialTheme.typography.titleSmall.copy(color = MaterialTheme.colorScheme.onBackground, fontSize = 16.sp),
199-
text = initial.toString(),
200-
modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp)
201-
)
194+
data.forEach { (title, currencies) ->
195+
if (title.isNotBlank()) {
196+
stickyHeader {
197+
Box(
198+
modifier = Modifier
199+
.fillMaxWidth()
200+
.background(color = MaterialTheme.colorScheme.surface)
201+
) {
202+
Text(
203+
style = MaterialTheme.typography.titleSmall.copy(color = MaterialTheme.colorScheme.onBackground, fontSize = 16.sp),
204+
text = title,
205+
modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp)
206+
)
207+
}
202208
}
203209
}
204210

205-
val size = currenciesForInitial.size
211+
val size = currencies.size
206212
itemsIndexed(
207-
items = currenciesForInitial,
208-
key = { _, item -> item.currency.countryCode + item.currency.currencyCode }
213+
items = currencies,
214+
key = { index, item -> index.toString() + item.currency.countryCode + item.currency.currencyCode }
209215
) { index, item ->
210216
CurrencyItemCell(
211217
currencySymbol = item.currencySymbol,
212218
flag = item.flag,
213219
countryName = item.countryName,
214-
selected = item.selected,
220+
selected = item.currency == selectedCurrency,
215221
shape = cellShape(index, size),
216222
showDivider = shouldShowDivider(index, size),
217223
onClick = { onItemClick(item) }
@@ -234,6 +240,7 @@ private fun CurrencyItemCell(
234240
modifier = Modifier
235241
.fillMaxWidth()
236242
.padding(horizontal = 16.dp)
243+
.clip(shape)
237244
.clickable(onClick = onClick),
238245
shape = shape,
239246
color = MaterialTheme.colorScheme.secondary,

app/src/main/java/com/wisnu/kurniawan/wallee/features/onboarding/ui/OnboardingState.kt

+15-16
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,37 @@ import com.wisnu.kurniawan.wallee.model.Currency
88

99
@Immutable
1010
data class OnboardingState(
11-
val currencyItems: List<CurrencyItem> = listOf()
11+
val selectedCurrency: Currency? = null,
12+
val currencyItems: List<CurrencyItem> = listOf(),
13+
val currentCountryCode: String = ""
1214
)
1315

1416
data class CurrencyItem(
1517
val currencySymbol: String,
1618
val flag: String,
17-
val selected: Boolean,
1819
val countryName: String,
20+
val countryCode: String,
1921
val currency: Currency
2022
)
2123

2224
// Derived state
2325
@Composable
2426
fun OnboardingState.rememberGroupedCurrencyItems() = remember(currencyItems) {
25-
derivedStateOf { currencyItems.groupedCurrencyItems() }
26-
}
27-
28-
// Collections
29-
fun List<CurrencyItem>.select(currency: Currency): List<CurrencyItem> {
30-
return map {
31-
it.copy(selected = it.currency == currency)
27+
derivedStateOf {
28+
val defaultCurrency = currencyItems.find { it.countryCode == currentCountryCode }
29+
if (defaultCurrency != null) {
30+
mapOf(" " to listOf(defaultCurrency)) + currencyItems.groupedCurrencyItems()
31+
} else {
32+
currencyItems.groupedCurrencyItems()
33+
}
3234
}
3335
}
3436

35-
fun List<CurrencyItem>.selected(): CurrencyItem? {
36-
return find { it.selected }
37-
}
38-
39-
fun List<CurrencyItem>.groupedCurrencyItems(): Map<Char, List<CurrencyItem>> {
40-
return groupBy { it.countryName.first() }
37+
// Collections
38+
fun List<CurrencyItem>.groupedCurrencyItems(): Map<String, List<CurrencyItem>> {
39+
return groupBy { it.countryName.first().toString() }
4140
}
4241

4342
fun OnboardingState.canSave(): Boolean {
44-
return currencyItems.any { it.selected }
43+
return selectedCurrency != null
4544
}

app/src/main/java/com/wisnu/kurniawan/wallee/features/onboarding/ui/OnboardingViewModel.kt

+10-6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ class OnboardingViewModel @Inject constructor(
2727
}
2828

2929
private fun initLoad() {
30+
viewModelScope.launch {
31+
environment.getCurrentCountryCode()
32+
.collect {
33+
setState { copy(currentCountryCode = it) }
34+
}
35+
}
3036
viewModelScope.launch {
3137
setState { copy(currencyItems = initialCurrencyItems()) }
3238
}
@@ -38,10 +44,8 @@ class OnboardingViewModel @Inject constructor(
3844
.filter { it }
3945
.distinctUntilChanged { old, new -> old != new }
4046
.collect {
41-
state.value.currencyItems.selected()?.let {
42-
environment.saveAccount(it.currency)
43-
setEffect(OnboardingEffect.ClosePage)
44-
}
47+
environment.saveAccount(state.value.selectedCurrency!!)
48+
setEffect(OnboardingEffect.ClosePage)
4549
}
4650
}
4751
}
@@ -53,7 +57,7 @@ class OnboardingViewModel @Inject constructor(
5357
}
5458
is OnboardingAction.SelectCurrency -> {
5559
viewModelScope.launch {
56-
setState { copy(currencyItems = currencyItems.select(action.item)) }
60+
setState { copy(selectedCurrency = action.item) }
5761
}
5862
}
5963
}
@@ -69,8 +73,8 @@ class OnboardingViewModel @Inject constructor(
6973
CurrencyItem(
7074
currencySymbol = value.symbol,
7175
flag = flag,
72-
selected = false,
7376
countryName = countryName,
77+
countryCode = it,
7478
currency = Currency(
7579
key,
7680
it
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.wisnu.kurniawan.wallee.foundation.extension
2+
3+
import android.content.Context
4+
import android.telephony.TelephonyManager
5+
import java.util.*
6+
7+
/**
8+
* Returns the upper-case ISO 3166-1 alpha-2 country code of the current registered operator's MCC
9+
* (Mobile Country Code), or the country code of the default Locale if not available.
10+
*
11+
* @param context A context to access the telephony service. If null, only the Locale can be used.
12+
* @return The upper-case ISO 3166-1 alpha-2 country code, or an empty String if unavailable.
13+
*/
14+
fun getCountryCode(context: Context): String {
15+
val telephonyManager: TelephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
16+
val countryCode: String = telephonyManager.networkCountryIso
17+
return if (countryCode.isNotEmpty()) {
18+
countryCode.uppercase(Locale.US)
19+
} else {
20+
Locale.getDefault().country.uppercase(Locale.US)
21+
}
22+
}

0 commit comments

Comments
 (0)