Run a method at a specific time without repeat - android

I need to call a web API at a specific time without repeat it. I use AlarmManager but it never fired. I also implement handler.postDelayed but it repeats itself. I don't know how to implement it correctly.
this is my alarmManager :
alarmManager = (getCTX()?.getSystemService(Context.ALARM_SERVICE)) as (AlarmManager)
var intent: Intent = Intent(getCTX(), NextLiveReceiver::class.java)
intent.putExtra("next", next)
intent.putExtra("curTime", curTime)
myPendingIntent =
PendingIntent.getBroadcast(
getCTX(),
0,//next.toInt(),
intent,
0
)
var ALARM_TYPE = AlarmManager.RTC_WAKEUP
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
alarmManager.setExactAndAllowWhileIdle(ALARM_TYPE, next, myPendingIntent);
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
alarmManager.setExact(ALARM_TYPE, next, myPendingIntent);
else
alarmManager.set(ALARM_TYPE, next, myPendingIntent)
this is AlarmManagerReceiver :
class NextLiveReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
var next: Long = intent.getLongExtra("next", 0)
var current: Long = intent.getLongExtra("curTime", 0)
if (next > 0 && current > 0) {
EventBus.send(NextEvent(true, next, current))
}
}
}
and this is my handler :
var handler: Handler = Handler()
handler.postDelayed({
viewModel.fetchLiveInfo(viewModel.currentChannelId.value!!, next, 0)
}, delay)
handler.removeCallbacks(null)

WorkManager, Handler, and AlarmManager did not work as I expected. So, I resolved it with Kotlin delay in this way :
runBlocking {
scope.launch(Dispatchers.Main + handler) {
Timber.i("--Launch-- start : ${getUnixTime()}")
delay(delayTime)
removeRate()
refetchLiveInfo(channelId, liveInfoResult[0].next, 1)
Timber.i("--Launch-- ended : ${getUnixTime()}")
}
}

Related

WakeLock not turning on the screen

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?

How to set an alarm on Android Q?

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)

How to Repeat a function every Hour in Android Studio using Kotlin?

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"
}

Sending notification via AlarmManager and BroadcastReceiver does not work?

I'm scheduling a notification for 5 days ahead, so I create an alarm using the AlarmManager who fires a PendingIntent which triggers my BroadcastReceiver.
If I try the code for 10 seconds, it works. When I try it for 5 days, nothing happens.
The class NotificationScheduler is a helper class for setting and updating alarms.
The fire-dates are correct since I store them in a database, and I already proofed it.
Manifest:
<receiver android:name=".reminder.ReminderReceiver" />
NotificationScheduler:
class NotificationScheduler {
companion object {
const val NOTIFICATION_EXTRA_CLAIM_ID = "notification_extra_bookentry_id"
const val INTENT_ACTION_REMINDER = "at.guger.moneybook.reminder"
fun setReminder(context: Context, bookEntryId: Long, fireDate: Date? = null) {
val mCalendar = Calendar.getInstance()
val lFireDate = if (fireDate == null) {
mCalendar.timeInMillis += 5 * 24 * 3600 * 1000
mCalendar.set(Calendar.HOUR_OF_DAY, 12)
mCalendar.time
} else {
fireDate
}
create(context, bookEntryId, lFireDate.time)
AppDatabase.getInstance(context).reminderDao().insert(Reminder(bookEntryId, lFireDate))
}
fun updateReminder(context: Context, bookEntryId: Long) {
cancel(context, bookEntryId)
val mCalendar = Calendar.getInstance()
mCalendar.timeInMillis += 5 * 24 * 3600 * 1000
mCalendar.set(Calendar.HOUR_OF_DAY, 12)
create(context, bookEntryId, mCalendar.timeInMillis)
AppDatabase.getInstance(context).reminderDao().update(Reminder(bookEntryId, mCalendar.time))
}
fun cancelReminder(context: Context, bookEntryId: Long) {
cancel(context, bookEntryId)
AppDatabase.getInstance(context).reminderDao().delete(Reminder(bookEntryId))
}
private fun create(context: Context, bookEntryId: Long, fireDate: Long) {
val mAlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val mComponentName = ComponentName(context, ReminderReceiver::class.java)
context.packageManager.setComponentEnabledSetting(mComponentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
val mIntent = Intent(context, ReminderReceiver::class.java)
mIntent.action = INTENT_ACTION_REMINDER
mIntent.putExtra(NOTIFICATION_EXTRA_CLAIM_ID, bookEntryId)
val mPendingIntent = PendingIntent.getBroadcast(context, bookEntryId.toInt(), mIntent, PendingIntent.FLAG_UPDATE_CURRENT)
if (Utils.isKitKat()) {
mAlarmManager.setWindow(AlarmManager.RTC, fireDate, AlarmManager.INTERVAL_HOUR, mPendingIntent)
} else {
mAlarmManager.set(AlarmManager.RTC, fireDate, mPendingIntent)
}
}
private fun cancel(context: Context, bookEntryId: Long) {
val mAlarmManager: AlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val mComponentName = ComponentName(context, ReminderReceiver::class.java)
context.packageManager.setComponentEnabledSetting(mComponentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
val mIntent = Intent(context, ReminderReceiver::class.java)
mIntent.putExtra(NOTIFICATION_EXTRA_CLAIM_ID, bookEntryId)
val mPendingIntent = PendingIntent.getBroadcast(context, bookEntryId.toInt(), mIntent, PendingIntent.FLAG_UPDATE_CURRENT)
mAlarmManager.cancel(mPendingIntent)
mPendingIntent.cancel()
}
}
}
BroadcastReceiver:
class ReminderReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (context != null && intent != null) {
when (intent.action) {
NotificationScheduler.INTENT_ACTION_REMINDER -> {
val mPowerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
val mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this::class.simpleName)
mWakeLock.acquire(WAKELOCK_TIME)
val iClaimEntryId = intent.getLongExtra(NotificationScheduler.NOTIFICATION_EXTRA_CLAIM_ID, -1)
showNotification(context, iClaimEntryId)
AppDatabase.getInstance(context).reminderDao().delete(Reminder(iClaimEntryId))
mWakeLock.release()
}
}
}
}
private fun showNotification(context: Context, claimEntryId: Long) {
val mNotificationManager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val nBuilder: Notification.Builder
if (Utils.isOreo()) {
val mNotificationChannel = NotificationChannel(NOTIFICATIONCHANNEL_CLAIMREMINDERID, context.getString(R.string.notificationchannel_claimreminder_title), NotificationManager.IMPORTANCE_DEFAULT)
mNotificationChannel.description = context.getString(R.string.notificationchannel_claimreminder_description)
mNotificationManager.createNotificationChannel(mNotificationChannel)
nBuilder = Notification.Builder(context, NOTIFICATIONCHANNEL_CLAIMREMINDERID)
} else {
nBuilder = Notification.Builder(context)
}
val mClaimEntry: BookEntry = AppDatabase.getInstance(context).bookEntryDao().get(claimEntryId)
val mCurrencyFormatter = DecimalFormat.getCurrencyInstance(Preferences.getInstance(context).currency.locale)
nBuilder.setSmallIcon(R.drawable.ic_money)
nBuilder.setContentTitle(context.getString(R.string.notification_claimreminder_title, mCurrencyFormatter.format(mClaimEntry.dValue)))
val sContacts = mClaimEntry.getContacts(context).joinToString().takeIf { it.isNotEmpty() }
?: "-"
nBuilder.setContentText(context.getString(R.string.notification_claimreminder_content, sContacts))
nBuilder.setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
nBuilder.setAutoCancel(true)
mNotificationManager.notify(mClaimEntry.lId!!.toInt(), nBuilder.build())
}
companion object {
const val NOTIFICATIONCHANNEL_CLAIMREMINDERID = "notification_channel_claimreminder"
const val WAKELOCK_TIME: Long = 1000
}
}
I had issues with AlarmManager, so I am using WorkManager now.
WorkManager was introduced with Android Jetpack / Architecture Components.
Solution
First add dependency for WorkManager, you can find latest versions here.
Create a Worker class, and code for showing notification in doWork().
Schedule this work on your app start, also check if already not scheduled. I already created a method named isWorkScheduled() in below class for ease.
You can send additional data to your task (like putExtra()) by setInputData().
Schedule your one time task in first activity onCreate() or Application class onCreate.
Example
public static final String TAG_WORK = "myTag";
if(!MyWork.isWorkScheduled(TAG_WORK))
MyWork.scheduleOneTimeTask(TAG_WORK, 5, TimeUnit.DAYS)
MyWork.java
public class MyWork extends Worker {
public static void scheduleOneTimeTask(String tag, long duration, TimeUnit timeUnit) {
OneTimeWorkRequest compressionWork =
new OneTimeWorkRequest.Builder(MyWork.class).setInitialDelay(duration, timeUnit).addTag(tag)
.build();
WorkManager instance = WorkManager.getInstance();
if (instance != null) {
instance.enqueue(compressionWork);
}
}
private boolean isWorkScheduled(String tag) {
WorkManager instance = WorkManager.getInstance();
if (instance == null) return false;
LiveData<List<WorkStatus>> statuses = instance.getStatusesByTag(tag);
if (statuses.getValue() == null) return false;
boolean running = false;
for (WorkStatus workStatus : statuses.getValue()) {
running = workStatus.getState() == State.RUNNING | workStatus.getState() == State.ENQUEUED;
}
return running;
}
#NonNull
#Override
public Result doWork() {
// show notification
return Result.SUCCESS;
// (Returning RETRY tells WorkManager to try this task again
// later; FAILURE says not to try again.)
}
}
I suggested you WorkManger only, because I created a sample earlier
days with JobScheduler, EvernoteJobs,
AlarmManager, JobService, and WorkManager. In which I started periodic task of 15 minutes with each of these. and
wrote logs of each in separate file when invoked.
Conclusion of this test was that. WorkManager and EvernoteJobs were
most efficient to do jobs. Now because EvernoteJobs will use
WorkManager from next version. So I came up with WorkManager.
Update
Minimum time for scheduling periodic task is 15 minutes so please keep in mind. You can read more in documentation.
Is the device ever shut down during the five-day period? According to the documentation:
By default, all alarms are canceled when a device shuts down. To prevent this from happening, you can design your application to automatically restart a repeating alarm if the user reboots the device. This ensures that the AlarmManager will continue doing its task without the user needing to manually restart the alarm.
Set an alarm when the device restarts.
Another thing that comes to mind is you may want to use AlarmManager.RCT_WAKEUP instead of AlarmManager.RTC to make sure the device is awake to deliver the intent and create and dispatch your notification.
Try:
if (Utils.isKitKat()) {
mAlarmManager.setWindow(AlarmManager.RTC_WAKEUP, fireDate, AlarmManager.INTERVAL_HOUR, mPendingIntent)
} else {
mAlarmManager.set(AlarmManager.RTC_WAKEUP, fireDate, mPendingIntent)
}
Most probably , it is the case when your device is going in Doze mode and Alarm manager is not triggering your scheduled alarm
Try using( this is Java code ):
AlarmManager alarmManager = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
}
else{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
}
}
Take a look at setExactAndAllowWhileIdle

Android Alarm Not Set

I used to set alarms using the following code-segment in other projects as repeating and non-repeating, but it's now driving me crazy about what may the silly mistake be that I've made for not the alarm speaking to my current implementation :\ :
private fun setAlarm(obj: MyObject, time: Long) {
val intent = Intent(applicationContext, MyAlarmIntentService::class.java)
intent.putExtra(C.KEY_ME, obj)
val pendingIntent = PendingIntent.getService(applicationContext, 43, intent,
PendingIntent.FLAG_UPDATE_CURRENT)
val context = this#MyActivity
val cal = Calendar.getInstance()
cal.timeInMillis = time * 1000 // time is in seconds
Log.d("setAlarm", "setting time -> $time for obj = $obj")
Log.d("setAlarm", "Set calendar: " + cal.toString())
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
#RequiresApi(Build.VERSION_CODES.LOLLIPOP) // Overriding doze-mode
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val alarmClockInfo =
AlarmManager.AlarmClockInfo(cal.timeInMillis, null)
alarmManager.setAlarmClock(alarmClockInfo, pendingIntent)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) // Overriding doze-mode
// Not-working in Nexus 6 - API 25 (7.1.1), but works in API 23 (6.0.1, tested in multiple devices)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
cal.timeInMillis, pendingIntent)
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) // Works
alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.timeInMillis, pendingIntent)
else // works
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.timeInMillis, pendingIntent)
}
The IntentService class is as the following:
class MyAlarmIntentService : IntentService("Notification") {
override fun onHandleIntent(intent: Intent?) {
val obj = intent?.getParcelableExtra<MyObj>(C.KEY_ME) ?: return
Lg.d(TAG, "My alarm fired for - \n" + obj.toString())
}
companion object {
private val TAG = ContestAlarmIntentService::class.java.simpleName
}
}
The service is declared in manifest as:
<service android:name=".services.MyAlarmIntentService" />
The following is an example of the mentioned log inside setAlarm() method:
E/setAlarm: setting time -> 1514993850 for obj = MyObj{...}
E/setAlarm: Set calendar: java.util.GregorianCalendar[time=1514993850000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=libcore.util.ZoneInfo[id="Asia/Dhaka",mRawOffset=21600000,mEarliestRawOffset=23400000,mUseDst=false,mDstSavings=0,transitions=7],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2018,MONTH=0,WEEK_OF_YEAR=1,WEEK_OF_MONTH=1,DAY_OF_MONTH=3,DAY_OF_YEAR=3,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=37,SECOND=30,MILLISECOND=0,ZONE_OFFSET=21600000,DST_OFFSET=0]
This used to be a straight-forward code to me, but which fundamental may I be missing for these 3 days' of test sessions?
Sorry I did not go through all your code as I was busy in another task, so here's my code used with Broadcast
Intent intent = new Intent(context, AlarmReceiver.class);
intent.setAction(UtilAlarmConstants.ALARM_ACTION);
pIBroadCast = PendingIntent.getBroadcast(context, ALARM_PI_REQ_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
alarmManager.setExactAndAllowWhileIdle(ALARM_TYPE, calendar.getTimeInMillis(),pendingIntent);
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
AlarmManager.AlarmClockInfo alarmClockInfo
= new AlarmManager.AlarmClockInfo(System.currentTimeMillis() + fireAT, null);
alarmManager.setAlarmClock(alarmClockInfo, pIBroadCast);
}
//setExact for 19
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, fireAT, pIBroadCast);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, fireAT, pIBroadCast);
}
}
and Broadcast
if (intent != null && intent.getAction() != null) {
if (intent.getAction().equalsIgnoreCase(UtilAlarmConstants.ALARM_ACTION)) {
setUpNotification(context, intent);}
sorry for not giving answer related to service as they are all same wacky dacky :)

Categories

Resources