Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Authentication - Part 1 #39

Merged
merged 59 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
8a2025b
added: credential manager dependencies to version catalog
enesky Nov 18, 2023
4a0993f
added: new proguard keepers for credential manager
enesky Nov 18, 2023
b8dfd79
Merge branch 'main' into feature/credentials_manager
enesky Nov 18, 2023
d5f3d53
updated: default bar color
enesky Nov 18, 2023
7ee1401
updated: android studio and agp to latest vers
enesky Nov 19, 2023
0cb5858
updated: kotlin & firebase to latest vers
enesky Nov 19, 2023
1b67aa3
updated: compose compiler to compatible kotlin vers
enesky Nov 19, 2023
6c4ec6e
added: empty string companion and common module added to FeaturePlugin
enesky Nov 19, 2023
5074059
added: localization lo login module
enesky Nov 19, 2023
f979d13
updated: String usages
enesky Nov 19, 2023
f20f1d6
created: AuthenticationPlugin with Authentication libs implemented an…
enesky Nov 19, 2023
9d03d77
fixed: lint issues
enesky Nov 19, 2023
92077e1
added: Google Play services auth library
enesky Nov 19, 2023
e721add
added: lifecycle-runtime-compose lib for collectAsStateWithLifecycle(…
enesky Nov 21, 2023
cda7ee8
added: google icon
enesky Nov 21, 2023
c085cc9
removed: navigation folder and its contents moved out
enesky Nov 21, 2023
b5d864f
added: UiStateDelegate.kt class for delegation of ui state usages
enesky Nov 21, 2023
344e2c8
added: UiStateDelegate usage to LoginViewModel and LoginScreen
enesky Nov 21, 2023
3b3b029
added: LoginUiState class for Login screen's state management
enesky Nov 21, 2023
ea08d96
added: core/data module added to FeaturePlugin and directly to core/c…
enesky Nov 21, 2023
9490c6d
updated: gitignore -> added: google-services.json
enesky Nov 21, 2023
1feced6
updated: gitignore -> added: google-services.json
enesky Nov 21, 2023
6604372
removed: google-services.json
enesky Nov 21, 2023
d1c8505
updated: AuthenticationPlugin usage on modules
enesky Nov 21, 2023
8e7d8ca
added: Google auth key to local.properties and its used with buildcon…
enesky Nov 21, 2023
ceb0d8f
added: AuthManager to handle all auth operations
enesky Nov 21, 2023
7e24147
added: AuthManager provider to appModule
enesky Nov 22, 2023
77ef6cd
added: Custom Logger utils to handle logs only on debug mode
enesky Nov 22, 2023
e3ab41b
fixed: Spotless + Detekt issues
enesky Nov 22, 2023
2d43358
added: User login check to NavHost
enesky Nov 22, 2023
5b699ce
added: GoogleButton in order to handle google sign ins
enesky Nov 22, 2023
04a31f1
Merge branch 'main' into feature/authentication
enesky Nov 22, 2023
b4388cf
added: Google api key to local.properties section
enesky Nov 22, 2023
7dd0f2e
added: google-services.json to project because cant create builds on …
enesky Nov 22, 2023
b813dff
updated: LoginUiState with signInResult data
enesky Nov 23, 2023
8ac23b0
added: signInWithGoogle funcs to LoginViewModel
enesky Nov 23, 2023
9838d8b
removed: app_name value removed because it can affect actual app naming
enesky Nov 24, 2023
8c42ef3
updated: modules for authmanager
enesky Nov 24, 2023
c7acbd9
updated: AuthManager with Executor usage instead of Activity
enesky Nov 24, 2023
0d6a4c0
updated: UiStateDelegate with new initialState setup
enesky Nov 24, 2023
cfcaa64
updated: LoginScreen with GoogleSignIn
enesky Nov 24, 2023
2fa2a1f
fixed: spotless-detekt issues
enesky Nov 24, 2023
574da9d
added: new exception check on signInGoogle
enesky Nov 24, 2023
5b8030d
added: result fail check on LoginScreen
enesky Nov 24, 2023
41ec01f
updated: google-services.json
enesky Nov 24, 2023
862fb53
updated: Starting destination with authenticated user check
enesky Nov 25, 2023
6b3c743
updated: AuthManager with clearing basic suspend funcs and returning …
enesky Nov 25, 2023
7a91199
added: ui module to FeaturePlugin and designSystem module to ui module
enesky Nov 25, 2023
6d0e65f
added: 2 different loading animations
enesky Nov 25, 2023
9852178
added: SignInAnonymously func to LoginViewModel
enesky Nov 25, 2023
3132b59
completed: SignInAnonymously action
enesky Nov 25, 2023
99600ba
added: TODO about global loading screen
enesky Nov 25, 2023
9f5c286
added: Loading to home screen
enesky Nov 25, 2023
a151383
updated: LoginScreen ui with new SignInButtonWithLogo component
enesky Nov 25, 2023
0880355
updated: sign in with email functions on AuthManager
enesky Nov 25, 2023
dc850a5
updated: LoginScreen design and function usages
enesky Nov 25, 2023
05aa9b4
added: email sign in functions to LoginViewModel
enesky Nov 25, 2023
46d464d
fixed: detekt issues
enesky Nov 25, 2023
f4bd719
fixed: spotless issues
enesky Nov 25, 2023
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: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,4 @@ proguard/
*.log

# AjCore files
ajcore.*
ajcore.*
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ Doodle demonstrates modern Android development with Koin, Coroutines, Flow, Jetp

```properties
doodle.api.url="https://api.jikan.moe/v4/"
doodle.api.key="sample api key"
#Jikan API doesn't require an API key, but I'm using it as an example for later usages.
doodle.api.key="sample api key" #Jikan API doesn't require an API key, but I'm using it as an example for later usages.
doodle.google.api.key="sample google api ket" #Google API key is required for Google Authentication
```

- <a href="https://github.com/pinterest/ktlint" target="_blank">Ktlint</a> should be added using brew
Expand Down
2 changes: 1 addition & 1 deletion app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,4 @@ proguard/
*.log

# AjCore files
ajcore.*
ajcore.*
25 changes: 20 additions & 5 deletions app/google-services.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
],
"api_key": [
{
"current_key": "AIzaSyADM5K8M_9pE7nU-OkFFeadYLWK0MexOsc"
"current_key": "AIzaSyAGd3e4gg1HrUbHqBHWDiAFj_iqN4P_Hf4"
},
{
"current_key": "AIzaSyAFs09_-yOtdWuK7olSYjQdgGQxjQOIrfs"
}
],
"services": {
Expand Down Expand Up @@ -65,7 +68,10 @@
],
"api_key": [
{
"current_key": "AIzaSyADM5K8M_9pE7nU-OkFFeadYLWK0MexOsc"
"current_key": "AIzaSyAGd3e4gg1HrUbHqBHWDiAFj_iqN4P_Hf4"
},
{
"current_key": "AIzaSyAFs09_-yOtdWuK7olSYjQdgGQxjQOIrfs"
}
],
"services": {
Expand Down Expand Up @@ -102,7 +108,10 @@
],
"api_key": [
{
"current_key": "AIzaSyADM5K8M_9pE7nU-OkFFeadYLWK0MexOsc"
"current_key": "AIzaSyAGd3e4gg1HrUbHqBHWDiAFj_iqN4P_Hf4"
},
{
"current_key": "AIzaSyAFs09_-yOtdWuK7olSYjQdgGQxjQOIrfs"
}
],
"services": {
Expand Down Expand Up @@ -139,7 +148,10 @@
],
"api_key": [
{
"current_key": "AIzaSyADM5K8M_9pE7nU-OkFFeadYLWK0MexOsc"
"current_key": "AIzaSyAGd3e4gg1HrUbHqBHWDiAFj_iqN4P_Hf4"
},
{
"current_key": "AIzaSyAFs09_-yOtdWuK7olSYjQdgGQxjQOIrfs"
}
],
"services": {
Expand Down Expand Up @@ -176,7 +188,10 @@
],
"api_key": [
{
"current_key": "AIzaSyADM5K8M_9pE7nU-OkFFeadYLWK0MexOsc"
"current_key": "AIzaSyAGd3e4gg1HrUbHqBHWDiAFj_iqN4P_Hf4"
},
{
"current_key": "AIzaSyAFs09_-yOtdWuK7olSYjQdgGQxjQOIrfs"
}
],
"services": {
Expand Down
8 changes: 7 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,10 @@

# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken

# Keep Credentials API
-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
*;
}
4 changes: 4 additions & 0 deletions app/src/main/java/dev/enesky/doodle/app/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package dev.enesky.doodle.app.di

import dev.enesky.core.network.di.networkModule
import dev.enesky.feature.login.di.loginManagerModule
import org.koin.core.annotation.KoinExperimentalAPI
import org.koin.core.module.includes
import org.koin.dsl.lazyModule
Expand All @@ -30,4 +31,7 @@ val appModule = lazyModule {

// Includes all the lazy modules from the core modules
includes(networkModule, viewModelModule)

// Includes all the lazy modules from the feature modules
includes(loginManagerModule)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import androidx.navigation.compose.NavHost
import dev.enesky.core.navigation.DoodleNavigationDestination
import dev.enesky.feature.details.navigation.DetailsDestination
import dev.enesky.feature.details.navigation.detailsGraph
import dev.enesky.feature.login.navigation.loginGraph
import dev.enesky.feature.login.loginGraph
import dev.enesky.feature.main.navigation.HomeDestination
import dev.enesky.feature.main.navigation.homeGraph

Expand Down
21 changes: 20 additions & 1 deletion app/src/main/java/dev/enesky/doodle/app/ui/DoodleApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ import dev.enesky.core.design_system.DoodleTheme
import dev.enesky.doodle.app.navigation.DoodleNavHost
import dev.enesky.doodle.app.ui.component.DoodleSnackbarHost
import dev.enesky.doodle.app.ui.component.LocalSnackbarHostState
import dev.enesky.feature.login.manager.AuthManager
import dev.enesky.feature.main.navigation.HomeDestination
import org.koin.compose.koinInject

@OptIn(ExperimentalLayoutApi::class)
@Suppress("ModifierMissing")
@Composable
fun DoodleApp(
modifier: Modifier = Modifier,
appState: DoodleAppState = rememberDoodleAppState(),
// TODO: add new parameter -> systemBarsColor: Color = DoodleTheme.colors.primaryDark
) {
Expand Down Expand Up @@ -64,6 +67,22 @@ fun DoodleApp(
// Use this when you want to use all the screen
// contentWindowInsets = WindowInsets(0.dp, 0.dp, 0.dp, 0.dp),
) { innerPadding ->

/**
* Update the start destination according to the user's login status
*/
val authManager: AuthManager = koinInject<AuthManager>()
if (authManager.isUserLoggedIn()) {
appState.startDestination = HomeDestination
}

/**
* TODO: Loading screen
* if (appState.showLoading) {
* LoadingWithTriangleDots()
* }
*/

DoodleNavHost(
modifier = Modifier
.padding(paddingValues = innerPadding)
Expand Down
8 changes: 3 additions & 5 deletions app/src/main/java/dev/enesky/doodle/app/ui/DoodleAppState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import dev.enesky.core.navigation.DoodleNavigationDestination
import dev.enesky.feature.login.navigation.LoginDestination
import dev.enesky.feature.login.LoginDestination
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
Expand All @@ -25,8 +25,7 @@ fun rememberDoodleAppState(
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
coroutineScope: CoroutineScope = rememberCoroutineScope(),
navController: NavHostController = rememberNavController(),
// TODO: Check if user is logged in or not
startDestination: LoginDestination = LoginDestination,
startDestination: DoodleNavigationDestination = LoginDestination,
) = remember(
snackbarHostState,
coroutineScope,
Expand All @@ -46,7 +45,7 @@ class DoodleAppState(
val snackbarHostState: SnackbarHostState,
val coroutineScope: CoroutineScope,
val navController: NavHostController,
val startDestination: LoginDestination,
var startDestination: DoodleNavigationDestination,
) {
init {
coroutineScope.launch {
Expand All @@ -61,7 +60,6 @@ class DoodleAppState(
}
}
}

val currentDestination: NavDestination?
@Composable get() = navController.currentBackStackEntryAsState().value?.destination

Expand Down
4 changes: 4 additions & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ gradlePlugin {
id = libs.plugins.common.spotless.get().pluginId
implementationClass = "$rootPath.common.SpotlessPlugin"
}
register("authentication") {
id = libs.plugins.common.authentication.get().pluginId
implementationClass = "$rootPath.common.AuthenticationPlugin"
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ internal fun Project.configureAndroidCompose(commonExtension: CommonExtension<*,
val composeBomPlatform = platform(libs.compose.bom.get().toString())
add("implementation", composeBomPlatform)
add("implementation", libs.bundles.compose.materials)
add("implementation", libs.androidx.navigation.compose)
add("androidTestImplementation", composeBomPlatform)

add("implementation", libs.androidx.navigation.compose)
add("implementation", libs.lifecycle.runtime.compose)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,20 @@ import org.gradle.api.Project
import org.gradle.api.plugins.ExtensionAware
import org.gradle.kotlin.dsl.the
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import java.util.Properties

internal val Project.libs get() = the<LibrariesForLibs>()

internal fun CommonExtension<*, *, *, *, *>.kotlinOptions(block: KotlinJvmOptions.() -> Unit) =
(this as ExtensionAware).extensions.configure("kotlinOptions", block)

internal fun getLocalProperties(rootProject: Project): Properties {
val localProperties = Properties()
val localPropertiesFile = rootProject.file("local.properties") // It's ignored by git
if (localPropertiesFile.exists() && localPropertiesFile.isFile) {
localPropertiesFile.inputStream().use { input ->
localProperties.load(input)
}
}
return localProperties
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,18 @@
package dev.enesky.build_logic.convention.plugins.common

import com.android.build.gradle.LibraryExtension
import dev.enesky.build_logic.convention.getLocalProperties
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
import java.util.Properties

/**
* Configure Api Key Provider
* -> Only for core/network/build.gradle.kts <-
*/
class ApiKeyProviderPlugin : Plugin<Project> {
override fun apply(target: Project) = with(target) {
val localProperties = Properties()
val localPropertiesFile = rootProject.file("local.properties") // It's ignored by git
if (localPropertiesFile.exists() && localPropertiesFile.isFile) {
localPropertiesFile.inputStream().use { input ->
localProperties.load(input)
}
}

val localProperties = getLocalProperties(rootProject)
val doodleApiUrl: String = checkNotNull(
localProperties.getProperty("doodle.api.url") ?: System.getenv("DOODLE_API_URL") ?: "\"\"",
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dev.enesky.build_logic.convention.plugins.common

import com.android.build.gradle.LibraryExtension
import dev.enesky.build_logic.convention.getLocalProperties
import dev.enesky.build_logic.convention.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType

/**
* Created by Enes Kamil YILMAZ on 19/11/2023
*/
class AuthenticationPlugin : Plugin<Project> {
override fun apply(target: Project) = with(target) {
val localProperties = getLocalProperties(rootProject)
val googleApiKey: String = checkNotNull(
localProperties.getProperty("doodle.google.api.key")
?: System.getenv("DOODLE_GOOGLE_API_KEY")
?: "\"\"",
)

with(extensions.getByType<LibraryExtension>()) {
buildFeatures.buildConfig = true
defaultConfig.buildConfigField("String", "DOODLE_GOOGLE_API_KEY", googleApiKey)
}

dependencies {
// Firebase Authentication
add("implementation", platform(libs.firebase.bom))
add("implementation", libs.firebase.authentication)

// Google Sign In
add("implementation", libs.google.auth)

// Credential Manager
add("implementation", libs.credential.manager)
add("implementation", libs.credential.manager.play.services.auth)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class FeaturePlugin : Plugin<Project> {
override fun apply(target: Project) = with(target) {
dependencies {
add("implementation", project(":core:design-system"))
add("implementation", project(":core:common"))
add("implementation", project(":core:data"))
add("implementation", project(":core:ui"))
}
}
}
2 changes: 1 addition & 1 deletion config/detekt/compose-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Compose:
# You can optionally disable the checks in this rule for regex matches against the composable name (e.g. molecule presenters)
# allowedComposableFunctionNames: .*Presenter,.*MoleculePresenter
ComposableParamOrder:
active: true
active: false
PreviewAnnotationNaming:
active: true
PreviewPublic:
Expand Down
2 changes: 1 addition & 1 deletion config/detekt/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ exceptions:
ThrowingNewInstanceOfSameException:
active: true
TooGenericExceptionCaught:
active: true
active: false
excludes: ['**/test/**', '**/androidTest/**']
exceptionNames:
- ArrayIndexOutOfBoundsException
Expand Down
4 changes: 4 additions & 0 deletions core/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ plugins {
}

android.namespace = "dev.enesky.core.common"

dependencies {
implementation(projects.core.data)
}
Loading