Skip to content

Commit d27f78a

Browse files
committed
TFA composable fixes for example application
1 parent ca9956b commit d27f78a

File tree

15 files changed

+406
-508
lines changed

15 files changed

+406
-508
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ android {
1414
minSdk = 26
1515
//noinspection EditedTargetSdkVersion,OldTargetApi
1616
targetSdk = 35
17-
versionCode = 4
18-
versionName = "0.1"
17+
versionCode = 3
18+
versionName = "0.3"
1919

2020
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2121
vectorDrawables {

app/src/main/java/com/sap/cdc/bitsnbytes/ui/route/NavigationHosts.kt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,10 @@ import com.sap.cdc.bitsnbytes.ui.viewmodel.LoginOptionsViewModel
3838
import com.sap.cdc.bitsnbytes.ui.viewmodel.OtpSignInViewModel
3939
import com.sap.cdc.bitsnbytes.ui.viewmodel.OtpVerifyViewModel
4040
import com.sap.cdc.bitsnbytes.ui.viewmodel.PendingRegistrationViewModel
41-
import com.sap.cdc.bitsnbytes.ui.viewmodel.PhoneSelectionViewModel
42-
import com.sap.cdc.bitsnbytes.ui.viewmodel.PhoneVerificationViewModel
4341
import com.sap.cdc.bitsnbytes.ui.viewmodel.RegisterViewModel
4442
import com.sap.cdc.bitsnbytes.ui.viewmodel.ScreenSetViewModel
4543
import com.sap.cdc.bitsnbytes.ui.viewmodel.SignInViewModel
46-
import com.sap.cdc.bitsnbytes.ui.viewmodel.TOTPVerificationViewModel
44+
import com.sap.cdc.bitsnbytes.ui.viewmodel.TFAAuthenticationViewModel
4745
import com.sap.cdc.bitsnbytes.ui.viewmodel.WelcomeViewModel
4846
import com.sap.cdc.bitsnbytes.ui.viewmodel.factory.CustomViewModelFactory
4947
import kotlinx.serialization.json.Json
@@ -235,36 +233,38 @@ fun ProfileNavHost() {
235233
composable("${ProfileScreenRoute.AuthMethods.route}/{resolvableContext}") { backStackEntry ->
236234
val resolvableJson = backStackEntry.arguments?.getString("resolvableContext")
237235
val resolvable = Json.decodeFromString<ResolvableContext>(resolvableJson!!)
238-
//TODO: Viewmodel?
239-
AuthMethodsView(resolvable)
236+
val viewModel: TFAAuthenticationViewModel = viewModel(
237+
factory = CustomViewModelFactory(context)
238+
)
239+
AuthMethodsView(viewModel, resolvable)
240240
}
241241
composable("${ProfileScreenRoute.PhoneSelection.route}/{resolvableContext}") { backStackEntry ->
242242
val resolvableJson = backStackEntry.arguments?.getString("resolvableContext")
243243
val resolvable = Json.decodeFromString<ResolvableContext>(resolvableJson!!)
244-
val viewModel: PhoneSelectionViewModel = viewModel(
244+
val viewModel: TFAAuthenticationViewModel = viewModel(
245245
factory = CustomViewModelFactory(context)
246246
)
247-
PhoneSelectionView(viewModel, resolvable)
247+
PhoneSelectionView(viewModel)
248248
}
249249
composable("${ProfileScreenRoute.PhoneVerification.route}/{resolvableContext}") { backStackEntry ->
250250
val resolvableJsonEncoded = backStackEntry.arguments?.getString("resolvableContext")
251251
val resolvableJson =
252252
String(Base64.decode(resolvableJsonEncoded, Base64.DEFAULT), Charsets.UTF_8)
253253
val resolvable = Json.decodeFromString<ResolvableContext>(resolvableJson)
254-
val viewModel: PhoneVerificationViewModel = viewModel(
254+
val viewModel: TFAAuthenticationViewModel = viewModel(
255255
factory = CustomViewModelFactory(context)
256256
)
257-
PhoneVerificationView(viewModel, resolvable)
257+
PhoneVerificationView(viewModel)
258258
}
259259
composable("${ProfileScreenRoute.TOTPVerification.route}/{resolvableContext}") { backStackEntry ->
260260
val resolvableJsonEncoded = backStackEntry.arguments?.getString("resolvableContext")
261261
val resolvableJson =
262262
String(Base64.decode(resolvableJsonEncoded, Base64.DEFAULT), Charsets.UTF_8)
263263
val resolvable = Json.decodeFromString<ResolvableContext>(resolvableJson)
264-
val viewModel: TOTPVerificationViewModel = viewModel(
264+
val viewModel: TFAAuthenticationViewModel = viewModel(
265265
factory = CustomViewModelFactory(context)
266266
)
267-
TOTPVerificationView(viewModel, resolvable)
267+
TOTPVerificationView(viewModel)
268268
}
269269
}
270270
}

app/src/main/java/com/sap/cdc/bitsnbytes/ui/view/screens/AuthMethods.kt

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.sap.cdc.bitsnbytes.ui.view.screens
22

3-
import android.util.Base64
43
import androidx.compose.foundation.background
54
import androidx.compose.foundation.layout.Column
65
import androidx.compose.foundation.layout.Spacer
@@ -11,6 +10,7 @@ import androidx.compose.foundation.layout.size
1110
import androidx.compose.material3.HorizontalDivider
1211
import androidx.compose.material3.Text
1312
import androidx.compose.runtime.Composable
13+
import androidx.compose.runtime.LaunchedEffect
1414
import androidx.compose.ui.Alignment
1515
import androidx.compose.ui.Modifier
1616
import androidx.compose.ui.graphics.Color
@@ -25,9 +25,20 @@ import com.sap.cdc.bitsnbytes.ui.route.ProfileScreenRoute
2525
import com.sap.cdc.bitsnbytes.ui.theme.AppTheme
2626
import com.sap.cdc.bitsnbytes.ui.view.composables.IconAndTextOutlineButton
2727
import com.sap.cdc.bitsnbytes.ui.view.composables.SmallActionTextButton
28+
import com.sap.cdc.bitsnbytes.ui.viewmodel.ITFAAuthenticationViewModel
29+
import com.sap.cdc.bitsnbytes.ui.viewmodel.TFAAuthenticationViewModelPreview
2830

2931
@Composable
30-
fun AuthMethodsView(resolvableContext: ResolvableContext) {
32+
fun AuthMethodsView(
33+
viewModel: ITFAAuthenticationViewModel,
34+
resolvableContext: ResolvableContext
35+
) {
36+
LaunchedEffect(Unit) {
37+
// Update resolvable context in shared view model.
38+
viewModel.updateResolvableContext(resolvableContext)
39+
}
40+
41+
//TODO: Dynamically show the auth methods based on the resolvable context available/unavailable providers
3142
Column(
3243
horizontalAlignment = Alignment.CenterHorizontally,
3344
modifier = Modifier
@@ -56,28 +67,23 @@ fun AuthMethodsView(resolvableContext: ResolvableContext) {
5667
Spacer(modifier = Modifier.size(24.dp))
5768

5869
// Send Code to email button
59-
IconAndTextOutlineButton(
60-
modifier = Modifier.size(width = 240.dp, height = 44.dp),
61-
text = "Send Code to Email",
62-
onClick = {
63-
NavigationCoordinator.INSTANCE.navigate("${ProfileScreenRoute.AuthTabView.route}/1")
64-
},
65-
iconResourceId = R.drawable.ic_email,
66-
67-
)
68-
Spacer(modifier = Modifier.size(10.dp))
70+
// IconAndTextOutlineButton(
71+
// modifier = Modifier.size(width = 240.dp, height = 44.dp),
72+
// text = "Send Code to Email",
73+
// onClick = {
74+
// },
75+
// iconResourceId = R.drawable.ic_email,
76+
//
77+
// )
78+
// Spacer(modifier = Modifier.size(10.dp))
6979

7080
// Send Code to Phone button
7181
IconAndTextOutlineButton(
7282
modifier = Modifier.size(width = 240.dp, height = 44.dp),
7383
text = "Send Code to Phone",
7484
onClick = {
7585
NavigationCoordinator.INSTANCE
76-
.navigate(
77-
"${ProfileScreenRoute.PhoneSelection.route}/${
78-
resolvableContext.toJson()
79-
}"
80-
)
86+
.navigate(ProfileScreenRoute.PhoneSelection.route)
8187
},
8288
iconResourceId = R.drawable.ic_device,
8389

@@ -90,14 +96,7 @@ fun AuthMethodsView(resolvableContext: ResolvableContext) {
9096
text = "Use a TOTP App",
9197
onClick = {
9298
NavigationCoordinator.INSTANCE
93-
.navigate(
94-
"${ProfileScreenRoute.TOTPVerification.route}/${
95-
Base64.encodeToString(
96-
resolvableContext.toJson().toByteArray(Charsets.UTF_8),
97-
Base64.DEFAULT
98-
)
99-
}"
100-
)
99+
.navigate(ProfileScreenRoute.TOTPVerification.route)
101100
},
102101
iconResourceId = R.drawable.ic_lock,
103102

@@ -117,6 +116,7 @@ fun AuthMethodsView(resolvableContext: ResolvableContext) {
117116
fun AuthMethodsViewPreview() {
118117
AppTheme {
119118
AuthMethodsView(
119+
viewModel = TFAAuthenticationViewModelPreview(),
120120
resolvableContext = ResolvableContext(
121121
regToken = ""
122122
)

app/src/main/java/com/sap/cdc/bitsnbytes/ui/view/screens/PhoneSelection.kt

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@file:OptIn(ExperimentalComposeUiApi::class)
1+
@file:OptIn(ExperimentalComposeUiApi::class, ExperimentalComposeUiApi::class)
22

33
package com.sap.cdc.bitsnbytes.ui.view.screens
44

@@ -39,9 +39,6 @@ import androidx.compose.ui.text.input.KeyboardType
3939
import androidx.compose.ui.tooling.preview.Preview
4040
import androidx.compose.ui.unit.dp
4141
import androidx.compose.ui.unit.sp
42-
import com.sap.cdc.android.sdk.auth.ResolvableContext
43-
import com.sap.cdc.android.sdk.auth.ResolvableTFA
44-
import com.sap.cdc.android.sdk.auth.tfa.TFAProvidersEntity
4542
import com.sap.cdc.bitsnbytes.ui.route.NavigationCoordinator
4643
import com.sap.cdc.bitsnbytes.ui.route.ProfileScreenRoute
4744
import com.sap.cdc.bitsnbytes.ui.theme.AppTheme
@@ -54,14 +51,12 @@ import com.sap.cdc.bitsnbytes.ui.view.composables.LargeVerticalSpacer
5451
import com.sap.cdc.bitsnbytes.ui.view.composables.SimpleErrorMessages
5552
import com.sap.cdc.bitsnbytes.ui.view.composables.SmallActionTextButton
5653
import com.sap.cdc.bitsnbytes.ui.view.composables.SmallVerticalSpacer
57-
import com.sap.cdc.bitsnbytes.ui.viewmodel.IPhoneSelectionViewModel
58-
import com.sap.cdc.bitsnbytes.ui.viewmodel.PhoneSelectionViewModelPreview
54+
import com.sap.cdc.bitsnbytes.ui.viewmodel.ITFAAuthenticationViewModel
55+
import com.sap.cdc.bitsnbytes.ui.viewmodel.TFAAuthenticationViewModelPreview
5956

60-
@OptIn(ExperimentalComposeUiApi::class)
6157
@Composable
6258
fun PhoneSelectionView(
63-
viewModel: IPhoneSelectionViewModel,
64-
resolvableContext: ResolvableContext
59+
viewModel: ITFAAuthenticationViewModel,
6560
) {
6661
var loading by remember { mutableStateOf(false) }
6762
var verificationError by remember { mutableStateOf("") }
@@ -95,19 +90,17 @@ fun PhoneSelectionView(
9590

9691
// Vary provider state to show different phone numbers
9792
// or to register a new one if none available.
98-
if (resolvableContext.tfa?.tfaProviders?.activeProviders?.isEmpty() == true) {
93+
if (viewModel.resolvableContext.collectAsState().value?.tfa?.tfaProviders?.activeProviders?.isEmpty() == true) {
9994
// Need to register a new phone number
10095
RegisterNewPhoneNumber(
10196
viewModel = viewModel,
102-
resolvableContext = resolvableContext,
10397
onLoadChanged = { loading = it },
10498
onVerificationErrorChanged = { verificationError = it }
10599
)
106100
} else {
107101
// Show available phone numbers
108102
RegisteredPhoneNumbers(
109103
viewModel = viewModel,
110-
resolvableContext = resolvableContext,
111104
onLoadChanged = { loading = it },
112105
onVerificationErrorChanged = { verificationError = it }
113106
)
@@ -126,8 +119,7 @@ fun PhoneSelectionView(
126119

127120
@Composable
128121
fun RegisterNewPhoneNumber(
129-
viewModel: IPhoneSelectionViewModel,
130-
resolvableContext: ResolvableContext,
122+
viewModel: ITFAAuthenticationViewModel,
131123
onLoadChanged: (Boolean) -> Unit,
132124
onVerificationErrorChanged: (String) -> Unit,
133125
) {
@@ -195,7 +187,6 @@ fun RegisterNewPhoneNumber(
195187
onVerificationErrorChanged("")
196188
viewModel.registerTFAPhoneNumber(
197189
inputField,
198-
resolvableContext,
199190
"en",
200191
onVerificationCodeSent = { authResponse ->
201192
onLoadChanged(false)
@@ -221,8 +212,7 @@ fun RegisterNewPhoneNumber(
221212

222213
@Composable
223214
fun RegisteredPhoneNumbers(
224-
viewModel: IPhoneSelectionViewModel,
225-
resolvableContext: ResolvableContext,
215+
viewModel: ITFAAuthenticationViewModel,
226216
onLoadChanged: (Boolean) -> Unit,
227217
onVerificationErrorChanged: (String) -> Unit,
228218
) {
@@ -241,15 +231,17 @@ fun RegisteredPhoneNumbers(
241231
onLoadChanged(true)
242232
viewModel.sendRegisteredPhoneCode(
243233
tfaPhoneEntity.id ?: "",
244-
resolvableContext,
245234
"en",
246235
onVerificationCodeSent = { authResponse ->
247236
onLoadChanged(false)
248237
val resolvableJson = authResponse?.resolvable()!!.toJson()
249238
NavigationCoordinator.INSTANCE
250239
.navigate(
251240
"${ProfileScreenRoute.PhoneVerification.route}/${
252-
Base64.encodeToString(resolvableJson.toByteArray(Charsets.UTF_8), Base64.DEFAULT)
241+
Base64.encodeToString(
242+
resolvableJson.toByteArray(Charsets.UTF_8),
243+
Base64.DEFAULT
244+
)
253245
}"
254246
)
255247
},
@@ -274,7 +266,6 @@ fun RegisteredPhoneNumbers(
274266

275267
onLoadChanged(true)
276268
viewModel.getRegisteredPhoneNumbers(
277-
resolvableContext,
278269
onRegisteredPhoneNumbers = {
279270
onLoadChanged(false)
280271
onVerificationErrorChanged("")
@@ -294,14 +285,7 @@ fun RegisteredPhoneNumbers(
294285
fun PhoneSelectionViewPreview() {
295286
AppTheme {
296287
PhoneSelectionView(
297-
viewModel = PhoneSelectionViewModelPreview(),
298-
resolvableContext = ResolvableContext(
299-
"", tfa = ResolvableTFA(
300-
tfaProviders = TFAProvidersEntity(
301-
activeProviders = listOf(), inactiveProviders = listOf()
302-
),
303-
)
304-
)
288+
viewModel = TFAAuthenticationViewModelPreview(),
305289
)
306290
}
307291
}

app/src/main/java/com/sap/cdc/bitsnbytes/ui/view/screens/PhoneVerification.kt

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import androidx.compose.ui.text.font.FontWeight
3333
import androidx.compose.ui.tooling.preview.Preview
3434
import androidx.compose.ui.unit.dp
3535
import androidx.compose.ui.unit.sp
36-
import com.sap.cdc.android.sdk.auth.ResolvableContext
3736
import com.sap.cdc.bitsnbytes.ui.route.NavigationCoordinator
3837
import com.sap.cdc.bitsnbytes.ui.route.ProfileScreenRoute
3938
import com.sap.cdc.bitsnbytes.ui.theme.AppTheme
@@ -42,14 +41,13 @@ import com.sap.cdc.bitsnbytes.ui.utils.connectNode
4241
import com.sap.cdc.bitsnbytes.ui.utils.defaultFocusChangeAutoFill
4342
import com.sap.cdc.bitsnbytes.ui.view.composables.IndeterminateLinearIndicator
4443
import com.sap.cdc.bitsnbytes.ui.view.composables.OtpTextField
45-
import com.sap.cdc.bitsnbytes.ui.viewmodel.IPhoneVerificationViewModel
46-
import com.sap.cdc.bitsnbytes.ui.viewmodel.PhoneVerificationViewModelPreview
44+
import com.sap.cdc.bitsnbytes.ui.viewmodel.ITFAAuthenticationViewModel
45+
import com.sap.cdc.bitsnbytes.ui.viewmodel.TFAAuthenticationViewModelPreview
4746

4847
@OptIn(ExperimentalComposeUiApi::class)
4948
@Composable
5049
fun PhoneVerificationView(
51-
viewModel: IPhoneVerificationViewModel,
52-
resolvableContext: ResolvableContext,
50+
viewModel: ITFAAuthenticationViewModel,
5351
) {
5452
var loading by remember { mutableStateOf(false) }
5553

@@ -152,9 +150,8 @@ fun PhoneVerificationView(
152150
shape = RoundedCornerShape(6.dp),
153151
onClick = {
154152
loading = true
155-
viewModel.verifyTFACode(
153+
viewModel.verifyPhoneCode(
156154
otpValue,
157-
resolvableContext,
158155
rememberDevice = false,
159156
onVerified = {
160157
loading = false
@@ -188,10 +185,6 @@ fun PhoneVerificationView(
188185
modifier = Modifier
189186
.clickable(enabled = !codeSent) {
190187
codeSent = true
191-
//TODO: cancel timer when composable is not active.
192-
viewModel.startOtpTimer {
193-
codeSent = false
194-
}
195188
}
196189
.padding(start = 16.dp, end = 16.dp),
197190
)
@@ -205,8 +198,7 @@ fun PhoneVerificationView(
205198
fun PhoneVerificationViewPreview() {
206199
AppTheme {
207200
PhoneVerificationView(
208-
viewModel = PhoneVerificationViewModelPreview(),
209-
resolvableContext = ResolvableContext(regToken = "")
201+
viewModel = TFAAuthenticationViewModelPreview(),
210202
)
211203
}
212204
}

0 commit comments

Comments
 (0)