News App using Kotlin, MVVM, Navigation Component, Room, Retrofit, and Coroutines

News App using Kotlin, MVVM, Navigation Component, Room, Retrofit, and Coroutines

Dependencies (build.gradle of App)

id ‘com.android.application’

id ‘kotlin-android’

id ‘kotlin-android-extensions’

id ‘kotlin-kapt’

id “androidx.navigation.safeargs.kotlin”

}

// Architectural Components
implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0”
// Room
implementation “androidx.room:room-runtime:2.2.5”
kapt “androidx.room:room-compiler:2.2.5”
// Kotlin Extensions and Coroutines support for Room
implementation “androidx.room:room-ktx:2.2.5”
// Coroutines
implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5’
implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5’
// Coroutine Lifecycle Scopes
implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0”
implementation “androidx.lifecycle:lifecycle-runtime-ktx:2.2.0”
// Retrofit
implementation ‘com.squareup.retrofit2:retrofit:2.6.0’
implementation ‘com.squareup.retrofit2:converter-gson:2.6.0’
implementation “com.squareup.okhttp3:logging-interceptor:4.5.0”
// Navigation Components
implementation “androidx.navigation:navigation-fragment-ktx:2.2.1”
implementation “androidx.navigation:navigation-ui-ktx:2.2.1”
// Glide
implementation ‘com.github.bumptech.glide:glide:4.11.0’
kapt ‘com.github.bumptech.glide:compiler:4.11.0’
//shimmer effect or loading effect
implementation ‘com.facebook.shimmer:shimmer:0.1.0@aar’
def paging_version = “2.1.2”

implementation “androidx.paging:paging-runtime:$paging_version” //

//swipe refresh layout
implementation ‘androidx.swiperefreshlayout:swiperefreshlayout:1.1.0’

dependencies (build.gradle of Project)

Drawables

Model (Article)

NewsResponse

Source

Utils (Constants)

Resource

Util

Repository > Service (RetrofitClient)

NewsApi

Repository > datasource (ArticleDataSource)

ArticleDataSourceFActory

Repository > db(ArticleDAO)

import androidx.room.*

import com.codewithgolap.newssplash.model.Article

@Dao

interface ArticleDAO {

@Insert(onConflict = OnConflictStrategy.REPLACE)

suspend fun insert(article: Article) : Long

@Query(“SELECT * FROM articles”)

fun getArticles() : LiveData<List<Article>>

@Delete

suspend fun deleteArticle(article: Article)

}

ArticleDAtabase

Converters

Respository > NewsRepository

ViewModel > NewsViewModel

NewsViewModelFactory

MainActivity

Adapters > ArticleAdapter

BreakingNewsFragment

}

ArticleFragment

SavedNewsAdapter

private val diffUtilCallback = object : DiffUtil.ItemCallback<Article>() {
override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem.url == newItem.url
}
override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem.id == newItem.id
}
}
inner class SavedNewsViewHolder(var view: ItemSavedNewsBinding) :
RecyclerView.ViewHolder(view.root)
val differ = AsyncListDiffer(this, diffUtilCallback)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SavedNewsViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = DataBindingUtil.inflate<ItemSavedNewsBinding>(
inflater,
R.layout.item_saved_news,
parent,
false
)
return SavedNewsViewHolder(view)
}
override fun getItemCount() = differ.currentList.size
override fun onBindViewHolder(holder: SavedNewsViewHolder, position: Int) {
val article = differ.currentList[position]
holder.view.article = article
// Item CLick Listener
//Bind these click listeners later
holder.itemView.setOnClickListener {
onItemClickListener?.let {
article.let { article ->
it(article)
}
}
}
holder.view.ivShare.setOnClickListener {
onShareNewsClick?.let {
article?.let { it1 -> it(it1) }
}
}
}
private var onItemClickListener: ((Article) -> Unit)? = null
private var onShareNewsClick: ((Article) -> Unit)? = null
fun setOnItemCLickListener(listener: ((Article) -> Unit)) {
onItemClickListener = listener
}
fun onShareNewsClick(listener: (Article) -> Unit) {
onShareNewsClick = listener
}
}

SavedNewsFragment

class SavedNewsFragment : Fragment(R.layout.fragment_saved_news) {
lateinit var viewModel: NewsViewModel
lateinit var newsAdapter: SavedNewsAdapter
val TAG = “SavedNewsFragment”
private fun setupRecyclerView() {
newsAdapter = SavedNewsAdapter()
rvSavedNews.apply {
adapter = newsAdapter
layoutManager = LinearLayoutManager(activity)
}
newsAdapter.setOnItemCLickListener {
val bundle = Bundle().apply {
putSerializable(“article”, it)
}
findNavController().navigate(
R.id.action_savedNewsFragment_to_articleFragment,
bundle
)
}
newsAdapter.onShareNewsClick {
shareNews(context, it)
}
// swipe to delete
val onItemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
)
{
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
val article = newsAdapter.differ.currentList[position]
viewModel.deleteArticle(article)
Snackbar.make(requireView(), “Deleted Successfully”, Snackbar.LENGTH_LONG).apply {
setAction(“Undo”){
viewModel.insertArticle(article)
}
show()
}
}
}
ItemTouchHelper(onItemTouchHelperCallback).apply {
attachToRecyclerView(rvSavedNews)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = (activity as MainActivity).viewModel
setupRecyclerView()
setViewModelObserver()
}
private fun setViewModelObserver() {
viewModel = (activity as MainActivity).viewModel
viewModel.getSavedArticles().observe(viewLifecycleOwner, Observer {
Log.i(TAG,””+it.size)
newsAdapter.differ.submitList(it)
rvSavedNews.visibility = View.VISIBLE
shimmerFrameLayout2.stopShimmerAnimation()
shimmerFrameLayout2.visibility = View.GONE
})
}

}

SearchNewsFragment

Watch the full video on YouTube:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Golap Gunjan Barman

Hi everyone, myself Golap an Android app developer with UI/UX designer.