mirror of
https://github.com/reactivedroid/TvFlix.git
synced 2025-08-06 14:22:22 +08:00
Merge pull request #10 from ashwini009/di-improvement
Faster initialisation via enhanced DI, Improved test, More Kotlin sweetness
This commit is contained in:
24
.github/workflows/android.yml
vendored
24
.github/workflows/android.yml
vendored
@ -1,17 +1,17 @@
|
||||
name: Android CI
|
||||
|
||||
on: [push]
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
- uses: actions/checkout@v1
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew assembleDebug
|
||||
|
@ -1,5 +1,4 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: "$project.rootDir/tools/findbugs.gradle"
|
||||
apply from: "$project.rootDir/tools/checkstyle.gradle"
|
||||
apply from: "$project.rootDir/tools/pmd.gradle"
|
||||
apply plugin: 'kotlin-android'
|
||||
@ -61,6 +60,10 @@ android {
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
apply from: 'dependencies.gradle'
|
||||
|
@ -1,20 +1,19 @@
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
//Support
|
||||
implementation deps.support_lib.design
|
||||
implementation deps.support_lib.appcompat
|
||||
implementation deps.support_lib.support_annotations
|
||||
implementation deps.support_lib.recyclerview
|
||||
implementation deps.support_lib.cardview
|
||||
implementation deps.androidx_lib.design
|
||||
implementation deps.androidx_lib.appcompat
|
||||
implementation deps.androidx_lib.support_annotations
|
||||
implementation deps.androidx_lib.recyclerview
|
||||
implementation deps.androidx_lib.cardview
|
||||
implementation deps.androidx_lib.ktx_core
|
||||
implementation deps.androidx_lib.ktx_collections
|
||||
implementation deps.androidx_lib.ktx_fragment
|
||||
implementation deps.androidx_lib.ktx_activity
|
||||
implementation deps.constraint_layout
|
||||
|
||||
implementation deps.multidex
|
||||
|
||||
implementation deps.rxjava.android
|
||||
// Because RxAndroid releases are few and far between, it is recommended you also
|
||||
// explicitly depend on RxJava's latest version for bug fixes and new features.
|
||||
implementation deps.rxjava.main
|
||||
|
||||
implementation deps.okhttp.main
|
||||
implementation deps.okhttp.logging_interceptor
|
||||
|
||||
@ -39,6 +38,7 @@ dependencies {
|
||||
implementation deps.lifecycle.extensions
|
||||
kapt deps.lifecycle.compiler
|
||||
implementation deps.lifecycle.viewmodel_ktx
|
||||
implementation deps.lifecycle.livedata_ktx
|
||||
|
||||
// Paging
|
||||
implementation deps.paging.runtime
|
||||
@ -47,7 +47,6 @@ dependencies {
|
||||
// Room
|
||||
implementation deps.room.runtime
|
||||
kapt deps.room.compiler
|
||||
implementation deps.room.rxjava2
|
||||
testImplementation deps.room.testing
|
||||
implementation deps.room.ktx
|
||||
|
||||
@ -82,4 +81,7 @@ dependencies {
|
||||
implementation deps.coroutines.rx2
|
||||
implementation deps.coroutines.android
|
||||
testImplementation deps.coroutines.test
|
||||
|
||||
debugImplementation deps.chucker.debug
|
||||
releaseImplementation deps.chucker.release
|
||||
}
|
||||
|
@ -1,20 +1,26 @@
|
||||
package com.android.tvmaze.di
|
||||
|
||||
import com.android.tvmaze.favorite.FavoriteShowsActivity
|
||||
import com.android.tvmaze.favorite.FavoriteShowsModule
|
||||
import com.android.tvmaze.home.HomeActivity
|
||||
import com.android.tvmaze.home.HomeModule
|
||||
import com.android.tvmaze.shows.AllShowsActivity
|
||||
import com.android.tvmaze.shows.ShowsModule
|
||||
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
|
||||
@Module
|
||||
abstract class ActivityBuildersModule {
|
||||
@ContributesAndroidInjector
|
||||
@ActivityScoped
|
||||
@ContributesAndroidInjector(modules = [HomeModule::class])
|
||||
abstract fun bindHomeActivity(): HomeActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
@ActivityScoped
|
||||
@ContributesAndroidInjector(modules = [ShowsModule::class])
|
||||
abstract fun bindAllShowsActivity(): AllShowsActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
@ActivityScoped
|
||||
@ContributesAndroidInjector(modules = [FavoriteShowsModule::class])
|
||||
abstract fun bindFavoriteShowsActivity(): FavoriteShowsActivity
|
||||
}
|
||||
|
@ -10,7 +10,13 @@ import dagger.android.support.AndroidSupportInjectionModule
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Component(modules = [AndroidSupportInjectionModule::class, AppModule::class, ActivityBuildersModule::class, NetworkModule::class, TvMazeDbModule::class])
|
||||
@Component(
|
||||
modules = [AndroidSupportInjectionModule::class,
|
||||
AppModule::class,
|
||||
ActivityBuildersModule::class,
|
||||
NetworkModule::class,
|
||||
TvMazeDbModule::class]
|
||||
)
|
||||
interface AppComponent {
|
||||
fun inject(tvMazeApplication: TvMazeApplication)
|
||||
|
||||
|
@ -6,9 +6,8 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module(includes = [ViewModelModule::class])
|
||||
@Module
|
||||
object AppModule {
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideContext(application: Application): Context {
|
||||
|
8
app/src/main/java/com/android/tvmaze/di/DaggerScopes.kt
Normal file
8
app/src/main/java/com/android/tvmaze/di/DaggerScopes.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package com.android.tvmaze.di
|
||||
|
||||
import javax.inject.Scope
|
||||
|
||||
@Scope
|
||||
@MustBeDocumented
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ActivityScoped
|
7
app/src/main/java/com/android/tvmaze/di/DaggerTypes.kt
Normal file
7
app/src/main/java/com/android/tvmaze/di/DaggerTypes.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package com.android.tvmaze.di
|
||||
|
||||
/**
|
||||
* Sugar over multibindings that helps with Kotlin wildcards.
|
||||
*/
|
||||
typealias DaggerSet<T> = @JvmSuppressWildcards Set<T>
|
||||
typealias DaggerMap<K, V> = @JvmSuppressWildcards Map<K, V>
|
@ -7,9 +7,9 @@ import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@ActivityScoped
|
||||
class TvMazeViewModelFactory @Inject
|
||||
constructor(private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) :
|
||||
constructor(private val creators: DaggerMap<Class<out ViewModel>, Provider<ViewModel>>) :
|
||||
ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -0,0 +1,11 @@
|
||||
package com.android.tvmaze.di
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
||||
@Module
|
||||
abstract class ViewModelFactoryBindingModule {
|
||||
@Binds
|
||||
abstract fun bindViewModelFactory(tvMazeViewModelFactory: TvMazeViewModelFactory): ViewModelProvider.Factory
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package com.android.tvmaze.di
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.android.tvmaze.favorite.FavoriteShowsViewModel
|
||||
import com.android.tvmaze.home.HomeViewModel
|
||||
import com.android.tvmaze.shows.ShowsViewModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
abstract class ViewModelModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(HomeViewModel::class)
|
||||
abstract fun bindHomeViewModel(homeViewModel: HomeViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(ShowsViewModel::class)
|
||||
abstract fun bindShowsViewModel(showsViewModel: ShowsViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(FavoriteShowsViewModel::class)
|
||||
abstract fun bindFavoriteShowsViewModel(favoriteShowsViewModel: FavoriteShowsViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
abstract fun bindViewModelFactory(factory: TvMazeViewModelFactory): ViewModelProvider.Factory
|
||||
}
|
@ -8,31 +8,30 @@ import android.text.Spanned
|
||||
import android.text.style.ImageSpan
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.android.tvmaze.R
|
||||
import com.android.tvmaze.base.TvMazeBaseActivity
|
||||
import com.android.tvmaze.db.favouriteshow.FavoriteShow
|
||||
import com.android.tvmaze.di.ActivityScoped
|
||||
import com.android.tvmaze.utils.GridItemDecoration
|
||||
import kotlinx.android.synthetic.main.activity_favorite_shows.*
|
||||
import kotlinx.android.synthetic.main.toolbar.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class FavoriteShowsActivity : TvMazeBaseActivity(), FavoriteShowsAdapter.Callback {
|
||||
@ActivityScoped
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
private lateinit var favoriteShowsViewModel: FavoriteShowsViewModel
|
||||
private val favoriteShowsViewModel by viewModels<FavoriteShowsViewModel> { viewModelFactory }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_favorite_shows)
|
||||
setToolbar()
|
||||
favoriteShowsViewModel = ViewModelProviders.of(this, viewModelFactory)
|
||||
.get(FavoriteShowsViewModel::class.java)
|
||||
favoriteShowsViewModel.loadFavoriteShows()
|
||||
favoriteShowsViewModel.getFavoriteShowsLiveData()
|
||||
.observe(this, Observer { showFavorites(it) })
|
||||
@ -55,7 +54,7 @@ class FavoriteShowsActivity : TvMazeBaseActivity(), FavoriteShowsAdapter.Callbac
|
||||
val favoriteShowsAdapter = FavoriteShowsAdapter(favoriteShows.toMutableList(), this)
|
||||
shows.adapter = favoriteShowsAdapter
|
||||
val spacing = resources.getDimensionPixelSize(R.dimen.show_grid_spacing)
|
||||
shows.addItemDecoration(GridItemDecoration(spacing, COLUMNS_COUNT))
|
||||
shows.addItemDecoration(GridItemDecoration(spacing, COLUMNS_COUNT))
|
||||
shows.visibility = View.VISIBLE
|
||||
} else {
|
||||
val bookmarkSpan = ImageSpan(this, R.drawable.favorite_border)
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.android.tvmaze.favorite
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.android.tvmaze.di.ViewModelFactoryBindingModule
|
||||
import com.android.tvmaze.di.ViewModelKey
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module(includes = [ViewModelFactoryBindingModule::class])
|
||||
abstract class FavoriteShowsModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(FavoriteShowsViewModel::class)
|
||||
abstract fun bindFavoriteShowsViewModel(favoriteShowsViewModel: FavoriteShowsViewModel): ViewModel
|
||||
}
|
@ -5,13 +5,14 @@ import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.android.tvmaze.R
|
||||
import com.android.tvmaze.base.TvMazeBaseActivity
|
||||
import com.android.tvmaze.di.ActivityScoped
|
||||
import com.android.tvmaze.favorite.FavoriteShowsActivity
|
||||
import com.android.tvmaze.shows.AllShowsActivity
|
||||
import com.android.tvmaze.utils.GridItemDecoration
|
||||
@ -20,15 +21,15 @@ import kotlinx.android.synthetic.main.toolbar.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeActivity : TvMazeBaseActivity(), ShowsAdapter.Callback {
|
||||
@ActivityScoped
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
private lateinit var homeViewModel: HomeViewModel
|
||||
private val homeViewModel by viewModels<HomeViewModel> { viewModelFactory }
|
||||
private lateinit var showsAdapter: ShowsAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_home)
|
||||
homeViewModel = ViewModelProviders.of(this, viewModelFactory).get(HomeViewModel::class.java)
|
||||
setToolbar()
|
||||
homeViewModel.onScreenCreated()
|
||||
homeViewModel.getHomeViewState().observe(this, Observer { setViewState(it) })
|
||||
|
16
app/src/main/java/com/android/tvmaze/home/HomeModule.kt
Normal file
16
app/src/main/java/com/android/tvmaze/home/HomeModule.kt
Normal file
@ -0,0 +1,16 @@
|
||||
package com.android.tvmaze.home
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.android.tvmaze.di.ViewModelFactoryBindingModule
|
||||
import com.android.tvmaze.di.ViewModelKey
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module(includes = [ViewModelFactoryBindingModule::class])
|
||||
abstract class HomeModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(HomeViewModel::class)
|
||||
abstract fun bindHomeViewModel(homeViewModel: HomeViewModel): ViewModel
|
||||
}
|
@ -3,13 +3,16 @@ package com.android.tvmaze.home
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.liveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.tvmaze.favorite.FavoriteShowsRepository
|
||||
import com.android.tvmaze.network.TvMazeApi
|
||||
import com.android.tvmaze.network.home.Episode
|
||||
import com.android.tvmaze.network.home.Show
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
@ -30,15 +33,17 @@ class HomeViewModel @Inject constructor(
|
||||
val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception ->
|
||||
onError(exception)
|
||||
}
|
||||
// viewModelScope launch the new coroutine on Main Dispatcher internally
|
||||
// viewModelScope launch the new coroutine on Main Dispatcher internally, so move to db and network opn on IO
|
||||
viewModelScope.launch(coroutineExceptionHandler) {
|
||||
// Get favorite shows from db, suspend function in room will launch a new coroutine with IO dispatcher
|
||||
val favoriteShowIds = favoriteShowsRepository.allFavoriteShowIds()
|
||||
// Get shows from network, suspend function in retrofit will launch a new coroutine with IO dispatcher
|
||||
val episodes = tvMazeApi.getCurrentSchedule(COUNTRY_US, currentDate)
|
||||
|
||||
// Return the result on main thread via Dispatchers.Main
|
||||
homeViewStateLiveData.value = Success(HomeViewData(getShowsWithFavorites(episodes, favoriteShowIds)))
|
||||
withContext(Dispatchers.IO) {
|
||||
val favoriteShowIds = favoriteShowsRepository.allFavoriteShowIds()
|
||||
val episodes = tvMazeApi.getCurrentSchedule(COUNTRY_US, currentDate)
|
||||
withContext(Dispatchers.Main) {
|
||||
// Return the result on main thread via Dispatchers.Main
|
||||
homeViewStateLiveData.value =
|
||||
Success(HomeViewData(getShowsWithFavorites(episodes, favoriteShowIds)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,12 @@ package com.android.tvmaze.network
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ApiInterceptor @Inject constructor() : Interceptor {
|
||||
|
||||
class ApiInterceptor : Interceptor {
|
||||
@Throws(IOException::class)
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val original = chain.request()
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.android.tvmaze.network
|
||||
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.IntoSet
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
|
||||
@Module
|
||||
abstract class InterceptorModule {
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun bindApiInterceptor(apiInterceptor: ApiInterceptor): Interceptor
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun bindHttpLoggingInterceptor(httpLoggingInterceptor: HttpLoggingInterceptor): Interceptor
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun bindChuckerInterceptor(chuckerInterceptor: ChuckerInterceptor): Interceptor
|
||||
}
|
@ -1,28 +1,38 @@
|
||||
package com.android.tvmaze.network
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Looper
|
||||
import com.android.tvmaze.BuildConfig
|
||||
import com.android.tvmaze.di.DaggerSet
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import dagger.Lazy
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import okhttp3.Cache
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import javax.inject.Named
|
||||
import javax.inject.Qualifier
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module(includes = [TvMazeApiModule::class])
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Qualifier
|
||||
private annotation class InternalApi
|
||||
|
||||
@Module(includes = [TvMazeApiModule::class, InterceptorModule::class])
|
||||
object NetworkModule {
|
||||
const val TVMAZE_BASE_URL = "tvmaze_base_url"
|
||||
private const val BASE_URL = "https://api.tvmaze.com/"
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Named(TVMAZE_BASE_URL)
|
||||
fun provideBaseUrlString(): String {
|
||||
return BASE_URL
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
|
||||
@ -33,22 +43,55 @@ object NetworkModule {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOkHttpClient(loggingInterceptor: HttpLoggingInterceptor, cache: Cache): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.addInterceptor(loggingInterceptor)
|
||||
.cache(cache)
|
||||
.build()
|
||||
fun provideChuckInterceptor(context: Context): ChuckerInterceptor {
|
||||
return ChuckerInterceptor(context)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
// Use newBuilder() to customize so that thread-pool and connection-pool same are used
|
||||
@Provides
|
||||
fun provideOkHttpClientBuilder(
|
||||
@InternalApi okHttpClient: Lazy<OkHttpClient>
|
||||
): OkHttpClient.Builder {
|
||||
return okHttpClient.get().newBuilder()
|
||||
}
|
||||
|
||||
@InternalApi
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBaseOkHttpClient(
|
||||
interceptors: DaggerSet<Interceptor>,
|
||||
cache: Cache
|
||||
): OkHttpClient {
|
||||
check(Looper.myLooper() != Looper.getMainLooper()) { "HTTP client initialized on main thread." }
|
||||
val builder = OkHttpClient.Builder()
|
||||
builder.interceptors()
|
||||
.addAll(interceptors)
|
||||
builder.cache(cache)
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideCache(context: Context): Cache {
|
||||
val cacheSize = 5 * 1024 * 1024 // 5 MB
|
||||
check(Looper.myLooper() != Looper.getMainLooper()) { "Cache initialized on main thread." }
|
||||
val cacheSize = 10 * 1024 * 1024 // 10 MB
|
||||
val cacheDir = context.cacheDir
|
||||
return Cache(cacheDir, cacheSize.toLong())
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetrofit(
|
||||
@InternalApi
|
||||
okHttpClient: Lazy<OkHttpClient>,
|
||||
@Named(TVMAZE_BASE_URL) baseUrl: String
|
||||
): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.addConverterFactory(MoshiConverterFactory.create())
|
||||
.callFactory { okHttpClient.get().newCall(it) }
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
13
app/src/main/java/com/android/tvmaze/network/RetrofitExt.kt
Normal file
13
app/src/main/java/com/android/tvmaze/network/RetrofitExt.kt
Normal file
@ -0,0 +1,13 @@
|
||||
package com.android.tvmaze.network
|
||||
|
||||
import okhttp3.Call
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import retrofit2.Retrofit
|
||||
|
||||
@PublishedApi
|
||||
internal inline fun Retrofit.Builder.callFactory(
|
||||
crossinline body: (Request) -> Call
|
||||
) = callFactory(object : Call.Factory {
|
||||
override fun newCall(request: Request): Call = body(request)
|
||||
})
|
@ -2,25 +2,16 @@ package com.android.tvmaze.network
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
object TvMazeApiModule {
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTvMazeApi(
|
||||
okHttpClient: OkHttpClient,
|
||||
@Named(NetworkModule.TVMAZE_BASE_URL) baseUrl: String
|
||||
retrofit: Retrofit
|
||||
): TvMazeApi {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.addConverterFactory(MoshiConverterFactory.create())
|
||||
.client(okHttpClient)
|
||||
.build().create(TvMazeApi::class.java)
|
||||
return retrofit.create(TvMazeApi::class.java)
|
||||
}
|
||||
}
|
||||
|
@ -6,31 +6,32 @@ import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.paging.PagedList
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.android.tvmaze.R
|
||||
import com.android.tvmaze.base.TvMazeBaseActivity
|
||||
import com.android.tvmaze.di.ActivityScoped
|
||||
import com.android.tvmaze.network.home.Show
|
||||
import kotlinx.android.synthetic.main.activity_all_shows.*
|
||||
import kotlinx.android.synthetic.main.toolbar.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class AllShowsActivity : TvMazeBaseActivity(), ShowsPagedAdaptor.Callback {
|
||||
@ActivityScoped
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
private lateinit var showsViewModel: ShowsViewModel
|
||||
private val showsViewModel by viewModels<ShowsViewModel> { viewModelFactory }
|
||||
private lateinit var showsPagedAdaptor: ShowsPagedAdaptor
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_all_shows)
|
||||
setToolbar()
|
||||
showsViewModel = ViewModelProviders.of(this, viewModelFactory).get(ShowsViewModel::class.java)
|
||||
showsViewModel.onScreenCreated()
|
||||
initAdapter()
|
||||
showsViewModel.initialLoadState().observe(this, Observer { setProgress(it) })
|
||||
|
16
app/src/main/java/com/android/tvmaze/shows/ShowsModule.kt
Normal file
16
app/src/main/java/com/android/tvmaze/shows/ShowsModule.kt
Normal file
@ -0,0 +1,16 @@
|
||||
package com.android.tvmaze.shows
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.android.tvmaze.di.ViewModelFactoryBindingModule
|
||||
import com.android.tvmaze.di.ViewModelKey
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module(includes = [ViewModelFactoryBindingModule::class])
|
||||
abstract class ShowsModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(ShowsViewModel::class)
|
||||
abstract fun bindShowsViewModel(showsViewModel: ShowsViewModel): ViewModel
|
||||
}
|
@ -6,6 +6,7 @@ import com.android.tvmaze.network.TvMazeApi
|
||||
import com.android.tvmaze.utils.LiveDataTestUtil
|
||||
import com.android.tvmaze.utils.MainCoroutineRule
|
||||
import com.android.tvmaze.utils.TestUtil
|
||||
import com.android.tvmaze.utils.runBlockingTest
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@ -32,8 +33,10 @@ class HomeViewModelTest {
|
||||
|
||||
@Mock
|
||||
private lateinit var tvMazeApi: TvMazeApi
|
||||
|
||||
@Mock
|
||||
private lateinit var favoriteShowsRepository: FavoriteShowsRepository
|
||||
|
||||
@InjectMocks
|
||||
private lateinit var homeViewModel: HomeViewModel
|
||||
|
||||
@ -44,7 +47,7 @@ class HomeViewModelTest {
|
||||
|
||||
@Test
|
||||
fun testHomeIsLoadedWithShowsWithoutFavorites() {
|
||||
mainCoroutineRule.runBlockingTest {
|
||||
mainCoroutineRule.testDispatcher.runBlockingTest {
|
||||
// Stubbing network calls with fake episode list
|
||||
whenever(tvMazeApi.getCurrentSchedule("US", TestUtil.currentDate))
|
||||
.thenReturn(TestUtil.getFakeEpisodeList())
|
||||
@ -52,30 +55,27 @@ class HomeViewModelTest {
|
||||
whenever(favoriteShowsRepository.allFavoriteShowIds())
|
||||
.thenReturn(emptyList())
|
||||
|
||||
// Pause coroutine to listen for loading initial state
|
||||
mainCoroutineRule.pauseDispatcher()
|
||||
|
||||
homeViewModel.onScreenCreated()
|
||||
// Check if status is loading
|
||||
assertThat(LiveDataTestUtil.getValue(homeViewModel.getHomeViewState())).isEqualTo(Loading)
|
||||
|
||||
// Resume coroutine dispatcher to execute pending coroutine actions
|
||||
mainCoroutineRule.resumeDispatcher()
|
||||
|
||||
// Observe on home view state live data
|
||||
val homeViewState = LiveDataTestUtil.getValue(homeViewModel.getHomeViewState())
|
||||
// Check for success data
|
||||
assertThat(homeViewState is Success).isTrue()
|
||||
val homeViewData = (homeViewState as Success).homeViewData
|
||||
assertThat(homeViewData.episodes).isNotEmpty()
|
||||
// compare the response with fake list
|
||||
assertThat(homeViewData.episodes).hasSize(TestUtil.getFakeEpisodeList().size)
|
||||
// compare the data and also order
|
||||
assertThat(homeViewData.episodes).containsExactlyElementsIn(
|
||||
TestUtil.getFakeEpisodeViewDataList(
|
||||
false
|
||||
)
|
||||
).inOrder()
|
||||
LiveDataTestUtil.getValue(homeViewModel.getHomeViewState()) {
|
||||
when (it) {
|
||||
is Loading -> assertThat(it).isNotNull()
|
||||
is Success -> {
|
||||
assertThat(it.homeViewData).isNotNull()
|
||||
val episodes = it.homeViewData.episodes
|
||||
assertThat(episodes.isNotEmpty())
|
||||
// compare the response with fake list
|
||||
assertThat(episodes).hasSize(TestUtil.getFakeEpisodeList().size)
|
||||
// compare the data and also order
|
||||
assertThat(episodes).containsExactlyElementsIn(
|
||||
TestUtil.getFakeEpisodeViewDataList(
|
||||
false
|
||||
)
|
||||
).inOrder()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,30 +89,26 @@ class HomeViewModelTest {
|
||||
whenever(favoriteShowsRepository.allFavoriteShowIds())
|
||||
.thenReturn(arrayListOf(1, 2))
|
||||
|
||||
// Pause coroutine to listen for loading initial state
|
||||
mainCoroutineRule.pauseDispatcher()
|
||||
|
||||
homeViewModel.onScreenCreated()
|
||||
// Check if status is loading
|
||||
assertThat(LiveDataTestUtil.getValue(homeViewModel.getHomeViewState())).isEqualTo(Loading)
|
||||
|
||||
// Resume coroutine dispatcher to execute pending coroutine actions
|
||||
mainCoroutineRule.resumeDispatcher()
|
||||
|
||||
// Observe on home view state live data
|
||||
val homeViewState = LiveDataTestUtil.getValue(homeViewModel.getHomeViewState())
|
||||
// Check for success data
|
||||
assertThat(homeViewState is Success).isTrue()
|
||||
val homeViewData = (homeViewState as Success).homeViewData
|
||||
assertThat(homeViewData.episodes).isNotEmpty()
|
||||
// compare the response with fake list
|
||||
assertThat(homeViewData.episodes).hasSize(TestUtil.getFakeEpisodeList().size)
|
||||
// compare the data and also order
|
||||
assertThat(homeViewData.episodes).containsExactlyElementsIn(
|
||||
TestUtil.getFakeEpisodeViewDataList(
|
||||
true
|
||||
)
|
||||
).inOrder()
|
||||
LiveDataTestUtil.getValue(homeViewModel.getHomeViewState()) {
|
||||
when (it) {
|
||||
is Loading -> assertThat(it).isNotNull()
|
||||
is Success -> {
|
||||
assertThat(it.homeViewData).isNotNull()
|
||||
val episodes = it.homeViewData.episodes
|
||||
assertThat(episodes.isNotEmpty())
|
||||
// compare the response with fake list
|
||||
assertThat(episodes).hasSize(TestUtil.getFakeEpisodeList().size)
|
||||
// compare the data and also order
|
||||
assertThat(episodes).containsExactlyElementsIn(
|
||||
TestUtil.getFakeEpisodeViewDataList(
|
||||
true
|
||||
)
|
||||
).inOrder()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,20 +122,13 @@ class HomeViewModelTest {
|
||||
whenever(favoriteShowsRepository.allFavoriteShowIds())
|
||||
.thenReturn(arrayListOf(1, 2))
|
||||
|
||||
// Pause coroutine to listen for loading initial state
|
||||
mainCoroutineRule.pauseDispatcher()
|
||||
|
||||
homeViewModel.onScreenCreated()
|
||||
// Check if status is loading
|
||||
assertThat(LiveDataTestUtil.getValue(homeViewModel.getHomeViewState())).isEqualTo(Loading)
|
||||
|
||||
// Resume coroutine dispatcher to execute pending coroutine actions
|
||||
mainCoroutineRule.resumeDispatcher()
|
||||
|
||||
// Observe on home view state live data
|
||||
val homeViewState = LiveDataTestUtil.getValue(homeViewModel.getHomeViewState())
|
||||
// Check for success data
|
||||
assertThat(homeViewState is NetworkError).isTrue()
|
||||
LiveDataTestUtil.getValue(homeViewModel.getHomeViewState()) {
|
||||
when (it) {
|
||||
is Loading -> assertThat(it).isNotNull()
|
||||
is NetworkError -> assertThat(it.message).isNotEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -30,4 +30,14 @@ object LiveDataTestUtil {
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
fun <T> getValue(
|
||||
liveData: LiveData<T>,
|
||||
callback: (T) -> Unit = {}
|
||||
) {
|
||||
val observer = Observer<T> { o ->
|
||||
callback.invoke(o)
|
||||
}
|
||||
liveData.observeForever(observer)
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +1,39 @@
|
||||
package com.android.tvmaze.utils
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
import kotlinx.coroutines.test.TestCoroutineScope
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.rules.TestWatcher
|
||||
import org.junit.runner.Description
|
||||
import kotlin.coroutines.ContinuationInterceptor
|
||||
|
||||
/**
|
||||
* Sets the main coroutines dispatcher to a [TestCoroutineScope] for unit testing. A
|
||||
* [TestCoroutineScope] provides control over the execution of coroutines.
|
||||
*
|
||||
* Declare it as a JUnit Rule:
|
||||
*
|
||||
* ```
|
||||
* @get:Rule
|
||||
* var mainCoroutineRule = MainCoroutineRule()
|
||||
* ```
|
||||
*
|
||||
* Use it directly as a [TestCoroutineScope]:
|
||||
*
|
||||
* ```
|
||||
* mainCoroutineRule.pauseDispatcher()
|
||||
* ...
|
||||
* mainCoroutineRule.resumeDispatcher()
|
||||
* ...
|
||||
* mainCoroutineRule.runBlockingTest { }
|
||||
* ...
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
@ExperimentalCoroutinesApi
|
||||
class MainCoroutineRule : TestWatcher(), TestCoroutineScope by TestCoroutineScope() {
|
||||
class MainCoroutineRule(
|
||||
val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
|
||||
) : TestWatcher() {
|
||||
|
||||
override fun starting(description: Description?) {
|
||||
super.starting(description)
|
||||
Dispatchers.setMain(this.coroutineContext[ContinuationInterceptor] as CoroutineDispatcher)
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
}
|
||||
|
||||
override fun finished(description: Description?) {
|
||||
super.finished(description)
|
||||
Dispatchers.resetMain()
|
||||
testDispatcher.cleanupTestCoroutines()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
fun MainCoroutineRule.runBlockingTest(block: suspend () -> Unit) =
|
||||
this.testDispatcher.runBlockingTest {
|
||||
block()
|
||||
}
|
@ -4,45 +4,47 @@ ext.versions = [:]
|
||||
def versions = [:]
|
||||
//sdk versions
|
||||
versions.compile_sdk = 28
|
||||
versions.min_sdk = 16
|
||||
versions.min_sdk = 21
|
||||
versions.target_sdk = 26
|
||||
versions.app_version_code = 103
|
||||
versions.app_version_name = '2.0.1'
|
||||
|
||||
// android and google essentials
|
||||
versions.android_plugin = '3.5.3'
|
||||
versions.android_plugin = '4.0.0'
|
||||
versions.constraint_layout = '2.0.0-beta4'
|
||||
versions.google_services = '3.0.0'
|
||||
versions.lifecycle = '2.1.0'
|
||||
versions.google_services = '4.3.3'
|
||||
versions.lifecycle = '2.2.0'
|
||||
versions.support_lib = '27.1.0'
|
||||
versions.support_x = '1.0.0'
|
||||
versions.android_test = '1.2.0'
|
||||
versions.android_test_junit = '1.1.1'
|
||||
versions.multidex = '2.0.0'
|
||||
versions.multidex = '2.0.1'
|
||||
versions.espresso = '3.2.0'
|
||||
versions.dagger = '2.24'
|
||||
versions.dagger = '2.28'
|
||||
versions.glide = '4.9.0'
|
||||
versions.junit = '4.12'
|
||||
versions.mockito = '3.2.4'
|
||||
versions.okhttp = '4.3.1'
|
||||
versions.retrofit = '2.6.0'
|
||||
versions.rxjava = '2.2.10'
|
||||
versions.rxandroid = '2.1.1'
|
||||
versions.rxbinding = '2.0.0'
|
||||
versions.okhttp = '4.7.2'
|
||||
versions.retrofit = '2.9.0'
|
||||
versions.findbugs = '3.0.1'
|
||||
versions.paging = '2.1.0'
|
||||
versions.room = '2.1.0'
|
||||
versions.kotlin = '1.3.61'
|
||||
versions.room = '2.2.5'
|
||||
versions.kotlin = '1.3.72'
|
||||
versions.timber = '4.7.1'
|
||||
versions.mockito_kotlin = '2.2.0'
|
||||
versions.arch_core_testing = '2.0.0'
|
||||
versions.robolectric = '4.3.1'
|
||||
versions.moshi = '1.9.2'
|
||||
versions.coroutines = '1.3.3'
|
||||
versions.coroutines_test = '1.3.3'
|
||||
versions.truth = '1.0'
|
||||
versions.coroutines = '1.3.7'
|
||||
versions.coroutines_test = '1.3.7'
|
||||
versions.truth = '1.0.1'
|
||||
versions.annotation = '1.1.0'
|
||||
versions.appcompat = '1.1.0'
|
||||
versions.chucker = '3.2.0'
|
||||
versions.ktx_core = '1.2.0'
|
||||
versions.ktx_collections = '1.1.0'
|
||||
versions.ktx_fragment = '1.2.4'
|
||||
versions.ktx_activity = '1.1.0'
|
||||
ext.versions = versions
|
||||
|
||||
def deps = [:]
|
||||
@ -54,19 +56,24 @@ deps.constraint_layout = "androidx.constraintlayout:constraintlayout:$versions.c
|
||||
deps.multidex = "androidx.multidex:multidex:$versions.multidex"
|
||||
deps.google_services = "com.google.gms:google-services:$versions.google_services"
|
||||
|
||||
def support_lib = [:]
|
||||
support_lib.design = "com.google.android.material:material:$versions.support_x"
|
||||
support_lib.appcompat = "androidx.appcompat:appcompat:$versions.appcompat"
|
||||
support_lib.recyclerview = "androidx.recyclerview:recyclerview:$versions.support_x"
|
||||
support_lib.cardview = "androidx.cardview:cardview:$versions.support_x"
|
||||
support_lib.support_annotations = "androidx.annotation:annotation:$versions.annotation"
|
||||
deps.support_lib = support_lib
|
||||
def androidx_lib = [:]
|
||||
androidx_lib.design = "com.google.android.material:material:$versions.support_x"
|
||||
androidx_lib.appcompat = "androidx.appcompat:appcompat:$versions.appcompat"
|
||||
androidx_lib.recyclerview = "androidx.recyclerview:recyclerview:$versions.support_x"
|
||||
androidx_lib.cardview = "androidx.cardview:cardview:$versions.support_x"
|
||||
androidx_lib.support_annotations = "androidx.annotation:annotation:$versions.annotation"
|
||||
androidx_lib.ktx_core = "androidx.core:core-ktx:$versions.ktx_core"
|
||||
androidx_lib.ktx_collections = "androidx.collection:collection-ktx:$versions.ktx_collections"
|
||||
androidx_lib.ktx_fragment = "androidx.fragment:fragment-ktx:$versions.ktx_fragment"
|
||||
androidx_lib.ktx_activity = "androidx.activity:activity-ktx:$versions.ktx_activity"
|
||||
deps.androidx_lib = androidx_lib
|
||||
|
||||
|
||||
def lifecycle = [:]
|
||||
lifecycle.extensions = "androidx.lifecycle:lifecycle-extensions:$versions.lifecycle"
|
||||
lifecycle.compiler = "androidx.lifecycle:lifecycle-common-java8:$versions.lifecycle"
|
||||
lifecycle.viewmodel_ktx = "androidx.lifecycle:lifecycle-viewmodel-ktx:$versions.lifecycle"
|
||||
lifecycle.livedata_ktx = "androidx.lifecycle:lifecycle-livedata-ktx:$versions.lifecycle"
|
||||
deps.lifecycle = lifecycle
|
||||
|
||||
def espresso = [:]
|
||||
@ -104,14 +111,6 @@ retrofit.main = "com.squareup.retrofit2:retrofit:$versions.retrofit"
|
||||
retrofit.moshi = "com.squareup.retrofit2:converter-moshi:$versions.retrofit"
|
||||
deps.retrofit = retrofit
|
||||
|
||||
def rxjava = [:]
|
||||
rxjava.main = "io.reactivex.rxjava2:rxjava:$versions.rxjava"
|
||||
rxjava.android = "io.reactivex.rxjava2:rxandroid:$versions.rxandroid"
|
||||
deps.rxjava = rxjava
|
||||
|
||||
def rxbinding = [:]
|
||||
rxbinding.platform = "com.jakewharton.rxbinding2:rxbinding:$versions.rxbinding"
|
||||
deps.rxbinding = rxbinding
|
||||
|
||||
def android_test = [:]
|
||||
android_test.rules = "androidx.test:rules:$versions.android_test"
|
||||
@ -134,7 +133,6 @@ deps.paging = paging
|
||||
def room = [:]
|
||||
room.runtime = "androidx.room:room-runtime:$versions.room"
|
||||
room.compiler = "androidx.room:room-compiler:$versions.room"
|
||||
room.rxjava2 = "androidx.room:room-rxjava2:$versions.room"
|
||||
room.testing = "androidx.room:room-testing:$versions.room"
|
||||
room.ktx = "androidx.room:room-ktx:$versions.room"
|
||||
deps.room = room
|
||||
@ -166,4 +164,9 @@ coroutines.test = "org.jetbrains.kotlinx:kotlinx-coroutines-test:$versions.corou
|
||||
deps.coroutines = coroutines
|
||||
|
||||
deps.truth = "com.google.truth:truth:$versions.truth"
|
||||
|
||||
def chucker = [:]
|
||||
chucker.debug = "com.github.chuckerteam.chucker:library:$versions.chucker"
|
||||
chucker.release = "com.github.chuckerteam.chucker:library-no-op:$versions.chucker"
|
||||
deps.chucker = chucker
|
||||
ext.deps = deps
|
@ -14,8 +14,6 @@ org.gradle.parallel=true
|
||||
# Google Play and Firebase dependencies are migrating to androidX. This is to fix the duplicate class resolution build error to use the androidX libraries
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
# Enable Robolectric to use the same resources as Android
|
||||
android.enableUnitTestBinaryResources=true
|
||||
# All kapt to use workers
|
||||
kapt.use.worker.api=true
|
||||
kapt.incremental.apt=true
|
||||
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Tue Aug 20 23:28:57 IST 2019
|
||||
#Sun Jun 14 15:55:13 IST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
|
@ -1,18 +0,0 @@
|
||||
apply plugin: 'findbugs'
|
||||
|
||||
task findbugs(type: FindBugs) {
|
||||
description 'Find bugs mainly design flaws, bad practices, multithreaded correctness and code vulnerabilities.'
|
||||
group 'verification'
|
||||
excludeFilter = file("$project.rootDir/tools/rules-findbugs.xml")
|
||||
classes = fileTree("$project.buildDir/intermediates/classes/dev/debug/com/android")
|
||||
source = fileTree('src/main/java')
|
||||
effort 'max'
|
||||
reportLevel = "high"
|
||||
classpath = files()
|
||||
|
||||
reports {
|
||||
xml.enabled = false
|
||||
html.enabled = true
|
||||
html.destination file("$project.buildDir/outputs/findbugs/findbugs.html")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user