Triggering Notification from service when app is in background / killed - android

I'm making a todolist app where the user needs to add a task along with date and time that i use later to trigger the notification , when app is in foreground , it works fine but since services are submitted to limitations after android oreo , now i'm lost on how to trigger the notification when app is in background or killed , if you guys could enlighten me , i woudl appreciate it
This is my service class
class NotificationService(var context: FragmentActivity) : Service(){
private lateinit var remindersViewModel: remindersViewModel
private lateinit var compat : NotificationManagerCompat
override fun onCreate() {
super.onCreate()
compat = NotificationManagerCompat.from(this)
remindersViewModel = ViewModelProvider(context)[remindersViewModel::class.java]
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
isNotificationEnabled()
return START_NOT_STICKY
}
override fun onDestroy() {
super.onDestroy()
}
private fun isNotificationEnabled(){
//TODO : NOTIFICATION
val notificationPrefs = getSharedPreferences("notificationPrefs", Context.MODE_PRIVATE)
val isNotificationEnabled = notificationPrefs.getBoolean("notification", false)
remindersViewModel.getAllTasks().observe(context, Observer {
if(isNotificationEnabled){
CoroutineScope(Dispatchers.Main).launch {
delay(3000)
HandleOperations.taskNotification(it, context, compat)
}
}
})
}
}

As of Android Oreo, all background services will be killed if not visible by a foreground service. The best case scenario for your problem is to use a Job Scheduler (here).
For timely event based tasks Job Sceduler is the only options though it's no longer available in the support library.
You will also need a Notification Channel for devices with Oreo and above.

Related

Is ExoPlayer’s audio offload mode supported while using MediaSessionService from Media3?

I am working on a native music player app for android using ExoPlayer and MediaSessionService from Media3. Now I want to make playback more energy efficient while the screen is off by using experimentalSetOffloadSchedulingEnabled, but it seems like I’m not able to get the offloading to work.
From the main activity of the app I send ACTION_START_AUDIO_OFFLOAD in the onStop() method to my service (the relevant parts of the service are show below), and ACTION_STOP_AUDIO_OFFLOAD in the onStart() method. In this way I have been able to get correct true/false responses from the onExperimentalOffloadSchedulingEnabledChanged listener, but I do not get any responses from the onExperimentalOffloadedPlayback or onExperimentalSleepingForOffloadChanged listeners, so it seems like the player never enters power saving mode.
My tests were made with Media3 version 1.0.0-beta03 on Android 13 (emulator) and Android 10 (phone) using MP3 files. I am aware that Media3 is in beta and that the offload scheduling method is experimental, but I'm not sure if that is the limitation or if I have done something wrong. Any ideas what could be the issue?
#androidx.media3.common.util.UnstableApi
class PlaybackService: MediaSessionService(), MediaSession.Callback {
private val listener = object : ExoPlayer.AudioOffloadListener {
override fun onExperimentalOffloadSchedulingEnabledChanged(offloadSchedulingEnabled: Boolean) {
Log.d("PlaybackService","offloadSchedulingEnabled: $offloadSchedulingEnabled")
super.onExperimentalOffloadSchedulingEnabledChanged(offloadSchedulingEnabled)
}
override fun onExperimentalOffloadedPlayback(offloadedPlayback: Boolean) {
Log.d("PlaybackService","offloadedPlayback: $offloadedPlayback")
super.onExperimentalOffloadedPlayback(offloadedPlayback)
}
override fun onExperimentalSleepingForOffloadChanged(sleepingForOffload: Boolean) {
Log.d("PlaybackService","sleepingForOffload: $sleepingForOffload")
super.onExperimentalSleepingForOffloadChanged(sleepingForOffload)
}
}
private lateinit var player: ExoPlayer
private var mediaSession: MediaSession? = null
override fun onCreate() {
super.onCreate()
player = ExoPlayer.Builder(
this,
DefaultRenderersFactory(this)
.setEnableAudioOffload(true)
)
.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus = */ true)
.setHandleAudioBecomingNoisy(true)
.setSeekBackIncrementMs(10_000)
.setSeekForwardIncrementMs(10_000)
.setWakeMode(C.WAKE_MODE_LOCAL)
.build()
player.addAudioOffloadListener(listener)
mediaSession = MediaSession
.Builder(this, player)
.setCallback(this)
.build()
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
mediaSession
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when(intent?.action) {
ACTION_START_AUDIO_OFFLOAD -> startAudioOffload()
ACTION_STOP_AUDIO_OFFLOAD -> stopAudioOffload()
}
return super.onStartCommand(intent, flags, startId)
}
private fun startAudioOffload() {
player.experimentalSetOffloadSchedulingEnabled(true)
}
private fun stopAudioOffload() {
player.experimentalSetOffloadSchedulingEnabled(false)
}
override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}
companion object {
const val ACTION_START_AUDIO_OFFLOAD = "ACTION_START_AUDIO_OFFLOAD"
const val ACTION_STOP_AUDIO_OFFLOAD = "ACTION_STOP_AUDIO_OFFLOAD"
}
}

Unable to start service lateinit property has not been initialized

Hi and thank you for your expertise.
I am trying to run a service which is going to display a notification and after some time check if it is still there or user has got rid of it.
So in the MainActivity.kt when onPause function is triggered I start the service like this
startService(Intent(this,NewService::class.java))
then in the NewService.kt I would like to check using MainActivity.kt function checkNotifications() if there is a notification displayed
class NewService:Service() {
#RequiresApi(Build.VERSION_CODES.M)
override fun onStartCommand(init : Intent, flag : Int, startId: Int):Int{
MainActivity().checkNotifications()
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
}
This is what checkNotifications() function in MainActivity.kt looks like
#RequiresApi(Build.VERSION_CODES.M)
fun checkNotifications(){
val notifications: Array<StatusBarNotification> = notificationManager.activeNotifications
if(notifications.isNotEmpty())
{
println("Notification exists")
}
else
{
println("No notifications")
}
}
So my app works 'fine' as long as I don't call checkNotifications(). What I mean the service starts and so on. However when I try to call checkNotifications() I get this error
java.lang.RuntimeException: Unable to start service abc.com.app.NewService#b380e9f with Intent { cmp=abc.com.app/.NewService }: kotlin.UninitializedPropertyAccessException: lateinit property notificationManager has not been initialized
The property notificationManager is initialized in onCreate() function and everyting is working up until I call checkNotifications()
Would anyone be so kind and tell me what am I doing wrong?
Thank You
MainActivity().checkNotifications()
Never create an instance of an activity yourself. Move checkNotifications() into the service or into some utility class (where you provide a Context as a parameter to checkNotifications() for the purposes of obtaining a NotificationManager).

How to return response or send data to service from activity from another app ? Android Kotlin

I have a started service app. It intent to activity from another app, but still running in foreground. After a button click in that activity, I want to send data (for example a string "potato") to service without startService() in order to continue, not restart. That's how service keeps running till get the data, while(requiredData != "potato"){}.start. How can I send it, or return response ? I think to use Messenger or Broadcast, but I'm not sure it fits well and how to do.
Note: Service App connected to an activity from another app.
Service App
class RegistryService : Service() {
override fun onBind(p0: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val i = packageManager.getLaunchIntentForPackage("com.myexample.potatoactivity")
if (i!=null) {
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(i)
} else {
Toast.makeText(this,"Fail",Toast.LENGTH_SHORT).show()
}
while (true) { // requiredData != "potato"
//Log.d("MyService", "Wait for potato")
}
return START_STICKY
}
}
Potato Activity
class PotatoActivity : AppCompatActivity() {
private lateinit var binding: ActivityPotatoBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPotatoBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.buttonSendData.setOnClickListener {
//it.putExtra("REQUIRED_DATA", "potato")
}
}
}

Long running background task from BroadcastReceiver - which class to use?

Since AsyncTask, IntentSerrvice and JobIntentService are all deprecated, which tool or class should I go for in 2022?
I want to re-schedule alarms in a BroadcastReceiver after a device rebooted (since alarms get lost in the process). The task will most probably take < 1 min to finish. I just need the safety of it completing and not being killed off by the system.
The documentation on Broadcasts shows an (outdated) example with goAsync() and the deprecated AsyncTask.
But it also mentions JobService. Is that the replacement? What about WorkManager?
goAsync() return a PendingIntent - it mean you ask for android system extend time life of Broadcast receiver => goAsync() is used for short background task.
Life time of BroadcastReceiver is very short, so... for long time background task, you must to change to other context has longer life time, such as: Service, JobService..etc.
Example:
BroadcastReceiver received intent
BroadcastReceiver start a service, run worker thread to process long time task
after worker thread finish, call finish service too
=========================================
class MyIntentService : Service() {
private val handleThread = HandlerThread("MyThread")
private lateinit var workerHandler: Handler
override fun onCreate() {
super.onCreate()
handleThread.start()
workerHandler = Handler(handleThread.looper)
}
override fun onDestroy() {
workerHandler.removeCallbacksAndMessages(null)
handleThread.quitSafely()
super.onDestroy()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val data = intent?.data
workerTask(data)
return START_NOT_STICKY
}
private fun workerTask(data: Uri?) {
workerHandler.post {
heavyTask(data)
finishMyIntentService()
}
}
private fun finishMyIntentService() {
stopSelf()
}
private fun heavyTask(data: Uri?) {
// to do heavyTask example
for (i in 1..20)
{
Log.d("test","#heavyTask() $i")
Thread.sleep(1000)
}
}
override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}
}
then startService from BroadCastReceiver

Foreground Service Stopself after hours working

I did a Foreground Service using Kotlin. It's works , but after seven hours running my service stop and my app returned to its first page (login page). But the only method to stop my service is executed when I click in a "stop service" button, so why my service is stopping after 7 hours if i didn't press any button? i'm using a moto g7, android 9.0
class RastreioService : Service() {
companion object {
var serviceAtivado = false //service activated
}
override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}
override fun onCreate() {
super.onCreate()
serviceAtivado = true
val notificationIntent = Intent(this, RastreioService::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
val notification = Notification.Builder(this, "1")
.setSmallIcon(R.drawable.ic_gps_ativo)
.setContentTitle("Localização Sendo Acessada")
.setContentText("A sua localização está sendo acessada")
.setContentIntent(pendingIntent)
.build()
startForeground(1, notification)
/*request location methods*/
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_NOT_STICKY
}
override fun onDestroy() {
serviceAtivado = false
super.onDestroy()
this.gerenciadorDeLocalizacao.DesativarBuscaPorLocalizaca()
}
}
OS still can shut down services if it feels like it needs to, but it can be resumed afterward.
on onStartCommand you are returning START_NOT_STICKY
you can use which does not re start the service.
You can use START_REDELIVER_INTENT to save progress of your service and re start it from where it left of.
Link to the docs
A foreground service can still be killed by the OS, you have no guarantees that the service will last forever. A foreground service just makes it less likely to be killed

Categories

Resources