diff --git a/app/build.gradle b/app/build.gradle index af2c1a9..d7ac897 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -90,11 +90,11 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // Google Support Libraries - compile 'com.android.support:support-v4:25.1.0' - compile 'com.android.support:support-fragment:25.1.0' - compile 'com.android.support:appcompat-v7:25.1.0' - compile 'com.android.support:design:25.1.0' - compile 'com.android.support:recyclerview-v7:25.1.0' + compile 'com.android.support:support-v4:25.2.0' + compile 'com.android.support:support-fragment:25.2.0' + compile 'com.android.support:appcompat-v7:25.2.0' + compile 'com.android.support:design:25.2.0' + compile 'com.android.support:recyclerview-v7:25.2.0' // RxJava compile 'io.reactivex.rxjava2:rxjava:2.0.0' @@ -118,6 +118,10 @@ dependencies { // Databinding kapt "com.android.databinding:compiler:$android_plugin_version" + // ActivityStarter + compile 'com.github.marcinmoskala.activitystarter:activitystarter:0.30' + kapt 'com.github.marcinmoskala.activitystarter:activitystarter-compiler:0.30' + // Unit Testing testCompile 'junit:junit:4.12' testCompile "org.mockito:mockito-core:1.10.19" diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ApplicationComponent.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ApplicationComponent.kt index c5e79e8..405fbc1 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ApplicationComponent.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ApplicationComponent.kt @@ -23,5 +23,6 @@ interface ApplicationComponent { // Submodule methods // Every screen is its own submodule of the graph and must be added here. fun plus(module: ListModule): ListComponent + fun plus(module: DetailModule): DetailComponent } \ No newline at end of file diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/network/NetworkInteractorImpl.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/network/NetworkInteractorImpl.kt index 5395385..b4cc387 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/network/NetworkInteractorImpl.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/network/NetworkInteractorImpl.kt @@ -13,11 +13,9 @@ class NetworkInteractorImpl @Inject constructor( override fun hasNetworkConnection(): Boolean = connectivityManager.activeNetworkInfo?.isConnectedOrConnecting ?: false - override fun hasNetworkConnectionCompletable(): Completable = - if (hasNetworkConnection()) { - Completable.complete() - } else { - Completable.error { NetworkInteractor.NetworkUnavailableException() } - } + override fun hasNetworkConnectionCompletable(): Completable = when { + hasNetworkConnection() -> Completable.complete() + else -> Completable.error { NetworkInteractor.NetworkUnavailableException() } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/remote/ApiModule.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/remote/ApiModule.kt index 0b70530..4b3f498 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/remote/ApiModule.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/remote/ApiModule.kt @@ -11,30 +11,22 @@ import javax.inject.Singleton class ApiModule { @Provides @Singleton - fun provideApiService(retrofit: Retrofit): GithubApiService { - return retrofit.create(GithubApiService::class.java) - } + fun provideApiService(retrofit: Retrofit) = retrofit.create(GithubApiService::class.java)!! @Provides @Singleton fun provideRetrofit( rxJavaCallAdapterFactory: RxJava2CallAdapterFactory, gsonConverterFactory: GsonConverterFactory - ): Retrofit { - return Retrofit.Builder() - .baseUrl(ApiConstants.GITHUB_API_BASE_ENDPOINT) - .addCallAdapterFactory(rxJavaCallAdapterFactory) - .addConverterFactory(gsonConverterFactory) - .build() - } + ) = Retrofit.Builder() + .baseUrl(ApiConstants.GITHUB_API_BASE_ENDPOINT) + .addCallAdapterFactory(rxJavaCallAdapterFactory) + .addConverterFactory(gsonConverterFactory) + .build()!! @Provides @Singleton - fun provideGsonConverterFactory(): GsonConverterFactory { - return GsonConverterFactory.create() - } + fun provideGsonConverterFactory() = GsonConverterFactory.create()!! @Provides @Singleton - fun provideRxJavaCallAdapter(): RxJava2CallAdapterFactory { - return RxJava2CallAdapterFactory.create() - } + fun provideRxJavaCallAdapter() = RxJava2CallAdapterFactory.create()!! } \ No newline at end of file diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/remote/model/Parcels.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/remote/model/Parcels.kt index 1d7a5f9..ee809ad 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/remote/model/Parcels.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/data/remote/model/Parcels.kt @@ -12,7 +12,7 @@ interface DefaultParcelable : Parcelable { override fun describeContents(): Int = 0 companion object { - fun generateCreator(create: (source: Parcel) -> T): Parcelable.Creator = object: Parcelable.Creator { + fun generateCreator(create: (source: Parcel) -> T): Parcelable.Creator = object : Parcelable.Creator { override fun createFromParcel(source: Parcel): T = create(source) override fun newArray(size: Int): Array? = newArray(size) @@ -20,6 +20,7 @@ interface DefaultParcelable : Parcelable { } } + inline fun Parcel.read(): T = readValue(T::class.javaClass.classLoader) as T fun Parcel.write(vararg values: Any?) = values.forEach { writeValue(it) } diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/extensions/ViewExtensions.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/extensions/ViewExtensions.kt index e592095..6bdb699 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/extensions/ViewExtensions.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/extensions/ViewExtensions.kt @@ -4,6 +4,7 @@ import android.content.Context import android.support.annotation.ColorInt import android.support.annotation.StringRes import android.support.design.widget.Snackbar +import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -13,41 +14,50 @@ import com.squareup.picasso.Picasso var View.isVisible: Boolean get() = visibility == View.VISIBLE set(value) { - visibility = if(value) View.VISIBLE else View.GONE + visibility = if (value) View.VISIBLE else View.GONE } -fun Context.inflateLayout(layoutResId: Int): View { - return inflateView(this, layoutResId, null, false) -} - -fun Context.inflateLayout(layoutResId: Int, parent: ViewGroup): View { - return inflateLayout(layoutResId, parent, true) -} - -fun Context.inflateLayout(layoutResId: Int, parent: ViewGroup, attachToRoot: Boolean): View { - return inflateView(this, layoutResId, parent, attachToRoot) -} - -private fun inflateView(context: Context, layoutResId: Int, parent: ViewGroup?, attachToRoot: Boolean): View { - return LayoutInflater.from(context).inflate(layoutResId, parent, attachToRoot) -} +fun Context.inflateLayout( + layoutResId: Int, + parent: ViewGroup? = null, + attachToRoot: Boolean = parent != null +): View = + LayoutInflater.from(this).inflate(layoutResId, parent, attachToRoot) fun ImageView.loadImage(url: String) { Picasso.with(context).load(url).into(this) } -fun View.showSnackbar(message: String, length: Int = Snackbar.LENGTH_LONG, f: (Snackbar.() -> Unit) = {}) { - val snack = Snackbar.make(this, message, length) - snack.f() - snack.show() +fun View.showSnackbar( + message: String, + length: Int = Snackbar.LENGTH_LONG, + f: (Snackbar.() -> Unit) = {} +) { + Snackbar.make(this, message, length).apply { + f() + show() + } } -fun View.showSnackbar(@StringRes message: Int, length: Int = Snackbar.LENGTH_LONG, f: (Snackbar.() -> Unit) = {}) { +fun View.showSnackbar( + @StringRes message: Int, + length: Int = Snackbar.LENGTH_LONG, + f: (Snackbar.() -> Unit) = {} +) { showSnackbar(resources.getString(message), length, f) } -fun Snackbar.action(action: String, @ColorInt color: Int? = null, listener: (View) -> Unit) { +fun Snackbar.action( + action: String, + @ColorInt color: Int? = null, + listener: (View) -> Unit +) { setAction(action, listener) color?.let { setActionTextColor(color) } } +val RecyclerView.childList: List + get() = (0..childCount - 1).map { getChildAt(it) } + +val View.recyclerParams: RecyclerView.LayoutParams + get() = layoutParams as RecyclerView.LayoutParams diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/AbstractViewModel.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/AbstractViewModel.kt index d9b0171..b247613 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/AbstractViewModel.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/AbstractViewModel.kt @@ -2,7 +2,7 @@ package io.github.plastix.kotlinboilerplate.ui.base import android.databinding.BaseObservable -abstract class AbstractViewModel : BaseObservable(), ViewModel { +abstract class AbstractViewModel : BaseObservable(), ViewModel { override fun bind() { } diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/BaseActivity.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/BaseActivity.kt index af87865..25f2c6e 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/BaseActivity.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/BaseActivity.kt @@ -1,16 +1,18 @@ package io.github.plastix.kotlinboilerplate.ui.base +import activitystarter.ActivityStarter import android.os.Bundle import android.support.annotation.CallSuper import android.support.v7.app.AppCompatActivity import io.github.plastix.kotlinboilerplate.ApplicationComponent import io.github.plastix.kotlinboilerplate.KotlinBoilerplateApp -abstract class BaseActivity: AppCompatActivity() { +abstract class BaseActivity : AppCompatActivity() { @CallSuper - override fun onCreate(savedInstanceState: Bundle?){ + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + ActivityStarter.fill(this) injectDependencies(KotlinBoilerplateApp.graph) } diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/ViewModel.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/ViewModel.kt index bbdebc4..089fae2 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/ViewModel.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/ViewModel.kt @@ -1,6 +1,7 @@ package io.github.plastix.kotlinboilerplate.ui.base -interface ViewModel { +interface +ViewModel { fun bind() diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/ViewModelLoader.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/ViewModelLoader.kt index 54d466f..975db3c 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/ViewModelLoader.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/base/ViewModelLoader.kt @@ -31,7 +31,6 @@ class ViewModelLoader @Inject constructor( override fun onReset() { super.onReset() - viewModel?.onDestroy() } } \ No newline at end of file diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/detail/DetailActivity.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/detail/DetailActivity.kt index 930bb6c..db4bfe6 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/detail/DetailActivity.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/detail/DetailActivity.kt @@ -1,7 +1,6 @@ package io.github.plastix.kotlinboilerplate.ui.detail -import android.content.Context -import android.content.Intent +import activitystarter.Arg import android.databinding.DataBindingUtil import android.os.Bundle import io.github.plastix.kotlinboilerplate.ApplicationComponent @@ -13,17 +12,7 @@ import io.github.plastix.kotlinboilerplate.ui.base.ViewModelActivity open class DetailActivity : ViewModelActivity() { - companion object { - val EXTRA_REPO_OBJECT = "REPO_ITEM" - - fun newIntent(context: Context, repo: Repo): Intent { - val intent = Intent(context, DetailActivity::class.java) - intent.putExtra(EXTRA_REPO_OBJECT, repo) - return intent - } - } - - private val repo by lazy { intent.getParcelableExtra(EXTRA_REPO_OBJECT) } + @Arg lateinit var repo: Repo override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -36,9 +25,8 @@ open class DetailActivity : ViewModelActivity() { @Inject - lateinit var adapter: RepoAdapter + lateinit var listAdapter: RepoAdapter @Inject - lateinit var layoutManager: LinearLayoutManager + lateinit var listLayoutManager: LinearLayoutManager @Inject lateinit var dividerDecorator: SimpleDividerItemDecoration @@ -43,27 +43,10 @@ class ListActivity : ViewModelActivity() { setupRecyclerView() setupSwipeRefresh() updateEmptyView() - - disposables.add(viewModel.getRepos().subscribe { - updateList(it) - }) - - disposables.add(viewModel.loadingState().subscribe { - binding.listSwipeRefresh.isRefreshing = it - }) - - disposables.add(viewModel.fetchErrors().subscribe { - errorFetchRepos() - }) - - disposables.add(viewModel.networkErrors().subscribe { - errorNoNetwork() - }) + addDisposables() } - override fun getViewBinding(): ActivityListBinding { - return DataBindingUtil.setContentView(this, R.layout.activity_list) - } + override fun getViewBinding(): ActivityListBinding = DataBindingUtil.setContentView(this, R.layout.activity_list) private fun setupSwipeRefresh() { binding.listSwipeRefresh.setOnRefreshListener { @@ -72,13 +55,12 @@ class ListActivity : ViewModelActivity() { } private fun setupRecyclerView() { - binding.listRecyclerView.adapter = adapter - binding.listRecyclerView.layoutManager = layoutManager - binding.listRecyclerView.addItemDecoration(dividerDecorator) - - adapter.setClickListener { - onItemClick(it) + binding.listRecyclerView.apply { + adapter = listAdapter + layoutManager = listLayoutManager + addItemDecoration(dividerDecorator) } + listAdapter.setClickListener(this::onItemClick) } override fun injectDependencies(graph: ApplicationComponent) { @@ -93,24 +75,33 @@ class ListActivity : ViewModelActivity() { } private fun onItemClick(repo: Repo) { - startActivity(DetailActivity.newIntent(this, repo)) + DetailActivityStarter.start(this, repo) } private fun updateList(repos: List) { - adapter.updateRepos(repos) + listAdapter.updateRepos(repos) updateEmptyView() } private fun updateEmptyView() { - val thereIsNoItems = adapter.itemCount == 0 + val thereIsNoItems = listAdapter.itemCount == 0 binding.emptyView.root.isVisible = thereIsNoItems } - private fun errorNoNetwork() { + private fun addDisposables() { + disposables.apply { + add(viewModel.getRepos().subscribe(this@ListActivity::updateList)) + add(viewModel.loadingState().subscribe(binding.listSwipeRefresh::setRefreshing)) + add(viewModel.fetchErrors().subscribe(this@ListActivity::errorFetchRepos)) + add(viewModel.networkErrors().subscribe(this@ListActivity::errorNoNetwork)) + } + } + + private fun errorNoNetwork(throwable: Throwable) { binding.listCoordinatorLayout.showSnackbar(R.string.list_error_no_network) } - private fun errorFetchRepos() { + private fun errorFetchRepos(throwable: Throwable) { binding.listCoordinatorLayout.showSnackbar(R.string.list_error_failed_fetch) } @@ -119,29 +110,24 @@ class ListActivity : ViewModelActivity() { disposables.clear() } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - return when (item.itemId) { - R.id.action_settings -> { - Timber.d("Settings menu clicked!") - true - } - - R.id.action_night -> { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - recreate() - true - } - - R.id.action_day -> { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) - recreate() - true - } - - else -> super.onOptionsItemSelected(item) + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + R.id.action_settings -> { + Timber.d("Settings menu clicked!") + true + } + R.id.action_night -> { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + recreate() + true + } + R.id.action_day -> { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + recreate() + true } + else -> super.onOptionsItemSelected(item) } } diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/ListViewModel.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/ListViewModel.kt index c98da82..10cfd19 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/ListViewModel.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/ListViewModel.kt @@ -8,6 +8,7 @@ import io.github.plastix.kotlinboilerplate.data.remote.model.SearchResponse import io.github.plastix.kotlinboilerplate.ui.base.RxViewModel import io.github.plastix.rxdelay.RxDelay import io.reactivex.Observable +import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposables @@ -30,37 +31,26 @@ class ListViewModel @Inject constructor( private val networkErrors: PublishSubject = PublishSubject.create() fun fetchRepos() { - networkRequest = networkInteractor.hasNetworkConnectionCompletable() - .andThen(apiService.repoSearch(ApiConstants.SEARCH_QUERY_KOTLIN, - ApiConstants.SEARCH_SORT_STARS, - ApiConstants.SEARCH_ORDER_DESCENDING)) - .subscribeOn(Schedulers.io()) - .compose(RxDelay.delaySingle(getViewState())) - .observeOn(AndroidSchedulers.mainThread()) - .doOnSubscribe { - networkRequest.dispose() // Cancel any current running request - loadingState.onNext(true) - } - .doOnEvent { searchResponse, throwable -> - loadingState.onNext(false) - } - .subscribeWith(object : DisposableSingleObserver() { - override fun onError(e: Throwable) { - System.out.println(e.toString()) - when (e) { - is NetworkInteractor.NetworkUnavailableException -> networkErrors.onNext(e) - else -> fetchErrors.onNext(e) - } - } - - override fun onSuccess(value: SearchResponse) { - repos.onNext(value.repos) - } - }) + networkRequest = networkInteractor + .hasNetworkConnectionCompletable() + .andThen(getRepoSearch()) + .applySchedulers(getViewState()) + .addOnResultEvents() + .subscribe(this::onRequestSuccess, this::onRequestError) addDisposable(networkRequest) } + private fun getRepoSearch() = apiService.repoSearch( + ApiConstants.SEARCH_QUERY_KOTLIN, + ApiConstants.SEARCH_SORT_STARS, + ApiConstants.SEARCH_ORDER_DESCENDING + ) + + fun onRequestSuccess(value: SearchResponse) { + repos.onNext(value.repos) + } + fun getRepos(): Observable> = repos.hide() fun fetchErrors(): Observable = fetchErrors.hide() @@ -69,4 +59,26 @@ class ListViewModel @Inject constructor( fun loadingState(): Observable = loadingState.hide() + private fun Single.applySchedulers(viewState: Observable) = this + .subscribeOn(Schedulers.io()) + .compose(RxDelay.delaySingle(viewState)) + .observeOn(AndroidSchedulers.mainThread()) + + private fun Single.addOnResultEvents() = this + .doOnSubscribe { + networkRequest.dispose() // Cancel any current running request + loadingState.onNext(true) + } + .doOnEvent { _, _ -> + loadingState.onNext(false) + } + + private fun onRequestError(e: Throwable) { + System.out.println(e.toString()) + when (e) { + is NetworkInteractor.NetworkUnavailableException -> networkErrors.onNext(e) + else -> fetchErrors.onNext(e) + } + } + } \ No newline at end of file diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/RepoAdapter.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/RepoAdapter.kt index f9633e4..3cc7fd4 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/RepoAdapter.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/RepoAdapter.kt @@ -60,7 +60,7 @@ class RepoAdapter @Inject constructor() : RecyclerView.Adapter Unit)?){ + fun setClickListener(callback: ((Repo) -> Unit)?) { binding.viewModel.clicks().subscribe { callback?.invoke(binding.viewModel.repo) } diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/RepoDiffCallback.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/RepoDiffCallback.kt index a364e5f..66656e0 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/RepoDiffCallback.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/list/RepoDiffCallback.kt @@ -5,19 +5,13 @@ import io.github.plastix.kotlinboilerplate.data.remote.model.Repo class RepoDiffCallback(private val old: List, private val new: List) : DiffUtil.Callback() { - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - return old[oldItemPosition].fullName == new[newItemPosition].fullName - } + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = + old[oldItemPosition].fullName == new[newItemPosition].fullName - override fun getOldListSize(): Int { - return old.size - } + override fun getOldListSize() = old.size - override fun getNewListSize(): Int { - return new.size - } + override fun getNewListSize() = new.size - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - return old[oldItemPosition] == new[newItemPosition] - } + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = + old[oldItemPosition] == new[newItemPosition] } \ No newline at end of file diff --git a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/misc/SimpleDividerItemDecoration.kt b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/misc/SimpleDividerItemDecoration.kt index 371b4dc..5a40ada 100644 --- a/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/misc/SimpleDividerItemDecoration.kt +++ b/app/src/main/kotlin/io/github/plastix/kotlinboilerplate/ui/misc/SimpleDividerItemDecoration.kt @@ -7,6 +7,8 @@ import android.support.v4.content.ContextCompat import android.support.v7.widget.RecyclerView import io.github.plastix.kotlinboilerplate.ApplicationQualifier import io.github.plastix.kotlinboilerplate.R +import io.github.plastix.kotlinboilerplate.extensions.childList +import io.github.plastix.kotlinboilerplate.extensions.recyclerParams import javax.inject.Inject /** @@ -24,17 +26,16 @@ class SimpleDividerItemDecoration @Inject constructor( val left = parent.paddingLeft val right = parent.width - parent.paddingRight - val childCount = parent.childCount - for (i in 0..childCount - 1) { - val child = parent.getChildAt(i) - - val params = child.layoutParams as RecyclerView.LayoutParams - - val top = child.bottom + params.bottomMargin + parent.childList.forEach { child -> + val top = child.bottom + child.recyclerParams.bottomMargin val bottom = top + divider.intrinsicHeight - divider.setBounds(left, top, right, bottom) - divider.draw(c) + drawDivider(c, left, top, right, bottom) } } + + private fun drawDivider(c: Canvas, left: Int, top: Int, right: Int, bottom: Int) { + divider.setBounds(left, top, right, bottom) + divider.draw(c) + } } \ No newline at end of file diff --git a/app/src/unitTests/kotlin/io/github/plastix/kotlinboilerplate/ui/detail/DetailViewModelTest.kt b/app/src/unitTests/kotlin/io/github/plastix/kotlinboilerplate/ui/detail/DetailViewModelTest.kt index a6771b4..4b32ee3 100644 --- a/app/src/unitTests/kotlin/io/github/plastix/kotlinboilerplate/ui/detail/DetailViewModelTest.kt +++ b/app/src/unitTests/kotlin/io/github/plastix/kotlinboilerplate/ui/detail/DetailViewModelTest.kt @@ -14,15 +14,19 @@ class DetailViewModelTest { @Before fun setUp() { - owner = Owner("Author", - "someURL") + owner = Owner( + "Author", + "someURL" + ) - repo = Repo("Name", + repo = Repo( + "Name", "Author/Name", owner, "Some random repo", 50, - 100) + 100 + ) viewModel = DetailViewModel(repo) viewModel.bind() diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 971b1e8..6cbd0e3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Sep 22 18:24:14 EDT 2016 +#Sat Mar 18 12:40:59 CET 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip