Skip to content

Commit

Permalink
basic oauth dagger setup
Browse files Browse the repository at this point in the history
  • Loading branch information
MohsenSameti committed Jan 9, 2023
1 parent 0db7611 commit dcd1734
Show file tree
Hide file tree
Showing 17 changed files with 259 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
.externalNativeBuild
.cxx
local.properties
client_secret.properties
24 changes: 24 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}

android {
Expand All @@ -10,11 +11,25 @@ android {
defaultConfig {
applicationId "com.samentic.youtubeclient"
minSdk 24
//noinspection OldTargetApi
targetSdk 32
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

def props = new Properties()
File secretFile = rootProject.file("client_secret.properties")
if(secretFile.exists()) {
secretFile.withInputStream { props.load(it)}
}

String clientId = props.getProperty("ID", "")
String redirectScheme = clientId.tokenize(".").reverse().join(".")
manifestPlaceholders += [redirectUriScheme: redirectScheme]
buildConfigField("String", "CLIENT_ID", "\"${clientId}\"")
buildConfigField("String", "CLIENT_SECRET", "\"${props.getProperty("SECRET", "")}\"")
buildConfigField("String", "REDIRECT_URL_SCHEME", "\"${redirectScheme}\"")
}

buildTypes {
Expand Down Expand Up @@ -49,6 +64,15 @@ dependencies {
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'

// Dagger
implementation 'com.google.dagger:dagger:2.44.2'
kapt 'com.google.dagger:dagger-compiler:2.44.2'

// OAuth2
implementation 'net.openid:appauth:0.11.1'

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".YoutubeApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
Expand All @@ -19,6 +22,20 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="net.openid.appauth.RedirectUriReceiverActivity"
android:exported="true"
tools:node="replace">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="${redirectUriScheme}" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
11 changes: 11 additions & 0 deletions app/src/main/java/com/samentic/youtubeclient/YoutubeApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.samentic.youtubeclient

import android.app.Application
import com.samentic.youtubeclient.core.di.AppComponent
import com.samentic.youtubeclient.core.di.DaggerAppComponent

class YoutubeApplication : Application() {
val appComponent: AppComponent by lazy {
DaggerAppComponent.factory().create(this)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.samentic.youtubeclient.core.di

import com.samentic.youtubeclient.YoutubeApplication
import com.samentic.youtubeclient.core.di.viewmodel.ViewModelModule
import com.samentic.youtubeclient.features.auth.data.AuthModule
import com.samentic.youtubeclient.features.auth.ui.AuthFragment
import dagger.BindsInstance
import dagger.Component
import javax.inject.Singleton

@Singleton
@Component(
modules = [
ViewModelModule::class, ContextModule::class, LocalModule::class,
AuthModule::class
]
)
interface AppComponent {

fun inject(fragment: AuthFragment)

@Component.Factory
interface Factory {
fun create(
@BindsInstance application: YoutubeApplication
): AppComponent
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.samentic.youtubeclient.core.di

import android.app.Application
import android.content.Context
import com.samentic.youtubeclient.YoutubeApplication
import dagger.Binds
import dagger.Module
import dagger.Provides

@Module
abstract class ContextModule {

@Binds
abstract fun bindApplication(application: YoutubeApplication): Application

companion object {
@Provides
fun provideContext(application: Application): Context {
return application.applicationContext
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.samentic.youtubeclient.core.di

import androidx.fragment.app.Fragment
import com.samentic.youtubeclient.YoutubeApplication

fun Fragment.findAppComponent(): AppComponent {
return (requireActivity().application as YoutubeApplication).appComponent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.samentic.youtubeclient.core.di

import android.content.Context
import android.content.SharedPreferences
import dagger.Module
import dagger.Provides

@Module
object LocalModule {
private const val PREF_NAME = "youtube"

@Provides
fun provideSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.samentic.youtubeclient.core.di.viewmodel

import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass

@MapKey
@MustBeDocumented
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
annotation class ViewModelKey(val value: KClass<out ViewModel>)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.samentic.youtubeclient.core.di.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.samentic.youtubeclient.features.auth.ui.AuthViewModel
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap

@Module
abstract class ViewModelModule {

@Binds
abstract fun bindViewModelFactory(factory: YoutubeViewModelFactory): ViewModelProvider.Factory

@IntoMap
@ViewModelKey(AuthViewModel::class)
@Binds
abstract fun bindAuthViewModel(viewModel: AuthViewModel): ViewModel

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.samentic.youtubeclient.core.di.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton

@Singleton
class YoutubeViewModelFactory @Inject constructor(
private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = creators[modelClass]
?: throw IllegalArgumentException("unknown model class $modelClass")
try {
@Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.samentic.youtubeclient.features.auth.data

import android.content.SharedPreferences
import com.samentic.youtubeclient.features.auth.data.AuthRepository.Companion.KEY_AUTH_STATE
import dagger.Module
import dagger.Provides
import net.openid.appauth.AuthState
import javax.inject.Singleton

@Module
object AuthModule {
@Singleton
@Provides
fun provideAuthState(pref: SharedPreferences): AuthState {
val authState = pref.getString(KEY_AUTH_STATE, null)?.let {
AuthState.jsonDeserialize(it)
}
return authState ?: AuthState()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.samentic.youtubeclient.features.auth.data

import android.content.SharedPreferences
import net.openid.appauth.AuthState
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class AuthRepository @Inject constructor(
private val authState: AuthState,
private val prefs: SharedPreferences
) {

fun isAuthorized(): Boolean {
return authState.isAuthorized
}

companion object {
const val KEY_AUTH_STATE = "authState"
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
package com.samentic.youtubeclient.features.auth.ui

import android.content.Context
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import com.samentic.youtubeclient.R
import com.samentic.youtubeclient.core.di.findAppComponent
import com.samentic.youtubeclient.databinding.FragmentAuthBinding
import com.zhuinden.fragmentviewbindingdelegatekt.viewBinding
import javax.inject.Inject

class AuthFragment : Fragment(R.layout.fragment_auth) {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory

private val binding by viewBinding(FragmentAuthBinding::bind)
private val viewModel by viewModels<AuthViewModel> { viewModelFactory }

override fun onAttach(context: Context) {
super.onAttach(context)
findAppComponent().inject(this)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnAuthenticate.setOnClickListener {
Toast.makeText(requireContext(), "Clicked!", Toast.LENGTH_SHORT).show()

binding.btnAuthorize.setOnClickListener {
Toast.makeText(requireContext(), "${viewModel.isAuthorized()}", Toast.LENGTH_SHORT).show()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.samentic.youtubeclient.features.auth.ui

import androidx.lifecycle.ViewModel
import com.samentic.youtubeclient.features.auth.data.AuthRepository
import javax.inject.Inject

class AuthViewModel @Inject constructor(
private val authRepository: AuthRepository
) : ViewModel() {

fun isAuthorized() = authRepository.isAuthorized()
}
4 changes: 2 additions & 2 deletions app/src/main/res/layout/fragment_auth.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
tools:context="com.samentic.youtubeclient.features.auth.ui.AuthFragment">

<Button
android:id="@+id/btn_authenticate"
android:id="@+id/btn_authorize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_authenticate"
android:text="@string/label_authorize"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<resources>
<string name="app_name">YouTubeClient</string>
<string name="label_authenticate">Authenticate</string>
<string name="label_authorize">Authorize</string>
</resources>

0 comments on commit dcd1734

Please sign in to comment.