From dcd173461370f61f4bdc9e032720b799f6a0e32a Mon Sep 17 00:00:00 2001 From: Mohsen Sameti Date: Mon, 9 Jan 2023 23:47:44 +0330 Subject: [PATCH] basic oauth dagger setup --- .gitignore | 1 + app/build.gradle | 24 ++++++++++++++++ app/src/main/AndroidManifest.xml | 17 +++++++++++ .../youtubeclient/YoutubeApplication.kt | 11 ++++++++ .../youtubeclient/core/di/AppComponent.kt | 28 +++++++++++++++++++ .../youtubeclient/core/di/ContextModule.kt | 22 +++++++++++++++ .../youtubeclient/core/di/Extensions.kt | 8 ++++++ .../youtubeclient/core/di/LocalModule.kt | 16 +++++++++++ .../core/di/viewmodel/ViewModelKey.kt | 15 ++++++++++ .../core/di/viewmodel/ViewModelModule.kt | 21 ++++++++++++++ .../di/viewmodel/YoutubeViewModelFactory.kt | 23 +++++++++++++++ .../features/auth/data/AuthModule.kt | 20 +++++++++++++ .../features/auth/data/AuthRepository.kt | 21 ++++++++++++++ .../features/auth/ui/AuthFragment.kt | 20 +++++++++++-- .../features/auth/ui/AuthViewModel.kt | 12 ++++++++ app/src/main/res/layout/fragment_auth.xml | 4 +-- app/src/main/res/values/strings.xml | 2 +- 17 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/com/samentic/youtubeclient/YoutubeApplication.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/core/di/AppComponent.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/core/di/ContextModule.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/core/di/Extensions.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/core/di/LocalModule.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/ViewModelKey.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/ViewModelModule.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/YoutubeViewModelFactory.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/features/auth/data/AuthModule.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/features/auth/data/AuthRepository.kt create mode 100644 app/src/main/java/com/samentic/youtubeclient/features/auth/ui/AuthViewModel.kt diff --git a/.gitignore b/.gitignore index b3c16f9..57029ed 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ .externalNativeBuild .cxx local.properties +client_secret.properties diff --git a/app/build.gradle b/app/build.gradle index 8ba2f77..e5d9e1c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'kotlin-kapt' } android { @@ -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 { @@ -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' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d738272..b96966c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,10 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/YoutubeApplication.kt b/app/src/main/java/com/samentic/youtubeclient/YoutubeApplication.kt new file mode 100644 index 0000000..4e45226 --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/YoutubeApplication.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/core/di/AppComponent.kt b/app/src/main/java/com/samentic/youtubeclient/core/di/AppComponent.kt new file mode 100644 index 0000000..8a55183 --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/core/di/AppComponent.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/core/di/ContextModule.kt b/app/src/main/java/com/samentic/youtubeclient/core/di/ContextModule.kt new file mode 100644 index 0000000..38de06a --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/core/di/ContextModule.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/core/di/Extensions.kt b/app/src/main/java/com/samentic/youtubeclient/core/di/Extensions.kt new file mode 100644 index 0000000..f72b835 --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/core/di/Extensions.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/core/di/LocalModule.kt b/app/src/main/java/com/samentic/youtubeclient/core/di/LocalModule.kt new file mode 100644 index 0000000..ab944d3 --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/core/di/LocalModule.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/ViewModelKey.kt b/app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/ViewModelKey.kt new file mode 100644 index 0000000..5453512 --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/ViewModelKey.kt @@ -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) \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/ViewModelModule.kt b/app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/ViewModelModule.kt new file mode 100644 index 0000000..bf7ae98 --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/ViewModelModule.kt @@ -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 + +} \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/YoutubeViewModelFactory.kt b/app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/YoutubeViewModelFactory.kt new file mode 100644 index 0000000..33a74b5 --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/core/di/viewmodel/YoutubeViewModelFactory.kt @@ -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, @JvmSuppressWildcards Provider> +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): 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) + } + } +} diff --git a/app/src/main/java/com/samentic/youtubeclient/features/auth/data/AuthModule.kt b/app/src/main/java/com/samentic/youtubeclient/features/auth/data/AuthModule.kt new file mode 100644 index 0000000..95a970b --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/features/auth/data/AuthModule.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/features/auth/data/AuthRepository.kt b/app/src/main/java/com/samentic/youtubeclient/features/auth/data/AuthRepository.kt new file mode 100644 index 0000000..c6d53b6 --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/features/auth/data/AuthRepository.kt @@ -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" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/features/auth/ui/AuthFragment.kt b/app/src/main/java/com/samentic/youtubeclient/features/auth/ui/AuthFragment.kt index 5b60790..181d770 100644 --- a/app/src/main/java/com/samentic/youtubeclient/features/auth/ui/AuthFragment.kt +++ b/app/src/main/java/com/samentic/youtubeclient/features/auth/ui/AuthFragment.kt @@ -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 { 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() } } } \ No newline at end of file diff --git a/app/src/main/java/com/samentic/youtubeclient/features/auth/ui/AuthViewModel.kt b/app/src/main/java/com/samentic/youtubeclient/features/auth/ui/AuthViewModel.kt new file mode 100644 index 0000000..4841be1 --- /dev/null +++ b/app/src/main/java/com/samentic/youtubeclient/features/auth/ui/AuthViewModel.kt @@ -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() +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_auth.xml b/app/src/main/res/layout/fragment_auth.xml index d15d366..927f8c7 100644 --- a/app/src/main/res/layout/fragment_auth.xml +++ b/app/src/main/res/layout/fragment_auth.xml @@ -7,10 +7,10 @@ tools:context="com.samentic.youtubeclient.features.auth.ui.AuthFragment">