Skip to content

Commit

Permalink
Add WidgetAlarm for managing recurring widget updates
Browse files Browse the repository at this point in the history
- Introduced `WidgetAlarm` object to handle the setting and cancellation of recurring alarms for widget updates.
- Added methods to create or retrieve `PendingIntent` instances associated with widgets.
- Ensured that alarms are set to trigger widget updates every minute, avoiding multiple alarms for the same widget.
- Integrated logging with Timber for tracking alarm creation and cancellation.
  • Loading branch information
xenonnn4w committed Aug 22, 2024
1 parent 958ed5c commit fa2bd84
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 2 deletions.
5 changes: 3 additions & 2 deletions AnkiDroid/src/main/java/com/ichi2/widget/DeckPickerWidget.kt
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ class DeckPickerWidget : AnalyticsWidgetProvider() {
Timber.d("Selected deck IDs: ${selectedDeckIds.joinToString(", ")} for widget ID: $widgetId")
updateWidget(context, appWidgetManager, widgetId, selectedDeckIds)
}
WidgetAlarm.setRecurringAlarm(context, widgetId)
}

Timber.d("Widget update process completed for appWidgetIds: ${appWidgetIds.joinToString(", ")}")
Expand Down Expand Up @@ -236,7 +237,7 @@ class DeckPickerWidget : AnalyticsWidgetProvider() {
)
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
Timber.d("Deleting widget with ID: $appWidgetId")

WidgetAlarm.cancelRecurringAlarm(context, appWidgetId)
widgetPreferences.deleteDeckPickerWidgetData(appWidgetId)
} else {
Timber.e("Invalid widget ID received in ACTION_APPWIDGET_DELETED")
Expand Down Expand Up @@ -269,7 +270,7 @@ class DeckPickerWidget : AnalyticsWidgetProvider() {
val widgetPreferences = WidgetPreferences(context)

appWidgetIds?.forEach { widgetId ->

WidgetAlarm.cancelRecurringAlarm(context, widgetId)
widgetPreferences.deleteDeckPickerWidgetData(widgetId)
}
}
Expand Down
118 changes: 118 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/widget/WidgetAlarm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2024 Anoop <[email protected]>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.ichi2.widget

import android.app.AlarmManager
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.os.SystemClock
import timber.log.Timber
import kotlin.time.Duration.Companion.seconds

object WidgetAlarm {

/**
* Provides the AlarmManager instance.
*
* @param context the context of the application
* @return the AlarmManager instance
*/
private fun alarmManager(context: Context): AlarmManager {
return context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
}

/**
* Retrieves or creates a PendingIntent for the widget.
*
* @param context the context of the application
* @param appWidgetId the ID of the widget
* @param create whether to create a new PendingIntent or just retrieve it
* @return the PendingIntent for the widget
*/
private fun getPendingIntent(
context: Context,
appWidgetId: AppWidgetId,
create: Boolean
): PendingIntent? {
val intent = Intent(context, DeckPickerWidget::class.java).apply {
action = DeckPickerWidget.ACTION_UPDATE_WIDGET
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
}
return PendingIntent.getBroadcast(
context,
appWidgetId,
intent,
if (create) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
}
)
}

/**
* Ensure a recurring alarm is set to update the widget every minute.
*
* If the alarm is already set for the widget, this method does nothing.
* This ensures that multiple alarms are not created for the same widget,
* preventing potential performance issues or unexpected behavior.
*
* @param context the context of the application
* @param appWidgetId the ID of the widget
*/
fun setRecurringAlarm(context: Context, appWidgetId: AppWidgetId) {
val pendingIntent = getPendingIntent(context, appWidgetId, create = false)

if (pendingIntent != null) {
Timber.v("Recurring alarm PendingIntent already exists for widget ID: $appWidgetId")
return
}

Timber.v("Creating a new recurring alarm PendingIntent for widget ID: $appWidgetId")

val alarmManager = alarmManager(context)
val newPendingIntent = getPendingIntent(context, appWidgetId, create = true)

// Set alarm to trigger every minute
val ONE_MINUTE_MILLIS = 60.seconds.inWholeMilliseconds
if (newPendingIntent != null) {
alarmManager.setRepeating(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + ONE_MINUTE_MILLIS,
ONE_MINUTE_MILLIS,
newPendingIntent
)
}
}

/**
* Cancels the recurring alarm for the widget.
*
* @param context the context of the application
* @param appWidgetId the ID of the widget
*/
fun cancelRecurringAlarm(context: Context, appWidgetId: AppWidgetId) {
val pendingIntent = getPendingIntent(context, appWidgetId, create = true)
val alarmManager = alarmManager(context)
Timber.d("Canceling recurring alarm for widget ID: $appWidgetId")
if (pendingIntent != null) {
alarmManager.cancel(pendingIntent)
}
}
}

0 comments on commit fa2bd84

Please sign in to comment.