Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update Android Widget at Midnight

Tags:

android

widget

How to update Android widget everyday at midnight?? Below solution is formed by looking at other questions in stack overflow regarding to this topic.

1) schedule(context) is called when widget is created in the activity that extends AppWidgetProvider

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;
        schedule(context);
    super.onUpdate(context, appWidgetManager, appWidgetIds);

}

2) Schedule method should set a timer and alarm so that the widget gets updated at everyday at midnight. however this doesn't work.

protected void schedule(Context context) {
    final Intent i = new Intent(context, CalendarWidget.class);
    service = PendingIntent.getService(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.HOUR, 0);
    calendar.set(Calendar.AM_PM, Calendar.AM);
    calendar.add(Calendar.DAY_OF_MONTH, 1); 

    AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

//This doesn't work
       // alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), service);
//This doesn't work either.
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, service);
    }

The solution doesn't work; and it is hard to test it because it can be tested only at midnight everyday; hence, the bounty. I will awarded it to the first person that makes it work.

like image 954
coolcool1994 Avatar asked Dec 11 '14 11:12

coolcool1994


People also ask

How do I force a widget to update?

Any call to updateWidgets() will cause all instances of our widget to be updated. Of course, be careful not to update to often - widget updates use battery power. Bear in mind that the broadcast is received by ALL widget providers, and its our special keys that ensure it is only our widgets that actually update.

How do you update Android widgets?

Full update: Call AppWidgetManager. updateAppWidget(int, android. widget. RemoteViews) to fully update the widget.


2 Answers

denvercoder9's answer works (sort of) but it could be simplified. An AppWidgetProvider is already a BroadcastReceiver, so we can actually direct the intent back on the AppWidgetProvider, and override onReceive to handle it.

In your class extending AppWidgetProvider:

private static final String ACTION_SCHEDULED_UPDATE = "your.package.name.SCHEDULED_UPDATE";

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals(ACTION_SCHEDULED_UPDATE)) {
        AppWidgetManager manager = AppWidgetManager.getInstance(context);
        int[] ids = manager.getAppWidgetIds(new ComponentName(context, AppWidget.class));
        onUpdate(context, manager, ids);
    }

    super.onReceive(context, intent);
}

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    // Do your updates...

    _scheduleNextUpdate(context);
}

private static void _scheduleNextUpdate(Context context) {
    AlarmManager alarmManager =
            (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    // Substitute AppWidget for whatever you named your AppWidgetProvider subclass
    Intent intent = new Intent(context, AppWidget.class);
    intent.setAction(ACTION_SCHEDULED_UPDATE);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

    // Get a calendar instance for midnight tomorrow.
    Calendar midnight = Calendar.getInstance();
    midnight.set(Calendar.HOUR_OF_DAY, 0);
    midnight.set(Calendar.MINUTE, 0);
    // Schedule one second after midnight, to be sure we are in the right day next time this 
    // method is called.  Otherwise, we risk calling onUpdate multiple times within a few 
    // milliseconds
    midnight.set(Calendar.SECOND, 1);
    midnight.set(Calendar.MILLISECOND, 0);
    midnight.add(Calendar.DAY_OF_YEAR, 1);

    // For API 19 and later, set may fire the intent a little later to save battery,
    // setExact ensures the intent goes off exactly at midnight.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        alarmManager.set(AlarmManager.RTC_WAKEUP, midnight.getTimeInMillis(), pendingIntent);
    } else {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, midnight.getTimeInMillis(), pendingIntent);
    }
}

We need a custom intent action, because AppWidgetManager.ACTION_APPWIDGET_UPDATE doesn't update all widgets, even if you pass in the full array of ids as an extra under AppWidgetManager.EXTRA_APPWIDGET_IDS. It just updates the first widget created.

Here is a project I'm working on that implements a scheduled widget update similar to this.

like image 127
Alexander Otavka Avatar answered Oct 21 '22 02:10

Alexander Otavka


I created a date and time widget a while back. Below is the implementation:

In AppWidgetProvider class:

private boolean first_run = true;
public AlarmManager am;
public PendingIntent pi;

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    first_run = context.getSharedPreferences("mywidet", MODE_PRIVATE).getBoolean("first_run", true);
    if(first_run) {
        am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
        pi = PendingIntent.getBroadcast(context, 0, intent, 0);
        long current = System.currentTimeMillis();

        LocalTime localTime = LocalTime.now();
        long triggerOffset = (24*60*60 - localTime.getHour()*60*60 - localTime.getMinute()*60 - localTime.getSecond())*1000;
        am.setRepeating(AlarmManager.RTC_WAKEUP, current + triggerOffset, 86400000, pi);
        SharedPreferences.Editor editor = context.getSharedPreferences("mywidet", MODE_PRIVATE).edit();
        editor.putBoolean("first_run", false);
        editor.apply();
    }
    for (int appWidgetId : appWidgetIds) {
        appWidgetManager.updateAppWidget(appWidgetId, remoteView);
    }
}

in AlarmManagerBroadcasrReceiver class:

@Override
public void onReceive(Context context, Intent intent) {
    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "YOUR TAG");
    //Acquire the lock
    wl.acquire();

    Log.d("widget", "in alarm manager. static method will come next");

    ComponentName thisWidget = new ComponentName(context, NewAppWidget.class);
    AppWidgetManager manager = AppWidgetManager.getInstance(context);
    manager.updateAppWidget(thisWidget, remoteViews);
    //Release the lock
    wl.release();
}

EDIT 1

From API 19 onward all setReapeating() alarms are inexact. So, one might want to do the following:

if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, nexMin.getTimeInMillis()-current.getTimeInMillis(), 60000, pendingIntent);
} else {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, nexMin.getTimeInMillis() - current.getTimeInMillis(), pendingIntent);
}

You can find the full implementation here.

like image 37
denvercoder9 Avatar answered Oct 21 '22 01:10

denvercoder9