diff --git a/app/app/build.gradle.kts b/app/app/build.gradle.kts
index 44f3719..0fc7438 100644
--- a/app/app/build.gradle.kts
+++ b/app/app/build.gradle.kts
@@ -85,6 +85,7 @@ kapt {
dependencies {
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
+ implementation(libs.androidx.biometric)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.datastore)
implementation(libs.androidx.lifecycle.runtime.ktx)
diff --git a/app/app/proguard-rules.pro b/app/app/proguard-rules.pro
index 481bb43..b8e2ed9 100644
--- a/app/app/proguard-rules.pro
+++ b/app/app/proguard-rules.pro
@@ -18,4 +18,8 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
+#-renamesourcefileattribute SourceFile
+-if class androidx.credentials.CredentialManager
+-keep class androidx.credentials.playservices.** {
+ *;
+}
\ No newline at end of file
diff --git a/app/app/src/main/AndroidManifest.xml b/app/app/src/main/AndroidManifest.xml
index 913b745..bc5b292 100644
--- a/app/app/src/main/AndroidManifest.xml
+++ b/app/app/src/main/AndroidManifest.xml
@@ -14,6 +14,11 @@
android:supportsRtl="true"
android:theme="@style/Theme.DebtPirate"
android:networkSecurityConfig="@xml/network_security_config">
+
+
+
-
+
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/host/MainActivity.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/host/MainActivity.kt
index 059baee..d0ee4c3 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/host/MainActivity.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/host/MainActivity.kt
@@ -27,6 +27,8 @@ import ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.confirm.Conf
import ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.confirm.ConfirmationScreen
import ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.login.LoginScreen
import ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.register.RegistrationScreen
+import ing.bikeshedengineer.debtpirate.app.screen.home.presentation.overview.OverviewScreen
+import ing.bikeshedengineer.debtpirate.domain.usecase.GetStoredTokensUseCase
import ing.bikeshedengineer.debtpirate.navigation.Destination
import ing.bikeshedengineer.debtpirate.navigation.NavigationAction
import ing.bikeshedengineer.debtpirate.navigation.Navigator
@@ -42,6 +44,9 @@ class MainActivity : ComponentActivity() {
@Inject
lateinit var navigator: Navigator
+ @Inject
+ lateinit var getStoredTokens: GetStoredTokensUseCase
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -104,6 +109,14 @@ class MainActivity : ComponentActivity() {
ConfirmationScreen(ConfirmationData.UserConfirmationData(userId, verificationToken))
}
}
+
+ navigation(
+ startDestination = Destination.HomeOverview
+ ) {
+ composable {
+ OverviewScreen()
+ }
+ }
}
}
}
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreen.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreen.kt
index 6c9e666..4d56200 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreen.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreen.kt
@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.compose.foundation.background
-import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -32,8 +31,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -44,10 +41,8 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+import androidx.credentials.CredentialManager
import androidx.hilt.navigation.compose.hiltViewModel
-import ing.bikeshedengineer.debtpirate.domain.model.AccountManagerResult
-import ing.bikeshedengineer.debtpirate.domain.repository.AccountManager
-import kotlinx.coroutines.launch
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
@@ -55,23 +50,7 @@ fun LoginScreen(
viewModel: LoginScreenViewModel = hiltViewModel()
) {
val context = LocalContext.current as ComponentActivity
- val accountManager = remember {
- AccountManager(context)
- }
-
- val coroutineScope = rememberCoroutineScope()
- LaunchedEffect(true) {
- coroutineScope.launch {
- val result = accountManager.getCredentials()
- when (result) {
- is AccountManagerResult.FoundCredentials -> {
- val (emailAddress, password) = result
- }
-
- else -> {}
- }
- }
- }
+ val _credentialManager = CredentialManager.create(context)
val toastMessages = viewModel.toastMessages.collectAsState("")
LaunchedEffect(toastMessages.value) {
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenViewModel.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenViewModel.kt
index 6db3d9b..d0ee3b2 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenViewModel.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenViewModel.kt
@@ -1,6 +1,7 @@
package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.login
import android.util.Log
+import androidx.credentials.CreatePasswordRequest
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -31,9 +32,10 @@ class LoginScreenViewModel @Inject constructor(
private val _isEmailAddressPristine = MutableStateFlow(true)
private val _emailAddress = MutableStateFlow("")
val emailAddress = _emailAddress.asStateFlow()
- val isValidEmailAddress = _isEmailAddressPristine.combine(_emailAddress.map { it.isNotBlank() }) { isPristine, isValid ->
- isPristine || isValid
- }
+ val isValidEmailAddress =
+ _isEmailAddressPristine.combine(_emailAddress.map { it.isNotBlank() }) { isPristine, isValid ->
+ isPristine || isValid
+ }
private val _isPasswordPristine = MutableStateFlow(true)
private val _password = MutableStateFlow("")
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreen.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreen.kt
index fc98d8b..7a6e1c3 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreen.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreen.kt
@@ -1,8 +1,6 @@
package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.register
-import android.annotation.SuppressLint
import android.util.Log
-import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
@@ -32,8 +30,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -50,37 +46,33 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.credentials.CreatePasswordRequest
+import androidx.credentials.CredentialManager
+import androidx.credentials.exceptions.CreateCredentialException
import androidx.hilt.navigation.compose.hiltViewModel
-import ing.bikeshedengineer.debtpirate.domain.model.AccountManagerResult
-import ing.bikeshedengineer.debtpirate.domain.repository.AccountManager
-import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
-@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable()
fun RegistrationScreen(
viewModel: RegistrationScreenViewModel = hiltViewModel()
) {
- val context = LocalContext.current as ComponentActivity
- val accountManager = remember { AccountManager(context) }
+ val localContext = LocalContext.current
+ val onRegistrationMessage = viewModel.onRegistrationComplete.collectAsState(null)
+ LaunchedEffect(onRegistrationMessage.value) {
+ val message = onRegistrationMessage.value
+ if (message != null) {
+ val (username, password) = message
+ val credentialManager = CredentialManager.create(localContext)
+ val createPasswordRequest = CreatePasswordRequest(id = username, password = password)
- val coroutineScope = rememberCoroutineScope()
- LaunchedEffect(true) {
- coroutineScope.launch {
- viewModel.onRegistrationComplete.collect { credentials ->
- val (emailAddress, password) = credentials
- val result = accountManager.storeCredentials(emailAddress, password)
-
- if (result !is AccountManagerResult.Success) {
- Log.i(
- "RegistrationScreen",
- "Not able to store credentials in CredentialManager"
- )
- }
-
- viewModel.onAction(RegistrationScreenAction.ResetFields)
- viewModel.navigateToConfirmationScreen(emailAddress)
+ try {
+ credentialManager.createCredential(localContext, createPasswordRequest)
+ Log.d("RegistrationScreen", "Successfully stored login credentials")
+ } catch (err: CreateCredentialException) {
+ // TODO: Display a toast...
}
+
+ viewModel.navigateToConfirmationScreen(username)
}
}
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreenViewModel.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreenViewModel.kt
index 7faf874..eb28056 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreenViewModel.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreenViewModel.kt
@@ -1,5 +1,6 @@
package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.register
+import androidx.credentials.CreatePasswordRequest
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -32,11 +33,7 @@ class RegistrationScreenViewModel @Inject constructor(
fun navigateToConfirmationScreen(emailAddress: String) {
viewModelScope.launch {
- navigator.navigate(Destination.AuthRegistrationConfirmation(emailAddress)) {
- popUpTo(Destination.AuthLogin) {
- inclusive = true
- }
- }
+ navigator.navigate(Destination.AuthRegistrationConfirmation(emailAddress))
}
}
@@ -118,7 +115,7 @@ class RegistrationScreenViewModel @Inject constructor(
val result =
submitAccountRegistrationRequest(emailAddress, name, confirmPassword)
- val (userId, expiresAt, sessionToken) = result
+ val (_, expiresAt, sessionToken) = result
updateStoreData(
userId = result.userId,
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/home/presentation/overview/OverviewScreen.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/home/presentation/overview/OverviewScreen.kt
new file mode 100644
index 0000000..ea5bd2b
--- /dev/null
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/home/presentation/overview/OverviewScreen.kt
@@ -0,0 +1,31 @@
+package ing.bikeshedengineer.debtpirate.app.screen.home.presentation.overview
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable()
+fun OverviewScreen() {
+ Scaffold(
+ modifier = Modifier.fillMaxSize()
+ ) { innerPadding ->
+ Column(
+ modifier = Modifier
+ .padding(innerPadding)
+ .padding(16.dp)
+ )
+ {
+ OverviewComponent()
+ }
+ }
+}
+
+@Composable
+fun OverviewComponent() {
+ Text("Hello, world!")
+}
\ No newline at end of file
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/data/repository/AuthRepository.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/data/repository/AuthRepository.kt
index c851a3e..0e4ff3c 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/data/repository/AuthRepository.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/data/repository/AuthRepository.kt
@@ -1,10 +1,25 @@
package ing.bikeshedengineer.debtpirate.data.repository
import ing.bikeshedengineer.debtpirate.data.remote.ApiResponse
+import ing.bikeshedengineer.debtpirate.data.remote.endpoint.AuthEndpoint
import ing.bikeshedengineer.debtpirate.data.remote.model.auth.AuthLoginPostRequest
import ing.bikeshedengineer.debtpirate.data.remote.model.auth.AuthLoginPostResponse
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
import retrofit2.Response
+import retrofit2.Retrofit
interface AuthRepository {
suspend fun submitAuthLoginRequest(credentials: AuthLoginPostRequest): Response>
+}
+
+class AuthRepositoryImpl(private val httpClient: Retrofit) : AuthRepository {
+ private val authEndpoint: AuthEndpoint = this.httpClient.create(AuthEndpoint::class.java)
+
+ override suspend fun submitAuthLoginRequest(credentials: AuthLoginPostRequest): Response> {
+ return withContext(Dispatchers.IO) {
+ val response = authEndpoint.submitAuthLoginRequest(credentials)
+ return@withContext response
+ }
+ }
}
\ No newline at end of file
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/data/repository/UserRepository.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/data/repository/UserRepository.kt
index 4e15104..29ae64f 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/data/repository/UserRepository.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/data/repository/UserRepository.kt
@@ -1,11 +1,59 @@
package ing.bikeshedengineer.debtpirate.data.repository
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import ing.bikeshedengineer.debtpirate.data.remote.ApiResponse
+import ing.bikeshedengineer.debtpirate.data.remote.endpoint.UserEndpoint
import ing.bikeshedengineer.debtpirate.data.remote.model.user.UserCreatePostRequest
import ing.bikeshedengineer.debtpirate.data.remote.model.user.UserCreatePostResponse
import ing.bikeshedengineer.debtpirate.data.remote.model.user.UserVerificationGetRequest
import ing.bikeshedengineer.debtpirate.data.remote.model.user.UserVerificationGetResponse
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import retrofit2.Retrofit
interface UserRepository {
suspend fun createUserPostRequest(request: UserCreatePostRequest): UserCreatePostResponse
suspend fun verifyUserGetRequest(request: UserVerificationGetRequest): UserVerificationGetResponse
+}
+
+class UserRepositoryImpl(httpClient: Retrofit) : UserRepository {
+ private val userEndpoint: UserEndpoint = httpClient.create(UserEndpoint::class.java)
+
+ override suspend fun createUserPostRequest(request: UserCreatePostRequest): UserCreatePostResponse {
+ return withContext(Dispatchers.IO) {
+ val response = userEndpoint.createUserPostRequest(request)
+
+ if (response.isSuccessful) {
+ val body = response.body()
+ return@withContext body!!.data!!
+ } else {
+ val gson = Gson()
+ val errorType = object : TypeToken>() {}.type
+ val body =
+ gson.fromJson>(response.errorBody()!!.charStream(), errorType)
+
+ throw Throwable(body!!.error!!)
+ }
+ }
+ }
+
+ override suspend fun verifyUserGetRequest(request: UserVerificationGetRequest): UserVerificationGetResponse {
+ return withContext(Dispatchers.IO) {
+ val (userId, verificationToken) = request
+ val response = userEndpoint.userVerificationGetRequest(userId, verificationToken)
+
+ if (response.isSuccessful) {
+ val body = response.body()
+ return@withContext body!!.data!!
+ } else {
+ val gson = Gson()
+ val errorType = object : TypeToken>() {}.type
+ val body =
+ gson.fromJson>(response.errorBody()!!.charStream(), errorType)
+
+ throw Throwable(body!!.error!!)
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/UseCaseModule.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/UseCaseModule.kt
index b8ce299..fd2b582 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/UseCaseModule.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/UseCaseModule.kt
@@ -6,6 +6,7 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import ing.bikeshedengineer.debtpirate.AppDataStore
+import ing.bikeshedengineer.debtpirate.domain.usecase.GetStoredTokensUseCase
import ing.bikeshedengineer.debtpirate.domain.usecase.UpdateStoreDataUseCase
import javax.inject.Singleton
@@ -16,4 +17,8 @@ object UseCaseModule {
@Provides
@Singleton
fun provideUpdateStoreDataUseCase(store: DataStore) = UpdateStoreDataUseCase(store)
+
+ @Provides
+ @Singleton
+ fun provideGetStoredTokensUseCase(store: DataStore) = GetStoredTokensUseCase(store)
}
\ No newline at end of file
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/repository/AuthRepositoryModule.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/repository/AuthRepositoryModule.kt
index 0a6b8cf..340f24d 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/repository/AuthRepositoryModule.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/repository/AuthRepositoryModule.kt
@@ -6,7 +6,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
import ing.bikeshedengineer.debtpirate.data.repository.AuthRepository
-import ing.bikeshedengineer.debtpirate.domain.repository.AuthRepositoryImpl
+import ing.bikeshedengineer.debtpirate.data.repository.AuthRepositoryImpl
import retrofit2.Retrofit
@Module
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/repository/UserRepositoryModule.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/repository/UserRepositoryModule.kt
index 4d47321..29be89a 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/repository/UserRepositoryModule.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/di/repository/UserRepositoryModule.kt
@@ -6,7 +6,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
import ing.bikeshedengineer.debtpirate.data.repository.UserRepository
-import ing.bikeshedengineer.debtpirate.domain.repository.UserRepositoryImpl
+import ing.bikeshedengineer.debtpirate.data.repository.UserRepositoryImpl
import retrofit2.Retrofit
@Module
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AccountManager.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AccountManager.kt
deleted file mode 100644
index 0ebc15e..0000000
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AccountManager.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package ing.bikeshedengineer.debtpirate.domain.repository
-
-import android.app.Activity
-import android.util.Log
-import androidx.credentials.CreatePasswordRequest
-import androidx.credentials.CredentialManager
-import androidx.credentials.GetCredentialRequest
-import androidx.credentials.GetPasswordOption
-import androidx.credentials.PasswordCredential
-import androidx.credentials.exceptions.CreateCredentialCancellationException
-import androidx.credentials.exceptions.CreateCredentialException
-import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException
-import ing.bikeshedengineer.debtpirate.domain.model.AccountManagerResult
-
-class AccountManager(private val context: Activity) {
- private val credentialManager = CredentialManager.create(context)
-
- suspend fun storeCredentials(
- emailAddress: String,
- password: String
- ): AccountManagerResult {
- return try {
- credentialManager.createCredential(
- context, request = CreatePasswordRequest(
- id = emailAddress,
- password
- )
- )
-
- AccountManagerResult.Success
- } catch (err: CreateCredentialNoCreateOptionException) {
- Log.w(
- "AccountManager",
- "Cannot store credentials; a Google account isn't associated with this device"
- )
-
- AccountManagerResult.Unavailable
- } catch (_: CreateCredentialCancellationException) {
- AccountManagerResult.Canceled
- } catch (err: CreateCredentialException) {
- err.printStackTrace()
- Log.i(
- "AccountManager",
- "Unable to store credentials: ${err.message}"
- )
-
- AccountManagerResult.Failure
- }
- }
-
- suspend fun getCredentials(): AccountManagerResult {
- return try {
- val result = credentialManager.getCredential(
- context, request = GetCredentialRequest(
- credentialOptions = listOf(GetPasswordOption(isAutoSelectAllowed = true))
- )
- )
-
- val credentials = result.credential
- when (credentials) {
- is PasswordCredential -> {
- val emailAddress = credentials.id
- val password = credentials.password
-
- AccountManagerResult.FoundCredentials(emailAddress, password)
- }
-
- else -> AccountManagerResult.Invalid
- }
- } catch (err: Exception) {
- AccountManagerResult.Failure
- }
- }
-}
\ No newline at end of file
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AuthRepositoryImpl.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AuthRepositoryImpl.kt
deleted file mode 100644
index e8faef5..0000000
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AuthRepositoryImpl.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ing.bikeshedengineer.debtpirate.domain.repository
-
-import ing.bikeshedengineer.debtpirate.data.remote.ApiResponse
-import ing.bikeshedengineer.debtpirate.data.remote.endpoint.AuthEndpoint
-import ing.bikeshedengineer.debtpirate.data.remote.model.auth.AuthLoginPostRequest
-import ing.bikeshedengineer.debtpirate.data.remote.model.auth.AuthLoginPostResponse
-import ing.bikeshedengineer.debtpirate.data.repository.AuthRepository
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
-import retrofit2.Response
-import retrofit2.Retrofit
-
-class AuthRepositoryImpl(private val httpClient: Retrofit) : AuthRepository {
- private val authEndpoint: AuthEndpoint = this.httpClient.create(AuthEndpoint::class.java)
-
- override suspend fun submitAuthLoginRequest(credentials: AuthLoginPostRequest): Response> {
- return withContext(Dispatchers.IO) {
- val response = authEndpoint.submitAuthLoginRequest(credentials)
- return@withContext response
- }
- }
-}
\ No newline at end of file
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/UserRepositoryImpl.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/UserRepositoryImpl.kt
deleted file mode 100644
index 4cc6977..0000000
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/UserRepositoryImpl.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package ing.bikeshedengineer.debtpirate.domain.repository
-
-import android.util.Log
-import com.google.gson.Gson
-import com.google.gson.reflect.TypeToken
-import ing.bikeshedengineer.debtpirate.data.remote.ApiResponse
-import ing.bikeshedengineer.debtpirate.data.remote.endpoint.UserEndpoint
-import ing.bikeshedengineer.debtpirate.data.remote.model.user.UserCreatePostRequest
-import ing.bikeshedengineer.debtpirate.data.remote.model.user.UserCreatePostResponse
-import ing.bikeshedengineer.debtpirate.data.remote.model.user.UserVerificationGetRequest
-import ing.bikeshedengineer.debtpirate.data.remote.model.user.UserVerificationGetResponse
-import ing.bikeshedengineer.debtpirate.data.repository.UserRepository
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
-import retrofit2.Retrofit
-
-class UserRepositoryImpl(httpClient: Retrofit) : UserRepository {
- private val userEndpoint: UserEndpoint = httpClient.create(UserEndpoint::class.java)
-
- override suspend fun createUserPostRequest(request: UserCreatePostRequest): UserCreatePostResponse {
- return withContext(Dispatchers.IO) {
- val response = userEndpoint.createUserPostRequest(request)
-
- if (response.isSuccessful) {
- val body = response.body()
- return@withContext body!!.data!!
- } else {
- val gson = Gson()
- val errorType = object : TypeToken>() {}.type
- val body =
- gson.fromJson>(response.errorBody()!!.charStream(), errorType)
-
- throw Throwable(body!!.error!!)
- }
- }
- }
-
- override suspend fun verifyUserGetRequest(request: UserVerificationGetRequest): UserVerificationGetResponse {
- return withContext(Dispatchers.IO) {
- val (userId, verificationToken) = request
- val response = userEndpoint.userVerificationGetRequest(userId, verificationToken)
-
- if (response.isSuccessful) {
- val body = response.body()
- return@withContext body!!.data!!
- } else {
- val gson = Gson()
- val errorType = object : TypeToken>() {}.type
- val body =
- gson.fromJson>(response.errorBody()!!.charStream(), errorType)
-
- throw Throwable(body!!.error!!)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/usecase/GetStoredTokensUseCase.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/usecase/GetStoredTokensUseCase.kt
new file mode 100644
index 0000000..3467575
--- /dev/null
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/usecase/GetStoredTokensUseCase.kt
@@ -0,0 +1,42 @@
+package ing.bikeshedengineer.debtpirate.domain.usecase
+
+import androidx.datastore.core.DataStore
+import ing.bikeshedengineer.debtpirate.AppDataStore
+import ing.bikeshedengineer.debtpirate.domain.model.Token
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import java.time.Instant
+import java.time.ZoneOffset
+import java.time.ZonedDateTime
+
+class GetStoredTokensUseCase(private val store: DataStore) {
+ operator fun invoke(): Flow?> {
+ return store.data.map { store ->
+ if (store.isInitialized) {
+ val now = ZonedDateTime.now()
+ val authTokenExpiration = ZonedDateTime.ofInstant(Instant.ofEpochSecond(store.authToken.expiresAt), ZoneOffset.UTC)
+ if (authTokenExpiration.isBefore(now)) {
+ return@map null
+ }
+
+ val sessionTokenExpiration = ZonedDateTime.ofInstant(Instant.ofEpochSecond(store.sessionToken.expiresAt), ZoneOffset.UTC)
+ return@map Pair(
+ Token(
+ store.authToken.token,
+ authTokenExpiration
+ ),
+ if (sessionTokenExpiration.isBefore(now)) {
+ null
+ } else {
+ Token(
+ store.sessionToken.token,
+ sessionTokenExpiration
+ )
+ }
+ )
+ } else {
+ return@map null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/usecase/UpdateStoreDataUseCase.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/usecase/UpdateStoreDataUseCase.kt
index 913eafe..5dd8a43 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/usecase/UpdateStoreDataUseCase.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/usecase/UpdateStoreDataUseCase.kt
@@ -4,7 +4,7 @@ import androidx.datastore.core.DataStore
import ing.bikeshedengineer.debtpirate.AppDataStore
import java.time.OffsetDateTime
-class UpdateStoreDataUseCase(val store: DataStore) {
+class UpdateStoreDataUseCase(private val store: DataStore) {
suspend operator fun invoke(
userId: Int? = null,
authToken: String? = null,
diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/navigation/Destination.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/navigation/Destination.kt
index 1fce751..d8f8981 100644
--- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/navigation/Destination.kt
+++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/navigation/Destination.kt
@@ -24,4 +24,11 @@ sealed interface Destination {
val userId: Int,
val verificationToken: String
) : Destination
+
+ /* Home Destinations */
+ @Serializable
+ data object HomeGraph : Destination
+
+ @Serializable
+ data object HomeOverview : Destination
}
\ No newline at end of file
diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml
index 4be604b..c51740c 100644
--- a/app/app/src/main/res/values/strings.xml
+++ b/app/app/src/main/res/values/strings.xml
@@ -1,3 +1,8 @@
Debt Pirate
+
+ [{
+ \"include\": \"https://debtpirate.app/.well-known/assetlinks.json\"
+ }]
+
\ No newline at end of file
diff --git a/app/gradle/libs.versions.toml b/app/gradle/libs.versions.toml
index 94287f7..9eaac3b 100644
--- a/app/gradle/libs.versions.toml
+++ b/app/gradle/libs.versions.toml
@@ -2,12 +2,13 @@
activityCompose = "1.9.3"
agp = "8.7.3"
appcompat = "1.7.0"
-composeBom = "2024.11.00"
+biometric = "1.4.0-alpha02"
+composeBom = "2024.12.01"
coreKtx = "1.15.0"
datastore = "1.1.1"
espressoCore = "3.6.1"
hilt = "2.51.1"
-iconsExtended = "1.7.5"
+iconsExtended = "1.7.6"
junit = "4.13.2"
junitVersion = "1.2.1"
kotlin = "2.0.10"
@@ -17,19 +18,20 @@ lifecycleRuntimeKtx = "2.8.7"
lifecycleViewModelKtx = "2.8.7"
lifecycleViewmodelCompose = "2.8.7"
material = "1.12.0"
-material3 = "1.4.0-alpha04"
-navigation = "2.8.4"
+material3 = "1.4.0-alpha05"
+navigation = "2.8.5"
protobuf = "0.9.4"
protoLite = "3.21.11"
okhttp = "4.10.0"
retrofit = "2.9.0"
hiltNavigationCompose = "1.2.0"
-fonts = "1.7.5"
+fonts = "1.7.6"
credentialManager = "1.5.0-beta01"
[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+androidx-biometric = { group = "androidx.biometric", name = "biometric", version.ref = "biometric" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-credentials-core = { group = "androidx.credentials", name = "credentials", version.ref = "credentialManager" }