Skip to content

Instantly share code, notes, and snippets.

@bitsydarel
Created November 3, 2017 15:22
Show Gist options
  • Save bitsydarel/52188f08139c1b163ed7b3832a9ac311 to your computer and use it in GitHub Desktop.
Save bitsydarel/52188f08139c1b163ed7b3832a9ac311 to your computer and use it in GitHub Desktop.
MVP RecyclerView
/*
* Copyright (C) 2017 Darel Bitsy
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.dbeginc.dbweather.config.managesources.adapter.presenter
import com.dbeginc.dbweather.config.managesources.adapter.SourceContract
import com.dbeginc.dbweather.viewmodels.news.SourceModel
import com.dbeginc.dbweather.viewmodels.news.toDomain
import com.dbeginc.dbweatherdomain.entities.requests.news.SourceRequest
import com.dbeginc.dbweatherdomain.usecases.news.SubscribeToSource
import com.dbeginc.dbweatherdomain.usecases.news.UnSubscribeToSource
/**
* Created by darel on 27.10.17.
*
* Source Presenter Implementation
*/
class SourcePresenterImpl(private val source: SourceModel,
private val subscribeToSource: SubscribeToSource,
private val unSubscribeToSource: UnSubscribeToSource) : SourceContract.SourcePresenter {
private lateinit var view: SourceContract.SourceView
override fun bind(view: SourceContract.SourceView) {
this.view = view
this.view.setupView()
}
override fun unBind() {
subscribeToSource.clean()
unSubscribeToSource.clean()
}
override fun loadSource() {
view.displaySource(source)
}
override fun onAction() {
view.goToSourceDetail()
}
override fun onSubscribe() {
if (source.subscribed) {
unSubscribeToSource.execute(SourceRequest(source.id, source.apply { subscribed = false }.toDomain()))
.doOnSubscribe { view.showUpdateStatus() }
.doOnTerminate { view.hideUpdateStatus() }
.subscribe(
{ view.showUnSubscribed() },
{ error -> view.showError(error.localizedMessage) }
)
} else {
subscribeToSource.execute(SourceRequest(source.id, source.apply { subscribed = true }.toDomain()))
.doOnSubscribe { view.showUpdateStatus() }
.doOnTerminate { view.hideUpdateStatus() }
.subscribe(
{ view.showSubscribed() },
{ error -> view.showError(error.localizedMessage) }
)
}
}
override fun getData(): SourceModel = source
}
/*
* Copyright (C) 2017 Darel Bitsy
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.dbeginc.dbweather.config.managesources.adapter
import com.dbeginc.dbweather.base.IPresenter
import com.dbeginc.dbweather.base.IView
import com.dbeginc.dbweather.viewmodels.news.SourceModel
/**
* Created by darel on 27.10.17.
*
* Source Contract
*/
interface SourceContract {
interface SourceView : IView {
fun displaySource(source: SourceModel)
fun definePresenter(presenter: SourcePresenter)
fun showSubscribed()
fun showUnSubscribed()
fun showUpdateStatus()
fun hideUpdateStatus()
fun goToSourceDetail()
fun showError(message: String)
}
interface SourcePresenter: IPresenter<SourceView> {
fun loadSource()
fun onAction()
fun onSubscribe()
fun getData(): SourceModel
}
}
/*
* Copyright (C) 2017 Darel Bitsy
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.dbeginc.dbweather.config.managesources.adapter
import android.support.v7.util.DiffUtil
import com.dbeginc.dbweather.viewmodels.news.SourceModel
/**
* Created by darel on 27.10.17.
*
* Source Difference Util
*/
class SourceDiffUtil(private val old: List<SourceContract.SourcePresenter>, private val new: List<SourceModel>) : DiffUtil.Callback() {
override fun getOldListSize(): Int = old.size
override fun getNewListSize(): Int = new.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return old[oldItemPosition].getData().id == new[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return old[oldItemPosition].getData() == new[newItemPosition]
}
}
/*
* Copyright (C) 2017 Darel Bitsy
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.dbeginc.dbweather.config.managesources.adapter
import android.databinding.DataBindingUtil
import android.support.v7.util.DiffUtil
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import com.dbeginc.dbweather.R
import com.dbeginc.dbweather.config.managesources.adapter.presenter.SourcePresenterImpl
import com.dbeginc.dbweather.config.managesources.adapter.view.SourceViewHolder
import com.dbeginc.dbweather.viewmodels.news.SourceModel
import com.dbeginc.dbweatherdomain.usecases.news.SubscribeToSource
import com.dbeginc.dbweatherdomain.usecases.news.UnSubscribeToSource
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
import org.jetbrains.anko.coroutines.experimental.bg
import java.util.*
/**
* Created by darel on 27.10.17.
*
* Sources Adapter
*/
class SourcesAdapter(sources: List<SourceModel>,
private val subscribeToSource: SubscribeToSource,
private val unSubscribeToSource: UnSubscribeToSource) : RecyclerView.Adapter<SourceViewHolder>() {
private var container: RecyclerView? = null
private val presenters: LinkedList<SourceContract.SourcePresenter>
init {
presenters = LinkedList(
sources.map { source -> SourcePresenterImpl(source, subscribeToSource, unSubscribeToSource) }
.sortedBy { presenter -> presenter.getData().name }
)
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView?) {
super.onAttachedToRecyclerView(recyclerView)
container = recyclerView
}
override fun getItemCount(): Int = presenters.size
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): SourceViewHolder {
return SourceViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent?.context), R.layout.source_item, parent, false))
}
override fun onBindViewHolder(holder: SourceViewHolder, position: Int) {
val presenter = presenters[position]
// cleaning state of view holder before binding it to new data
holder.cleanState()
holder.definePresenter(presenter)
presenter.bind(holder)
presenter.loadSource()
}
fun update(newData: List<SourceModel>) {
async(UI) {
val result = bg { DiffUtil.calculateDiff(SourceDiffUtil(presenters, newData.sortedBy { source -> source.name })) }.await()
container?.post {
presenters.clear()
newData.mapTo(presenters) { source -> SourcePresenterImpl(source, subscribeToSource, unSubscribeToSource) }
presenters.sortBy { presenter -> presenter.getData().name }
result.dispatchUpdatesTo(this@SourcesAdapter)
}
}
}
fun getData(): List<SourceModel> = presenters.map { presenter -> presenter.getData() }
}
/*
* Copyright (C) 2017 Darel Bitsy
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.dbeginc.dbweather.config.managesources.adapter.view
import android.support.v7.widget.RecyclerView
import com.dbeginc.dbweather.R
import com.dbeginc.dbweather.config.managesources.adapter.SourceContract
import com.dbeginc.dbweather.databinding.SourceItemBinding
import com.dbeginc.dbweather.utils.utility.remove
import com.dbeginc.dbweather.utils.utility.show
import com.dbeginc.dbweather.utils.utility.toast
import com.dbeginc.dbweather.viewmodels.news.SourceModel
/**
* Created by darel on 27.10.17.
*
* Source View Holder
*/
class SourceViewHolder(private val binding: SourceItemBinding): RecyclerView.ViewHolder(binding.root), SourceContract.SourceView {
private var presenter: SourceContract.SourcePresenter? = null
override fun setupView() {/*** Not needed here ****/}
override fun cleanState() {
presenter?.unBind()
}
override fun displaySource(source: SourceModel) {
binding.source = source
binding.sourceSubscribed.setImageResource(if (source.subscribed) R.drawable.follow_icon else R.drawable.un_follow_icon)
binding.executePendingBindings()
}
override fun showSubscribed() = binding.sourceSubscribed.setImageResource(R.drawable.follow_icon)
override fun showUnSubscribed() = binding.sourceSubscribed.setImageResource(R.drawable.un_follow_icon)
override fun goToSourceDetail() = binding.sourceLayout.toast("Going to Detail for ${binding.source?.name}")
override fun showUpdateStatus() = binding.sourceUpdatingStatus.show()
override fun hideUpdateStatus() = binding.sourceUpdatingStatus.remove()
override fun showError(message: String) = binding.sourceLayout.toast(message)
override fun definePresenter(presenter: SourceContract.SourcePresenter) {
this.presenter = presenter
binding.sourceLayout.setOnClickListener { this.presenter?.onAction() }
binding.sourceSubscribed.setOnClickListener { this.presenter?.onSubscribe() }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment