From 69b174b2116f18e9c77f5b8f1896d2d5857b3f31 Mon Sep 17 00:00:00 2001 From: Adithya-hv Date: Sun, 10 Mar 2024 20:26:38 -0400 Subject: [PATCH] Refactored RealtimeService using WorkManager --- .../directions/realtime/RealTimeWorker.java | 196 ++++++++++ .../directions/realtime/RealtimeService.java | 360 ++++-------------- .../realtime/RealtimeWakefulReceiver.java | 36 -- .../android/directions/util/OTPConstants.java | 11 +- .../directions/util/TripRequestBuilder.java | 66 +++- .../android/ui/TripResultsFragment.java | 8 +- 6 files changed, 344 insertions(+), 333 deletions(-) create mode 100644 onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealTimeWorker.java delete mode 100644 onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeWakefulReceiver.java diff --git a/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealTimeWorker.java b/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealTimeWorker.java new file mode 100644 index 000000000..27a10ac2f --- /dev/null +++ b/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealTimeWorker.java @@ -0,0 +1,196 @@ +package org.onebusaway.android.directions.realtime; + +import org.onebusaway.android.R; +import org.onebusaway.android.app.Application; +import org.onebusaway.android.directions.model.ItineraryDescription; +import org.onebusaway.android.directions.tasks.TripRequest; +import org.onebusaway.android.directions.util.OTPConstants; +import org.onebusaway.android.directions.util.TripRequestBuilder; +import org.opentripplanner.api.model.Itinerary; +import org.opentripplanner.api.model.TripPlan; + +import android.app.Activity; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Bundle; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import androidx.core.app.NotificationCompat; +import androidx.work.WorkManager; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +public class RealTimeWorker extends Worker { + private static final String TAG = "RealTimeWorker"; + private static final String ITINERARY_DESC = ".ItineraryDesc"; + private static final String ITINERARY_END_DATE = ".ItineraryEndDate"; + private Result mResult = Result.success(); + + public RealTimeWorker(@androidx.annotation.NonNull Context context, @androidx.annotation.NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @androidx.annotation.NonNull + @Override + public Result doWork() { + Bundle bundle = RealtimeService.toBundle(getInputData()); + Log.d(TAG, bundle.toString()+" Worker"); + + checkForItineraryChange(bundle); + return mResult; + } + private void checkForItineraryChange(final Bundle bundle) { + TripRequestBuilder builder = TripRequestBuilder.initFromBundleSimple(bundle); + ItineraryDescription desc = getItineraryDescription(bundle); + Class target = getNotificationTarget(bundle); + if (target == null) { + disableListenForTripUpdates(); + return; + } + checkForItineraryChange(target, builder, desc); + } + private void checkForItineraryChange(final Class source, final TripRequestBuilder builder, final ItineraryDescription itineraryDescription) { + + Log.d(TAG, "Check for change"); + + TripRequest.Callback callback = new TripRequest.Callback() { + @Override + public void onTripRequestComplete(TripPlan tripPlan, String url) { + if (tripPlan == null || tripPlan.itineraries == null || tripPlan.itineraries.isEmpty()) { + onTripRequestFailure(-1, null); + return; + } + + // Check each itinerary. Notify user if our *current* itinerary doesn't exist + // or has a lower rank. + for (int i = 0; i < tripPlan.itineraries.size(); i++) { + ItineraryDescription other = new ItineraryDescription(tripPlan.itineraries.get(i)); + + if (itineraryDescription.itineraryMatches(other)) { + + long delay = itineraryDescription.getDelay(other); + Log.d(TAG, "Schedule deviation on itinerary: " + delay); + + if (Math.abs(delay) > OTPConstants.REALTIME_SERVICE_DELAY_THRESHOLD) { + Log.d(TAG, "Notify due to large early/late schedule deviation."); + showNotification(itineraryDescription, + (delay > 0) ? R.string.trip_plan_delay + : R.string.trip_plan_early, + R.string.trip_plan_notification_new_plan_text, + source, builder.getBundle(), tripPlan.itineraries); + disableListenForTripUpdates(); + return; + } + + // Otherwise, we are still good. + Log.d(TAG, "Itinerary exists and no large schedule deviation."); + checkDisableDueToTimeout(itineraryDescription); + + return; + } + } + Log.d(TAG, "Did not find a matching itinerary in new call - notify user that something has changed."); + showNotification(itineraryDescription, + R.string.trip_plan_notification_new_plan_title, + R.string.trip_plan_notification_new_plan_text, source, + builder.getBundle(), tripPlan.itineraries); + disableListenForTripUpdates(); + } + + @Override + public void onTripRequestFailure(int result, String url) { + Log.e(TAG, "Failure checking itineraries. Result=" + result + ", url=" + url); + disableListenForTripUpdates(); + + } + }; + + builder.setListener(callback); + + try { + builder.execute(); + } catch (Exception e) { + e.printStackTrace(); + disableListenForTripUpdates(); + } + } + private void showNotification(ItineraryDescription description, int title, int message, + Class notificationTarget, + Bundle params, List itineraries) { + + String titleText = Resources.getSystem().getString(title); + String messageText = Resources.getSystem().getString(message); + + Intent openIntent = new Intent(getApplicationContext(), notificationTarget); + openIntent.putExtras(params); + openIntent.putExtra(OTPConstants.INTENT_SOURCE, OTPConstants.Source.NOTIFICATION); + openIntent.putExtra(OTPConstants.ITINERARIES, (ArrayList) itineraries); + openIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + int flags; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + flags = PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE; + } else { + flags = PendingIntent.FLAG_CANCEL_CURRENT; + } + PendingIntent openPendingIntent = PendingIntent + .getActivity(getApplicationContext(), + 0, + openIntent, + flags); + + NotificationCompat.Builder mBuilder = + new NotificationCompat.Builder(getApplicationContext(), Application.CHANNEL_TRIP_PLAN_UPDATES_ID) + .setSmallIcon(R.drawable.ic_stat_notification) + .setContentTitle(titleText) + .setStyle(new NotificationCompat.BigTextStyle().bigText(messageText)) + .setContentText(messageText) + .setPriority(NotificationCompat.PRIORITY_MAX) + .setContentIntent(openPendingIntent); + + NotificationManager notificationManager = + (NotificationManager) getApplicationContext() + .getSystemService(Context.NOTIFICATION_SERVICE); + Notification notification = mBuilder.build(); + notification.defaults = Notification.DEFAULT_ALL; + notification.flags |= Notification.FLAG_AUTO_CANCEL | Notification.FLAG_SHOW_LIGHTS; + + Integer notificationId = description.getId(); + notificationManager.notify(notificationId, notification); + } + private void disableListenForTripUpdates() { + Log.d(TAG, "disabled"); + mResult = Result.failure(); + WorkManager.getInstance().getWorkInfosForUniqueWork(OTPConstants.REALTIME_UNIQUE_WORKER_NAME).cancel(true); + + } + private void checkDisableDueToTimeout(ItineraryDescription itineraryDescription) { + if (itineraryDescription.isExpired()) { + Log.d(TAG, "End of trip has passed."); + disableListenForTripUpdates(); + } + } + private ItineraryDescription getItineraryDescription(Bundle bundle) { + String ids[] = bundle.getStringArray(ITINERARY_DESC); + long date = bundle.getLong(ITINERARY_END_DATE); + return new ItineraryDescription(Arrays.asList(ids), new Date(date)); + } + + private Class getNotificationTarget(Bundle bundle) { + try { + Class target = bundle.getSerializable(OTPConstants.NOTIFICATION_TARGET).getClass(); + return target; + } catch(Exception e) { + Log.e(TAG, e.toString()); + } + return null; + } +} diff --git a/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeService.java b/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeService.java index 0621de357..c085f52b8 100644 --- a/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeService.java +++ b/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeService.java @@ -15,34 +15,29 @@ */ package org.onebusaway.android.directions.realtime; -import android.app.Activity; -import android.app.AlarmManager; -import android.app.IntentService; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.util.Log; - -import androidx.core.app.NotificationCompat; - -import org.onebusaway.android.R; import org.onebusaway.android.app.Application; import org.onebusaway.android.directions.model.ItineraryDescription; -import org.onebusaway.android.directions.tasks.TripRequest; import org.onebusaway.android.directions.util.OTPConstants; import org.onebusaway.android.directions.util.TripRequestBuilder; import org.opentripplanner.api.model.Itinerary; import org.opentripplanner.api.model.Leg; -import org.opentripplanner.api.model.TripPlan; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import androidx.work.Data; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; /** * This service is started after a trip is planned by the user so they can be notified if the @@ -50,312 +45,119 @@ * and then the top result for that trip gets delayed by 20 minutes, the user will be notified * that new trip results are available. */ -public class RealtimeService extends IntentService { - +public class RealtimeService { private static final String TAG = "RealtimeService"; - private static final String ITINERARY_DESC = ".ItineraryDesc"; private static final String ITINERARY_END_DATE = ".ItineraryEndDate"; - public RealtimeService() { - super("RealtimeService"); - } - /** * Start realtime updates. * - * @param source Activity from which updates are started + * @param source Activity from which class information is read * @param bundle Bundle with selected itinerary/parameters + * @param context Context with which work manager is initialized */ - public static void start(Activity source, Bundle bundle) { + public static void start(Bundle bundle, Activity source, Context context){ - SharedPreferences prefs = Application.getPrefs(); - if (!prefs.getBoolean(OTPConstants.PREFERENCE_KEY_LIVE_UPDATES, true)) { + // FIXME - Figure out why sometimes the bundle is empty - see #790 and #791 + if(bundle == null){ + Log.d(TAG, "Bundle is null"); return; } - bundle.putSerializable(OTPConstants.NOTIFICATION_TARGET, source.getClass()); - Intent intent = new Intent(OTPConstants.INTENT_START_CHECKS); - intent.putExtras(bundle); - source.sendBroadcast(intent); - } + if(isRealTimeEnabled() && isItineraryRealTime(bundle)){ + Data data = buildData(bundle, source.getClass()); - @Override - public void onHandleIntent(Intent intent) { - Bundle bundle = intent.getExtras(); + PeriodicWorkRequest checkItineraries = new PeriodicWorkRequest + .Builder(RealTimeWorker.class, OTPConstants.DEFAULT_UPDATE_INTERVAL_TRIP_TIME, TimeUnit.MILLISECONDS) + .setInputData(data) + .setInitialDelay(getStartTime(bundle), TimeUnit.MILLISECONDS) + .build(); - if (intent.getAction().equals(OTPConstants.INTENT_START_CHECKS)) { - disableListenForTripUpdates(); - if (!rescheduleRealtimeUpdates(bundle)) { - Itinerary itinerary = getItinerary(bundle); - startRealtimeUpdates(bundle, itinerary); - } - } else if (intent.getAction().equals(OTPConstants.INTENT_CHECK_TRIP_TIME)) { - checkForItineraryChange(bundle); - } + Log.d(TAG, "RealtimeService Enqueued"); + WorkManager workManager = WorkManager.getInstance(context); - RealtimeWakefulReceiver.completeWakefulIntent(intent); + workManager.enqueueUniquePeriodicWork( + OTPConstants.REALTIME_UNIQUE_WORKER_NAME, + ExistingPeriodicWorkPolicy.REPLACE, + checkItineraries); + } } - // Depending on preferences / whether there is realtime info, start updates. - private void startRealtimeUpdates(Bundle params, Itinerary itinerary) { - - Log.d(TAG, "Checking whether to start realtime updates."); + private static boolean isRealTimeEnabled(){ + SharedPreferences prefs = Application.getPrefs(); + Log.d(TAG, "RealtimeEnabled :"+String.valueOf(prefs.getBoolean(OTPConstants.PREFERENCE_KEY_LIVE_UPDATES, true))); + return prefs.getBoolean(OTPConstants.PREFERENCE_KEY_LIVE_UPDATES, true); + } + private static boolean isItineraryRealTime(Bundle bundle){ + Itinerary itinerary = getItinerary(bundle); boolean realtimeLegsOnItineraries = false; - for (Leg leg : itinerary.legs) { if (leg.realTime) { realtimeLegsOnItineraries = true; } } - - if (realtimeLegsOnItineraries) { - Log.d(TAG, "Starting realtime updates for itinerary"); - - // init alarm mgr - getAlarmManager().setInexactRepeating(AlarmManager.RTC, new Date().getTime(), - OTPConstants.DEFAULT_UPDATE_INTERVAL_TRIP_TIME, getAlarmIntent(params)); - } else { - Log.d(TAG, "No realtime legs on itinerary"); - } - + Log.d(TAG, "RealtimeItinerary :"+String.valueOf(realtimeLegsOnItineraries)); + return realtimeLegsOnItineraries; } - /** - * Check to see if the start of real-time trip updates should be rescheduled, and if necessary - * reschedule it - * - * @param bundle trip details to be passed to TripRequestBuilder constructor - * @return true if the start of trip real-time updates has been rescheduled, false if updates - * should begin immediately - */ - private boolean rescheduleRealtimeUpdates(Bundle bundle) { - // Delay if this trip doesn't start for at least an hour - Date start = new TripRequestBuilder(bundle).getDateTime(); - if (start == null) { - // To avoid NPE, return true to say that it's been rescheduled, but don't actually reschedule it - // FIXME - Figure out why sometimes the bundle is empty - see #790 and #791 - return true; - } - Date queryStart = new Date(start.getTime() - OTPConstants.REALTIME_SERVICE_QUERY_WINDOW); - boolean reschedule = new Date().before(queryStart); - - if (reschedule) { - Log.d(TAG, "Start service at " + queryStart); - Intent future = new Intent(OTPConstants.INTENT_START_CHECKS); - future.putExtras(bundle); - - int flags; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { - flags = PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE; - } else { - flags = PendingIntent.FLAG_CANCEL_CURRENT; - } - PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), - 0, future, flags); - getAlarmManager().set(AlarmManager.RTC_WAKEUP, queryStart.getTime(), pendingIntent); - } - - return reschedule; - } - - private void checkForItineraryChange(final Bundle bundle) { - TripRequestBuilder builder = TripRequestBuilder.initFromBundleSimple(bundle); - ItineraryDescription desc = getItineraryDescription(bundle); - Class target = getNotificationTarget(bundle); - if (target == null) { - disableListenForTripUpdates(); - return; - } - checkForItineraryChange(target, builder, desc); - } + private static Data buildData(Bundle bundle, final Class source) { + bundle.putSerializable(OTPConstants.NOTIFICATION_TARGET, source); - private void checkForItineraryChange(final Class source, final TripRequestBuilder builder, final ItineraryDescription itineraryDescription) { + Data result = new TripRequestBuilder(bundle).getRealTimeData(); + Data.Builder builder = new Data.Builder(); + builder.putAll(result); - Log.d(TAG, "Check for change"); - - TripRequest.Callback callback = new TripRequest.Callback() { - @Override - public void onTripRequestComplete(TripPlan tripPlan, String url) { - if (tripPlan == null || tripPlan.itineraries == null || tripPlan.itineraries.isEmpty()) { - onTripRequestFailure(-1, null); - return; - } - - // Check each itinerary. Notify user if our *current* itinerary doesn't exist - // or has a lower rank. - for (int i = 0; i < tripPlan.itineraries.size(); i++) { - ItineraryDescription other = new ItineraryDescription(tripPlan.itineraries.get(i)); - - if (itineraryDescription.itineraryMatches(other)) { - - long delay = itineraryDescription.getDelay(other); - Log.d(TAG, "Schedule deviation on itinerary: " + delay); - - if (Math.abs(delay) > OTPConstants.REALTIME_SERVICE_DELAY_THRESHOLD) { - Log.d(TAG, "Notify due to large early/late schedule deviation."); - showNotification(itineraryDescription, - (delay > 0) ? R.string.trip_plan_delay - : R.string.trip_plan_early, - R.string.trip_plan_notification_new_plan_text, - source, builder.getBundle(), tripPlan.itineraries); - disableListenForTripUpdates(); - return; - } - - // Otherwise, we are still good. - Log.d(TAG, "Itinerary exists and no large schedule deviation."); - checkDisableDueToTimeout(itineraryDescription); - - return; - } - } - Log.d(TAG, "Did not find a matching itinerary in new call - notify user that something has changed."); - showNotification(itineraryDescription, - R.string.trip_plan_notification_new_plan_title, - R.string.trip_plan_notification_new_plan_text, source, - builder.getBundle(), tripPlan.itineraries); - disableListenForTripUpdates(); - } - - @Override - public void onTripRequestFailure(int result, String url) { - Log.e(TAG, "Failure checking itineraries. Result=" + result + ", url=" + url); - disableListenForTripUpdates(); - } - }; - - builder.setListener(callback); - - try { - builder.execute(); - } catch (Exception e) { - e.printStackTrace(); - disableListenForTripUpdates(); - } - } - - private void showNotification(ItineraryDescription description, int title, int message, - Class notificationTarget, - Bundle params, List itineraries) { - - String titleText = getResources().getString(title); - String messageText = getResources().getString(message); - - Intent openIntent = new Intent(getApplicationContext(), notificationTarget); - openIntent.putExtras(params); - openIntent.putExtra(OTPConstants.INTENT_SOURCE, OTPConstants.Source.NOTIFICATION); - openIntent.putExtra(OTPConstants.ITINERARIES, (ArrayList) itineraries); - openIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - int flags; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { - flags = PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE; - } else { - flags = PendingIntent.FLAG_CANCEL_CURRENT; - } - PendingIntent openPendingIntent = PendingIntent - .getActivity(getApplicationContext(), - 0, - openIntent, - flags); + Itinerary itinerary = getItinerary(bundle); + ItineraryDescription desc = new ItineraryDescription(itinerary); + List idList = desc.getTripIds(); - NotificationCompat.Builder mBuilder = - new NotificationCompat.Builder(getApplicationContext(), Application.CHANNEL_TRIP_PLAN_UPDATES_ID) - .setSmallIcon(R.drawable.ic_stat_notification) - .setContentTitle(titleText) - .setStyle(new NotificationCompat.BigTextStyle().bigText(messageText)) - .setContentText(messageText) - .setPriority(NotificationCompat.PRIORITY_MAX) - .setContentIntent(openPendingIntent); + String[] ids = idList.toArray(new String[idList.size()]); + long endDate = desc.getEndDate().getTime(); + builder.putStringArray(ITINERARY_DESC, ids); + builder.putLong(ITINERARY_END_DATE, endDate); - NotificationManager notificationManager = - (NotificationManager) getApplicationContext() - .getSystemService(Context.NOTIFICATION_SERVICE); - Notification notification = mBuilder.build(); - notification.defaults = Notification.DEFAULT_ALL; - notification.flags |= Notification.FLAG_AUTO_CANCEL | Notification.FLAG_SHOW_LIGHTS; + builder.putString(OTPConstants.NOTIFICATION_TARGET, + bundle.getSerializable(OTPConstants.NOTIFICATION_TARGET).toString()); - Integer notificationId = description.getId(); - notificationManager.notify(notificationId, notification); + Data simplifiedData = builder.build(); + return simplifiedData; } - // If the end time for this itinerary has passed, disable trip updates. - private void checkDisableDueToTimeout(ItineraryDescription itineraryDescription) { - if (itineraryDescription.isExpired()) { - Log.d(TAG, "End of trip has passed."); - disableListenForTripUpdates(); - } - } + public static Bundle toBundle(Data data) { + Bundle bundle = TripRequestBuilder.convertRealTimeData(data); + Map map = data.getKeyValueMap(); - public void disableListenForTripUpdates() { - Log.d(TAG, "Disable trip updates."); - getAlarmManager().cancel(getAlarmIntent(null)); - } + bundle.putStringArray(ITINERARY_DESC, (String[]) map.get(ITINERARY_DESC)); + bundle.putLong(ITINERARY_END_DATE, (Long) map.get(ITINERARY_END_DATE)); + bundle.putSerializable(OTPConstants.NOTIFICATION_TARGET, (String) map.get(OTPConstants.NOTIFICATION_TARGET)); - private AlarmManager getAlarmManager() { - return (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE); + return bundle; } - private PendingIntent getAlarmIntent(Bundle bundle) { - Intent intent = new Intent(OTPConstants.INTENT_CHECK_TRIP_TIME); - if (bundle != null) { - Bundle extras = getSimplifiedBundle(bundle); - intent.putExtras(extras); - } - int flags; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { - flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE; - } else { - flags = PendingIntent.FLAG_UPDATE_CURRENT; + /** + * Check to see if the start of real-time trip updates should be rescheduled, and if necessary + * reschedule it + * + * @param bundle trip details to be passed to TripRequestBuilder constructor + * @return Time to delay the start of trip updates + */ + private static long getStartTime(Bundle bundle){ + Date start = new TripRequestBuilder(bundle).getDateTime(); + Date queryStart = new Date(start.getTime() - OTPConstants.REALTIME_SERVICE_QUERY_WINDOW); + boolean reschedule = new Date().before(queryStart); + if(reschedule){ + return queryStart.getTime(); } - PendingIntent alarmIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, - flags); - return alarmIntent; + return 0; } - private Itinerary getItinerary(Bundle bundle) { + private static Itinerary getItinerary(Bundle bundle) { ArrayList itineraries = (ArrayList) bundle .getSerializable(OTPConstants.ITINERARIES); int i = bundle.getInt(OTPConstants.SELECTED_ITINERARY); return itineraries.get(i); } - - private ItineraryDescription getItineraryDescription(Bundle bundle) { - String ids[] = bundle.getStringArray(ITINERARY_DESC); - long date = bundle.getLong(ITINERARY_END_DATE); - return new ItineraryDescription(Arrays.asList(ids), new Date(date)); - } - - private Class getNotificationTarget(Bundle bundle) { - String name = bundle.getString(OTPConstants.NOTIFICATION_TARGET); - try { - return Class.forName(name); - } catch(ClassNotFoundException e) { - Log.e(TAG, "unable to find class for name " + name); - } - return null; - } - - private Bundle getSimplifiedBundle(Bundle params) { - Itinerary itinerary = getItinerary(params); - ItineraryDescription desc = new ItineraryDescription(itinerary); - - Bundle extras = new Bundle(); - new TripRequestBuilder(params).copyIntoBundleSimple(extras); - - List idList = desc.getTripIds(); - String[] ids = idList.toArray(new String[idList.size()]); - extras.putStringArray(ITINERARY_DESC, ids); - extras.putLong(ITINERARY_END_DATE, desc.getEndDate().getTime()); - - Class source = (Class) - params.getSerializable(OTPConstants.NOTIFICATION_TARGET); - - extras.putString(OTPConstants.NOTIFICATION_TARGET, source.getName()); - - return extras; - } - } diff --git a/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeWakefulReceiver.java b/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeWakefulReceiver.java deleted file mode 100644 index 298d867a5..000000000 --- a/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeWakefulReceiver.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (C) 2016 Cambridge Systematics, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onebusaway.android.directions.realtime; - -import android.content.Context; -import android.content.Intent; - -import androidx.legacy.content.WakefulBroadcastReceiver; - -public class RealtimeWakefulReceiver extends WakefulBroadcastReceiver { - - @Override - public void onReceive(Context context, Intent intent) { - - // Just proxy intent to RealtimeService - Intent service = new Intent(context, RealtimeService.class); - service.putExtras(intent.getExtras()); - service.setAction(intent.getAction()); - startWakefulService(context, service); - } - -} \ No newline at end of file diff --git a/onebusaway-android/src/main/java/org/onebusaway/android/directions/util/OTPConstants.java b/onebusaway-android/src/main/java/org/onebusaway/android/directions/util/OTPConstants.java index 735e00570..c163f8346 100644 --- a/onebusaway-android/src/main/java/org/onebusaway/android/directions/util/OTPConstants.java +++ b/onebusaway-android/src/main/java/org/onebusaway/android/directions/util/OTPConstants.java @@ -22,16 +22,11 @@ import java.util.concurrent.TimeUnit; public class OTPConstants { - - public static final String INTENT_CHECK_TRIP_TIME - = BuildConfig.APPLICATION_ID + ".directions.action.CHECK"; - - public static final String INTENT_START_CHECKS - = BuildConfig.APPLICATION_ID + ".directions.action.START_CHECKS"; - public static final String PREFERENCE_KEY_LIVE_UPDATES = "live_updates"; - public static final long DEFAULT_UPDATE_INTERVAL_TRIP_TIME = TimeUnit.SECONDS.toMillis(60); + public static final long DEFAULT_UPDATE_INTERVAL_TRIP_TIME = TimeUnit.MINUTES.toMillis(15); + + public static final String REALTIME_UNIQUE_WORKER_NAME = "check_itineraries"; public static final long REALTIME_SERVICE_QUERY_WINDOW = TimeUnit.HOURS.toMillis(1); diff --git a/onebusaway-android/src/main/java/org/onebusaway/android/directions/util/TripRequestBuilder.java b/onebusaway-android/src/main/java/org/onebusaway/android/directions/util/TripRequestBuilder.java index 84468aaec..ac14c2fc0 100644 --- a/onebusaway-android/src/main/java/org/onebusaway/android/directions/util/TripRequestBuilder.java +++ b/onebusaway-android/src/main/java/org/onebusaway/android/directions/util/TripRequestBuilder.java @@ -16,11 +16,6 @@ package org.onebusaway.android.directions.util; -import android.app.Activity; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.Log; - import org.onebusaway.android.R; import org.onebusaway.android.app.Application; import org.onebusaway.android.directions.tasks.TripRequest; @@ -30,6 +25,11 @@ import org.opentripplanner.routing.core.OptimizeType; import org.opentripplanner.routing.core.TraverseMode; +import android.app.Activity; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; + import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; import java.net.MalformedURLException; @@ -40,6 +40,9 @@ import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.Map; + +import androidx.work.Data; public class TripRequestBuilder { @@ -369,6 +372,59 @@ public void copyIntoBundleSimple(Bundle target) { } } + /** + * Converts all the data from this builder's bundle into Data only using simple data types + */ + public Data getRealTimeData() { + Data.Builder dataBuilder = new Data.Builder(); + dataBuilder.putBoolean(ARRIVE_BY, getArriveBy()); + CustomAddress from = getFrom(), to = getTo(); + dataBuilder.putDouble(FROM_LAT, from.getLatitude()); + dataBuilder.putDouble(FROM_LON, from.getLongitude()); + dataBuilder.putString(FROM_NAME, from.toString()); + dataBuilder.putDouble(TO_LAT, to.getLatitude()); + dataBuilder.putDouble(TO_LON, to.getLongitude()); + dataBuilder.putString(TO_NAME, to.toString()); + dataBuilder.putString(OPTIMIZE_TRANSFERS, getOptimizeType().toString()); + dataBuilder.putBoolean(WHEELCHAIR_ACCESSIBLE, getWheelchairAccessible()); + dataBuilder.putString(MODE_SET, getModeString()); + if (getMaxWalkDistance() != null) { + dataBuilder.putDouble(MAX_WALK_DISTANCE, getMaxWalkDistance()); + } + if (getDateTime() != null) { + dataBuilder.putLong(DATE_TIME, getDateTime().getTime()); + } + + Data result = dataBuilder.build(); + return result; + } + + /** + * Converts the data back into a Bundle only using simple data types + * @param data Data to be converted + */ + public static Bundle convertRealTimeData(Data data) { + Bundle result = new Bundle(); + Map map = data.getKeyValueMap(); + result.putBoolean(ARRIVE_BY, (Boolean) map.get(ARRIVE_BY)); + result.putDouble(FROM_LAT, (Double) map.get(FROM_LAT)); + result.putDouble(FROM_LON, (Double) map.get(FROM_LON)); + result.putString(FROM_NAME, (String) map.get(FROM_NAME)); + result.putDouble(TO_LAT, (Double) map.get(TO_LAT)); + result.putDouble(TO_LON, (Double) map.get(TO_LON)); + result.putString(TO_NAME, (String) map.get(TO_NAME)); + result.putString(OPTIMIZE_TRANSFERS, (String) map.get(OPTIMIZE_TRANSFERS)); + result.putBoolean(WHEELCHAIR_ACCESSIBLE, (Boolean) map.get(WHEELCHAIR_ACCESSIBLE)); + if (map.get(MAX_WALK_DISTANCE) != null) { + result.putDouble(MAX_WALK_DISTANCE, (Double) map.get(MAX_WALK_DISTANCE)); + } + result.putString(MODE_SET, (String) map.get(MODE_SET)); + if (map.get(DATE_TIME) != null) { + result.putLong(DATE_TIME, (Long) map.get(DATE_TIME)); + } + return result; + } + /** * Initialize from a BaseBundle */ diff --git a/onebusaway-android/src/main/java/org/onebusaway/android/ui/TripResultsFragment.java b/onebusaway-android/src/main/java/org/onebusaway/android/ui/TripResultsFragment.java index baca8d776..870d4f157 100755 --- a/onebusaway-android/src/main/java/org/onebusaway/android/ui/TripResultsFragment.java +++ b/onebusaway-android/src/main/java/org/onebusaway/android/ui/TripResultsFragment.java @@ -342,13 +342,11 @@ void updateInfo() { NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); NotificationChannel channel = manager.getNotificationChannel(Application.CHANNEL_TRIP_PLAN_UPDATES_ID); if(channel.getImportance() != NotificationManager.IMPORTANCE_NONE){ - RealtimeService.start(getActivity(), getArguments()); + RealtimeService.start(getArguments(), getActivity(), context); } } else { - if (Application.getPrefs() - .getBoolean(getString(R.string.preference_key_trip_plan_notifications), true)) { - - RealtimeService.start(getActivity(), getArguments()); + if (Application.getPrefs().getBoolean(getString(R.string.preference_key_trip_plan_notifications), true)) { + RealtimeService.start(getArguments(), getActivity(), context); } } }