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);
}
Related
This is my Service Class
public class ContactSync extends IntentService {
private static final String TAG = "com.ric.connectme.Services.ContactSync";
public ContactSync() {
super(TAG);
}
public ContactSync(String name) {
super(name);
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
if (intent != null) {
Toast t = Toast.makeText(getBaseContext(), message, Toast.LENGTH_LONG);
t.setGravity(Gravity.CENTER, 0, 0);
t.show();
GoForUplodeContect(intent);
}
}
private void GoForUplodeContect(Intent intent){
RsXtraUserNumbersBackupUploder.Uplode(getBaseContext(),intent);
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
This Is What I Inserted In AndroidManifest
<service android:name=".Services.ContactSync">
</service>
And This is how I am calling this service
Intent intent = new Intent(context,ContactSync.class);
intent.putExtra(RsXtraIntetBundelKeys.USER_ID,userId);
context.startService(intent);
I Tried everything and system starting the service very well but the onHandleIntent is not executing.
Can any one help me on this ?
I want to check internet connection continuously on every activity with the broadcastreceiver. I already write the code and it perfectly working. But I want to use it in every activity. How can I modify this code?
public class MainActivity extends Activity {
private static final String LOG_TAG = "CheckNetworkStatus";
private NetworkChangeReceiver receiver;
private boolean isConnected = false;
private TextView networkStatus;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
receiver = new NetworkChangeReceiver();
registerReceiver(receiver, filter);
networkStatus = (TextView) findViewById(R.id.networkStatus);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
protected void onDestroy() {
Log.v(LOG_TAG, "onDestory");
super.onDestroy();
unregisterReceiver(receiver);
}
public class NetworkChangeReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
Log.v(LOG_TAG, "Receieved notification about network status");
isNetworkAvailable(context);
}
private boolean isNetworkAvailable(Context context) {
ConnectivityManager connectivity = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null) {
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[i].getState() == NetworkInfo.State.CONNECTED) {
if(!isConnected){
Log.v(LOG_TAG, "Now you are connected to Internet!");
networkStatus.setText("Now you are connected to Internet!");
isConnected = true;
//do your processing here ---
//if you need to post any data to the server or get status
//update from the server
}
return true;
}
}
}
}
Log.v(LOG_TAG, "You are not connected to Internet!");
networkStatus.setText("You are not connected to Internet!");
isConnected = false;
return false;
}
}
}
I want to use it in every activity. How can I modify this code?
Create a BaseActivity class which extends AppCompatActivity, and then make all of your Activity classes extend this BaseActivity class. Put your code to check internet connection in the BaseActivity class. cheers :)
Create a BaseActivity without a layout which extends to AppCompatActivity or Activity.
Extend all the other activities (or the one where you need to check for internet connectivity) of your app to the BaseActivity.
Now use a broadcast receiver in the BaseActivity which constantly checks for network and connectivity state.
Reference code for BaseActivity. (In my case, I show a snackbar when there is no connection detected.)
public class BaseActivity extends AppCompatActivity {
private static final int WIFI_ENABLE_REQUEST = 0x1006;
private BroadcastReceiver mNetworkDetectReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
checkInternetConnection();
}
};
private AlertDialog mInternetDialog;
private boolean isConnected;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isConnected = false;
registerReceiver(mNetworkDetectReceiver, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION));
}
#Override
protected void onDestroy() {
unregisterReceiver(mNetworkDetectReceiver);
super.onDestroy();
}
private void checkInternetConnection() {
ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = manager.getActiveNetworkInfo();
if (ni != null && ni.getState() == NetworkInfo.State.CONNECTED) {
isConnected =true;
showNoConnectionSnackBar("Connected", isConnected, 1500);
} else {
isConnected= false;
showNoConnectionSnackBar("No active Internet connection found.", isConnected,6000);
}
}
private void showNoConnectionSnackBar(String message, boolean isConnected, int duration) {
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content),
message, duration);
View sbView = snackbar.getView();
TextView textView = (TextView) sbView
.findViewById(android.support.design.R.id.snackbar_text);
textView.setTextColor(ContextCompat.getColor(this, android.R.color.white));
if (isConnected){
sbView.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
}else{
sbView.setBackgroundColor(getResources().getColor(R.color.google_button_color));
snackbar.setAction("Turn On", new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent internetOptionsIntent = new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS);
startActivityForResult(internetOptionsIntent, WIFI_ENABLE_REQUEST);
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.colorPrimary));
}
snackbar.show();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == WIFI_ENABLE_REQUEST) {
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
Create a service. start the service in the launcher activity and put the network check code in that service.
public class NetworkServiceextends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//register receiver here
// connection check code
return START_STICKY;
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onDestroy() {
super.onDestroy();
// unregister receiver
}
}
You can put your BroadcastReceiver in it's own class, and then declare/register it in your Manifest. That way it will share the lifetime of your application.
An additional benefit is that you dont have to worry about unregistering it, so you won't have to worry about memory leaks.
Step 1. Add the JitPack repository to your build file Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.SumonHub:EagleEye:1.0.0'
}
If you do not override the Application class, edit your manifest file to set android:name in the tag as follows:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<application
android:name="org.sumon.eagleeye.EagleEyeApplication" >
...
</application>
</manifest>
If you do override the Application class, change it to extend EagleEyeApplication (if possible) as follows:
public class MyApplication extends EagleEyeApplication { ... }
In youre activity/fragment get status like below
EagleEyeObserver.setConnectivityListener(new OnChangeConnectivityListener() {
#Override
public void onChanged(boolean status) {
Toast.makeText(MainActivity.this, "" + status, Toast.LENGTH_SHORT).show();
}
});
more info here
I have an application containing Activity A, Service S and BroadcastReceiver BR. What I want the application to do is to listen to when the user unlocks the phone. This is currently achieved by having A start S and bind to it. S will start and register BR to listen for "android.intent.action.USER_PRESENT".
The application should theoretically be able to listen for phone unlocks indefinitely in the background. My application completes its purpose, but i have seen in the Android Monitor that memory usage steadily increases, and will increase by approximately 0,033MB for each unlock (every time onReceive is called(?)).
Following is the code.
Activity:
public class MainActivity extends Activity implements BackgroundService.ServiceCallbacks
{
private TextView lastUnlock;
private boolean isBound = false;
private BackgroundService backgroundService;
private ServiceConnection serviceConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
BackgroundService.LocalBinder binder = (BackgroundService.LocalBinder) service;
backgroundService = binder.getServiceInstance();
backgroundService.registerActivity(MainActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName name)
{
isBound = false;
backgroundService = null;
}
};
/** Activity is created, start service and bind to it **/
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lastUnlock = (TextView) findViewById(R.id.lastUnlock_TW);
Intent serviceIntent = new Intent(this, BackgroundService.class);
startService(serviceIntent);
bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE);
isBound = true;
}
/** Activity is destroyed, it should unbind from the service **/
#Override
protected void onDestroy()
{
super.onDestroy();
if (isBound)
{
backgroundService.unregisterActivity();
unbindService(serviceConnection);
isBound = false;
}
}
/** The service can call this method to update the lastUnlock TextView **/
#Override
public void updateClient(String data)
{
lastUnlock.setText(data);
}
}
Service:
public class BackgroundService extends Service
{
ServiceCallbacks activity;
private final IBinder LOCAL_BINDER = new LocalBinder();
private UserPresentReceiver userPresentReceiver;
SharedPreferences sharedPreferences;
/** The service is created, receivers are registered here **/
#Override
public void onCreate()
{
super.onCreate();
userPresentReceiver = new UserPresentReceiver();
registerReceiver(userPresentReceiver, new IntentFilter("android.intent.action.USER_PRESENT"));
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
}
/** An activity called startService() or the process was killed, returning START_STICKY
** which restarts the service and this method is called **/
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return START_STICKY;
}
/** An activity just bound to the service **/
#Override
public IBinder onBind(Intent intent)
{
return LOCAL_BINDER;
}
/** Service is destroyed, unregister the receivers **/
#Override
public void onDestroy()
{
super.onDestroy();
unregisterReceiver(userPresentReceiver);
}
/** **/
public class LocalBinder extends Binder
{
public BackgroundService getServiceInstance()
{
return BackgroundService.this;
}
}
/** Update the activity if it is connected, and save the unlockStatus in the preferences **/
public void updateActivity(String unlockStatus)
{
if (activity != null)
{
this.activity.updateClient(unlockStatus);
}
SharedPreferences.Editor spEditor = sharedPreferences.edit();
spEditor.putString("saved_last_unlock", unlockStatus);
spEditor.apply();
}
/** Methods an activity can call to register or unregister itself to the service **/
public void registerActivity(Activity activity)
{
this.activity = (ServiceCallbacks) activity;
// If the last unlock is saved in the preferences, retrieve it from there
String savedLastUnlock = sharedPreferences.getString("saved_last_unlock", null);
if (savedLastUnlock != null)
{
this.activity.updateClient(savedLastUnlock);
}
}
public void unregisterActivity()
{
activity = null;
}
/** An activity has to implement this interface so that the service can send commands to it **/
public interface ServiceCallbacks
{
void updateClient(String data);
}
}
BroadcastReceiver:
public class UserPresentReceiver extends BroadcastReceiver
{
private final String TAG = "UserPresentReceiver";
UnlockStatus unlockStatus;
public UserPresentReceiver()
{
unlockStatus = new UnlockStatus();
}
#Override
public void onReceive(Context context, Intent intent)
{
unlockStatus.setStatus(new Date());
BackgroundService service = (BackgroundService) context;
service.updateActivity(unlockStatus.getStatus());
}
}
UnlockStatus:
public class UnlockStatus
{
private Calendar calendar;
private final SimpleDateFormat SDF = new SimpleDateFormat("dd.MM.yyyy' kl. 'HH:mm:ss");
private boolean unlockRegistered = false;
public UnlockStatus()
{
calendar = Calendar.getInstance();
}
public String getStatus()
{
if (unlockRegistered)
return SDF.format(calendar.getTime());
else
return null;
}
public void setStatus(Date date)
{
unlockRegistered = true;
calendar.setTime(date);
}
}
Please find the solution
public class MyApplication extends Application implements HM_Constants, Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
public static String stateOfLifeCycle = "";
public static boolean wasInBackground = false;
private static HydratemateApplication mInstance;
private static String TAG = HydratemateApplication.class.getName();
boolean status;
ScreenOffReceiver screenOffReceiver = new ScreenOffReceiver();
public static synchronized HydratemateApplication getInstance() {
return mInstance;
}
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
registerActivityLifecycleCallbacks(this);
registerReceiver(screenOffReceiver, new IntentFilter("android.intent.action.SCREEN_OFF"));
// new HM_PrefsManager(mInstance).save(Prefs_Keys.SERVICE_THREAD_STOP,"NO");
}
#Override
public void onActivityCreated(Activity activity, Bundle arg1) {
// Log.d(TAG, "onActivityCreated " + activity.getLocalClassName());
wasInBackground = false;
status = false;
stateOfLifeCycle = "Create";
}
#Override
public void onActivityStarted(Activity activity) {
// Log.d(TAG, "onActivityStarted " + activity.getLocalClassName());
stateOfLifeCycle = "Start";
}
#Override
public void onActivityResumed(Activity activity) {
Log.d(TAG, "onActivityResumed " + activity.getLocalClassName());
stateOfLifeCycle = "Resume";
try {
Intent service = new Intent(getApplicationContext(), VolumeService.class);
stopService(service);
} catch (Exception e) {
e.printStackTrace();
}
try {
if (BatteryHealthActivity.mBluetoothLeForegroundService != null && status) {
//BatteryHealthActivity.mBluetoothLeForegroundService.indicateCharacteristic(UUID.fromString(GattAttributes.BATTERY_LEVEL_SERVICE_UUID), UUID.fromString(GattAttributes.BATTERY_LEVEL_CHARACTERSTIC_UUID), true);
status = false;
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onActivityPaused(Activity activity) {
// Log.d(TAG, "onActivityPaused " + activity.getLocalClassName());
stateOfLifeCycle = "Pause";
}
#Override
public void onActivityStopped(Activity activity) {
// Log.d(TAG, "onActivityStopped " + activity.getLocalClassName());
stateOfLifeCycle = "Stop";
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle arg1) {
//Log.d(TAG, "onActivitySaveInstanceState " + activity.getLocalClassName());
}
#Override
public void onActivityDestroyed(Activity activity) {
// Log.d(TAG, "onActivityDestroyed " + activity.getLocalClassName());
Log.d("iam", "calling");
wasInBackground = false;
stateOfLifeCycle = "Destroy";
}
#Override
public void onTrimMemory(int level) {
if (stateOfLifeCycle.equals("Stop")) {
wasInBackground = true;
}
super.onTrimMemory(level);
Log.d(TAG, "onTrimMemory " + level);
onBackground();
}
public void onBackground(){
if (!isMyServiceRunning(VolumeService.class)) {
try {
status = true;
if (BatteryHealthActivity.mBluetoothLeForegroundService != null) {
BatteryHealthActivity.mBluetoothLeForegroundService.indicateCharacteristic(UUID.fromString(GattAttributes.BATTERY_LEVEL_SERVICE_UUID), UUID.fromString(GattAttributes.BATTERY_LEVEL_CHARACTERSTIC_UUID), false);
}
new ForegroundCheckTask().execute(mInstance).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
class ScreenOffReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
wasInBackground = true;
onBackground();
}
}}
My TTS in service is not stopping and speak two times.
In logcat the error is "stop failed: not bound to TTS engine"
I am stuck here that why TTS is not stoping. What wil I do here to stop it when speaked.
Here is my code
public class Speaker extends Service implements TextToSpeech.OnInitListener {
public static TextToSpeech mtts;
String speech = "";
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
}
#Override
public void onStart(Intent intent, int startid) {
speech=intent.getExtras().getString("speech");
mtts = new TextToSpeech(getApplicationContext(), this);
}
#Override
public void onDestroy() {
if (mtts != null) {
Log.e("Destroy", "Service");
mtts.stop();
mtts.shutdown();
}
super.onDestroy();
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS)
mtts.speak(speech, TextToSpeech.QUEUE_ADD, null);
}
Log.e("onInit ends", "Service");
}
}
}
One reason is that when a phone call is incoming then at this time your broadcast receiver would be calling this activity again and again unless you pick the phone
If you want to stop this service after make it speak for once.
Code for stopping the service.
Intent in = new Intent(this, Speaker.class);
stopService(in);
Use this code inside onDestroy() or in onStart() method.
Why you are using the service for TTS?, you can use any activity or class etc.
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.