I am trying to create a basic alarm clock app. I set the alarm using the setExact method of the AlarmManager. And then I created a BroadcastReceiver that will display a toast and will start a new activity when the previously set alarm goes off. I also have a WakeLock in my BroadcastReceiver to try to turn on the screen. The BroadcastReceiver is working. It is able to receive the event and then created the activity. But the screen is not turning on. I've read and followed a lot of answers of posts like this in SO but I still cant make it work.
This is my code for setting the alarm:
alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
PendingIntent.getBroadcast(context, 1, intent, 0)
}
val alarmTime = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
set(Calendar.HOUR_OF_DAY, alarmForm.time.get(Calendar.HOUR_OF_DAY))
set(Calendar.MINUTE, alarmForm.time.get(Calendar.MINUTE))
set(Calendar.SECOND, 0)
}
alarmManager.setExact(
AlarmManager.RTC_WAKEUP,
alarmTime.timeInMillis,
alarmIntent
)
This is my BroadcastReceiver:
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val wakeLock: PowerManager.WakeLock =
(context.getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag").apply {
acquire(10*60*1000L /*10 minutes*/)
}
}
Toast.makeText(context, "Wake Up!", Toast.LENGTH_LONG).show()
val alarmDisplayIntent = Intent(context, AlarmDisplayActivity::class.java)
alarmDisplayIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(alarmDisplayIntent)
wakeLock.release()
}
}
And this is the activity I display when alarm goes off:
class AlarmDisplayActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_alarm_display)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
override fun onResume() {
super.onResume()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setTurnScreenOn(true)
}
}
}
I am using a phone with android version 9. My minSdkVersion is 21.
What do you think I am doing wrong or am I missing something?
Related
Use-case: I want my app to announce the current time ("the current time is $hour $minute") at 5 minute intervals. The idea is to help me stay on time while getting ready in the morning, so it needs to fire at the exact time, every 5 minutes, without fail, even with the screen turned off.
Problem: Since Android alarmManager's SetRepeating is no longer exact, I am using SetExactandAllowWhileIdle. I can't figure out how to set a new alarm within my broadcast receiver to make it repeating. Is it possible to set an alarm within a running alarm class in Kotlin? The only examples I can find are in Java.
I've also read that Android limits alarms, so even with SetExactandAllowWhileIdle you are limited to one alarm every 1-15 minutes, so it's possible this may still not work. Is there a better way to do all this that I am missing?
class MainActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
var isRunning: Boolean = false
var tts: TextToSpeech? = null
private lateinit var alarmMgr: AlarmManager
private lateinit var pendingIntent: PendingIntent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var alarmMgr = getSystemService(Context.ALARM_SERVICE) as AlarmManager
var intent = Intent(this, MyAlarm::class.java)
var pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0)
button.setOnClickListener {
if (!isRunning) {
button.text = getString(R.string.stop)
button.setBackgroundColor(resources.getColor(R.color.colorRed))
isRunning = true
val calendar = Calendar.getInstance()
calendar.set(
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH),
calendar.get(HOUR_OF_DAY),
calendar.get(MINUTE),
0
)
startAnnouncing(calendar)
} //end if !isRunning
else if (isRunning) {
button.text = getString(R.string.start)
button.setBackgroundColor(resources.getColor(R.color.colorGreen))
isRunning = false
alarmMgr.cancel(pendingIntent)
Toast.makeText(this, "Announcing stopped!", Toast.LENGTH_SHORT).show()
} //end if isRunning
} //end button OnClickListener
tts = TextToSpeech(this, this)
} //end onCreate
private fun startAnnouncing(calendar: Calendar) {
var alarmMgr = getSystemService(Context.ALARM_SERVICE) as AlarmManager
var intent = Intent(this, MyAlarm::class.java)
var pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0)
alarmMgr.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis + 1000*60*1,
pendingIntent
)
Toast.makeText(this, "Announcing started!", Toast.LENGTH_SHORT).show()
} //end startAnnouncing
class MyAlarm : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent
) {
val ttsService = Intent(context, TTS::class.java)
context.startService(ttsService)
val calendar = Calendar.getInstance()
calendar.set(
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH),
calendar.get(HOUR_OF_DAY),
calendar.get(MINUTE),
0
)
//THIS is where I'd like to call a new alarm to make this a repeating alarm cycle
} //end onReceive
} //end MyAlarm
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
// set US English as language for tts
val result = tts!!.setLanguage(Locale.US)
}
} //end onInit
public override fun onDestroy() {
// Shutdown TTS
if (tts != null) {
tts!!.stop()
tts!!.shutdown()
}
super.onDestroy()
//alarmMgr.cancel(pendingIntent)
} //end onDestroy
} //end MainActivity
I've also read that Android limits alarms, so even with SetExactandAllowWhileIdle you are limited to one alarm every 1-15 minutes
This is correct, according the the documentation "Under normal system operation, it will not dispatch these alarms more than about every minute (at which point every such pending alarm is dispatched); when in low-power idle modes this duration may be significantly longer, such as 15 minutes"
You may be able to get away with using setExactandAllowWhileIdle, but I don't recall if there is documentation on how long it takes before the device goes into low power/doze mode. Also, it might be even more likely to work how you want if you add the app to the battery optimization exception list. See these docs for Google's suggestions on dealing with doze/app standby.
Another option appears to be setAlarmClock (docs) but I recommend against this as the intended use of this is for creating an alarm clock style application
I can't seem to get the AlarmManager to work inside a Fragment. My receiver's onReceive() method never gets executed. I assume that I might use context in a wrong way but then again I also couldn't get it to work inside an Activity. I've also registered the receiver in my manifest.
MyFragment.kt
class MyFragment : Fragment() {
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var alarmMgr: AlarmManager? = null
lateinit var alarmIntent: PendingIntent
alarmMgr = context!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmIntent = Intent(context, Receiver::class.java).let { intent ->
PendingIntent.getService(context, 0, intent, 0)
}
val calendar: Calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
// The EditText includes a time in 24-hour format (e.g. 12:34)
set(Calendar.HOUR_OF_DAY, editText.text.toString().substringBefore(":").toInt())
set(Calendar.MINUTE, editText.text.toString().substringAfter(":").toInt())
}
Log.d("ALARM", "CREATED")
alarmMgr?.set(
AlarmManager.RTC,
calendar.timeInMillis,
alarmIntent
)
}
}
Receiver.kt
class Receiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("ALARM", "RECEIVED")
}
}
AndroidManifest.xml
<application
...
<receiver android:name="com.example.name.Receiver" />
</application>
First things first:
AndroidManifest.xml
<receiver
android:name="com.example.name.Receiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
Then, in this case in your Fragment, however, I suggest doing this somewhere else:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, Receiver::class.java)
// Used for filtering inside Broadcast receiver
intent.action = "MyBroadcastReceiverAction"
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
// In this particular example we are going to set it to trigger after 30 seconds.
// You can work with time later when you know this works for sure.
val msUntilTriggerHour: Long = 30000
val alarmTimeAtUTC: Long = System.currentTimeMillis() + msUntilTriggerHour
// Depending on the version of Android use different function for setting an
// Alarm.
// setAlarmClock() - used for everything lower than Android M
// setExactAndAllowWhileIdle() - used for everything on Android M and higher
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
alarmManager.setAlarmClock(
AlarmManager.AlarmClockInfo(alarmTimeAtUTC, pendingIntent),
pendingIntent
)
} else {
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
alarmTimeAtUTC,
pendingIntent
)
}
In your Broadcast Receiver, we then do the following:
class Receiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// We use this to make sure that we execute code, only when this exact
// Alarm triggered our Broadcast receiver
if (intent?.action == "MyBroadcastReceiverAction") {
Log.d("ALARM", "RECEIVED")
}
}
}
I am implementing a custom alarm with some logic behind it. When the alarm fires and the Broadcast receiver run the onReceive, the app starts a new activity for vibration and ringtone deactivation.
Because is an alarm, I want it to fires also when the phone is locked, and in sleep mode as well.
The alarm fires properly, even if the phone is in sleep mode. However, when I test the app on my smartphone (not just mine, but also my brother one), if the screen is OFF, it is not turned ON. On the contrary, the system works properly on the simulator with a pixel2 and android 9.
Below the receiver, and the manifest for the receiver and the app to disable the vibration and the tone ring.
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val extras = intent?.getExtras()
val alarmId = intent?.getIntExtra("alarmId",-1)
if(alarmId != null){
val ta = MemoryManager.getAlarmWithId(alarmId.toInt())
if(ta != null && ta!!.isSet){
startAlarmActivity(context,alarmId)
}
}
}
}
<activity
android:name=".AlarmDeactivation"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="#string/title_activity_alarm_deactivation"
android:theme="#style/AppTheme.NoActionBar"
android:showOnLockScreen="true"
android:turnScreenOn="true">
</activity>
<receiver android:name=".AlarmReceiver">
</receiver>
And this how I set the alarm.
if (Build.VERSION.SDK_INT >= 23) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pendingIntent)
}
else if (Build.VERSION.SDK_INT >= 19) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent)
}
else{
alarmManager.set(AlarmManager.RTC_WAKEUP, time, pendingIntent)
}
How can I let the app to turn on the screen when the alarm fires?
Thanks to all.
EDIT
As suggested here, I tried to define a class where I implemented the powerManager WakeLock acquire and release, but without any changed.
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val extras = intent?.getExtras()
val alarmId = intent?.getIntExtra("alarmId",-1)
AlertWakeLock.acquireWakeLock(context!!)
if(alarmId != null){
val ta = MemoryManager.getAlarmWithId(alarmId.toInt())
if(ta != null && ta!!.isSet){
startAlarmActivity(context,alarmId)
}
}
}
}
class AlertWakeLock {
companion object {
private var wakeLock: PowerManager.WakeLock? = null
fun acquireWakeLock(context: Context) {
if (wakeLock != null) {
return
}
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK or
PowerManager.ACQUIRE_CAUSES_WAKEUP or
PowerManager.ON_AFTER_RELEASE, "ToddlerTimer:WakeLock"
)
wakeLock!!.acquire()
}
fun releaseWakeLock() {
if (wakeLock != null) {
wakeLock!!.release()
wakeLock = null
}
}
}
}
class AlarmDeactivation : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
try {
super.onCreate(savedInstanceState)
setContentView(R.layout.alarm_deactivation)
val alarmId = intent.getIntExtra("ALARM_ID", -1)
val ta = MemoryManager.getAlarmWithId(alarmId.toInt())
val button: View = findViewById(R.id.saveButton)
val afm = AlarmFeedbackManager(this)
afm.play()
button.setOnClickListener{
MemoryManager.deSetAlarm(alarmId)
MemoryManager.saveAlarmIntoMemory()
afm.stop()
val mCtx = GlobalApplication.appContext
val intent = Intent(mCtx, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
mCtx?.startActivity(intent)
finish()
}
}
catch (e: Exception){
var i = e
}
}
override fun onDestroy() {
super.onDestroy()
AlertWakeLock.releaseWakeLock()
}
}
Background
Android Q seems to have plenty of new restrictions, but alarms shouldn't be one of them:
https://developer.android.com/guide/components/activities/background-starts
The problem
It seems that old code that I made for setting an alarm, which worked fine on P, can't work well anymore:
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var manager: AlarmManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
manager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
button.setOnClickListener {
Log.d("AppLog", "alarm set")
Toast.makeText(this, "alarm set", Toast.LENGTH_SHORT).show()
val timeToTrigger = System.currentTimeMillis() + 10 * 1000
setAlarm(this, timeToTrigger, 1)
}
}
companion object {
fun setAlarm(context: Context, timeToTrigger: Long, requestId: Int) {
val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
when {
VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP -> manager.setAlarmClock(AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> manager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
else -> manager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
}
}
}
}
The receiver does get the Intent, but when it tries to open the Activity, sometimes nothing occurs:
AlarmReceiver.kt
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("AppLog", "AlarmReceiver onReceive")
context.startActivity(Intent(context, Main2Activity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
}
Seeing this as a bug, I reported here (including sample code)
What I've tried
I tried to find what's new on Q, to see what could cause it, and I couldn't find it.
I also tried (if you look at the code) to directly open the Activity instead of via a BroadcastReceiver.
And, I tried to set the BroadcastReceiver to run on a different process.
All of those didn't help.
What I have found is that while some alarm clock apps fail to work properly (such as Timely), some apps work just fine (such as "Alarm Clock Xtreme").
The questions
On Android Q, is there an official way to let alarms work correctly? To open an Activity that will be shown to the user, exactly as an alarm clock app should?
What's wrong in the code I've made? How come it works on P but not always on Q?
EDIT: OK after being adviced to have a notification shown while I start the Activity, and also use FullScreenIntent, I got something to work, but it's only working when the screen is turned off. When the screen is turned on, it only shows the notification, which is a bad thing because the whole point is to have an alarm being shown to the user, and some users (like me) don't want to have heads-up-notification for alarms, popping out in the middle of something and not pausing anything. I hope someone can help with this, as this used to be a very easy thing to do, and now it got way too complex...
Here's the current code (available here) :
NotificationId
object NotificationId {
const val ALARM_TRIGGERED = 1
#JvmStatic
private var hasInitialized = false
#UiThread
#JvmStatic
fun initNotificationsChannels(context: Context) {
if (hasInitialized || Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
return
hasInitialized = true
val channelsToUpdateOrAdd = HashMap<String, NotificationChannel>()
val channel = NotificationChannel(context.getString(R.string.channel_id__alarm_triggered), context.getString(R.string.channel_name__alarm_triggered), NotificationManager.IMPORTANCE_HIGH)
channel.description = context.getString(R.string.channel_description__alarm_triggered)
channel.enableLights(true)
channel.setSound(null, null)
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
channel.enableVibration(false)
channel.setShowBadge(false)
channelsToUpdateOrAdd[channel.id] = channel
//
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val existingChannels = notificationManager.notificationChannels
if (existingChannels != null)
for (existingChannel in existingChannels) {
// The importance of an existing channel will only be changed if the new importance is lower than the current value and the user has not altered any settings on this channel.
// The group an existing channel will only be changed if the channel does not already belong to a group. All other fields are ignored for channels that already exist.
val channelToUpdateOrAdd = channelsToUpdateOrAdd[existingChannel.id]
if (channelToUpdateOrAdd == null) //|| channelToUpdateOrAdd.importance > existingChannel.importance || (existingChannel.group != null && channelToUpdateOrAdd.group != existingChannel.group))
notificationManager.deleteNotificationChannel(existingChannel.id)
}
for (notificationChannel in channelsToUpdateOrAdd.values) {
notificationManager.createNotificationChannel(notificationChannel)
}
}
}
MyService.kt
class MyService : Service() {
override fun onBind(p0: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d("AppLog", "MyService onStartCommand")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationId.initNotificationsChannels(this)
val builder = NotificationCompat.Builder(this, getString(R.string.channel_id__alarm_triggered)).setSmallIcon(android.R.drawable.sym_def_app_icon) //
.setPriority(NotificationCompat.PRIORITY_HIGH).setCategory(NotificationCompat.CATEGORY_ALARM)
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
builder.setShowWhen(false)
builder.setContentText("Alarm is triggered!")
builder.setContentTitle("Alarm!!!")
val fullScreenIntent = Intent(this, Main2Activity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setFullScreenIntent(fullScreenPendingIntent, true)
startForeground(NotificationId.ALARM_TRIGGERED, builder.build())
startActivity(Intent(this, Main2Activity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
Handler().postDelayed({
stopForeground(true)
stopSelf()
}, 2000L)
} else {
startActivity(Intent(this, Main2Activity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
return super.onStartCommand(intent, flags, startId)
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var manager: AlarmManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
manager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
button.setOnClickListener {
Log.d("AppLog", "alarm set")
Toast.makeText(this, "alarm set", Toast.LENGTH_SHORT).show()
val timeToTrigger = System.currentTimeMillis() + 10 * 1000
setAlarm(this, timeToTrigger, 1)
}
}
companion object {
fun setAlarm(context: Context, timeToTrigger: Long, requestId: Int) {
val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
// val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, Main2Activity::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
// val pendingIntent = PendingIntent.getService(context, requestId, Intent(context, MyService::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
when {
VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP -> manager.setAlarmClock(AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> manager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
else -> manager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
}
}
}
}
AlarmReceiver.kt
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("AppLog", "AlarmReceiver onReceive")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(Intent(context, MyService::class.java))
} else context.startService(Intent(context, MyService::class.java))
}
}
What's wrong in the code I've made? How come it works on P but not always on Q?
You are attempting to start an activity from the background. That is banned on Android 10+ for the most part.
According to the docs, alarms shouldn't be harmed.
From the material that you quoted, with emphasis added: "The app receives a notification PendingIntent from the system". You are not using notifications. And, therefore, this exception does not apply.
On Android Q, is there an official way to let alarms work correctly? To open an Activity that will be shown to the user, exactly as an alarm clock app should?
Use a notification with a full-screen Intent, as is covered in the documentation. If the screen is locked, your activity will be displayed when the notification is raised. If the screen is unlocked, a high-priority ("heads up") notification will be displayed instead. In other words:
If the device is not being used, you get what you want
If the device is probably being used, the user find out about the event without your taking over the screen, so you do not interfere with whatever the user is doing (e.g., relying on a navigation app while driving)
I want to repeat a function( or any action ) every one Hour ( for example ) even if the app is not running.
I've created a demo project so you can take a look at it :
https://github.com/joancolmenerodev/BroadcastReceiverAndAlarmManagerInKotlin
You first have to create a BroadcastReceiver, and then using AlarmManager you can decide the interval of time you want to be called.
Create a BroadcastReceiver you can do it as follows :
val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
toast("This toast will be shown every X minutes")
}
}
And then you have this method to start the job :
val mIntent = Intent(context, broadCastReceiver)
val mPendingIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, mIntent, 0)
val mAlarmManager = context
.getSystemService(Context.ALARM_SERVICE) as AlarmManager
mAlarmManager.setRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMillis(),
CHANGETOYOURDESIREDSECONDS, mPendingIntent
)
And then you'll be able to see the Toast even if the app is closed.
Edit
You can register your BroadcastReceiver using context.registerReceiver(receiver, IntentFilter("something"))
and then adding to the mIntent and action for "something".
If you don't like this way, you can create a new class named MyReceiver that extends BradcastReceiver as follows :
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context,"This toast will be shown every X minutes", Toast.LENGTH_SHORT).show()
}
}
And then start the alarm doing this :
val mIntent = Intent(this, MyReceiver::class.java)
val mPendingIntent = PendingIntent.getBroadcast(this, 0, mIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val mAlarmManager = this
.getSystemService(Context.ALARM_SERVICE) as AlarmManager
mAlarmManager.setRepeating(
AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
WHATEVERYOUWANT, mPendingIntent
)
Note: By default is set to 60000
Value will be forced up to 60000 as of Android 5.1; don't rely on this to be exact
If you are using AndroidX(JetPack) library then concider to use Workmanager
Simple example:
public class MyWorker extends Worker {
static final String TAG = "workmng";
#NonNull
#Override
public WorkerResult doWork() {
Log.d(TAG, "doWork: start");
//Do your job here
Log.d(TAG, "doWork: end");
return WorkerResult.SUCCESS;
}
}
And start like this, to do your job each hour :
PeriodicWorkRequest myWorkRequest = new PeriodicWorkRequest.Builder(MyWorker.class, 60, TimeUnit.MINUTES)
.build();
Add in app gradle file:
dependencies {
def work_version = 2.0.0
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
}