3

my application gives error that BroadcastReceiver component are not allowed to bind to service. I am calling broadcast reciver when sms arrived and text will converted in to voice. my reciver called properly.

but My code give error like

FATAL EXCEPTION: main Process: texttospeech.tts.com.tts, PID: 12811 java.lang.RuntimeException: Unable to start receiver texttospeech.tts.com.tts.ttsBroadcast: android.content.ReceiverCallNotAllowedException: BroadcastReceiver components are not allowed to bind to services at android.app.ActivityThread.handleReceiver(ActivityThread.java:2618) at android.app.ActivityThread.access$1700(ActivityThread.java:148) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5312) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696) Caused by: android.content.ReceiverCallNotAllowedException: BroadcastReceiver components are not allowed to bind to services at android.app.ReceiverRestrictedContext.bindService(ContextImpl.java:215) at android.speech.tts.TextToSpeech.connectToEngine(TextToSpeech.java:800) at android.speech.tts.TextToSpeech.initTts(TextToSpeech.java:770) at android.speech.tts.TextToSpeech.(TextToSpeech.java:723) at android.speech.tts.TextToSpeech.(TextToSpeech.java:702) at android.speech.tts.TextToSpeech.(TextToSpeech.java:686) at texttospeech.tts.com.tts.ttsBroadcast.onReceive(ttsBroadcast.java:23) at android.app.ActivityThread.handleReceiver(ActivityThread.java:2611)             at android.app.ActivityThread.access$1700(ActivityThread.java:148)             at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369)             at android.os.Handler.dispatchMessage(Handler.java:102)             at android.os.Looper.loop(Looper.java:135)             at android.app.ActivityThread.main(ActivityThread.java:5312)             at java.lang.reflect.Method.invoke(Native Method)             at java.lang.reflect.Method.invoke(Method.java:372)             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)

this is my code

ttsBroadcast.java

public class ttsBroadcast extends BroadcastReceiver implements TextToSpeech.OnInitListener {
    private TextToSpeech tts;
    private String msg;
    @Override
    public void onReceive(Context context, Intent intent) {


        Toast.makeText(context,"sms recived",Toast.LENGTH_LONG).show();

        tts = new TextToSpeech(context,this);
        tts.speak(msg,TextToSpeech.QUEUE_FLUSH,null);


    }

    @Override
    public void onInit(int i) {

        tts.setLanguage(Locale.ENGLISH);
    }

}
Cœur
  • 37,241
  • 25
  • 195
  • 267

1 Answers1

5

Since it seems that the issue is still active i will add some lines. First I think you should have a service and call this service from the BroadcastReceiver. This could be a possible implementation.

The broadcast receiver:

public class ReadMessageBroadcastReceiver extends BroadcastReceiver {

public final static String TAG = "ReadMessageBroadcastReceiver";

public ReadMessageBroadcastReceiver() {
}

@Override
public void onReceive(Context context, Intent intent) {
    if(intent.getAction().equals(Actions.ACTION_TTS_READ_MESSAGE)) {
        Log.v(TAG, "Read messages on tts received trough notification");
        String[] unreadMessages = intent.getStringArrayExtra(Constants.EXTRA_UNREAD_MESSAGES);
        if (unreadMessages != null) {
            Log.v(TAG, "Read messages on tts trough notification received " + unreadMessages.length
                    + " total messages");
            String messageNumber = context.getString(R.string.notification_message_number);

            ArrayList<String> allMessagesToRead = new ArrayList<>(unreadMessages.length);
            for (int i = 0; i < unreadMessages.length; i++) {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append(messageNumber);
                stringBuilder.append(" ");
                stringBuilder.append(i + 1);
                stringBuilder.append(" ");
                stringBuilder.append(unreadMessages[i]);
                stringBuilder.append(" ");
                allMessagesToRead.add(stringBuilder.toString());
            }

            Log.d(TAG, "Texts to read loud: " + allMessagesToRead);
            Intent speechIntent = new Intent(context, TextToSpeechService.class);
            speechIntent.putStringArrayListExtra(TextToSpeechService.TEXT_TO_READ, allMessagesToRead);
            context.startService(speechIntent);

        }
    }
  }
}

This broadcast receiver gets called by a click on system bar notification. The receiver needs to be declared on the AndroidManifest.xml with an specific action.

 <application>(all your stuff)
  <receiver
        android:name=".broadcastreceivers.ReadMessageBroadcastReceiver"
        android:enabled="true">
        <intent-filter>
            <action android:name="com.example.action.ACTION_TTS_READ_MESSAGE" />
        </intent-filter>
    </receiver>
 </application>

You also need to declare this action somewhere in your app

public static final String ACTION_TTS_READ_MESSAGE = "com.example.action.ACTION_TTS_READ_MESSAGE";

Finally you need to implement the TTSService itself. I have used some parts of code from here

public class TextToSpeechService extends Service implements TextToSpeech.OnInitListener {
public final static String TAG = "TextToSpeechService";

public static final String TEXT_TO_READ = "text";
private final String UTTERANCE_ID = "FINISHED_PLAYING";
private final int MULTI_LINE = 2;

private TextToSpeech tts;
private ArrayList<String> texts;
private boolean isInit;

private UtteranceProgressListener utteranceProgressListener = new UtteranceProgressListener() {
    @Override
    public void onStart(String utteranceId) {

    }

    @Override
    public void onDone(String utteranceId) {
        if (utteranceId.equals(UTTERANCE_ID)) {
            stopSelf();
        }
    }

    @Override
    public void onError(String utteranceId) {
        stopSelf();
    }
};

@Override
public void onCreate() {
    super.onCreate();
    tts = new TextToSpeech(getApplicationContext(), this);
    tts.setOnUtteranceProgressListener(utteranceProgressListener);
    Log.d(TAG, "onCreate");

}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d(TAG, "onStartCommand");
    texts = intent.getStringArrayListExtra(TTSService.TEXT_TO_READ);

    if (isInit) {
        speak();
    }

    return TextToSpeechService.START_NOT_STICKY;
}

@Override
public void onDestroy() {
    if (tts != null) {
        tts.stop();
        tts.shutdown();
    }
    Log.d(TAG, "onDestroy");
    super.onDestroy();
}

@Override
public void onInit(int status) {
    Log.d(TAG, "onInit");
    if (status == TextToSpeech.SUCCESS) {
        int result = tts.setLanguage(Locale.getDefault());
        if (result != TextToSpeech.LANG_MISSING_DATA
                && result != TextToSpeech.LANG_NOT_SUPPORTED) {
            speak();
            isInit = true;
        }
    }
}

private void speak() {
    if (tts != null) {

        // Speak with 3 parameters deprecated but necessary on pre 21 version codes
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            // This is a single message
            String utteranceId = null;
            if (texts.size() < MULTI_LINE) {
                // If is a single message this needs to be the last one
                utteranceId = UTTERANCE_ID;
            }
            tts.speak(texts.get(0), TextToSpeech.QUEUE_FLUSH, null, utteranceId);
            if (texts.size() >= MULTI_LINE) {
                for (int i = 1; i < texts.size(); i++) {
                    if (texts.size() - 1 == i) {
                        // If is the last message add the id
                        utteranceId = UTTERANCE_ID;
                    }
                    tts.speak(texts.get(i), TextToSpeech.QUEUE_ADD, null, utteranceId);
                }
            }
        } else {
            HashMap<String, String> myHashAlarm = null;
            if (texts.size() < MULTI_LINE) {
                myHashAlarm = new HashMap<>();
                myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, UTTERANCE_ID);
            }
            tts.speak(texts.get(0), TextToSpeech.QUEUE_FLUSH, myHashAlarm);
            if (texts.size() >= MULTI_LINE) {
                for (int i = 1; i < texts.size(); i++) {
                    if (texts.size() - 1 == i) {
                        // If is the last message add the id
                        myHashAlarm = new HashMap<>();
                        myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,
                                UTTERANCE_ID);
                    }
                    tts.speak(texts.get(i), TextToSpeech.QUEUE_ADD, myHashAlarm);
                }
            }
        }
    }
}

@Override
public IBinder onBind(Intent arg0) {
    return null;
}

}

Finally don't forget to register your service on the AndroidManifest.xml file

<service
    android:name=".services.TextToSpeechService"
    android:exported="false"/>

Downside, I would say it takes a bit too much time to start (between one sec or 2 depending on the phone). Good side, you don't have the tts running all the time an starts ans stops on demand. There is some logic implemented to find out if is the last message from a series, since the parameter that is passed on the Intent to the service is an ArrayList<String>. If you just need a single string you can simplify the implementation.

pleonasmik
  • 779
  • 10
  • 16