Please be informed, we are trying to start activity from service class, which is fired on clicking push notification addaction intent. The service class contains two actions, one to stop playing ringtone and another to startactivity. But unfortunately the start activity just does not boot in our service class.
Service Class page is as given below:
public class RingtonePlayingService extends Service {
private static final String TAG = RingtonePlayingService.class.getSimpleName();
private static final String URI_BASE = RingtonePlayingService.class.getName() + ".";
public static final String ACTION_DISMISS = URI_BASE + "ACTION_DISMISS";
public static final String ACTION_START = URI_BASE + "ACTION_START";
private MediaPlayer mp;
private Ringtone ringtone;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
int notificationId = intent.getIntExtra("notificationId", 0);
if (intent == null) {
Log.d(TAG, "The intent is null.");
return START_REDELIVER_INTENT;
}
String action = intent.getAction();
if (ACTION_START.equals(action)) {
String uri = String.valueOf(intent.getIntExtra("uri", 0));
Intent intents = new Intent(this, MainActivity.class);
intents.putExtra("uri", uri);
intents.putExtra("notification_id", notificationId);
intents.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
this.startActivity(intents);
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mp.reset ();
}}
Firebase (from where service class's intent is fired)
Intent startIntent = new Intent(this, RingtonePlayingService.class);
startIntent.setAction(RingtonePlayingService.ACTION_START);
startIntent.putExtra("uri", uri);
startIntent.putExtra("notification_id", notification_id);
PendingIntent pt = PendingIntent.getService(this,123, startIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action action_n = new NotificationCompat.Action.Builder(R.mipmap.ic_launcher, tag, pt).build();
The objective here is on firing of pending Intent 'pt'. We want to start the activity (that opens the app's url) as well as run the service class (which stops the service and the ringtone).
Please help us find a solution on this never ending issue.
The service class contains two actions, one to stop playing ringtone and another to startactivity. But unfortunately the start activity just does not boot in our service class.
You cannot start an activity from the background on modern versions of Android.
Firebase (from where service class's intent is fired)
That code has problems:
Your Intent is for RingtonePlayingService, which according to your first code snippet is a Service. Yet, you try using it with PendingIntent.getActivity(), rather than PendingIntent.getService().
You are using addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), which are irrelevant for a Service.
Related
This is my first question and I've been trying to find a solution to this for hours but can't get it to work. I'm building an android app that takes an input from the user (number of hours) to fast (not eat). The input is then taken to the service where it does a countdown in the background. Along the way, I'd like the user to access other activities that could you the results from the countdown timer (eg, time_left/total_time = percentage complete). So far, my button that I've created works to make the call for the service. but the service never gets called to update the text view. Thanks
Here is what I have,
public class StartFast extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_fast);
startService(new Intent(this, MyService.class));
Log.i("Started service", "hello started service...");
registerReceiver(br, new IntentFilter("COUNTDOWN_UPDATED"));
}
private BroadcastReceiver br = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
intent.getExtras();
long millisUntilFinished = intent.getLongExtra("countdown",0);
String time = Long.toString((millisUntilFinished));
TextView tv = findViewById(R.id.timeView1);
tv.setText(time);
}
};
public void BeginFast(View view){
//Intent intent = new Intent( this, StartFast.class);
// below is how to pass an intent for use in a Service to run in the backgroun
Intent intent =new Intent(this, MyService.class);
startService(intent);
// intent.putExtra() // putExtra longs ...will do after static run succeeds
//intent.putExtra("data", data); //adding the data
Intent intent1 = new Intent(this, Heart.class);
startActivity(intent1);
}
}
and here is the service class,
public class MyService extends Service {
private final static String TAG = "MyService";
public static final String COUNTDOWN_BR = "FastBreak.countdown_br";
Intent bi = new Intent(COUNTDOWN_BR);
CountDownTimer cdt = null;
public void OnCreate(){
super.onCreate();
Log.i(TAG, "starting timer...");
cdt = new CountDownTimer(30000,1000) {
#Override
public void onTick(long millisUntilFinished){
Log.i(TAG, "Countdown seconds remaining: " +millisUntilFinished /1000);
bi.putExtra("countdown", millisUntilFinished);
sendBroadcast(bi);
}
#Override
public void onFinish(){
Log.i(TAG, "Timer finished");
}
};
cdt.start();
}
#Override
public void onDestroy() {
cdt.cancel();
Log.i(TAG, "Timer cancelled");
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
https://github.com/greenrobot/EventBus
Check this out. This library is the best and easiest implementation of broadcasts. You can send any data to any object (StartFast service in your case) from any other object (StartFast activity in your case) and write code to run.
First, you need to start the service and register it in the manifest. After the service is started by an activity, it will keep running in the background.
You can send intents with a service and anybody who has registered a broadcast receiver listening to that intent can hear it.
Let's say FirstActivity started the service and registers receiver listening to intent with the tag BOBBY. The service is the one sending an intent BOBBY to anyone who is interested and has registered for it.
You want to move on to SecondActivity. Before you do that, onPause of FirstActivity you need to unregister that broadcastreceiver.
SecondActivity is interested in the intent with tag BOBBY, so he creates his own broadcast receiver and registers for it.
I hope you can see where this is going.A broadcastreceiver can listen to all sorts of intents that you make up.
Have fun.
I'm trying to create a foreground service to communicate with a bluetooth device and I've done so using this structure:
Using a service, which also registers a BroadcastReceiver as a private member variable (to handle Gatt events), then the service is started using the startService command. Then, I bind to that service inside my current Activity. Inside the onStartCommand, I assign a BluetoothDevice object (my_device) which was passed as an extra in the onStartCommand intent to a member variable in the service. However, after the service has run and the member variable is assigned, somehow it loses that member variable instance and it becomes null again when I try to do anything in the BroadcastReceiver that handles BLE Gatt Events.
public final class My_BLEService extends Service {
private My_BLEDevice my_device = null;
private BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Problem: my_device is null inside this function
}
};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Create the Foreground notification
if (intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION)) {
// Get BluetoothDevice object
if(this.my_device == null) {
// Initialize the my_device object
BluetoothDevice bleDevice = intent.getParcelableExtra(My_AppManager.RFSTAR);
this.my_device = new my_BLEDevice(bleDevice);
}
// Connect to the bluetooth device first thing:
if(!this.isConnected) {
this.connect();
}
// Start up the broadcast receiver here
if(!this.receiverRegistered) {
registerReceiver(this.gattUpdateReceiver, this.bleIntentFilter());
this.receiverRegistered = true;
}
Intent notificationIntent = new Intent(My_BLEService.this, MonitoringActivity.class);
// FIXME: Necessary? - notificationIntent.putExtra(My_AppManager.RFSTAR, bleDevice);
notificationIntent.setAction(Constants.ACTION.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.logo_md);
// TODO: Add close button
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("BLE Monitor")
.setTicker("BLE Monitor")
.setContentText("BLE")
// TODO; This isn't appearing correctly the first time
.setSmallIcon(R.drawable.logo_md)
//.setLargeIcon(
// Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
// Note: this doesn't seem to do anything (setting it to false doesn't change it either)
.setOngoing(true).build();
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE, notification);
} else if (intent.getAction().equals(Constants.ACTION.STOPFOREGROUND_ACTION)) {
if(this.receiverRegistered) {
unregisterReceiver(this.gattUpdateReceiver);
this.receiverRegistered = false;
}
if(this.isConnected) {
this.disconnect();
}
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
Alright, I created the problem myself. The problem was, in my service I was resetting the member variable my_device in the onBind method:
public IBinder onBind(Intent intent) {
this.my_device = intent.getParcelableExtra(Kidpod_AppManager.RFSTAR);
return this.kBinder;
}
with an extra which was not set when the bindService method was called in the activity, so it was setting it to null.
Is possible to set more than one intent for be launched when, in example, the user clicks an notification.
Let me explain my concrete problem:
I have an app with notifications. Each notification open a different Activity (with different extras too).
Now I want to extract info about the notifications usage. So, every time a notification gets open I'd like to launch a Service with some extras.
I'd like to implement that without modifying the existing activities, since they are not "guilty" of the change.
Ideally the pseudocode could be something like that:
Intent originalActivityIntent=...;
Intent notificationsAnalyticsIntent=getRegisterNotificationClick(notificationId,username);
PendingIntent pi= PendingIntent.multiple(
context,
originalActivityIntent,
notificationsAnalyticsIntent)
Having both intents launched when the notification is clicked.
Writting some kind of service/broadcast receiver could be pretty complex since I would need to handle the different params for each Activity.
Any ideas of how to keep this clean?
No.
The way we solved it is that every Activity in out app extends from some base activity and in each notification we pass an extra in the intent that the base activity handle it.
Reached a pretty clean solution. Not perfect but it works and keeps the code clean, It only requires to create a single adapter activity and its transparent for the rest of your app.
In the manifest:
<activity
android:name="com.tests.AnalyticsActivity"
android:noHistory="true">
</activity>
And the java code:
public class AnalyticsActivity extends Activity {
private static final String KEY_INTERNAL_COMPONENT = "com.tests.AnalyticsReceiver.COMPONENT";
private static final String KEY_NOTIFICATION_TYPE = "com.tests.AnalyticsReceiver.NOTIFICATION_TYPE";
private static final String KEY_USERNAME = "com.tests.AnalyticsReceiver.USERNAME";
public static Intent getLaunchIntent(
Context context, Intent intent, long notificationType, String username) {
intent.putExtra(KEY_INTERNAL_COMPONENT, intent.getComponent());
intent.setComponent(new ComponentName(context, AnalyticsActivity.class));
intent.putExtra(KEY_NOTIFICATION_TYPE, notificationType);
intent.putExtra(KEY_USERNAME,username);
return intent;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState==null){
Intent intent=getIntent();
long notifType=intent.getLongExtra(KEY_NOTIFICATION_TYPE, 0);
String username=intent.getStringExtra(KEY_USERNAME);
Log.e("RegisterEvent", "notif=" + notifType + ",username=" + username);
ComponentName componentName=intent.getParcelableExtra(KEY_INTERNAL_COMPONENT);
intent.setComponent(componentName);
startActivity(intent);
}
}
}
I was able to solve this by essentially chaining intents. Have the PendingIntent start your NotificationsAnalyticsIntent; and then have NotificationsAnalyticsIntent start the activity intent. There are several ways to "tell" the notification intent which activity to start. One way is to pass it as a string.
Intent intent = NotificationsAnalyticsIntent.newIntent(applicationContext, SomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getService(applicationContext, 0, intent, 0);
...
public class NotificationsAnalyticsIntent extends IntentService {
private static final String EXTRA_ACTIVITY_TO_START = "extra_activity_to_start";
public static Intent newIntent(final Context context, final Class activityToStart) {
Intent intent = new Intent(context, NotificationsAnalyticsIntent.class);
intent.putExtra(EXTRA_ACTIVITY_TO_START, activityToStart.getSimpleName());
return intent;
}
public NotificationsAnalyticsIntent() {
super(NotificationsAnalyticsIntent.class.getSimpleName());
}
#Override
protected void onHandleIntent(#Nullable final Intent intent) {
// .... do notification stuff
// TODO
// .... then start activity
String activityToStart = intent.getStringExtra(EXTRA_ACTIVITY_TO_START);
try {
Class<?> aClass = Class.forName(activityToStart);
Method method = aClass.getMethod("newIntent", Context.class);
Context params = getApplicationContext();
Intent activityIntent = (Intent) method.invoke(null, (Object) params);
startActivity(activityIntent);
} catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
// TODO
}
}
}
Several limitations:
activity class has to be fully qualified. make sure activityToStart.getName() returns com.example.app.SomeActivity
reflection :barf:
can't start SomeActivity with it's own intent extras without some extra uglieness
I want to listen the power key event in the service.
How can in do that ?
Currently I am working with an app, where I need to listen the power button for some events, from a service which is running in a background, even when the app is killed or stopped.
Somehow I can manage to get it.
But when I kill/stop the app, the service is getting stopped.
How can i overcome this ?
Currently the code i am using this :
Service Class:
public class SampleService extends Service
{
SettingContentObserver mSettingsContentObserver;
AudioManager mAudioManager;
private ComponentName mRemoteControlResponder;
private Intent intent;
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.v("StartServiceAtBoot", "StartAtBootService -- onStartCommand()");
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public void onStart(Intent intent, int startId) {
boolean screenOn = intent.getBooleanExtra("screen_state", false);
if (!screenOn) {
Toast.makeText(getApplicationContext(), "On", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "Off", Toast.LENGTH_SHORT).show();
}
}
public void onCreate()
{
mSettingsContentObserver = new SettingContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver
(android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mRemoteControlResponder = new ComponentName(getPackageName(),
StartAtBootServiceReceiver.class.getName());
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new StartAtBootServiceReceiver();
registerReceiver(mReceiver, filter);
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
public void onDestroy()
{
getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);
}
}
BroadcastReceiver Class:
public class StartAtBootServiceReceiver extends BroadcastReceiver
{
static boolean wasScreenOn;
private boolean screenOff;
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF))
{
wasScreenOn = false;
Toast.makeText(context, "Power Off", Toast.LENGTH_SHORT).show();
}
else if(intent.getAction().equals(Intent.ACTION_SCREEN_ON))
{
wasScreenOn = true;
}
Intent i = new Intent(context, SampleService.class);
i.putExtra("screen_state", screenOff);
i.setAction("com.example.antitheft.SampleService");
context.startService(i);
//
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Intent i1 = new Intent();
i1.setAction("com.example.sampleonkeylistener.MainActivity");
context.startService(i1);
}
}
}
given above is the sample code and i have created AndroidManifest.xml files also with user's permission but i cannot get the app continue service if it is killed or stopped.
Thanks in Advance.
#Override
public void onDestroy() {
super.onDestroy();
startService(new Intent(this, SampleService.class));
}
This is one way to ensure that service will never stop even user want to destroy it.
This is one Just ONE of ways to achieve what you are trying to achieve.
Secondly, you can try and run service in "foreground" by using startForeground().
Also, make sure that in you return "START_STICKY" (which you are doing in the sample code that you shared and I trust that you are also doing in App's code too :) ) in Services's onStartCommand().
This will ensure that If this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), then leave it in the started state but don't retain this delivered intent. Later the system will try to re-create the service.
And you may find some additional pointers/hints to make sure your service is not stopped at below link.
How can we prevent a Service from being killed by OS?
Just pick and choose the approach that best suits YOUR Need/implementation.
I have service which is basically timer. It receives object with location data, start time, user id etc, and every seconds it increments stop value. I'd like to have this service running non-stop until stopped in application. I've read that i need to start this service as sticky, so i did that. But I've noticed that after my main application is beeing killed by system or user, service restarts and lose all information (about current timers running etc - I have there array with list of objects).
Idea of that service was that:
user clicks button in app -> service is starting and counting time changing information in notification bar and sending broadcast to main application -> I wish to continue updating notification bar even if app is killed by user or system.
My class below:
public class TimerService extends Service{
private NotificationCompat.Builder builder;
private static JsonHistoryList activities;
private Intent intent;
private Handler handler;
private Runnable sendUpdateToUi = new Runnable() {
#Override
public void run() {
sendBroadcast();
handler.postDelayed(this, 1000);
}
};
private NotificationManager mNotificationManager;
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(ACTION_ADD)){
JsonHistory item = (JsonHistory) intent.getSerializableExtra(HISTORY_ARG);
new NetworkAsyncTask().execute(ACTION_ADD_ID, item);
}else if(action.equals(ACTION_DELETE)){
int id = intent.getIntExtra(CATEGORY_ID_ARG, -1);
if(id > 0){
new NetworkAsyncTask().execute(ACTION_DELETE_ID, id);
}
}else if(action.equals(ACTION_CLEAR)){
activities.clear();
}
}
};
public TimerService() {
handler = new Handler();
}
private void sendBroadcast() {
Long time = new Date().getTime();
for(JsonHistory item: activities){
ContentValues values = new ContentValues();
values.put(History.C_STOP, time);
String[] selectionArgs = {String.valueOf(item.id)};
getContentResolver().update(History.URI, values, History.C_ID + "=?", selectionArgs);
item.stop = time;
}
intent.putExtra(ACTIVITY_LIST_ARG, activities);
Intent bIntent = new Intent(this, MainActivity.class);
bIntent.putExtra(CATEGORY_ARG, activities);
builder.setContentText(String.format(getString(R.string.notification_bar_message), activities.size()));
Notification barNotif = builder.build();
mNotificationManager.notify(SERVICE_ID, barNotif);
sendBroadcast(intent);
}
#Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_ADD);
filter.addAction(ACTION_DELETE);
filter.addAction(ACTION_CLEAR);
registerReceiver(receiver, filter);
intent = new Intent(ACTION);
handler.removeCallbacks(sendUpdateToUi);
handler.postDelayed(sendUpdateToUi, 1000);
activities = new JsonHistoryList();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if(intent != null && intent.hasExtra(TimerService.ACTIVITY_LIST_ARG)){
ArrayList<JsonHistory> temp = (ArrayList<JsonHistory>) intent.getSerializableExtra(TimerService.ACTIVITY_LIST_ARG);
if(temp != nu;; && temp.size() > 0)activities.addAll(temp);
}
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Intent bIntent = new Intent(this, MainActivity.class);
PendingIntent pbIntent = PendingIntent.getActivity(this, 0, bIntent, 0);
builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(getString(R.string.app_name))
.setContentText(String.format(getString(R.string.notification_bar_message), activities.size()))
.setAutoCancel(true)
.setOngoing(true)
.setContentIntent(pbIntent);
Notification barNotif = builder.build();
this.startForeground(SERVICE_ID, barNotif);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
handler.removeCallbacks(sendUpdateToUi);
stopForeground(true);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private class NetworkAsyncTask extends AsyncTask<Object, Void, String>{
Gson gson;
public NetworkAsyncTask() {
super();
gson = new Gson();
}
#Override
protected String doInBackground(Object... params) {
Integer actionId = (Integer) params[0];
String result = null;
switch (actionId){
case ACTION_ADD_ID:
break;
case ACTION_DELETE_ID:
break;
default:
result = null;
break;
}
return result;
}
}
}
Thanks for any replies
When you stop the service it is true it loses its information if the application is destroyed. That is because that instance of the service is tied to that particular instance of the application.
In my opinion, an easy fix would be to store the information you still need in some type of persistent storage (SQLite, Internal/External Memory, Shared Preferences)
Then, every time you start your service with a new application launch just be sure to reload the information you want from whichever persistent storage method you choose.
Also, assuming you don't want this service to continue if the application is killed by the user, be sure to send an intent to the service to stop it like this:
intent = new Intent(this, BackgroundLocationService.class);
stopService(intent);