Skip to main content

Speak Out FCM Notification using Text to Speech (TTS)

Hello everyone! hope you are doing well .Here is another article on Speak out the notification received from Firebase .Basically we will use Text to Speech Service to achieve this. Before going further lets discuss something about FCM behavior.

There are two types of messages in FCM (Firebase Cloud Messaging):

Display Messages: These messages trigger the onMessageReceived() callback only when your app is in foreground.

Data Messages: Theses messages trigger the onMessageReceived() callback even if your app is in foreground/background/killed.

So we can say that we will not receive data messages from Firebase console when app is in background or killed. To solve this problem we have to send FCM notification using our Server or we can also use Postman for sending notification as a Server.

Here are some steps to follow for send notification using Postman.

  • Copy Legacy Server Key from Firebase Console > Project Settings > Cloud Messaging 
  • In Postman Select POST. Enter request URL as https://fcm.googleapis.com/fcm/send
  • Add Headers Authorization: key=<legacy_server_key> OR Authorization: key=<server_key>and Content-Type: application/json.

  • Now Select Body > raw > JSON (application/json) and add following code:
 {  
  "to" : "YOUR_FCM_TOKEN_WILL_BE_HERE",  
  "notification" : {  
    "body" : "Body of Your Notification",  
    "title": "Title of Your Notification"  
  },  
  "data" : {  
    "body" : "Body of Your Notification",  
    "title": "Title of Your Notification",  
    "message" : "The content here which is about to speak out",  
  }  
 }  

 Now that's it send from Postman using send button.

via GIPHY

But that's not our main motto .lets come to coding part for our main task which is speak out our notification .So lets move step by step:-

Step 1: Create FirebaseDataReceiver.java file 


package com.FCMVoice;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;


public class FirebaseDataReceiver extends BroadcastReceiver {

    private final String TAG = "FirebaseDataReceiver";

    public void onReceive(Context context, Intent intent) {
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
          
           String msg=bundle.get("message").toString();

           try{

               if(msg!=null) {

                   Intent speechIntent=new Intent();
                   speechIntent.putExtra("voicedata",msg);
                   MySpeakService.enqueueWork(context, speechIntent);

               }

           }
           catch (Exception e)
           {
               e.printStackTrace();
           }


        }
    }




}

here "mesage" is which we will receive from FCM data .We can not call our Text to Speech here because TTS(Text to Speech) is a service which we can not call to Broadcast Receiver class .So here we use JobIntentService which is subclass of Service .

There is one question in your mind that why we not using service class or foregroundservice here .The answer is that service will automatically stop after sometime when app goes into background and for the case of foregroundservice  we have to notify use a Service Notification all time ,which many users don't like this.


We can also use WorkManager class here which is very latest and useful but here we will use JobIntentService class.

JobIntentService is a modern way to run the background service from the background application. JobIntentService works in the same way as a Service however it enqueues the work into the JobScheduler on compatible Android targets( SDK 26 or more).


Step 2: Create MySpeakService.java class

package com.FCMVoice;

import android.content.Context;
import android.content.Intent;
import android.speech.tts.TextToSpeech;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.core.app.JobIntentService;

import java.util.Locale;

public class MySpeakService extends JobIntentService {

    private TextToSpeech mySpeakTextToSpeech = null;
    private boolean isSafeToDestroy = false;

    public static void enqueueWork(Context context, Intent intent) {
        enqueueWork(context, MySpeakService.class, 1, intent);
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        final String message = intent.getStringExtra("voicedata");

        mySpeakTextToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int status) {
                try {
                    if (mySpeakTextToSpeech != null && status == TextToSpeech.SUCCESS) {
                        Log.d("spoken text",message);
                        mySpeakTextToSpeech.setLanguage(new Locale("hi"));
                        mySpeakTextToSpeech.setPitch(1.0f);
                        mySpeakTextToSpeech.setSpeechRate(1.0f);
                        mySpeakTextToSpeech.speak(message, TextToSpeech.QUEUE_ADD, null);
                    }
                } catch (Exception ex) {
                    Log.d("Exception spoken text",message);
                    System.out.print("Error handling TextToSpeech GCM notification " + ex.getMessage());
                }
                while (mySpeakTextToSpeech.isSpeaking()) {

                }
                isSafeToDestroy = true;
            }
        });
    }

    @Override
    public void onDestroy() {
        if (isSafeToDestroy) {
            if (mySpeakTextToSpeech != null) {
                mySpeakTextToSpeech.shutdown();
            }
            super.onDestroy();
        }
    }
}


Step 3: Now add some important lines in AndroidManifest.xml 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.FCMVoice">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <uses-permission android:name="android.permission.WAKE_LOCK" />
 


    <application
        android:name="com.FCMVoice.MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        
        <activity
            android:name="com.FCMVoice.Splash"
            android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        

        <service android:name=".MyFirebaseInstanceIDService">
            <intent-filter>
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
            </intent-filter>
        </service>
        <service android:name=".MyFirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>


        <receiver
            android:name=".FirebaseDataReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter android:directBootAware="true">
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </receiver>



        <service android:name=".MySpeakService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:enabled="true"
            android:exported="true"/>



        <meta-data
            android:name="com.google.firebase.messaging.default_notification_icon"
            android:resource="@mipmap/ic_launcher_round" />
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_color"
            android:resource="@color/colorPrimaryDark" />
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="FCMVoice_01" />


    </application>

</manifest>


Now that's all, Thank you guys  .Clap clap clapπŸ‘πŸ‘πŸ‘ 

Comments

Popular posts from this blog

How to Download Apk file from Url and Install Programmatically

In this post we learn about download apk file from server or website and then install it Programmatically in Phone. Sometimes we have to download external apk file from server and then install if downloading successfully finished.For this we use AsyncTask class  for background process. So here is Code Snippet for this task.Lets Start :- Before this we have to add these Permissions in Manifest.xml file : <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> DownloadNewVersion.java class DownloadNewVersion extends AsyncTask<String,Integer,Boolean> { @Override protected void onPreExecute() { super.onPreExecute(); bar = new ProgressDialog(getActivity()); bar.setCancelable(false); bar.setMessage("Downl...

How to Implement Item Click Interface in Android?

In Android development, creating interactive lists is essential to most apps. Often, these lists include items that users can click on to trigger actions such as opening a new screen or displaying more information. In this tutorial, we’ll walk through how to implement an item click interface in Android using Java and XML. By the end of this guide, you’ll know how to create a RecyclerView with an item click listener that responds to user taps. This approach is widely used in modern Android development because RecyclerView is both flexible and efficient. Step 1: Set Up Your Android Project First, create a new Android project in Android Studio. Make sure to choose Java as the programming language. Step 2: Add Dependencies Make sure that you have the required dependencies for RecyclerView in your build.gradle file. If not, add them like this: dependencies {     implementation 'androidx.recyclerview:recyclerview:1.2.1' } Sync the project after adding the dependency. Step 3: Define ...

Working with Android 11 (Changes and Security Features)

 Hello everyone , I am here with new article which is hot topic nowadays "Android 11" .The stable version of Android.  Android 11 is the eleventh major release and 18th version of Android, the mobile operating system developed by the Open Handset Alliance led by Google. It was released on September 8, 2020.It is comes with many security features and other features as well . And it is now compulsory in play store  to upload new apps with API lavel 30 which is compatible with Android 11 and from November onwards old apps also have to update with API 30 .Some other guidelines you can check out from here . Play Store Guidelines So its clear that we have to update our apps with API level 30 .But Android 11 comes with some changes as well which we have to do in our projects. For example from Android developer site "Android 11 (API level 30) further enhances the platform, giving better protection to app and user data on external storage. ". Scoped storage enforcement: Apps...