Skip to content

Commit

Permalink
[WIP] Setting up Kodein, implementing Google sign in
Browse files Browse the repository at this point in the history
There's still a lot of work to do here. Maybe would be good to break
things here in smaller commits? (the first feature is always a bit
problematic on its commits size)
  • Loading branch information
rafaeltoledo committed May 2, 2018
1 parent 5e62200 commit de94fff
Show file tree
Hide file tree
Showing 25 changed files with 540 additions and 10 deletions.
29 changes: 29 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
apply plugin: 'com.android.application'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'org.jetbrains.kotlin.kapt'
apply plugin: 'com.getkeepsafe.dexcount'

apply from: "$rootDir/gradle/coverage.gradle"

Expand All @@ -13,6 +15,8 @@ android {
versionCode 1
versionName '1.0'

vectorDrawables.useSupportLibrary = true

testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}

Expand All @@ -22,6 +26,8 @@ android {
androidTest.java.srcDirs += 'src/androidTest/kotlin'
}

dataBinding.enabled true

signingConfigs {
debug {
storeFile file("$rootDir/distribution/debug.keystore")
Expand All @@ -40,13 +46,17 @@ android {

buildTypes {
debug {
buildConfigField 'String', 'GOOGLE_REQUEST_ID_TOKEN', "\"$System.env.GOOGLE_REQUEST_ID_TOKEN_DEVELOPMENT\""

testCoverageEnabled true

applicationIdSuffix '.dev'
signingConfig signingConfigs.debug
}

release {
buildConfigField 'String', 'GOOGLE_REQUEST_ID_TOKEN', "\"$System.env.GOOGLE_REQUEST_ID_TOKEN_RELEASE\""

signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
Expand All @@ -58,12 +68,31 @@ android {
}
}

kotlin {
experimental {
coroutines 'enable'
}
}

dependencies {
implementation "com.android.support:appcompat-v7:$versions.supportLibrary"
implementation "com.android.support:design:$versions.supportLibrary"

implementation 'androidx.constraintlayout:constraintlayout:1.1.0'

implementation "android.arch.lifecycle:extensions:$versions.arch"
kapt "android.arch.lifecycle:compiler:$versions.arch"

implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.22.5'

implementation "com.google.firebase:firebase-core:$versions.googleServices"
implementation "com.google.firebase:firebase-auth:$versions.googleServices"

implementation "com.google.android.gms:play-services-auth:$versions.googleServices"

implementation "org.kodein.di:kodein-di-generic-jvm:$versions.kodein"
implementation "org.kodein.di:kodein-di-framework-android:$versions.kodein"

testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:3.8'
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@
package="net.rafaeltoledo.social">

<application
android:name=".SocialApp"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">

<activity android:name=".ui.feature.main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name=".ui.feature.signin.SignInActivity" />
</application>
</manifest>
5 changes: 0 additions & 5 deletions app/src/main/kotlin/net/rafaeltoledo/social/MainActivity.kt

This file was deleted.

21 changes: 21 additions & 0 deletions app/src/main/kotlin/net/rafaeltoledo/social/SocialApp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.rafaeltoledo.social

import android.app.Application
import net.rafaeltoledo.social.di.authModule
import net.rafaeltoledo.social.di.firebaseModule
import net.rafaeltoledo.social.di.viewModelModule
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
import org.kodein.di.android.androidModule

open class SocialApp : Application(), KodeinAware {

override val kodein = initKodein()

private fun initKodein() = Kodein.lazy {
import(androidModule(this@SocialApp))
import(firebaseModule)
import(authModule)
import(viewModelModule)
}
}
3 changes: 3 additions & 0 deletions app/src/main/kotlin/net/rafaeltoledo/social/data/Models.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package net.rafaeltoledo.social.data

data class User(val id: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package net.rafaeltoledo.social.data.auth

import kotlinx.coroutines.experimental.Deferred
import net.rafaeltoledo.social.data.User

interface AuthManager {

fun googleSignIn(token: String): Deferred<User>

fun isUserLoggedIn(): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package net.rafaeltoledo.social.data.auth

import android.app.Activity
import android.content.Intent

interface DelegatedAuth {

fun <T : DelegatedAuth> build(activity: Activity): T

fun signIn(activity: Activity)

fun onResult(requestCode: Int, resultCode: Int, data: Intent?): AuthResult

fun signOut(callback: (Status) -> Unit)
}

enum class Status { CANCELED, SUCCESS, FAILURE }

data class AuthResult(val status: Status, val token: String? = null)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package net.rafaeltoledo.social.data.auth

import android.app.Activity
import android.content.Intent
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException
import net.rafaeltoledo.social.BuildConfig

class GoogleAuth : DelegatedAuth {

private var client: GoogleSignInClient? = null

override fun <T : DelegatedAuth> build(activity: Activity): T {
if (client != null) {
@Suppress("UNCHECKED_CAST") return this as T
}

val options = GoogleSignInOptions.Builder()
.requestIdToken(BuildConfig.GOOGLE_REQUEST_ID_TOKEN)
.requestEmail()
.build()

client = GoogleSignIn.getClient(activity, options)

@Suppress("UNCHECKED_CAST") return this as T
}

override fun signIn(activity: Activity) {
activity.startActivityForResult(client?.signInIntent, RESULT_SIGN_IN)
}

override fun onResult(requestCode: Int, resultCode: Int, data: Intent?): AuthResult {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
try {
val account = task.getResult(ApiException::class.java)
return AuthResult(Status.SUCCESS, account.idToken)
} catch (e: ApiException) {
return AuthResult(Status.FAILURE)
}
}

override fun signOut(callback: (Status) -> Unit) {
client?.signOut()?.addOnCompleteListener { callback.invoke(if (it.isSuccessful) Status.SUCCESS else Status.FAILURE) }
}

companion object {
const val RESULT_SIGN_IN = 1441
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package net.rafaeltoledo.social.data.firebase

import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.GoogleAuthProvider
import kotlinx.coroutines.experimental.CompletableDeferred
import kotlinx.coroutines.experimental.Deferred
import net.rafaeltoledo.social.data.User
import net.rafaeltoledo.social.data.auth.AuthManager

class FirebaseAuthManager : AuthManager {

private val auth = FirebaseAuth.getInstance()

override fun googleSignIn(token: String): Deferred<User> {
val deferred = CompletableDeferred<User>()

auth.signInWithCredential(GoogleAuthProvider.getCredential(token, null))
.addOnCompleteListener {
if (it.isSuccessful) {
deferred.complete(User(it.result.user.uid))
} else {
deferred.completeExceptionally(it.exception!!)
}
}

return deferred
}

override fun isUserLoggedIn() = auth.currentUser != null
}
12 changes: 12 additions & 0 deletions app/src/main/kotlin/net/rafaeltoledo/social/di/AuthModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package net.rafaeltoledo.social.di

import net.rafaeltoledo.social.data.auth.DelegatedAuth
import net.rafaeltoledo.social.data.auth.GoogleAuth
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance

val authModule = Kodein.Module {

bind<DelegatedAuth>() with instance(GoogleAuth())
}
12 changes: 12 additions & 0 deletions app/src/main/kotlin/net/rafaeltoledo/social/di/FirebaseModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package net.rafaeltoledo.social.di

import net.rafaeltoledo.social.data.auth.AuthManager
import net.rafaeltoledo.social.data.firebase.FirebaseAuthManager
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.singleton

val firebaseModule = Kodein.Module {

bind<AuthManager>() with singleton { FirebaseAuthManager() }
}
26 changes: 26 additions & 0 deletions app/src/main/kotlin/net/rafaeltoledo/social/di/ViewModelModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.rafaeltoledo.social.di

import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProvider
import net.rafaeltoledo.social.ui.BaseViewModel
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.singleton

val viewModelModule = Kodein.Module {

bind<ViewModelProvider.Factory>() with singleton {
object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {

val newInstance = modelClass.newInstance()

if (BaseViewModel::class.java.isInstance(newInstance)) {
(newInstance as BaseViewModel).whenReady(kodein)
}

return newInstance
}
}
}
}
21 changes: 21 additions & 0 deletions app/src/main/kotlin/net/rafaeltoledo/social/ui/BaseActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.rafaeltoledo.social.ui

import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProvider
import android.arch.lifecycle.ViewModelProviders
import android.support.v7.app.AppCompatActivity
import org.kodein.di.KodeinAware
import org.kodein.di.android.closestKodein
import org.kodein.di.generic.instance
import kotlin.reflect.KClass

abstract class BaseActivity : AppCompatActivity(), KodeinAware {

override val kodein by closestKodein()

private val viewModelFactory: ViewModelProvider.Factory by instance()

protected fun <T : ViewModel> getViewModelInstance(kClass: KClass<T>) =
ViewModelProviders.of(this, viewModelFactory)
.get(kClass.java)
}
21 changes: 21 additions & 0 deletions app/src/main/kotlin/net/rafaeltoledo/social/ui/BaseViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.rafaeltoledo.social.ui

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware

abstract class BaseViewModel : ViewModel(), KodeinAware {

override lateinit var kodein: Kodein

val loading = MutableLiveData<Boolean>()

init {
loading.postValue(false)
}

fun whenReady(kodein: Kodein) {
this.kodein = kodein
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package net.rafaeltoledo.social.ui.feature.main

import android.content.Intent
import android.databinding.DataBindingUtil
import android.os.Bundle
import net.rafaeltoledo.social.R
import net.rafaeltoledo.social.data.auth.AuthManager
import net.rafaeltoledo.social.databinding.ActivityMainBinding
import net.rafaeltoledo.social.ui.BaseActivity
import net.rafaeltoledo.social.ui.feature.signin.SignInActivity
import org.kodein.di.generic.instance

class MainActivity : BaseActivity() {

private val auth: AuthManager by instance()

private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
}

override fun onStart() {
super.onStart()
if (!auth.isUserLoggedIn()) {
startActivity(Intent(this, SignInActivity::class.java))
finish()
}
}
}
Loading

0 comments on commit de94fff

Please sign in to comment.