Add Dagger (#554)

* Add Dagger DI

* Preemptively fix tests

* Add missing licenses

* DI fixes

* ci fixes
This commit is contained in:
Ivan Kupalov 2018-03-27 20:47:00 +03:00 committed by Konrad Pozniak
commit a5cffe0fea
41 changed files with 1040 additions and 415 deletions

View file

@ -0,0 +1,63 @@
/* Copyright 2018 charlag
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.di
import com.keylesspalace.tusky.*
import dagger.Module
import dagger.android.ContributesAndroidInjector
/**
* Created by charlag on 3/24/18.
*/
@Module
abstract class ActivitiesModule {
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributesMainActivity(): MainActivity
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributesAccountActivity(): AccountActivity
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributesListsActivity(): ListsActivity
@ContributesAndroidInjector
abstract fun contributesComposeActivity(): ComposeActivity
@ContributesAndroidInjector
abstract fun contributesEditProfileActivity(): EditProfileActivity
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributesAccountListActivity(): AccountListActivity
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributesModalTimelineActivity(): ModalTimelineActivity
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributesViewTagActivity(): ViewTagActivity
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributesViewThreadActivity(): ViewThreadActivity
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributesFavouritesActivity(): FavouritesActivity
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contribtutesSearchAvtivity(): SearchActivity
@ContributesAndroidInjector()
abstract fun contributesAboutActivity(): AboutActivity
}

View file

@ -0,0 +1,46 @@
/* Copyright 2018 charlag
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.di
import com.keylesspalace.tusky.TuskyApplication
import dagger.BindsInstance
import dagger.Component
import dagger.android.AndroidInjectionModule
import javax.inject.Singleton
/**
* Created by charlag on 3/21/18.
*/
@Singleton
@Component(modules = [
AppModule::class,
NetworkModule::class,
AndroidInjectionModule::class,
ActivitiesModule::class
])
interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(tuskyApp: TuskyApplication): Builder
fun build(): AppComponent
}
fun inject(app: TuskyApplication)
}

View file

@ -0,0 +1,80 @@
/* Copyright 2018 charlag
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.di
import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.FragmentManager
import com.keylesspalace.tusky.TuskyApplication
import dagger.android.AndroidInjection
import dagger.android.support.AndroidSupportInjection
import dagger.android.support.HasSupportFragmentInjector
/**
* Created by charlag on 3/24/18.
*/
object AppInjector {
fun init(app: TuskyApplication) {
DaggerAppComponent.builder().application(app)
.build().inject(app)
app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
handleActivity(activity)
}
override fun onActivityPaused(activity: Activity?) {
}
override fun onActivityResumed(activity: Activity?) {
}
override fun onActivityStarted(activity: Activity?) {
}
override fun onActivityDestroyed(activity: Activity?) {
}
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
}
override fun onActivityStopped(activity: Activity?) {
}
})
}
private fun handleActivity(activity: Activity) {
if (activity is HasSupportFragmentInjector || activity is Injectable) {
AndroidInjection.inject(activity)
}
if (activity is FragmentActivity) {
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(
object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentCreated(fm: FragmentManager?, f: Fragment?,
savedInstanceState: Bundle?) {
if (f is Injectable) {
AndroidSupportInjection.inject(f)
}
}
}, true)
}
}
}

View file

@ -0,0 +1,68 @@
/* Copyright 2018 charlag
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.di
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.preference.PreferenceManager
import android.support.v4.content.LocalBroadcastManager
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.TuskyApplication
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.network.TimelineCases
import com.keylesspalace.tusky.network.TimelineCasesImpl
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
/**
* Created by charlag on 3/21/18.
*/
@Module
class AppModule {
@Provides
fun providesApplication(app: TuskyApplication): Application = app
@Provides
fun providesContext(app: Application): Context = app
@Provides
fun providesSharedPreferences(app: Application): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(app)
}
@Provides
fun providesBroadcastManager(app: Application): LocalBroadcastManager {
return LocalBroadcastManager.getInstance(app)
}
@Provides
fun providesTimelineUseCases(api: MastodonApi,
broadcastManager: LocalBroadcastManager): TimelineCases {
return TimelineCasesImpl(api, broadcastManager)
}
@Provides
@Singleton
fun providesAccountManager(app: TuskyApplication): AccountManager {
return app.serviceLocator.get(AccountManager::class.java)
}
}

View file

@ -0,0 +1,43 @@
/* Copyright 2018 charlag
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.di
import com.keylesspalace.tusky.fragment.*
import dagger.Module
import dagger.android.ContributesAndroidInjector
/**
* Created by charlag on 3/24/18.
*/
@Module
abstract class FragmentBuildersModule {
@ContributesAndroidInjector
abstract fun accountListFragment(): AccountListFragment
@ContributesAndroidInjector
abstract fun accountMediaFragment(): AccountMediaFragment
@ContributesAndroidInjector
abstract fun viewThreadFragment(): ViewThreadFragment
@ContributesAndroidInjector
abstract fun timelineFragment(): TimelineFragment
@ContributesAndroidInjector
abstract fun notificationsFragment(): NotificationsFragment
}

View file

@ -0,0 +1,23 @@
/* Copyright 2018 charlag
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.di
/**
* Created by charlag on 3/24/18.
*/
interface Injectable

View file

@ -0,0 +1,111 @@
/* Copyright 2018 charlag
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.di
import android.content.SharedPreferences
import android.text.Spanned
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonDeserializer
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.json.SpannedTypeAdapter
import com.keylesspalace.tusky.network.InstanceSwitchAuthInterceptor
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.OkHttpUtils
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import dagger.multibindings.IntoSet
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import retrofit2.Converter
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton
/**
* Created by charlag on 3/24/18.
*/
@Module
class NetworkModule {
@Provides
@IntoMap()
@ClassKey(Spanned::class)
fun providesSpannedTypeAdapter(): JsonDeserializer<*> = SpannedTypeAdapter()
@Provides
@Singleton
fun providesGson(adapters: @JvmSuppressWildcards Map<Class<*>, JsonDeserializer<*>>): Gson {
return GsonBuilder()
.apply {
for ((k, v) in adapters) {
registerTypeAdapter(k, v)
}
}
.create()
}
@Provides
@IntoSet
@Singleton
fun providesConverterFactory(gson: Gson): Converter.Factory = GsonConverterFactory.create(gson)
@Provides
@IntoSet
@Singleton
fun providesAuthInterceptor(accountManager: AccountManager): Interceptor {
// should accept AccountManager here probably but I don't want to break things yet
return InstanceSwitchAuthInterceptor(accountManager)
}
@Provides
@Singleton
fun providesHttpClient(interceptors: @JvmSuppressWildcards Set<Interceptor>,
preferences: SharedPreferences): OkHttpClient {
return OkHttpUtils.getCompatibleClientBuilder(preferences)
.apply {
interceptors.fold(this) { b, i ->
b.addInterceptor(i)
}
}
.build()
}
@Provides
@Singleton
fun providesRetrofit(httpClient: OkHttpClient,
converters: @JvmSuppressWildcards Set<Converter.Factory>): Retrofit {
return Retrofit.Builder().baseUrl("https://dummy.placeholder/")
.client(httpClient)
.let { builder ->
// Doing it this way in case builder will be immutable so we return the final
// instance
converters.fold(builder) { b, c ->
b.addConverterFactory(c)
}
}
.build()
}
@Provides
@Singleton
fun providesApi(retrofit: Retrofit): MastodonApi = retrofit.create(MastodonApi::class.java)
}