How to use Android AlarmManager in Fragment in Kotlin? - android

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

Related

Update appwidgets at exact time with repeating (AlarmManager with setExactAndAllowWhileIdle method)

I have a project with 2 types of appwidgets each of which has own AppWidgetProvider.
All widgets show current date, therefore have to be updated every midnight at exact time.
I've tested the situation when there are two appwidgets on Home Screen: one instance of each widget type.
On my Nokia with Android 10 everthing works fine: both of widgets are updated at every midnight stable. Action is repeated.
But on my OnePlus with Android 12 based on Oxygen OS I observe very strange behaviour: at the first midnight after putting widget on the home screen both of widgets were updated (date of the day was actual), but at the second midnight only one of the widgets was updated, and the second one stopped updating.
Important note: before tests on OnePlus I canceled battery optimization from OnePlus for the app - to avoid stopping process.
My code is below...
Android Manifest
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
// Android 12 requires for exact methods of AlarmManager
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
Every receiver for every type of appwidgets includes
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DATE_CHANGED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.TIME_SET"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
<intent-filter>
<action android:name="ACTION_AUTO_UPDATE_TODAY" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
</intent-filter>
<intent-filter>
<action android:name="android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED" />
</intent-filter>
In every AppWidgetProviderInfo (xml files) for each widget type i have a string
android:updatePeriodMillis="0"
AppWidgetProvider for the first widget
class WidgetPeriodProvider : AppWidgetProvider() {
private var APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE"
// For AlarmManager
private val ACTION_AUTO_UPDATE_PERIOD = "ACTION_AUTO_UPDATE_PERIOD"
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
...
// There may be multiple widgets active, so update all of them
for (appWidgetId in appWidgetIds) {
updateWidgetPeriod(context, appWidgetManager, appWidgetId)
}
super.onUpdate(context, appWidgetManager, appWidgetIds)
}
override fun onEnabled(context: Context) {
// Set Auto Update with AlarmManager
setAlarmManagerPeriod(context)
super.onEnabled(context)
}
override fun onDisabled(context: Context) {
// Disable Auto Update with AlarmManager
disableAlarmManagerPeriod(context)
super.onDisabled(context)
}
override fun onReceive(context: Context, intent: Intent) {
if (
intent.action.equals(AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED) ||
intent.action.equals(ACTION_AUTO_UPDATE_PERIOD) ||
intent.action.equals(Intent.ACTION_DATE_CHANGED) ||
intent.action.equals(Intent.ACTION_TIME_CHANGED) ||
intent.action.equals(Intent.ACTION_TIMEZONE_CHANGED) ||
intent.action.equals(Intent.ACTION_LOCALE_CHANGED) ||
intent.action.equals(Intent.ACTION_PROVIDER_CHANGED) ||
intent.action.equals(Intent.ACTION_BOOT_COMPLETED) ||
intent.action.equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE) ||
intent.action.equals(Intent.ACTION_MY_PACKAGE_REPLACED)
) {
val thisAppWidget = ComponentName(
context.packageName, javaClass.name
)
val appWidgetManager = AppWidgetManager
.getInstance(context)
val ids = appWidgetManager.getAppWidgetIds(thisAppWidget)
// Update widgets
for (appWidgetId in ids) {
updateWidgetPeriod(context, appWidgetManager, appWidgetId)
}
// AlarmManager Auto Update Reschedule
setAlarmManagerPeriod(context)
}
super.onReceive(context, intent)
}
AppWidgetProvider for the second widget
class WidgetTodayProvider : AppWidgetProvider() {
private var APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE"
// For AlarmManager
private val ACTION_AUTO_UPDATE_TODAY = "ACTION_AUTO_UPDATE_TODAY"
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
...
// There may be multiple widgets active, so update all of them
for (appWidgetId in appWidgetIds) {
updateWidgetToday(context, appWidgetManager, appWidgetId)
}
super.onUpdate(context, appWidgetManager, appWidgetIds)
}
override fun onEnabled(context: Context) {
// Set Auto Update with AlarmManager
setAlarmManagerToday(context)
super.onEnabled(context)
}
override fun onDisabled(context: Context) {
// Disable Auto Update with AlarmManager
disableAlarmManagerToday(context)
super.onDisabled(context)
}
override fun onReceive(context: Context, intent: Intent) {
if (
intent.action.equals(AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED) ||
intent.action.equals(ACTION_AUTO_UPDATE_TODAY) ||
intent.action.equals(Intent.ACTION_DATE_CHANGED) ||
intent.action.equals(Intent.ACTION_TIME_CHANGED) ||
intent.action.equals(Intent.ACTION_TIMEZONE_CHANGED) ||
intent.action.equals(Intent.ACTION_LOCALE_CHANGED) ||
intent.action.equals(Intent.ACTION_PROVIDER_CHANGED) ||
intent.action.equals(Intent.ACTION_BOOT_COMPLETED) ||
intent.action.equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE) ||
intent.action.equals(Intent.ACTION_MY_PACKAGE_REPLACED)
) {
val thisAppWidget = ComponentName(
context.packageName, javaClass.name
)
val appWidgetManager = AppWidgetManager
.getInstance(context)
val ids = appWidgetManager.getAppWidgetIds(thisAppWidget)
// Update widgets
for (appWidgetId in ids) {
updateWidgetToday(context, appWidgetManager, appWidgetId)
}
// AlarmManager Auto Update Reschedule
setAlarmManagerToday(context)
}
super.onReceive(context, intent)
}
Function for setting alarm - the first widget type
private fun setAlarmManagerPeriod(context: Context) {
val intent = Intent(context, WidgetPeriodProvider::class.java)
intent.action = ACTION_AUTO_UPDATE_PERIOD
val pIntent = PendingIntent.getBroadcast(context, 2, intent, PendingIntent.FLAG_IMMUTABLE)
val c = Calendar.getInstance()
c[Calendar.HOUR_OF_DAY] = 0
c[Calendar.MINUTE] = 0
c[Calendar.SECOND] = 3
val alarmManager = context
.getSystemService(Context.ALARM_SERVICE) as AlarmManager
// For Android 12 and higher check permission for AlarmManager at exact time
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val hasPermission: Boolean = alarmManager.canScheduleExactAlarms()
if(hasPermission) {
// Permission granted - use exact method
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
c.timeInMillis,
pIntent
)
} else {
// Permission is not granted - use inexact method
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
c.timeInMillis,
AlarmManager.INTERVAL_DAY,
pIntent
)
}
} else {
// For Android below 12 use exact method
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
c.timeInMillis,
pIntent
)
}
}
Function to disable alarm - the first widget type
private fun disableAlarmManagerPeriod(context: Context) {
val intent = Intent(context, WidgetPeriodProvider::class.java)
intent.action = ACTION_AUTO_UPDATE_PERIOD
val pIntent = PendingIntent.getBroadcast(context, 2, intent, PendingIntent.FLAG_IMMUTABLE)
val alarmManager = context
.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.cancel(pIntent)
}
Function for setting alarm - the second widget type
private fun setAlarmManagerToday(context: Context) {
val intent = Intent(context, WidgetTodayProvider::class.java)
intent.action = ACTION_AUTO_UPDATE_TODAY
val pIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_IMMUTABLE)
val c = Calendar.getInstance()
c[Calendar.HOUR_OF_DAY] = 0
c[Calendar.MINUTE] = 0
c[Calendar.SECOND] = 1
val alarmManager = context
.getSystemService(Context.ALARM_SERVICE) as AlarmManager
// For Android 12 and higher check permission for AlarmManager at exact time
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val hasPermission: Boolean = alarmManager.canScheduleExactAlarms()
if(hasPermission) {
// Permission granted - use exact method
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
c.timeInMillis,
pIntent
)
} else {
// Permission is not granted - use inexact method
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
c.timeInMillis,
AlarmManager.INTERVAL_DAY,
pIntent
)
}
} else {
// For Android below 12 use exact method
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
c.timeInMillis,
pIntent
)
}
}
Function to disable alarm - the first widget type
private fun disableAlarmManagerToday(context: Context) {
val intent = Intent(context, WidgetTodayProvider::class.java)
intent.action = ACTION_AUTO_UPDATE_TODAY
val pIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_IMMUTABLE)
val alarmManager = context
.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.cancel(pIntent)
}
Could you help me, please, to answer the question? - Why not all the widgets are updated at every midnight? What's wrong with my code?

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?

Notification in Kotlin repeating every day at the same time

None of the other Stackoverflow code I found works. All is either Java, or I'm too dumb to make it work.
How to fire notification at the same time every day? So basic thing and I can't find anything for Kotlin.
Use this code to schedule showing notification each day at 22:00 (or any other hour in HOUR_TO_SHOW_PUSH):
private val alarmManager = context.getSystemService(ALARM_SERVICE) as AlarmManager
private val alarmPendingIntent by lazy {
val intent = Intent(context, AlarmReceiver::class.java)
PendingIntent.getBroadcast(context, 0, intent, 0)
}
private const val HOUR_TO_SHOW_PUSH = 22
fun schedulePushNotifications() {
val calendar = GregorianCalendar.getInstance().apply {
if (get(Calendar.HOUR_OF_DAY) >= HOUR_TO_SHOW_PUSH) {
add(Calendar.DAY_OF_MONTH, 1)
}
set(Calendar.HOUR_OF_DAY, HOUR_TO_SHOW_PUSH)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
AlarmManager.INTERVAL_DAY,
alarmPendingIntent
)
}
It will trigger BroadcastReceiver called AlarmReceiver, so you'll have to implement it too:
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
showPushNotification() // implement showing notification in this function
}
}
Don't forget to register it in your AndroidManifest.xml:
<receiver android:name="com.your-package-name.AlarmReceiver" android:enabled="true"/>
Also note that to schedule these notifications you have to call schedulePushNotifications(), meaning that the app has to be launched at least once after each reboot. If you want your notifications to be shown after a reboot without launching your app, consider implementing BootReceiver that will be triggered immediately after reboot:
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == "android.intent.action.BOOT_COMPLETED") {
schedulePushNotifications()
}
}
}
Don't forget to register it in AndroidManifest.xml too:
<receiver android:name="com.your-package-name.BootReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>

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

Kotlin AlarmManager and BroadcastReceiver not working

I'm trying to set an alarm with AlarmManager, but my BroadcastReceiver never gets called. Here is my snippet.
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
//Never gets hit
}
}
context.registerReceiver(receiver, IntentFilter(LOCAL_NOTIFICATION))
val intent = Intent()
intent.action = LOCAL_NOTIFICATION
val alarmManager = context.getSystemService(ALARM_SERVICE) as? AlarmManager
val pendingIntent = PendingIntent.getService(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val calendar = Calendar.getInstance()
calendar.add(Calendar.SECOND, 10)
alarmManager?.set(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
I've tried registering a broadcast receiver in AndroidManifest.xml but nothing seems to be working.
I just noticed that I was calling getService() on PendingIntent instead of getBroadcast()
After changing that, it works perfectly!
In addition to the preferred answer, I set the class of the intent before it worked. See the example below:
val intent = Intent()
intent.action = LOCAL_NOTIFICATION
intent.setClass(context, MyBroadCastReceiver::class.java) //this line
before passing the intent to the pendintIntent.
hope it helps

Categories

Resources