/** * */ package com.transitwidget.service; import android.app.AlarmManager; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.text.format.DateFormat; import android.util.Log; import com.transitwidget.R; import com.transitwidget.api.NextBusAPI; import com.transitwidget.feed.model.BusPrediction; import com.transitwidget.feed.model.Stop; import com.transitwidget.prefs.NextBusObserverConfig; import com.transitwidget.utils.TimeUtils; import java.util.Arrays; import java.util.Calendar; import java.util.List; /** * Handles reading prediction data from feeds and triggering widget updates. */ public class MBTABackgroundService extends IntentService { private static final String TAG = MBTABackgroundService.class.getName(); public static final String ACTION_WAKEUP = "ACTION_WAKEUP"; public static final String ACTION_WAKEUP_IMMEDIATE = "ACTION_WAKEUP_IMMEDIATE"; public static final String EXTRA_WIDGET_ID = "EXTRA_WIDGET_ID"; public static final String EXTRA_IMMEDIATE = "EXTRA_IMMEDIATE"; public MBTABackgroundService() { super("MBTABackgroundService"); } @Override public void onCreate() { super.onCreate(); Log.i(TAG, "Service started"); } @Override protected void onHandleIntent(Intent intent) { String action = intent.getAction(); Log.i(TAG, "onHandleIntent " + action); int widgetId = intent.getIntExtra(EXTRA_WIDGET_ID, 0); if (widgetId == 0) { Log.e(TAG, "Got an invalid or no widgetID in BackgroundService"); return; } boolean immediate = intent.getBooleanExtra(EXTRA_IMMEDIATE, false); NextBusObserverConfig config = new NextBusObserverConfig(getApplicationContext(), widgetId); if (config.getAgency() == null) { // This is an old widget without a configuration! Log.w(TAG, "Unable to get widget preferences for widget " + widgetId); // Remove any alarms for this widget AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); alarmManager.cancel(getPendingIntent(getApplicationContext(), widgetId)); Log.i(TAG, "onHandleIntent: canceling alarm"); return; } Calendar endTime = TimeUtils.getCalendarWithTimeFromMidnight(config.getStopObserving()); // Calendar startTime = CalendarUtils.getCalendarWithTimeFromMidnight(config.getStartObserving()); String agency = config.getAgency().getTag(); String routeTag = config.getRoute().getTag(); String stopTag = config.getStop().getTag(); String directionTag = config.getDirection().getTag(); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.cancel(0); Log.i(TAG, String.format("onHandleIntent: widgetId=%d endTime=%s agency=%s directionTag=%s routeTag=%s stopTag=%s", widgetId, DateFormat.format("h:mmaa", endTime), agency, directionTag, routeTag, stopTag)); long now = System.currentTimeMillis(); if (!immediate && now >= endTime.getTimeInMillis()) { // Cancel the alarm if it wasn't a direct tap to update and the end time is passed Log.i(TAG, "onHandleIntent: canceling alarm and scheduling for tomorrow"); AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); alarmManager.cancel(getPendingIntent(getApplicationContext(), widgetId)); // Schedule the start time tomorrow Intent serviceIntent = new Intent(getApplicationContext(), AlarmSchedulerService.class); serviceIntent.putExtra(AlarmSchedulerService.EXTRA_WIDGET_ID, widgetId); serviceIntent.putExtra(AlarmSchedulerService.EXTRA_DAY_START_TIME, config.getStartObserving()); serviceIntent.putExtra(AlarmSchedulerService.EXTRA_DAY_END_TIME, config.getStopObserving()); startService(serviceIntent); return; } // Update the data, send notification List<BusPrediction> predictions = new NextBusAPI().getPredictions(agency, stopTag, directionTag, routeTag); Log.i(TAG, "Got predictions: " + predictions); if (predictions == null) { Log.w(TAG, "Unable to load predictions"); return; } BusPrediction nextPrediction = new BusPrediction(); BusPrediction secondPrediction = new BusPrediction(); if (predictions.isEmpty()) { Log.i(TAG, "No predictions available for selected route."); } else { nextPrediction = predictions.get(0); if (predictions.size() > 1) { secondPrediction = predictions.get(1); } CharSequence tickerText = predictions.toString(); long when = System.currentTimeMillis(); // Lookup stop String selection = Stop.TAG + " = ? AND " + Stop.AGENCY + " = ?"; String[] selectionArgs = new String[] {stopTag, agency}; Cursor result = getContentResolver().query(Stop.CONTENT_URI, null, selection, selectionArgs, null); String stopLabel = "Unknown"; if (result.moveToFirst()) { stopLabel = new Stop(result).getTitle(); } result.close(); Notification notification = new Notification(R.drawable.icon_bus, tickerText, when); Context context = getApplicationContext(); Intent notificationIntent = new Intent("notification-action"); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); CharSequence contentTitle = TimeUtils.formatTimeOfNextBus(nextPrediction.getEpochTime()); CharSequence contentText = routeTag + ": " + stopLabel; notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); nm.notify(widgetId, notification); Log.i(TAG, "Sent notification: " + tickerText); } int[] widgetIds = {widgetId}; Log.i(TAG, "Updating widgets: " + Arrays.asList(widgetIds)); Intent updateWidgetIntent = new Intent(getApplicationContext(), UpdateWidgetService.class); updateWidgetIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); updateWidgetIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds); updateWidgetIntent.putExtra(UpdateWidgetService.EXTRA_NEXT_TIME, nextPrediction.getEpochTime()); updateWidgetIntent.putExtra(UpdateWidgetService.EXTRA_SECOND_TIME, secondPrediction.getEpochTime()); startService(updateWidgetIntent); } @Override public void onDestroy() { Log.i(TAG, "Service destroyed"); super.onDestroy(); } public static Intent createPredictionIntent(Context ctx, int widgetId) { Intent intent = new Intent(ctx, MBTABackgroundService.class); intent.setAction(MBTABackgroundService.ACTION_WAKEUP + "-" + widgetId); intent.putExtra(EXTRA_WIDGET_ID, widgetId); return intent; } public static PendingIntent getPendingIntent(Context ctx, int widgetId) { PendingIntent pi = PendingIntent.getService( ctx, 0, createPredictionIntent(ctx, widgetId), PendingIntent.FLAG_CANCEL_CURRENT); return pi; } }