How can I use the the Text To Speech functionality onPause method using WakefulBroadcastReceiver I made the following classes for the purpose:
I am using Receiver for the GCM Push Notification and this code works fine onReceive method but when app is onPause state and the notification dispaly in NotificatioManager at that moment app crashed, help me to sort this problem
GcmBroadcastReceiver
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
Context mContext;
#Override
public void onReceive(Context context, Intent intent)
{
mContext = context;
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE);
if (!services.get(0).topActivity.getPackageName().toString().equalsIgnoreCase(context.getPackageName().toString()))
{
Speaker speaker = new Speaker(mContext);
speaker.allow(true);
speaker.speak("asdas","asdas");
}
}
}
Speaker.class
public class Speaker implements OnInitListener {
private TextToSpeech tts;
private boolean ready = false;
private boolean allowed = false;
public Speaker(Context context){
tts = new TextToSpeech(context, this);
}
public void allow(boolean allowed){
this.allowed = allowed;
}
#Override
public void onInit(int status) {
if(status == TextToSpeech.SUCCESS){
tts.setLanguage(new Locale("en-AU"));
ready = true;
}
else{
ready = false;
}
}
public void speak(String welcomeMessage, String body){
if(allowed) {
HashMap<String, String> hash = new HashMap<String,String>();
hash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_NOTIFICATION));
tts.setSpeechRate((float) 0.8);
tts.speak(welcomeMessage, TextToSpeech.QUEUE_ADD, hash);
tts.playSilence(500, TextToSpeech.QUEUE_ADD, null);
tts.speak(body, TextToSpeech.QUEUE_ADD, hash);
}
}
}
Following exception i have faced
Caused by: android.content.ReceiverCallNotAllowedException:
BroadcastReceiver components are not allowed to bind to services
at android.app.ReceiverRestrictedContext.bindService(ContextImpl.java:173)
at android.speech.tts.TextToSpeech.connectToEngine(TextToSpeech.java:627)
at android.speech.tts.TextToSpeech.initTts(TextToSpeech.java:597)
at android.speech.tts.TextToSpeech.<init>(TextToSpeech.java:553)
at android.speech.tts.TextToSpeech.<init>(TextToSpeech.java:527)
at android.speech.tts.TextToSpeech.<init>(TextToSpeech.java:512)
at sss.sss.sss.Speaker.<init>(Speaker.java:20)
at sss.sss.sss.GcmBroadcastReceiver.onReceive(GcmBroadcastReceiver.java:47)
You should start your service in onReceive passing the message to speak
#Override
public void onReceive(Context context, Intent intent)
{
intent.setAction("speak");
intent.putExtra("welcome_message", "asdas");
intent.putExtra("body", "asdas");
startService(intent.setComponent(new ComponentName(context.getPackageName(),
MyService.class.getName())));
// Where MyService is the service that you implements TTS
}
Then in the service class
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
if (intent != null)
{
String action = intent.getAction();
if ("speak".equals(action))
{
// check if TTS is initialize if so speak the extra
// otherwise save the extra to class members and when onInit is called
// check if these class members are null then speak
}
}
}
Related
I have interrogation about the way to use a BroadcastReceiver with a ResultReceiver in it.
I know that if "A BroadcastReceiver hasn't finished executing within 10 seconds.", there is an ANR.
I have an application that respond to an Intent, declared in the Manifest.
It is a BroadcastReceiver that start a service because it needs to make some networks operations:
public class MyReceiver extends BroadcastReceiver {
private Context context = null;
private MyResultReceiver myResultReceiver = null;
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
myResultReceiver = new MyResultReceiver(new Handler());
Intent i = new Intent();
i.setClass(context, MyService.class);
i.putExtra(Constants.EXTRA_RESULT_RECEIVER, myResultReceiver);
context.startService(i);
}
public class MyResultReceiver extends ResultReceiver {
public MyResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 42) {
// Something
} else {
// Something else
}
}
}
}
My service looks like this:
public class MyService extends Service {
private Context context = null;
private ResultReceiver resultReceiver = null;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.context = this;
resultReceiver = intent.getParcelableExtra(Constants.EXTRA_RESULT_RECEIVER);
MyTask myTask = new MyTask();
myTask.execute();
return super.onStartCommand(intent, flags, startId);
}
public class MyTask extends AsyncTask<Void, Void, Boolean> {
#Override
protected Boolean doInBackground(Void... params) {
// Network operation
return status;
}
#Override
protected void onPostExecute(final Boolean status) {
Bundle bundle = new Bundle();
if (status == true) {
if (resultReceiver != null) {
resultReceiver.send(42, null);
}
} else {
if (resultReceiver != null) {
resultReceiver.send(-1, null);
}
}
}
}
}
My question is, am I sure that the resultReceiver still exist and will do what it have to do if the network operation is longer than 10 seconds ?
Here's the relevant documentation from the SDK:
If this BroadcastReceiver was launched through a tag, then
the object is no longer alive after returning from this function. This
means you should not perform any operations that return a result to
you asynchronously -- in particular, for interacting with services,
you should use startService(Intent) instead of bindService(Intent,
ServiceConnection, int). If you wish to interact with a service that
is already running, you can use peekService(Context, Intent).
The Intent filters used in registerReceiver(BroadcastReceiver,
IntentFilter) and in application manifests are not guaranteed to be
exclusive. They are hints to the operating system about how to find
suitable recipients. It is possible for senders to force delivery to
specific recipients, bypassing filter resolution. For this reason,
onReceive() implementations should respond only to known actions,
ignoring any unexpected Intents that they may receive.
Bottom line:
If you start a service, use startService(Intent).
Don't do long running applications on onReceive.
AsyncTasks may be destroyed, your best bet is to use a Service. If you are using an AsyncTask inside of a Service, it should be fine.
I am developing a simple hello world application in Google Glass using Service. In my application I used TimelineManager to display a LiveCard. I want to invoke a http call when the app is visible to user (I mean when user scrolled from other app to our app).
I know if we use Activity, onResume() invoked automatically, but I am doing it in Service.
Please let me know which method will be invoked when app is resumed to user.
public class MyGlassService extends Service {
private static final String TAG = "SocketService";
private static final String LIVE_CARD_ID = "livecard";
private TimelineManager mTimelineManager;
private LiveCard mLiveCard;
private TextToSpeech mSpeech;
private final IBinder mBinder = new MainBinder();
private TextView txtName, txtBalance;
private RemoteViews remoteView;
private WakeLock screenLock;
public class MainBinder extends Binder {
public void sayMessage() {
mSpeech.speak(getString(R.string.hello_world),
TextToSpeech.QUEUE_FLUSH, null);
}
}
#Override
public void onCreate() {
super.onCreate();
mTimelineManager = TimelineManager.from(this);
mSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
// do nothing
}
});
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
remoteView = new RemoteViews(this.getPackageName(), R.layout.activity_main);
if (mLiveCard == null) {
mLiveCard = mTimelineManager.createLiveCard(LIVE_CARD_ID);
remoteView.setTextViewText(R.id.name, getString(R.string.pre_screen_msg));
mLiveCard.setViews(remoteView);
}
return START_STICKY;
}
#Override
public void onDestroy() {
if (mLiveCard != null && mLiveCard.isPublished()) {
mLiveCard.unpublish();
mLiveCard = null;
}
mSpeech.shutdown();
mSpeech = null;
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public String toHexString(byte[] data) {
String s = new String(data);
return s;
}
}
Live cards do not provide precise lifecycle methods like activities do to let you know when a user has scrolled to or away from one.
If you would like to see this feature, please post a feature request describing your use case on our issue tracker.
I have a service running on device boot. It checks for some data and send out Notifications.
I came across the following.
http://mobile.tutsplus.com/tutorials/android/android-sdk-using-the-text-to-speech-engine/
and I want to send voice notification. I do not need UI part of it. How do I add it in my project?
Differnt java class (calling Activity from service)
An internal class
Create class App and an instance of TextToSpeech in it:
public class App extends Application {
private static TextToSpeech mTts;
public static TextToSpeech getmTts() {
return mTts;
}
public void onCreate() {
super.onCreate();
// creating TTS:
mTts = new TextToSpeech(this, this);
mTts.setLanguage(Locale.US);
mTts.stop();
}
}
Declare App(above) in your manifest:
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:name="your.application.package.App" >
Send a broadcast by your service when you want to a BroadcastReceiver for example this:
public class TTSReceiver extends BroadcastReceiver implements OnInitListener {
private TextToSpeech mTts;
private String message;
#Override
public void onReceive(Context context, Intent intent) {
mTts = App.getmTts();
mTts.setLanguage(Locale.US);
message = "your message";
mTts.stop();
mTts.speak(message, TextToSpeech.QUEUE_FLUSH, null);
}
public void onInit(int status) {
}
}
public class SpeakService extends Service implements OnInitListener {
public static TextToSpeech tts;
private String string;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
tts = new TextToSpeech(this, this);
super.onCreate();
}
#Override
public void onDestroy() {
if (tts != null) {
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
string = intent.getStringExtra("string");
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
int result = tts.setLanguage(Locale.UK);
if (result == TextToSpeech.LANG_MISSING_DATA
|| result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.d("SpeakService", "Language is not available.");
} else {
if (!TextUtils.isEmpty(string)) {
speak(string);
} else {
speak("Error");
}
}
} else {
Log.d("SpeakService", "Could not initialize TextToSpeech.");
}
}
private void speak(String string) {
tts.speak(string, TextToSpeech.QUEUE_FLUSH, null);
}
I'm trying to start a service after the device has been started. The problem is that the service needs some arguments which it normally gets this way:
public class ServiceClass extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
searchString = (String) intent.getExtras().get(GET_SEARCHSTRING_AFTER_START);
[...]
return START_STICKY;
public static final String GET_SEARCHSTRING_AFTER_START = "SEARCHVALUE";
public class OnStartupStarter[...]
}
But when the service should be started via a BroadcastReceiver when the device has started I can't put the searchString because this is given by an activity. When the service starts after the device has been started the service should start with the searchString it had before the device was turned off.
The BroadcastReceiver is a subclass from the service's class:
public static class OnStartupStarter extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
/* TODO: Start the service with the searchString it
* had before the device has been turned off...
*/
}
}
Normally the service is started like this:
private OnCheckedChangeListener ServiceSwitchCheckedStatusChanged
= new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
Intent s = new Intent(getBaseContext(), ServiceClass.class);
s.putExtra(ServiceClass.GET_SEARCHSTRING_AFTER_START, <ARGUMENT>);
if(isChecked)
startService(s);
else
stopService(s);
}
};
Save last search string in SharedPreference in activity onPause method, retrieve last search string when you receive BootCompleted broadcast and start your service as usually.
Activity:
protected void onPause() {
super.onPause();
SharedPreferences pref = getSharedPreferences("my_pref", MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
editor.putString("last_search", mLastSearch);
editor.commit();
}
BroadcastReceiver:
public static class OnStartupStarter extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
SharedPreferences pref = context.getSharedPreferences("my_pref", MODE_PRIVATE);
String lastSearch = pref.getString("last_search", null);
if (lastSearch != null) {
// TODO: Start service
}
}
}
}
I need to call TTS service within subclass of BroadcastReceiver. When I am implement that class from OnInitListener, it gave run-time error.
Is there any other-way to implement TTS within BroadcastReceiver?
Thank You,
Sorry Code:
public class TextApp extends BroadcastReceiver implements OnInitListener {
private TextToSpeech tts;
private String message = "Hello";
#Override
public void onReceive(Context context, Intent intent) {
tts = new TextToSpeech(context, this);
message = "Hello TTS";
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS)
{
tts.speak(message, TextToSpeech.QUEUE_FLUSH, null);
}
}
}
Your code didn't work on :
tts = new TextToSpeech(context, this);
Context on BroadcastReceiver is a "restricted context". It means you cannot start service on context in BroadcastReceiver. Because TTS is a service, so it doesn't call anyting.
The Best Solutions is you start another intent on BroadcastReceiver with activity that call the service.
public void onReceive(Context context, Intent intent) {
....
Intent speechIntent = new Intent();
speechIntent.setClass(context, ReadTheMessage.class);
speechIntent.putExtra("MESSAGE", message.getMessageBody().toString());
speechIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(speechIntent);
....
}
And then on the activity you call the TTS service with parameter from extras
public class ReadTheMessage extends Activity implements OnInitListener,OnUtteranceCompletedListener {
private TextToSpeech tts = null;
private String msg = "";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent startingIntent = this.getIntent();
msg = startingIntent.getStringExtra("MESSAGE");
tts = new TextToSpeech(this,this);
}
#Override
protected void onDestroy() {
super.onDestroy();
if (tts!=null) {
tts.shutdown();
}
}
// OnInitListener impl
public void onInit(int status) {
tts.speak(msg, TextToSpeech.QUEUE_FLUSH, null);
}
// OnUtteranceCompletedListener impl
public void onUtteranceCompleted(String utteranceId) {
tts.shutdown();
tts = null;
finish();
}
}
You can try using either JobIntentService (Post Android-O) or IntentService to invoke TTS from Broadcast receiver. It has less overhead than launching an activity for the sake of giving TTS a correct context. Note that you cannot give a broadcast receiver's context to TTS.
Here is my code snippet where I acheived same thing using JobIntentService.
Inside your custom Broadcast receiver's onReceive() invoke your custom JobIntentService like this:
Intent speechIntent = new Intent();
speechIntent.putExtra("MESSAGE", "Bluetooth is on.");
MySpeakService.enqueueWork(context, speechIntent);
And MySpeakService.java is this:
import android.content.Context;
import android.content.Intent;
import android.speech.tts.TextToSpeech;
import android.support.annotation.NonNull;
import android.support.v4.app.JobIntentService;
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) {
String message = intent.getStringExtra("MESSAGE");
mySpeakTextToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
mySpeakTextToSpeech.speak(message, TextToSpeech.QUEUE_ADD, null, null);
while (mySpeakTextToSpeech.isSpeaking()) {
}
isSafeToDestroy = true;
}
});
}
#Override
public void onDestroy() {
if (isSafeToDestroy) {
if (mySpeakTextToSpeech != null) {
mySpeakTextToSpeech.shutdown();
}
super.onDestroy();
}
}
}
Al Zil answer is not totally correct. Android TTS is a bounded service. Broadcast receivers truly has a limited context but they can't bind themselves to any service. However, they can START a service. Starting the tts from activity is ok, but if you don't need UI you can also initialize it from a service. Look at this answer to see how it's done
Good luck.