package org.gscript.receiver; import java.util.Calendar; import org.gscript.data.ContentUri; import org.gscript.data.LibraryProvider; import org.gscript.data.ScheduleProvider; import org.gscript.data.library.ItemConditions; import org.gscript.process.ProcessService; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.util.Log; public class ScheduleReceiver extends BroadcastReceiver { static final String LOG_TAG = "ScheduleReceiver"; static final int NO_SCHEDULE_ID = -1; public static final String ACTION_RESCHEDULE = "org.gscript.action.RESCHEDULE"; public static final String ACTION_ALARM = "org.gscript.action.ALARM"; public static final String EXTRA_SCHEDULE_ID = "id"; @Override public void onReceive(Context context, Intent intent) { if (ACTION_RESCHEDULE.equals(intent.getAction())) { Log.d(LOG_TAG, "received reschedule intent"); if (intent.hasExtra(EXTRA_SCHEDULE_ID)) { int scheduleId = intent.getIntExtra(EXTRA_SCHEDULE_ID, NO_SCHEDULE_ID); if (scheduleId != NO_SCHEDULE_ID) { /* only reschedule a single schedule */ Log.d(LOG_TAG, "-- only rescheduling single " + scheduleId); Cursor c = context.getContentResolver().query( Uri.withAppendedPath(ContentUri.URI_SCHEDULE, String.valueOf(scheduleId)), null, null, null, null); if (c.moveToFirst()) { int scheduleDays = c.getInt(c .getColumnIndex(ScheduleProvider.COLUMN_DAYS)); int scheduleInterval = c .getInt(c .getColumnIndex(ScheduleProvider.COLUMN_INTERVAL)); int scheduleTimeStart = c .getInt(c .getColumnIndex(ScheduleProvider.COLUMN_TIME_START)); int scheduleTimeEnd = c .getInt(c .getColumnIndex(ScheduleProvider.COLUMN_TIME_END)); scheduleAlarm(context, true, scheduleId, scheduleDays, scheduleTimeStart, scheduleTimeEnd, scheduleInterval); } c.close(); } } else { /* reschedule all schedules */ Log.d(LOG_TAG, "-- rescheduling all"); Cursor c = context.getContentResolver().query( ContentUri.URI_SCHEDULE, null, null, null, null); while (c.moveToNext()) { int scheduleId = c.getInt(c .getColumnIndex(ScheduleProvider.COLUMN_ID)); int scheduleDays = c.getInt(c .getColumnIndex(ScheduleProvider.COLUMN_DAYS)); int scheduleInterval = c.getInt(c .getColumnIndex(ScheduleProvider.COLUMN_INTERVAL)); int scheduleTimeStart = c .getInt(c .getColumnIndex(ScheduleProvider.COLUMN_TIME_START)); int scheduleTimeEnd = c.getInt(c .getColumnIndex(ScheduleProvider.COLUMN_TIME_END)); scheduleAlarm(context, true, scheduleId, scheduleDays, scheduleTimeStart, scheduleTimeEnd, scheduleInterval); } c.close(); } } if (ACTION_ALARM.equals(intent.getAction())) { int scheduleId = intent.getIntExtra(EXTRA_SCHEDULE_ID, NO_SCHEDULE_ID); if (scheduleId != NO_SCHEDULE_ID) { Log.d(LOG_TAG, "received alarm for schedule " + scheduleId); Cursor c = context.getContentResolver().query( Uri.withAppendedPath(ContentUri.URI_SCHEDULE, String.valueOf(scheduleId)), null, null, null, null); if (c.moveToFirst()) { executeScheduledProcesses(context, scheduleId); int scheduleDays = c.getInt(c .getColumnIndex(ScheduleProvider.COLUMN_DAYS)); int scheduleInterval = c.getInt(c .getColumnIndex(ScheduleProvider.COLUMN_INTERVAL)); int scheduleTimeStart = c .getInt(c .getColumnIndex(ScheduleProvider.COLUMN_TIME_START)); int scheduleTimeEnd = c.getInt(c .getColumnIndex(ScheduleProvider.COLUMN_TIME_END)); scheduleAlarm(context, false, scheduleId, scheduleDays, scheduleTimeStart, scheduleTimeEnd, scheduleInterval); } c.close(); } } } void scheduleAlarm(Context context, boolean firstAlarm, int scheduleId, int scheduleDays, int scheduleStart, int scheduleEnd, int scheduleInterval) { Log.d(LOG_TAG, "scheduling alarm for schedule " + scheduleId); if (scheduleDays == 0) { Log.d(LOG_TAG, "no days selected in schedule... no alarm to set"); } else { Intent intent = new Intent(ACTION_ALARM); intent.putExtra(EXTRA_SCHEDULE_ID, scheduleId); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, scheduleId, intent, 0); long scheduleAlarm = Long.MAX_VALUE; Calendar current = Calendar.getInstance(); Calendar next = Calendar.getInstance(); Log.d(LOG_TAG, "first alarm: " + firstAlarm); Log.d(LOG_TAG, "start: " + scheduleStart); Log.d(LOG_TAG, "current: " + ((current.get(Calendar.HOUR_OF_DAY) * 60) + current .get(Calendar.MINUTE))); if ((scheduleDays & dayMask(current.get(Calendar.DAY_OF_WEEK))) != 0 && (scheduleInterval != 0 || firstAlarm)) { Log.d(LOG_TAG, "today in schedule days mask"); if (firstAlarm && ((current.get(Calendar.HOUR_OF_DAY) * 60) + current .get(Calendar.MINUTE)) < scheduleStart) { /* * current time below scheduleStart.. set next alarm to * scheduleStart */ next.set(Calendar.HOUR_OF_DAY, 0); next.set(Calendar.MINUTE, 0); next.set(Calendar.SECOND, 0); next.set(Calendar.MILLISECOND, 0); next.add(Calendar.MINUTE, scheduleStart); Log.d(LOG_TAG, "scheduling first alarm (today) on " + next.toString()); scheduleAlarm = next.getTimeInMillis(); } else { if (!firstAlarm) next.add(Calendar.MINUTE, scheduleInterval); /* * check if we are still in the same day and interval still * before todays schedule end ( in minutes ) */ if (next.get(Calendar.DAY_OF_MONTH) == current .get(Calendar.DAY_OF_MONTH) && (((next.get(Calendar.HOUR_OF_DAY) * 60) + next .get(Calendar.MINUTE)) <= scheduleEnd)) { Log.d(LOG_TAG, "scheduling next alarm (today) on " + next.toString()); scheduleAlarm = next.getTimeInMillis(); } else { /* revert next to previous state (-scheduleInterval) */ Log.d(LOG_TAG, "interval exceeds today... looking for next day"); if (!firstAlarm) next.add(Calendar.MINUTE, -scheduleInterval); } } } if (scheduleAlarm == Long.MAX_VALUE) { Log.d(LOG_TAG, "schedule not set... looking for next day"); int daysAdded = 0; while (scheduleAlarm == Long.MAX_VALUE && daysAdded <= 7) { /* * keep adding days until we come across a day which is set * in this schedule */ next.roll(Calendar.DATE, true); /* check if we are still in the same week */ if (next.get(Calendar.WEEK_OF_YEAR) != current .get(Calendar.WEEK_OF_YEAR)) { /* TODO: possibly allow skipping weeks in the future */ /* allow looping through another week */ // daysAdded = 0; } if ((scheduleDays & dayMask(next.get(Calendar.DAY_OF_WEEK))) != 0) { /* set next time to scheduleStart */ next.set(Calendar.HOUR_OF_DAY, 0); next.set(Calendar.MINUTE, 0); next.set(Calendar.SECOND, 0); next.set(Calendar.MILLISECOND, 0); next.add(Calendar.MINUTE, scheduleStart); Log.d(LOG_TAG, "next alarm on " + next.toString()); scheduleAlarm = next.getTimeInMillis(); } daysAdded++; } } if (scheduleAlarm != Long.MAX_VALUE) { AlarmManager alarmManager = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); alarmManager.set(AlarmManager.RTC_WAKEUP, scheduleAlarm, pendingIntent); } else { /* should never happen */ Log.e(LOG_TAG, "couldnt find next alarm for schedule " + scheduleId); } } } void executeScheduledProcesses(Context context, int scheduleId) { Cursor c = context.getContentResolver().query( ContentUri.URI_ITEM_CONDITIONS, null, LibraryProvider.COLUMN_KEY + "=? AND " + LibraryProvider.COLUMN_VALUE + "=?", new String[] { ItemConditions.CONDITION_SCHEDULE, String.valueOf(scheduleId) }, null); if (c.getCount() == 0) { Log.d(LOG_TAG, "no processes to execute for schedule " + scheduleId); } while (c.moveToNext()) { int libraryId = c.getInt(c .getColumnIndex(LibraryProvider.COLUMN_LIBRARY)); String itemPath = c.getString(c .getColumnIndex(LibraryProvider.COLUMN_PATH)); Uri itemUri = ContentUri.URI_LIBRARY_PATH(libraryId, itemPath); /* try to get the item so that we know it is accessible */ Cursor itemCursor = context.getContentResolver().query(itemUri, null, null, null, null); if (itemCursor.getCount() > 0) { Log.d(LOG_TAG, String.format( "executing process with schedule condition [ schedule:%d library:%d path:%s ]", scheduleId, libraryId, itemPath)); Intent processIntent = new Intent(context, ProcessService.class); processIntent.setAction(ProcessService.ACTION_EXECUTE); processIntent.setData(itemUri); context.startService(processIntent); } else { Log.d(LOG_TAG, String.format("onschedule: could not load item [ library:%d path:%s ]", libraryId, itemPath)); } itemCursor.close(); } c.close(); } int dayMask(int calendarDay) { switch (calendarDay) { case Calendar.MONDAY: return ScheduleProvider.MONDAY; case Calendar.TUESDAY: return ScheduleProvider.TUESDAY; case Calendar.WEDNESDAY: return ScheduleProvider.WEDNESDAY; case Calendar.THURSDAY: return ScheduleProvider.THURSDAY; case Calendar.FRIDAY: return ScheduleProvider.FRIDAY; case Calendar.SATURDAY: return ScheduleProvider.SATURDAY; case Calendar.SUNDAY: return ScheduleProvider.SUNDAY; } return 0; } }