
We will be starting with a new series where I’ll create the news app, the project is quite big so it will take around six videos to complete it, okay?
Also, we will be covering five major advanced topics such as MVVM, Retrofit, Navigation Components, Coroutines, and Room Databases in one single project. So, I highly recommend doing a revision of each topic before starting with the project.
Topic Videos:
Alright, let’s have a look at the demo.
This is how it looks!
I have kept the project very minimal and useful.

We have a bottom navigation with three categories – headlines, favorites, and search.
As you can see in the headlines the latest news is shown here. All, of them, are fetched from news API in real-time.
You, can even click on it and see it opens up the article in a web view, great right?

You can scroll through it and you can save it in your favorites section by clicking on this floating acting button.
Look, it’s saved here in favorites – not, just that but you can also remove it from favorites by swiping it and you can undo it as well, right?

Then, go to the search section.
Here, you can literally search anything it will come up with the results – see, there it is!

All, of these, might look very easy to you and it is indeed very easy…it’s, just that we have to write a lot of code for them.
Now, let’s go into the technical side of it.
To, display all these articles are using news API which I’ll show you later. So, with the help of that, it fetches the real-time data or news and displays it on the recycler view.
Then, with the help of web view – we, can open the article in the app itself without going to any browser.
Then, by clicking on add to fav – that, particular article gets saved in the room database so what you see here is all saved in the room database hence later whenever you open the app again – they, will be still here.
Lastly, search uses API only, and other topics like MVVM will help us to make the project organized and segregated in model view and ViewModel not, just that but also the navigation component will help us to navigate from one screen to another screen effortlessly.
Note: This is my practice project which works the same way as the original one but please copy only the required code because copying the entire code of gradle or manifest will lead to errors related to the project package name.
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="purple">#593c8f</color>
<color name="yellow">#FFC000</color>
</resources>
themes.xml
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.NewsProjectPractice" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<item name="colorPrimary">@color/purple</item>
<item name="colorPrimaryVariant">@color/purple</item>
<item name="android:statusBarColor">@color/purple</item>
</style>
<style name="Theme.NewsProjectPractice" parent="Base.Theme.NewsProjectPractice" />
<style name="App.Custom.Indicator" parent="Widget.Material3.BottomNavigationView.ActiveIndicator">
<item name="android:color">@color/yellow</item>
</style>
</resources>
Drawables:
search_border.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:color="@color/purple"
android:width="2dp"/>
<corners
android:radius="14dp"/>
</shape>
build gradle (Project)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
}
dependencies {
val navVersion = "2.7.5"
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$navVersion")
}
}
plugins {
id("com.android.application") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
id("com.google.devtools.ksp") version "1.9.0-1.0.13" apply false
}
build gradle (Module)
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp")
id("androidx.navigation.safeargs.kotlin")
}
android {
namespace = "com.example.newsprojectpractice"
compileSdk = 34
defaultConfig {
applicationId = "com.example.newsprojectpractice"
minSdk = 28
targetSdk = 33
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures{
viewBinding = true
}
}
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
// Architectural Components
implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
// Room
implementation ("androidx.room:room-runtime:2.6.0")
ksp ("androidx.room:room-compiler:2.6.0")
// Kotlin Extensions and Coroutines support for Room
implementation ("androidx.room:room-ktx:2.6.0")
// Coroutines
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")
// Coroutine Lifecycle Scopes
implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
// Retrofit
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
implementation ("com.squareup.okhttp3:logging-interceptor:4.5.0")
// Navigation Components
implementation ("androidx.navigation:navigation-fragment-ktx:2.7.5")
implementation ("androidx.navigation:navigation-ui-ktx:2.7.5")
// Glide
implementation ("com.github.bumptech.glide:glide:4.12.0")
ksp ("com.github.bumptech.glide:compiler:4.12.0")
}
bottom_navigation_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/headlinesFragment"
android:title="Headlines"
android:icon="@drawable/baseline_headlines_24" />
<item android:id="@+id/favouritesFragment"
android:title="Favourites"
android:icon="@drawable/baseline_favorite_24" />
<item android:id="@+id/searchFragment"
android:title="Search"
android:icon="@drawable/baseline_search_24" />
</menu>
Packages and Fragments

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.NewsProjectPractice"
tools:targetApi="31">
<activity
android:name=".ui.NewsActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>
fragment_article.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/baseline_favorite_24"
app:fabSize="normal"
android:layout_marginBottom="32dp"
android:layout_marginEnd="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_favorites.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Favourites."
android:textSize="30sp"
android:fontFamily="@font/poppins"
android:textColor="@color/purple"
android:textStyle="bold"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerFavourites"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="60dp"
android:layout_marginStart="8dp"/>
</FrameLayout>
item_error.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:cardCornerRadius="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/errorText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_weight="2"
android:textSize="16sp"
android:gravity="center_horizontal"
android:text="No Internet"
android:textAlignment="center" />
<Button
android:id="@+id/retryButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_weight="1"
android:text="Retry" />
</LinearLayout>
</androidx.cardview.widget.CardView>
fragment_headlines.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Headlines."
android:textSize="30sp"
android:fontFamily="@font/poppins"
android:textColor="@color/purple"
android:textStyle="bold"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerHeadlines"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="50dp"
android:layout_marginTop="70dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include
android:id="@+id/itemHeadlinesError"
layout="@layout/item_error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/paginationProgressBar"
style="?attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_search.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Search."
android:textSize="30sp"
android:fontFamily="@font/poppins"
android:textColor="@color/purple"
android:textStyle="bold"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<EditText
android:id="@+id/searchEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Search news..."
android:layout_marginTop="70dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:padding="8dp"
android:textSize="14sp"
android:fontFamily="@font/poppins"
android:drawableTint="@color/purple"
android:background="@drawable/search_border"
android:drawableEnd="@drawable/baseline_search_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerSearch"
android:layout_width="match_parent"
android:layout_height="0dp"
android:paddingBottom="50dp"
android:clipToPadding="false"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/searchEdit"
tools:layout_editor_absoluteX="0dp" />
<include
android:id="@+id/itemSearchError"
layout="@layout/item_error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/paginationProgressBar"
style="?attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
item_news.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<ImageView
android:id="@+id/articleImage"
android:layout_width="160dp"
android:layout_height="90dp"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/articleSource"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="SOURCE"
android:fontFamily="@font/poppins"
android:textColor="@color/purple"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/articleImage" />
<TextView
android:id="@+id/articleTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="2"
android:text="TITLE"
android:textColor="@color/black"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toRightOf="@+id/articleImage"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/articleDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="2"
android:text="DESCRIPTION"
android:textColor="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/articleImage"
app:layout_constraintTop_toBottomOf="@+id/articleTitle" />
<TextView
android:id="@+id/articleDateTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="DATE TIME"
android:textSize="11sp"
android:layout_marginStart="8dp"
android:textColor="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/articleDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_news.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.NewsActivity">
<FrameLayout
android:id="@+id/flFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/newsNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/news_nav_graph" />
</FrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="80dp"
app:menu="@menu/bottom_navigation_menu"
app:itemTextColor="@color/white"
app:itemIconTint="@color/white"
android:background="@color/purple"
app:itemActiveIndicatorStyle="@style/App.Custom.Indicator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
news_nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/news_nav_graph"
app:startDestination="@id/headlinesFragment">
<fragment
android:id="@+id/headlinesFragment"
android:name="com.example.newsprojectpractice.ui.fragments.HeadlinesFragment"
android:label="HeadlinesFragment">
<action
android:id="@+id/action_headlinesFragment_to_articleFragment"
app:destination="@id/articleFragment"/>
</fragment>
<fragment
android:id="@+id/favouritesFragment"
android:name="com.example.newsprojectpractice.ui.fragments.FavouritesFragment"
android:label="FavouritesFragment">
<action
android:id="@+id/action_favouritesFragment_to_articleFragment"
app:destination="@id/articleFragment"/>
</fragment>
<fragment
android:id="@+id/searchFragment"
android:name="com.example.newsprojectpractice.ui.fragments.SearchFragment"
android:label="SearchFragment">
<action
android:id="@+id/action_searchFragment_to_articleFragment"
app:destination="@id/articleFragment"/>
</fragment>
<fragment
android:id="@+id/articleFragment"
android:name="com.example.newsprojectpractice.ui.fragments.ArticleFragment"
android:label="ArticleFragment" >
<argument
android:name="article"
app:argType="com.example.newsprojectpractice.models.Article" />
</fragment>
</navigation>
Alright, now the only last thing that is left is the API key.
Where, do we get this API key from?
So, every API has its own key which is private and you are not supposed to share it with anyone, and with the help of that key, you can access the data.
As we want news data there are varieties of news APIs available on the internet – out, of which we will be using News API.
News API: https://newsapi.org/
It’s, free, easy to integrate, and widely used.
Just, to show you – we, also have another famous news API called g news – if, you want you can use it too but as a beginner follow me.
gNews:
So, the best part about news API is that it provides a lot of customization like country, language, publishers, or timing – you will know when we will implement retrofit.
Now, let’s focus on the API key.
As a – new user clicks on the get API key.
Fill, in all the details like name, email, and password and submit it…then, at the end, it will provide us with an api key…and, that’s it.
All, our prerequisites are done!
Now, in the upcoming videos – we can write the code without any interference…and, that’s where the real logic begins…so, stay tuned for that, okay!
You can create a splash screen if you want to – click here for the code
Please subscribe to my YouTube channel:
Android Knowledge – Click here