Search code examples
androidtimerwidgettimertaskappwidgetprovider

Using TimerTask and Timer with Widget AppWidgetProvider


I'm creating a clock widget using Android. I've tested the clock and the logic seems fine, but I need to get it to update every minute.

I set the updatePeriodMillis to 60000 in my XML but Android limits this to 30 minutes, so it only updates every half hour.

I did some research and a Service or AlarmManager might have worked except the method that I'm calling requires an AppWidgetManager and AppWidgetId that are passed to it via the onUpdate() method.

I've tried two (similar) methods to get the minute update working:

static void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager,
                            final int appWidgetId) {

    final Handler mHandler = new Handler();
    TimerTask minuteTask = new TimerTask() {

        @Override
        public void run() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {

                    //Do Stuff
            });
        }
    };

    timer = new Timer();

    // schedule the task to run starting now and then every minute
    timer.scheduleAtFixedRate(minuteTask, 0l, 1000 * 60);

}

and:

static void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager,
                            final int appWidgetId) {

    TimerTask minuteTask = new TimerTask() {

        @Override
        public void run() {

            //Do stuff
        }
    };

    timer = new Timer();

    // schedule the task to run starting now and then every minute
    timer.scheduleAtFixedRate(minuteTask, 0l, 1000 * 60);

}

Any help is very much appreciated.

Thanks.

EDIT: In the time it's taken me to debug and make this post, I ended up leaving the app installed for half an hour. It would appear the timer has started after half an hour when onUpdate() was first called, but it still doesn't work for the initial 30 mins.


Solution

  • Okay, finally got this working (had exams hence the long gap between posts).

    I ended up using a service, pending intent and alarm manager to fulfil my needs.

    ClockWidget.class:

    private static final String TAG = ClockWidget.class.getSimpleName();
    
    @Override
    public void onReceive(@NonNull Context context, @NonNull Intent intent) {
        super.onReceive(context, intent);
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
        ComponentName componentName = new ComponentName(context, ClockWidget.class);
        onUpdate(context, appWidgetManager, appWidgetManager.getAppWidgetIds(componentName));
    }
    
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        Log.d(TAG, "onUpdate");
    
        ComponentName componentName = new ComponentName(context, ClockWidget.class);
        int[] allWidgetIds = appWidgetManager.getAppWidgetIds(componentName);
    
        Intent intent = new Intent(context.getApplicationContext(), UpdateService.class);
        intent.setAction(UpdateService.ACTION_UPDATE);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
        context.startService(intent);
    
        PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    
        final AlarmManager m = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    
        m.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000 * 60, pendingIntent);
    }
    

    UpdateService.class:

    private static final String TAG = UpdateService.class.getSimpleName();
    
    public static final String ACTION_UPDATE = "uk.co.thebillington.laxe.ACTION_UPDATE";
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) {
            return super.onStartCommand(null, flags, startId);
        }
    
        String action = intent.getAction();
        if (action == null) {
            return super.onStartCommand(intent, flags, startId);
        }
    
        Log.d(TAG, String.format("onStartCommand: %s", action));
    
        if (action.equals(ACTION_UPDATE)) {
            int[] allWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
            for (int widgetId : allWidgetIds) {
                updateWidget(widgetId);
            }
        }
    
        return super.onStartCommand(intent, flags, startId);
    }
    
    private void updateWidget(int widgetId) {
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
    
        RemoteViews views = new RemoteViews(this.getPackageName(), R.layout.clock);
    
        //Do stuff
    
        appWidgetManager.updateAppWidget(widgetId, views);
    }