Building a News App with Kotlin and News API

In this tutorial, we will learn how to build a News App using Kotlin and the News API. Kotlin is a modern programming language developed by JetBrains, and the News API provides developers with a simple and convenient way to fetch news data from various sources. By the end of this tutorial, you will have a fully functional News App with features such as fetching news data, designing the user interface, handling user interactions, and adding additional features like search functionality, bookmarks, and dark mode.

building news app kotlin news api

Introduction

What is Kotlin?

Kotlin is a statically typed programming language that runs on the Java Virtual Machine (JVM). It was developed by JetBrains and officially released in 2016. Kotlin is fully interoperable with Java, which means you can use existing Java libraries and frameworks in your Kotlin projects. Kotlin provides a more concise and expressive syntax compared to Java, making it easier to write and maintain code.

What is the News API?

The News API is a service that provides developers with access to a wide range of news articles from various sources. It allows you to fetch news headlines, search for specific news articles, and get detailed information about each article. The News API supports multiple programming languages, including Kotlin, and provides a RESTful API for retrieving news data.

Why build a News App with Kotlin and News API?

Building a News App with Kotlin and the News API has several advantages. Firstly, Kotlin is a modern and powerful programming language that offers many features and improvements over Java. It provides a more concise syntax, null safety, coroutines for asynchronous programming, and many other language features that make development faster and more efficient.

Secondly, the News API provides a convenient way to fetch news data from various sources without the need for complex web scraping or API integration. It offers a simple and well-documented API that allows developers to easily retrieve news articles based on different criteria such as sources, categories, and search queries.

Setting Up the Project

Installing Kotlin

To start building a News App with Kotlin, you first need to install the Kotlin programming language. Kotlin can be installed using various methods, depending on your development environment. You can download and install the Kotlin compiler directly from the official Kotlin website, or you can use a build tool like Gradle or Maven to manage your Kotlin dependencies.

Creating a new Android project

Once you have Kotlin installed, you can create a new Android project in Android Studio. Open Android Studio and select "Create New Project" from the welcome screen. Choose a project name, package name, and location for your project. Select the minimum SDK version and other project settings according to your requirements. Finally, select Kotlin as the programming language for your project.

Adding dependencies

After creating a new Android project, you need to add the necessary dependencies to fetch news data and design the user interface. In your project's build.gradle file, add the following dependencies:

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'androidx.recyclerview:recyclerview:1.2.0'
}

The first two dependencies are for Retrofit, a widely used HTTP client library for Android. Retrofit makes it easy to perform API requests and handle API responses. The third dependency is for the RecyclerView, which is a powerful and flexible view for displaying large datasets. We will use it to display the news articles in our app.

Fetching News Data

Setting up API key

Before we can fetch news data from the News API, we need to obtain an API key. Visit the News API website and sign up for an account. Once you have an account, you can generate an API key that will be used to authenticate your requests. Make sure to keep your API key secure and do not share it publicly.

To use the API key in our app, we will create a Kotlin object that holds the API key as a constant:

object ApiKey {
    const val KEY = "YOUR_API_KEY"
}

Replace YOUR_API_KEY with your actual API key.

Making API requests

To fetch news data from the News API, we will use Retrofit to make HTTP requests. Create a new Kotlin file called NewsApiService.kt and add the following code:

interface NewsApiService {
    @GET("top-headlines")
    suspend fun getTopHeadlines(
        @Query("country") country: String = "us",
        @Query("apiKey") apiKey: String = ApiKey.KEY
    ): Response<NewsResponse>
}

data class NewsResponse(
    val status: String,
    val totalResults: Int,
    val articles: List<Article>
)

data class Article(
    val title: String,
    val description: String,
    val url: String,
    val imageUrl: String
)

In this code, we define an interface NewsApiService that represents the API endpoints provided by the News API. The getTopHeadlines function is used to fetch the top headlines from a specific country. We pass the country code and the API key as query parameters.

We also define the NewsResponse and Article data classes that represent the JSON response returned by the API. Each Article object contains properties such as the title, description, URL, and image URL of a news article.

Handling API responses

To handle API responses, we will use coroutines in Kotlin. Coroutines provide a simple and efficient way to perform asynchronous programming and handle long-running tasks without blocking the main thread. Create a new Kotlin file called NewsRepository.kt and add the following code:

class NewsRepository {
    private val apiService: NewsApiService = retrofit.create(NewsApiService::class.java)

    suspend fun getTopHeadlines(): List<Article> {
        val response = apiService.getTopHeadlines()
        if (response.isSuccessful) {
            return response.body()?.articles ?: emptyList()
        } else {
            throw ApiException(response.code(), response.message())
        }
    }
}

class ApiException(code: Int, message: String) : Exception("API request failed with code $code: $message")

In this code, we create a NewsRepository class that acts as a mediator between the API service and the UI. It uses the apiService to fetch the top headlines from the News API. If the API request is successful, we return the list of articles. Otherwise, we throw an ApiException with the error code and message returned by the API.

Designing the User Interface

Creating layout files

To design the user interface of our News App, we will use XML layout files in Android. Create a new XML layout file called activity_main.xml and add the following code:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

In this code, we define a LinearLayout as the root element of the layout. Inside the LinearLayout, we add a RecyclerView that will be used to display the news articles.

Implementing RecyclerView

To display the news articles in the RecyclerView, we need to create an adapter and a view holder. Create a new Kotlin file called NewsAdapter.kt and add the following code:

class NewsAdapter(private val articles: List<Article>) :
    RecyclerView.Adapter<NewsAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_article, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val article = articles[position]
        holder.titleTextView.text = article.title
        holder.descriptionTextView.text = article.description
        Glide.with(holder.itemView)
            .load(article.imageUrl)
            .into(holder.imageView)
    }

    override fun getItemCount(): Int {
        return articles.size
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
        val descriptionTextView: TextView = itemView.findViewById(R.id.descriptionTextView)
        val imageView: ImageView = itemView.findViewById(R.id.imageView)
    }
}

In this code, we define a NewsAdapter class that extends the RecyclerView.Adapter class. The NewsAdapter takes a list of articles as a parameter and uses it to populate the RecyclerView. In the onCreateViewHolder function, we inflate the item_article layout file and create a new ViewHolder for each item in the RecyclerView. In the onBindViewHolder function, we bind the data from each article to the corresponding views in the ViewHolder.

Customizing item views

To customize the appearance of each news article in the RecyclerView, we need to create another XML layout file called item_article.xml and add the following code:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:scaleType="centerCrop"
        android:src="@drawable/placeholder" />

    <TextView
        android:id="@+id/titleTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textStyle="bold"
        android:layout_marginTop="8dp" />

    <TextView
        android:id="@+id/descriptionTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp" />

</LinearLayout>

In this code, we define a LinearLayout as the root element of the item layout. Inside the LinearLayout, we add an ImageView to display the image of the news article, and two TextView elements to display the title and description of the article.

Handling User Interactions

Implementing click listeners

To handle user interactions in the News App, we will implement click listeners for each item in the RecyclerView. Create a new Kotlin file called NewsItemClickListener.kt and add the following code:

interface NewsItemClickListener {
    fun onItemClick(article: Article)
}

class NewsAdapter(private val articles: List<Article>, private val clickListener: NewsItemClickListener) :
    RecyclerView.Adapter<NewsAdapter.ViewHolder>() {

    // ...

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val article = articles[position]
        holder.itemView.setOnClickListener {
            clickListener.onItemClick(article)
        }
        // ...
    }

    // ...
}

In this code, we define an interface NewsItemClickListener that has a single function onItemClick which takes an Article object as a parameter. We modify the NewsAdapter class to accept a NewsItemClickListener as a parameter and call the onItemClick function when an item in the RecyclerView is clicked.

Opening news details

To open the details of a news article when it is clicked, we will create a new activity called NewsDetailsActivity. Create a new Kotlin file called NewsDetailsActivity.kt and add the following code:

class NewsDetailsActivity : AppCompatActivity() {

    companion object {
        const val EXTRA_ARTICLE = "extra_article"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_news_details)

        val article = intent.getParcelableExtra<Article>(EXTRA_ARTICLE)

        // Use the article object to display the details of the news article
    }
}

In this code, we create a new activity called NewsDetailsActivity that extends the AppCompatActivity class. In the onCreate function, we retrieve the Article object passed as an extra from the previous activity and use it to display the details of the news article.

Sharing news articles

To allow users to share news articles with others, we will add a share button to the NewsDetailsActivity. Modify the XML layout file activity_news_details.xml and add the following code:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- Add views to display the details of the news article -->

    <Button
        android:id="@+id/shareButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Share"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="16dp" />

</LinearLayout>

In this code, we add a Button element to the layout with the text "Share". We will use this button to share the news article when clicked.

To implement the sharing functionality, modify the NewsDetailsActivity and add the following code:

class NewsDetailsActivity : AppCompatActivity() {

    // ...

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_news_details)

        // ...

        val shareButton = findViewById<Button>(R.id.shareButton)
        shareButton.setOnClickListener {
            val shareIntent = Intent(Intent.ACTION_SEND)
            shareIntent.type = "text/plain"
            shareIntent.putExtra(Intent.EXTRA_SUBJECT, article.title)
            shareIntent.putExtra(Intent.EXTRA_TEXT, article.url)
            startActivity(Intent.createChooser(shareIntent, "Share via"))
        }
    }
}

In this code, we retrieve the shareButton from the layout and set a click listener on it. When the button is clicked, we create an Intent with the action ACTION_SEND and the type text/plain. We set the subject of the intent to the title of the news article and the text to the URL of the article. Finally, we start an activity chooser to allow the user to select an app to share the article.

Adding Additional Features

Implementing search functionality

To implement search functionality in the News App, we will add a search bar to the toolbar. Modify the XML layout file activity_main.xml and add the following code:

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:elevation="4dp"
    android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
    android:popupTheme="@style/ThemeOverlay.AppCompat.Light">

    <EditText
        android:id="@+id/searchEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Search"
        android:imeOptions="actionSearch"
        android:inputType="text"
        android:maxLines="1" />

</androidx.appcompat.widget.Toolbar>

In this code, we add a Toolbar element to the layout and an EditText element inside the toolbar. We will use the EditText to allow the user to enter a search query.

To implement the search functionality, modify the MainActivity and add the following code:

class MainActivity : AppCompatActivity() {

    private lateinit var searchEditText: EditText

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        searchEditText = findViewById(R.id.searchEditText)
        searchEditText.setOnEditorActionListener { _, actionId, _ ->
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                val query = searchEditText.text.toString()
                performSearch(query)
                true
            } else {
                false
            }
        }
    }

    private fun performSearch(query: String) {
        // Fetch news articles based on the search query
    }
}

In this code, we retrieve the searchEditText from the layout and set an OnEditorActionListener on it. When the user presses the search button on the keyboard, we retrieve the search query from the EditText and call the performSearch function with the query as a parameter.

Adding bookmarks

To allow users to bookmark news articles and view them later, we will add a bookmark button to the NewsDetailsActivity. Modify the XML layout file activity_news_details.xml and add the following code:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- Add views to display the details of the news article -->

    <Button
        android:id="@+id/bookmarkButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Bookmark"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="16dp" />

</LinearLayout>

In this code, we add a Button element to the layout with the text "Bookmark". We will use this button to bookmark the news article when clicked.

To implement the bookmark functionality, modify the NewsDetailsActivity and add the following code:

class NewsDetailsActivity : AppCompatActivity() {

    // ...

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_news_details)

        // ...

        val bookmarkButton = findViewById<Button>(R.id.bookmarkButton)
        bookmarkButton.setOnClickListener {
            // Add or remove the news article from bookmarks
        }
    }
}

In this code, we retrieve the bookmarkButton from the layout and set a click listener on it. When the button is clicked, we add or remove the news article from the list of bookmarks.

Implementing dark mode

To implement a dark mode in the News App, we will add a switch to the settings menu that allows the user to toggle between light and dark themes. Modify the XML layout file activity_main.xml and add the following code:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/action_dark_mode"
        android:title="Dark mode"
        android:checkable="true"
        android:checked="false"
        android:orderInCategory="100"
        android:showAsAction="always" />
</menu>

In this code, we define a menu item with the ID action_dark_mode. We set it as checkable and unchecked by default.

To implement the dark mode functionality, modify the MainActivity and add the following code:

class MainActivity : AppCompatActivity() {

    private var isDarkModeEnabled = false

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        val darkModeMenuItem = menu.findItem(R.id.action_dark_mode)
        darkModeMenuItem.isChecked = isDarkModeEnabled
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_dark_mode -> {
                isDarkModeEnabled = !isDarkModeEnabled
                item.isChecked = isDarkModeEnabled
                setDarkModeEnabled(isDarkModeEnabled)
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }

    private fun setDarkModeEnabled(enabled: Boolean) {
        val mode = if (enabled) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO
        AppCompatDelegate.setDefaultNightMode(mode)
        recreate()
    }
}

In this code, we define a boolean variable isDarkModeEnabled to keep track of the current dark mode state. In the onCreateOptionsMenu function, we inflate the menu layout and update the checked state of the dark mode menu item based on the isDarkModeEnabled variable.

In the onOptionsItemSelected function, we handle the click event of the dark mode menu item. When the item is clicked, we toggle the isDarkModeEnabled variable, update the checked state of the menu item, and call the setDarkModeEnabled function to apply the selected theme.

Testing and Deployment

Writing unit tests

To ensure the quality and correctness of our News App, we should write unit tests for critical components of the app. In this section, we will write a simple unit test for the NewsRepository class.

Create a new Kotlin file called NewsRepositoryTest.kt in the test source set and add the following code:

class NewsRepositoryTest {

    private lateinit var newsRepository: NewsRepository

    @Before
    fun setup() {
        val mockApiService = mock(NewsApiService::class.java)
        newsRepository = NewsRepository(mockApiService)
    }

    @Test
    fun `getTopHeadlines should return list of articles`() = runBlocking {
        val articles = listOf(Article("Title 1", "Description 1", "URL 1", "Image URL 1"))
        val response = Response.success(NewsResponse("ok", articles.size, articles))

        `when`(newsRepository.getTopHeadlines()).thenReturn(response.body()?.articles)

        val result = newsRepository.getTopHeadlines()

        assertThat(result).isEqualTo(articles)
    }
}

In this code, we use the Mockito library to create a mock instance of the NewsApiService interface. We then create an instance of the NewsRepository class with the mock API service. In the unit test, we define a test case for the getTopHeadlines function that asserts the returned list of articles is equal to the expected list.

Building and signing the APK

To build and sign the APK of our News App for deployment, we can use the Gradle build system integrated into Android Studio. Follow these steps to build and sign the APK:

  1. In Android Studio, go to the "Build" menu and select "Build Bundle(s) / APK(s)".
  2. Select "Build APK(s)".
  3. Wait for the build process to complete.
  4. Once the build is finished, the APK file will be located in the app/build/outputs/apk directory.

To sign the APK, you need to generate a keystore file and configure the signing properties in the project's build.gradle file. Follow the official Android documentation for more details on how to sign your APK.

Publishing the app

To publish the News App on the Google Play Store, you need to create a developer account and follow the guidelines provided by Google. Here are the basic steps to publish your app:

  1. Sign in to the Google Play Console with your developer account.
  2. Create a new application listing and provide the required information such as app name, description, screenshots, and app icon.
  3. Upload the signed APK file of your app.
  4. Fill in the content rating questionnaire and select the target audience for your app.
  5. Set the pricing and distribution options for your app.
  6. Review and publish your app.

Make sure to thoroughly test your app before publishing it to ensure a smooth user experience. Also, consider implementing crash reporting and analytics tools to monitor the app's performance and user engagement.

Conclusion

In this tutorial, we learned how to build a News App with Kotlin and the News API. We covered topics such as setting up the project, fetching news data using Retrofit, designing the user interface with RecyclerView, handling user interactions, and adding additional features like search functionality, bookmarks, and dark mode. We also discussed testing and deployment strategies for our News App. By following this tutorial, you should now have a solid understanding of how to build a News App using Kotlin and the News API, and you can use this knowledge to create your own news-related projects.