Integrating Firebase Features with XML UI

Integrating Firebase into Android applications often involves modern UI frameworks like Jetpack Compose. However, many existing and legacy projects still rely on XML layouts for their user interfaces. This post explores how to seamlessly integrate Firebase features such as Authentication, Realtime Database, and Cloud Messaging with XML-based UI in Android.

Understanding the Basics

Firebase is a powerful platform developed by Google for building mobile and web applications. It offers a suite of services, including:

  • Authentication: Manages user authentication.
  • Realtime Database: Provides a cloud-hosted NoSQL database.
  • Cloud Firestore: A flexible, scalable database for mobile and web development.
  • Cloud Messaging (FCM): Enables sending notifications and messages.
  • Cloud Storage: Offers cloud storage for files and blobs.

While modern Android development often favors Jetpack Compose, it’s essential to know how to integrate these Firebase features with XML UI, which remains common in many apps.

Setting Up Firebase in Your Android Project

Before integrating Firebase with your XML UI, you need to set up a Firebase project and connect it to your Android application.

Step 1: Create a Firebase Project

  1. Go to the Firebase Console.
  2. Click on “Add project” and follow the instructions.

Step 2: Add Firebase to Your Android App

  1. In the Firebase Console, select your project.
  2. Click on the Android icon to add Firebase to your Android app.
  3. Follow the setup steps, which include adding your app’s package name, registering the app, and downloading the google-services.json file.

Step 3: Configure Your Android Project

  1. Move the google-services.json file to your app module’s root directory.
  2. Add the following to your project-level build.gradle file:

buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.4.0' // Check for latest version
    }
}
  1. Add the following plugin to your app-level build.gradle file:

plugins {
    id 'com.android.application'
    id 'com.google.gms.google-services'
}
  1. Add the Firebase SDK dependencies to your app-level build.gradle file. For example, to use Firebase Authentication and Realtime Database:

dependencies {
    implementation 'com.google.firebase:firebase-auth-ktx:22.3.0'    // Check for latest version
    implementation 'com.google.firebase:firebase-database-ktx:20.3.0'  // Check for latest version
}
  1. Sync your Gradle project to apply the changes.

Integrating Firebase Authentication with XML UI

Firebase Authentication allows you to authenticate users using various methods such as email/password, Google Sign-In, Facebook, and more. Here’s how to integrate it with an XML UI.

Step 1: Create an XML Layout

Define your login layout in an XML file (e.g., activity_login.xml):


<?xml version=\"1.0\" encoding=\"utf-8\"?>
<LinearLayout
    xmlns:android=\"http://schemas.android.com/apk/res/android\"
    android:layout_width=\"match_parent\"
    android:layout_height=\"match_parent\"
    android:orientation=\"vertical\"
    android:padding=\"16dp\">

    <EditText
        android:id=\"@+id/emailEditText\"
        android:layout_width=\"match_parent\"
        android:layout_height=\"wrap_content\"
        android:hint=\"Email\"
        android:inputType=\"textEmailAddress\"/>

    <EditText
        android:id=\"@+id/passwordEditText\"
        android:layout_width=\"match_parent\"
        android:layout_height=\"wrap_content\"
        android:hint=\"Password\"
        android:inputType=\"textPassword\"/>

    <Button
        android:id=\"@+id/loginButton\"
        android:layout_width=\"match_parent\"
        android:layout_height=\"wrap_content\"
        android:text=\"Login\"/>

</LinearLayout>

Step 2: Implement Authentication Logic in Your Activity

In your Activity (e.g., LoginActivity.java or LoginActivity.kt), implement the Firebase Authentication logic:


import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.auth.FirebaseAuth

class LoginActivity : AppCompatActivity() {

    private lateinit var auth: FirebaseAuth

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

        // Initialize Firebase Auth
        auth = FirebaseAuth.getInstance()

        val emailEditText: EditText = findViewById(R.id.emailEditText)
        val passwordEditText: EditText = findViewById(R.id.passwordEditText)
        val loginButton: Button = findViewById(R.id.loginButton)

        loginButton.setOnClickListener {
            val email = emailEditText.text.toString()
            val password = passwordEditText.text.toString()

            auth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this) { task ->
                    if (task.isSuccessful) {
                        // Sign in success
                        Toast.makeText(baseContext, "Authentication successful.",
                            Toast.LENGTH_SHORT).show()
                        // Proceed to the next activity or UI
                    } else {
                        // If sign in fails, display a message to the user.
                        Toast.makeText(baseContext, "Authentication failed: ${task.exception?.message}",
                            Toast.LENGTH_SHORT).show()
                    }
                }
        }
    }
}

Explanation:

  • Initialization: Initialize Firebase Auth using FirebaseAuth.getInstance().
  • UI Elements: Get references to the EditText fields for email and password, and the login button.
  • Login Logic:
    • When the login button is clicked, retrieve the email and password from the EditText fields.
    • Call auth.signInWithEmailAndPassword(email, password) to attempt to sign in the user.
    • Attach an OnCompleteListener to handle the result of the sign-in attempt.
    • If the task is successful, show a success message. Otherwise, show an error message.

Integrating Firebase Realtime Database with XML UI

Firebase Realtime Database is a cloud-hosted NoSQL database that lets you store and sync data between users in real-time. Here’s how to integrate it with your XML UI.

Step 1: Add UI Elements in XML

Add UI elements to your XML layout (e.g., activity_database.xml) to display and update data:


<?xml version=\"1.0\" encoding=\"utf-8\"?>
<LinearLayout
    xmlns:android=\"http://schemas.android.com/apk/res/android\"
    android:layout_width=\"match_parent\"
    android:layout_height=\"match_parent\"
    android:orientation=\"vertical\"
    android:padding=\"16dp\">

    <EditText
        android:id=\"@+id/dataEditText\"
        android:layout_width=\"match_parent\"
        android:layout_height=\"wrap_content\"
        android:hint=\"Enter Data\"/>

    <Button
        android:id=\"@+id/saveButton\"
        android:layout_width=\"match_parent\"
        android:layout_height=\"wrap_content\"
        android:text=\"Save Data\"/>

    <TextView
        android:id=\"@+id/dataTextView\"
        android:layout_width=\"match_parent\"
        android:layout_height=\"wrap_content\"
        android:text=\"Data:\"/>

</LinearLayout>

Step 2: Implement Realtime Database Logic in Your Activity

In your Activity (e.g., DatabaseActivity.java or DatabaseActivity.kt), implement the Firebase Realtime Database logic:


import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener

class DatabaseActivity : AppCompatActivity() {

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

        val dataEditText: EditText = findViewById(R.id.dataEditText)
        val saveButton: Button = findViewById(R.id.saveButton)
        val dataTextView: TextView = findViewById(R.id.dataTextView)

        // Get a reference to the database
        val database = FirebaseDatabase.getInstance()
        val myRef = database.getReference("message")  // Root "message"

        // Read from the database
        myRef.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                // This method is called once with the initial value and again
                // whenever data at this location is updated.
                val value = dataSnapshot.getValue(String::class.java)
                dataTextView.text = "Data: $value"
            }

            override fun onCancelled(error: DatabaseError) {
                // Failed to read value
                dataTextView.text = "Failed to read value."
            }
        })

        // Save data to the database
        saveButton.setOnClickListener {
            val data = dataEditText.text.toString()
            myRef.setValue(data)  // Set the new data to "message"
        }
    }
}

Explanation:

  • Database Reference: Obtain a reference to the Firebase Realtime Database using FirebaseDatabase.getInstance().getReference("message"). This reference points to a node named “message” in the database.
  • Read Data:
    • Use addValueEventListener to listen for changes to the data at the specified location.
    • The onDataChange method is called whenever the data changes, allowing you to update the UI.
    • The onCancelled method is called if the read operation fails.
  • Save Data:
    • When the save button is clicked, retrieve the data from the EditText field.
    • Use myRef.setValue(data) to save the data to the “message” node in the database.

Integrating Firebase Cloud Messaging (FCM) with XML UI

Firebase Cloud Messaging (FCM) enables you to send notifications and messages to your app. Integrating FCM with XML-based UI involves handling the receipt and display of these messages.

Step 1: Set Up FCM in Your Project

Follow the Firebase documentation to set up FCM in your project. This typically involves creating a FirebaseMessagingService class to handle incoming messages.

Step 2: Create FirebaseMessagingService

Create a service class that extends FirebaseMessagingService (e.g., MyFirebaseMessagingService.java or MyFirebaseMessagingService.kt):


import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import your.package.name.MainActivity // Replace with your MainActivity

class MyFirebaseMessagingService : FirebaseMessagingService() {

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        // Handle FCM message here.
        Log.d(TAG, "From: ${remoteMessage.from}")

        // Check if message contains a data payload.
        if (remoteMessage.data.isNotEmpty()) {
            Log.d(TAG, "Message data payload: ${remoteMessage.data}")

            // Handle message data here
            val title = remoteMessage.data["title"] ?: "Default Title"
            val message = remoteMessage.data["body"] ?: "Default Message"
            sendNotification(title, message)
        }

        // Check if message contains a notification payload.
        remoteMessage.notification?.let {
            Log.d(TAG, "Message Notification Body: ${it.body}")
            sendNotification(it.title ?: "Default Title", it.body ?: "Default Message")
        }
    }

    override fun onNewToken(token: String) {
        Log.d(TAG, "Refreshed token: $token")

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // Instance ID token to your app server.
        sendRegistrationToServer(token)
    }

    private fun sendRegistrationToServer(token: String?) {
        // TODO: Implement this method to send token to your app server.
    }

    private fun sendNotification(title: String, messageBody: String) {
        val intent = Intent(this, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        val pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
            PendingIntent.FLAG_IMMUTABLE)

        val channelId = "default_notification_channel_id"
        val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
        val notificationBuilder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(your.package.name.R.drawable.ic_notification) // Replace with your notification icon
            .setContentTitle(title)
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent)

        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        // Since android Oreo notification channel is needed.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(channelId,
                "Channel human readable title",
                NotificationManager.IMPORTANCE_DEFAULT)
            notificationManager.createNotificationChannel(channel)
        }

        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build())
    }

    companion object {
        private const val TAG = "MyFirebaseMsgService"
    }
}

Explanation:

  • onMessageReceived:
    • This method is called when the app receives a message while in the foreground.
    • It checks for both data and notification payloads.
    • For a data payload, it extracts the title and message from the remoteMessage.data.
    • For a notification payload, it extracts the title and body from remoteMessage.notification.
    • It calls the sendNotification method to display the notification.
  • onNewToken:
    • This method is called when a new FCM token is generated for the app.
    • It logs the token and calls the sendRegistrationToServer method (which should be implemented to send the token to your server).
  • sendNotification:
    • This method builds and displays the notification.
    • It creates a PendingIntent to open the MainActivity when the notification is clicked.
    • It sets the notification title, body, icon, and sound.
    • For Android Oreo and higher, it creates a NotificationChannel.

Step 3: Declare the Service in AndroidManifest.xml


<service
    android:name=\".MyFirebaseMessagingService\"
    android:exported=\"false\">
    <intent-filter>
        <action android:name=\"com.google.firebase.MESSAGING_EVENT\"/>
    </intent-filter>
</service>

Conclusion

Integrating Firebase features such as Authentication, Realtime Database, and Cloud Messaging with XML-based UI in Android apps is crucial for many projects. By following these steps, you can seamlessly incorporate Firebase’s powerful functionalities while maintaining a traditional XML-based UI. Understanding these integrations allows developers to bridge the gap between modern backend services and existing Android applications.