Cancelling a service from a notification button - android

I'm trying to cancel an upload service from my progress notification using the 'cancel' button.
But I cannot get this working.
Notification is created
//Intent class changes from first activity then to service when it's uploading.
Intent cancel = new Intent(intentClass, BaseUploadService.class);
PendingIntent cancelUploadIntent = PendingIntent.getBroadcast(intentClass, 0, cancel, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder
.setSmallIcon(R.drawable.notificationicon)
.setContentTitle(title)
.setContentText(filename)
.setAutoCancel(totalAmount > uploadedAmount)
.setProgress((int) totalAmount, (int) uploadedAmount, false)
.addAction(R.string.assignment_icon_group, intentClass.getString(R.string.cancel), cancelUploadIntent)
;
return builder.build();
Listen for intent from notification button.
public class BaseUploadService extends IntentService {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.debug("onStartCommand - BaseUploadService");
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
public BaseUploadService(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent intent) {
}
}
Nothing is ever received in the service.
+ there could be multiple services.

Change PendingIntent.getBroadcast() to PendingIntent.getService().
If after that change the service is still not started, try building the notification with setContentIntent instead of addAction.
Check your manifest to confirm that BaseUploadService is declared correctly.

Related

Adding a Button in the Status Bar

I'm developing an android application where I want to add a Phone Button in Status Bar. Can I add a Phone button in Status Bar?
I want to add a button like showing in the above Image. I tried to do with the notification but the notification icon size is too small. How can I do it? Any Idea?
I think it goes against the design guidelines to put a button in the status bar since it's supposed to contain notification and system icons only. See https://material.io/design/platform-guidance/android-bars.html#status-bar.
Try a heads-up notification instead (https://developer.android.com/guide/topics/ui/notifiers/notifications.html).
You can put the button in the action area of the notification (https://material.io/design/platform-guidance/android-notifications.html#anatomy-of-a-notification).
Looks like you need foreground service with notification icon -
here
Sample code for foreground service -
public class ExampleService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Example Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
//do heavy work on a background thread
//stopSelf();
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}

Foreground service is destroyed after receiving the pending intent

I am trying to write a music player for android and am using foreground service to run the music player. I am sending a pendingIntent from the UI controls to the service to play songs.
After receiving the intent, onStartCommand and onDestroy are called immediately. I am not sure how to stop the onDestroy the call.
I tried changing the pending intent to startService/ContextCompat.startForegroundService but the issue still persists.
Service:
import android.app.Service;
public class PlayerService extends Service{
private TransportControls transportControls;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mediaSessionManager == null) {
initMediaSession();
}
handleIntent(intent);
return START_STICKY;
}
private initMediaSession(){
...
transportControls = mediaSession.getController().getTransportControls();
mediaSession.setCallback(new MediaSessionCompat.Callback() {
// Implement callbacks
#Override
public void onPlay() {
play();
}
...
}
handleIntent(Intent intent){
// Initial checks
if(/* action in intent is play*/) transportControls.play();
}
private void play(SongInfo songInfo){
...
buildNotificaiton(//play action);
}
private void buildNotification(){
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
this, "default").setContentIntent(intent)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setStyle(new MediaStyle()
.setMediaSession(mediaSession.getSessionToken())
.setShowActionsInCompactView(0, 1, 2))
.setContentText(StringUtils.parseArtists(songInfo.artists()))
.setContentTitle(songInfo.displayName())
.setContentInfo(songInfo.album())
.setSound(null);
NotificationManager notificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
return;
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("default",
"Bhatki media notification",
NotificationManager.IMPORTANCE_HIGH);
channel.setDescription(
"Notification displayed when music is being played. This notification is "
+ "required for the music to play.");
notificationManager.createNotificationChannel(channel);
}
// Execution is reaching this line.
startForeground(NOTIFICATION_ID, notificationBuilder.build());
}
}
In activity:
Intent intent = new Intent();
intent.setClass(context, PlayerService.class);
intent.setAction(action);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// one approach
ContextCompat.startForegroundService(context, intent);
// Different approach
// PendingIntent pendingIntent = PendingIntent
// .getService(serviceContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// try {
// pendingIntent.send(serviceContext, 0, intent);
// } catch (CanceledException e) {
// Log.d(TAG, "sendIntent: failed");
I also added that android foreground service permission in the manifest:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
I am expecting the notification and service to persist. Can't figure out why OnDestroy is called.
Once the service has been created, the service must call its startForeground() method within five seconds.
Apps that target Android 9 (API level 28) or higher and use foreground services must request the FOREGROUND_SERVICE permission
foreground service
Edit: I can't tell the problem from your code but from my experience, the notification block should be in override fun onCreate()

How to press ON a foreground service Notification?

I have a foreground service notification and i want when the user closes the app and presses the notification to open the application again.I implemented an
onclicklistener with onClick() method but it did nothing.If that is not possible i want the "Play" button to be shown in the notification because it is only being shown when i expand the notification.
Here is the service class:
public class MyForeGroundService extends Service {
public MyForeGroundService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG_FOREGROUND_SERVICE, "My foreground service onCreate().");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
String action = intent.getAction();
switch (action) {
case ACTION_START_FOREGROUND_SERVICE:
startForegroundService();
Toast.makeText(getApplicationContext(), "Foreground service is started.", Toast.LENGTH_LONG).show();
break;
case ACTION_STOP_FOREGROUND_SERVICE:
stopForegroundService();
Toast.makeText(getApplicationContext(), "Foreground service is stopped.", Toast.LENGTH_LONG).show();
break;
case ACTION_PLAY:
Toast.makeText(getApplicationContext(), "You click Play button.", Toast.LENGTH_LONG).show();
break;
case ACTION_PAUSE:
Toast.makeText(getApplicationContext(), "You click Pause button.", Toast.LENGTH_LONG).show();
break;
}
}
return super.onStartCommand(intent, flags, startId);
}
/* Used to build and start foreground service. */
private void startForegroundService() {
Log.d(TAG_FOREGROUND_SERVICE, "Start foreground service.");
// Create notification default intent.
Intent intent = new Intent();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
// Create notification builder.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// Make notification show big text.
NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle();
bigTextStyle.setBigContentTitle("Music player implemented by foreground service.");
bigTextStyle.bigText("Android foreground service is a android service which can run in foreground always, it can be controlled by user via notification.");
// Set big text style.
builder.setStyle(bigTextStyle);
builder.setWhen(System.currentTimeMillis());
builder.setSmallIcon(R.mipmap.ic_launcher);
Bitmap largeIconBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.strava);
builder.setLargeIcon(largeIconBitmap);
// Make the notification max priority.
builder.setPriority(Notification.PRIORITY_MAX);
// Make head-up notification.
builder.setFullScreenIntent(pendingIntent, true);
// Add Play button intent in notification.
Intent playIntent = new Intent(this, MyForeGroundService.class);
playIntent.setAction(ACTION_PLAY);
PendingIntent pendingPlayIntent = PendingIntent.getService(this, 0, playIntent, 0);
NotificationCompat.Action playAction = new NotificationCompat.Action(android.R.drawable.ic_media_play, "Play", pendingPlayIntent);
builder.addAction(playAction);
// Build the notification.
Notification notification = builder.build();
// Start foreground service.
startForeground(1, notification);
}
private void stopForegroundService() {
Log.d(TAG_FOREGROUND_SERVICE, "Stop foreground service.");
// Stop foreground service and remove the notification.
stopForeground(true);
// Stop the foreground service.
stopSelf();
}
}
So to add an "onClick" effect to a notification you should use the ".setContentIntent(pendingIntent)" method in the NotificationCompat.Builder.
For the play button to always be visible is a little bit more tricky from what I read, some say that "setPriority(Notification.PRIORITY_MAX) + setWhen(0)" can solve this, others say that it depends on device and if you have other notifications, are you connected to USB etc and that there is no single working solution for this.

Background service stops when phone goes in idle state

I've a sticky background service which has to run all the time. Basically it keeps track of the time, its a custom clock timer. But once the device goes to idle state (when phone screen is turned off) the timer (background service) also gets paused.
What should I do to make it always keep running even when the screen is turned off?
public class TimerService extends Service {
private Context context;
#Override
public IBinder onBind(Intent intent) {
Logger.LogI(TAG, "Service binding");
return null;
}
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
}
So there were some ways for a service not to be killed when device goes in sleep mode.
I started my service as a foreground service attached with the notification. I don't say that its the better approach for a long term service. But of-course this solution is open for more optimization. This solution doesn't get the service go in pause state while in sleep mode.
Following is the whole service code:
public class TimerService extends Service {
private ScheduledExecutorService scheduleTaskExecutor;
private Context context;
#Override
public IBinder onBind(Intent intent) {
Logger.LogI(TAG, "Service binding");
return null;
}
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
setNotification();
if (scheduleTaskExecutor == null) {
scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
scheduleTaskExecutor.scheduleAtFixedRate(new mainTask(), 0, 1, TimeUnit.SECONDS);
}
return Service.START_STICKY;
}
public void setNotification() {
PendingIntent contentIntent;
contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, Main_Activity.class), 0);
NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setColor(this.getResources().getColor(R.color.action_bar_color))
.setContentTitle("MainActivity")
.setOngoing(true)
.setAutoCancel(false)
.setContentText("MyApp");
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
Notification notification = mBuilder.build();
startForeground(NOTIFICATION_ID, notification); //NOTIFICATION_ID is a random integer value which has to be unique in an app
}
}
private class mainTask implements Runnable {
public void run() {
// 1 Second Timer
}
}
Helpful Links:
Run a service in the background forever..? Android
How to run background service after every 5 sec not working in android 5.1?
How to run a method every X seconds
How to make service run even in sleep mode?
part-1 persistent foreGround android service that starts by UI, works at sleep mode too, also starts at phone restart
part-2 persistent foreGround android service that starts by UI, works at sleep mode too, also starts at phone restart
Android - implementing startForeground for a service?
If you want make your service as never ending service then User this code in onStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Put code here
return START_REDELIVER_INTENT;
}
You can use START_STICKY also.

Android service gets killed on swiping out the application

I have an Android Service class the code for which is as follows:
public class LoginService extends Service {
BroadcastReceiver wifiStateChangeReciever;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("AndroidLearning", "Service onStartCommand Started.");
return Service.START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
Log.i("AndroidLearning", "Service Started.");
final IntentFilter intentFilter = new IntentFilter();
// intentFilter.addAction("android.net.wifi.WIFI_STATE_CHANGED");
intentFilter.addAction("android.net.wifi.STATE_CHANGE");
wifiStateChangeReciever = new WifiStateChangeReciever();
this.registerReceiver(wifiStateChangeReciever, intentFilter, null, null);
Log.i("AndroidLearning", "Reciever Registered.");
}
#Override
public void onDestroy() {
Log.i("AndroidLearning", "Service Destroyed.");
this.unregisterReceiver(wifiStateChangeReciever);
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Log.w("AndroidLearning", "On Task Remove: FLAG_STOP_WITH_TASK - "
+ ServiceInfo.FLAG_STOP_WITH_TASK);
this.unregisterReceiver(wifiStateChangeReciever);
Intent restartServiceIntent = new Intent(getApplicationContext(),
this.getClass()); restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(
getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
alarmService.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 1000, restartServicePendingIntent);
Log.w("AndroidLearning", "End on task removed");
super.onTaskRemoved(rootIntent);
}
}
It registers a BroadcastReciever. The Activity which starts this service has the following code:
Intent intent = new Intent(this, LoginService.class);
startService(intent);
However whenever the Activity is swiped out from the task list (recent) the service is also stopped. I over rode the onTaskRemoved to remedy it but it still does not seem to work and the AlarmManager never starts the pendingIntent. I have tries using both method: set and setExact for the AlarmManager.
I also tried adding the following options to <service> tags
android:stopWithTask="false"
android:process=":remote"
but to no avail.
What am I doing wrong here? Thanks for the help.
I finally found the answer to my own problem. It seems this was a problem with the particular flavor of android that I was running on my phone (Mi UI). There was a separate setting regarding each application whether it needed to be allowed to be restarted or not.
Unless this setting is configured no amount of changing permissions and setting Alarms helped me.
This is a different approach from you but I recently fixed this by adding a notification when the service was running
private void showNotification(){
NotificationCompat.Builder builer = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.logo)
.setContentTitle("Service active")
.setContentText("Your service keeps running")
.setOngoing(true);
mNotificationManager.notify(NOTIFICATION_ID, builer.build());
}
The notification is shown in onStartCommand and dismissed in the service ondestroy method.
You need to start service in foreground if you don't want android to shut it down.
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
http://developer.android.com/guide/components/services.html#Foreground
If you try this on some devices, sadly, it won't work.
Some OEMs decided to change the normal behavior of what happens when you remove an app from the recent tasks, so they become semi-disabled:
https://issuetracker.google.com/issues/122098785
https://dontkillmyapp.com/

Categories

Resources