/// <summary> /// Load the collection of train trips /// </summary> private static void LoadTrips() { // Load the trips int numberOfTrips = PersistentStorage.GetIntItem(TrainTripsSizeName, 0); trips = new List <TrainTrip>(); for (int tripIndex = 0; tripIndex < numberOfTrips; ++tripIndex) { TrainTrip tripToAdd = new TrainTrip { From = PersistentStorage.GetStringItem(TrainTripFromName + tripIndex, ""), To = PersistentStorage.GetStringItem(TrainTripToName + tripIndex, ""), FromCode = PersistentStorage.GetStringItem(TrainTripFromCodeName + tripIndex, ""), ToCode = PersistentStorage.GetStringItem(TrainTripToCodeName + tripIndex, "") }; trips.Add(tripToAdd); } // Get the current trip selectedTrip = PersistentStorage.GetIntItem(TrainTripSelectedName, 0); // Make sure that the selected trip is valid if (selectedTrip >= trips.Count) { selectedTrip = trips.Count - 1; PersistentStorage.SetIntItem(TrainTripSelectedName, selectedTrip); } }
/// <summary> /// Renders the visual contents of a specific widget contents into a RemoteViews object /// </summary> /// <param name="widgetContext">Widget context.</param> /// <param name="widgetId">Widget identifier.</param> private RemoteViews RenderWidgetContents(Context context, int widgetId) { // Initialise the persistent storage PersistentStorage.StorageMechanism = new StorageMechanism(context); PersistentStorage.UseCache = false; // Create a RemoteView for the widget RemoteViews widgetView = new RemoteViews(context.PackageName, Resource.Layout.widget); // Extract the current trip details and display them. // The trip details and selected trip can be changed independently by the main app so a new set of train trip details need to be read TrainTrips.Reset(); TrainTrip selectedTrip = TrainTrips.SelectedTrip; if (selectedTrip != null) { widgetView.SetTextViewText(Resource.Id.widgetTrip, string.Format("{0}:{1}", selectedTrip.FromCode, selectedTrip.ToCode)); } // Extract the next departure time and display it DateTime departureTime = NextDeparture.DepartureTime; widgetView.SetTextViewText(Resource.Id.widgetDeparture, departureTime.ToString("HH:mm")); // Register pending intents for clicking on the displayed fields RegisterClicks(context, widgetView); // Show the correct image for the running state of the update service if (PersistentStorage.GetBoolItem(UpdateInProgressName, false) == true) { // An update is actually in progress, so show the progress indicator and hide // the service status flags widgetView.SetViewVisibility(Resource.Id.layoutProgressBar, Android.Views.ViewStates.Visible); widgetView.SetViewVisibility(Resource.Id.imageStart, Android.Views.ViewStates.Gone); widgetView.SetViewVisibility(Resource.Id.imageStop, Android.Views.ViewStates.Gone); } else { // Hide the progress indicator and show the servide state widgetView.SetViewVisibility(Resource.Id.layoutProgressBar, Android.Views.ViewStates.Gone); if (PersistentStorage.GetBoolItem(UpdateServiceRunningName, false) == true) { widgetView.SetViewVisibility(Resource.Id.imageStart, Android.Views.ViewStates.Gone); widgetView.SetViewVisibility(Resource.Id.imageStop, Android.Views.ViewStates.Visible); } else { widgetView.SetViewVisibility(Resource.Id.imageStart, Android.Views.ViewStates.Visible); widgetView.SetViewVisibility(Resource.Id.imageStop, Android.Views.ViewStates.Gone); } } return(widgetView); }
/// <summary> /// Get a set of journeys for the specified trip /// </summary> /// <param name="requiredTrip"></param> public void GetJourneys(TrainTrip requiredTrip) { // Save the trip trip = requiredTrip; // As this is a new request clear the old results ClearJourneys(); // Make the request for one minute from now currentRequest = RequestType.NewRequest; MakeRequestAfterSpecifiedTime(DateTime.Now); }
/// <summary> /// Make a request for journeys for the current trip one minute after the specified time /// </summary> private void MakeRequestAfterSpecifiedTime(DateTime specifiedTime, TrainTrip requiredTrip) { // Make the request for one minute from specifiedTime DateTime requestTime = specifiedTime + TimeSpan.FromMinutes(1); // Record the day of the request requestDate = requestTime.Date; // Pass a cancellation token in case this request needs to be cancelled tokenSource = new CancellationTokenSource(); trainJourneyRequest.GetJourneys(requiredTrip.From, requiredTrip.To, requestTime, tokenSource.Token); }
/// <summary> /// Add a new trip to the collection and store. /// </summary> /// <param name="trip"></param> public static void AddTrip(TrainTrip trip) { // Store the trip PersistentStorage.SetStringItem(TrainTripFromName + Trips.Count, trip.From); PersistentStorage.SetStringItem(TrainTripToName + Trips.Count, trip.To); PersistentStorage.SetStringItem(TrainTripFromCodeName + Trips.Count, trip.FromCode); PersistentStorage.SetStringItem(TrainTripToCodeName + Trips.Count, trip.ToCode); // Add to the list Trips.Add(trip); // Update the count PersistentStorage.SetIntItem(TrainTripsSizeName, Trips.Count); }
/// <summary> /// Get a set of journeys for the specified trip /// </summary> /// <param name="requiredTrip"></param> public void GetJourneys(TrainTrip requiredTrip) { // Make the request for one minute from now MakeRequestAfterSpecifiedTime(DateTime.Now, requiredTrip); }
/// <summary> /// Called when a trip item has been long clicked. /// Confirm deletion and then delete the item. /// This may involve changing the currently selected trip /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TripLongClick(object sender, TripAdapter.LongClickEventArgs e) { TrainTrip tripToDelete = TrainTrips.Trips[e.TripPosition]; // Display a confirmation dialogue new AlertDialog.Builder(this) .SetTitle("Confirm trip deletion") .SetMessage(string.Format("Do you realy want to delete the '{0} to {1}' trip?", tripToDelete.From, tripToDelete.To)) .SetPositiveButton("Yes", (senderAlert, args) => { // If the position of the trip to delete is greater than the currently selected trip then just delete it if (e.TripPosition > TrainTrips.Selected) { // Delete the trip TrainTrips.DeleteTrip(e.TripPosition); // Refresh the spinner adapter spinnerAdapter.ReloadSpinner(TrainTrips.TripStrings()); } // If the position of the trip to delete is less than the currently selected trip the delete it and reduce the // index of the selected trip else if (e.TripPosition < TrainTrips.Selected) { // Delete the trip TrainTrips.DeleteTrip(e.TripPosition); // Refresh the spinner adapter spinnerAdapter.ReloadSpinner(TrainTrips.TripStrings()); // Select the previous trip tripSpinner.SetSelection(TrainTrips.Selected - 1); } // If the selected trip is being deleted then either keep the selected index the same and refresh it, or if // the end of the list has been reached then reduce the selected trip index. If there are no items left then set the index to -1 // and make sure that the results are cleared else { // Delete the trip TrainTrips.DeleteTrip(e.TripPosition); // Refresh the spinner adapter spinnerAdapter.ReloadSpinner(TrainTrips.TripStrings()); if (TrainTrips.Selected >= TrainTrips.Trips.Count) { if (TrainTrips.Selected > 0) { // Select the previous trip tripSpinner.SetSelection(TrainTrips.Selected - 1); } else { // Clear the selection. This will not cause the selected event to be called so clear the journey details explicitly tripSpinner.SetSelection(-1); TrainTrips.Selected = -1; // Clear the currently displayed data trainJourneyRetrieval.ClearJourneys(); // Reset the update time updateTimer.ResetTimer(); } } else { // Need to simulate a selection changed event TripItemSelected(null, new AdapterView.ItemSelectedEventArgs(null, null, TrainTrips.Selected, 0)); } } }) .SetNegativeButton("No", (EventHandler <DialogClickEventArgs>)null) .Create() .Show(); }
/// <summary> /// Retrieve a set of journeys for the specified from and to stations. /// </summary> /// <param name="from"></param> /// <param name="to"></param> public async void GetJourneys(string from, string to, DateTime requestTime, CancellationToken cancelToken) { try { // Check if a previously obtained session id cookie is still valid if ((DateTime.Now - sessionCookieTime).TotalMinutes > SeesionCookieValidTimeInMinutes) { // Must load the search page in order to set the session id cookie await client.GetAsync("http://ojp.nationalrail.co.uk/service/planjourney/search", cancelToken); // Save the time that this session id was obtained sessionCookieTime = DateTime.Now; } // Set up the variable parameters requestParameters["from.searchTerm"] = TrainTrip.ToWebFormat(from); requestParameters["to.searchTerm"] = TrainTrip.ToWebFormat(to); requestParameters["timeOfOutwardJourney.hour"] = requestTime.Hour.ToString(); requestParameters["timeOfOutwardJourney.minute"] = requestTime.Minute.ToString(); requestParameters["timeOfOutwardJourney.monthDay"] = requestTime.ToString("dd/MM/yy"); // Make the request HttpResponseMessage response = await client.PostAsync("http://ojp.nationalrail.co.uk/service/planjourney/plan", new FormUrlEncodedContent( requestParameters ), cancelToken); // Load the result of the request into an HtmlDocument HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(await response.Content.ReadAsStringAsync()); // Extract the journey, changes and date change nodes // The journeys are in table rows that contain cells of class 'dep'. Grab the containing row. // The changes associated with a journey are not in the same row as the journey so look for a cell with class 'origin'. // Changes are stored in the containing table. The extracted changes are stored with the last journey. // Date changes between journeys are contained in table rows with class 'day-heading'. // If date of the first journey are in an H3 element with class 'outward top ctf-h3'. HtmlNodeCollection dormNodes = doc.DocumentNode.SelectNodes( "//td[@class='dep']/..|//td[@class='origin']/..|//h3[@class='outward top ctf-h3']/.|//tr[@class='day-heading']/."); if (dormNodes != null) { // Assume that the journeys found are initially for the same day as the request DateTime responseDate = requestTime.Date; TrainJourneys journeys = new TrainJourneys(); // Fill the journeys list with the results foreach (HtmlNode journeyNode in dormNodes) { // Check if this is a train node if (journeyNode.SelectSingleNode("./td[@class='dep']") != null) { TrainJourney newJourney = new TrainJourney { ArrivalTime = journeyNode.SelectSingleNode("./td[@class='arr']").InnerText.Substring(0, 5), DepartureTime = journeyNode.SelectSingleNode("./td[@class='dep']").InnerText.Substring(0, 5), Duration = ReplaceWhitespace(journeyNode.SelectSingleNode("./td[@class='dur']").InnerText), Status = ReplaceWhitespace(journeyNode.SelectSingleNode("./td[@class='status']").InnerText) }; // Set the full departure timestamp from the responseDate and the departure time newJourney.DepartureDateTime = responseDate + TimeSpan.ParseExact(newJourney.DepartureTime, "h\\:mm", CultureInfo.InvariantCulture); journeys.Journeys.Add(newJourney); } else if (journeyNode.SelectSingleNode("./td[@class='origin']") != null) { // Only proceed with processing changes if a journey has been found if (journeys.Journeys.Count > 0) { // Get the change data ignoring any empty cells and whitespace HtmlNodeCollection cells = journeyNode.SelectNodes("./td"); if (cells != null) { // Somewhere to save the cell data List <string> cellContents = new List <string>(); foreach (HtmlNode cell in cells) { string innerText = ReplaceWhitespace(cell.InnerText); if (innerText.Length > 0) { cellContents.Add(innerText); } } // There should be 4 entries for a journey leg if (cellContents.Count == 4) { JourneyLeg leg = new JourneyLeg { DepartureTime = cellContents[0], From = cellContents[1], ArrivalTime = cellContents[2], To = cellContents[3] }; journeys.Journeys[journeys.Journeys.Count - 1].Legs.Add(leg); } } } } else { // If this is either the report header or a date change within the report. // Extraxct the date string headerDate = (journeyNode.Name == "h3") ? ReplaceWhitespace(journeyNode.InnerText).Replace("+", " ").Substring(17, 10) : ReplaceWhitespace(journeyNode.InnerText); // Date is now of the format DDD nn MMM where nn could be one or two numeric digits try { responseDate = DateTime.ParseExact(headerDate, new[] { "ddd dd MMM", "ddd d MMM" }, CultureInfo.InvariantCulture, DateTimeStyles.None); } catch (FormatException) { } } } // Save the journeys so they can be accessed from outside the class Journeys = journeys.Journeys; JourneysAvailableEvent?.Invoke(this, new JourneysAvailableArgs { JourneysAvailable = true }); } else { JourneysAvailableEvent?.Invoke(this, new JourneysAvailableArgs { JourneysAvailable = false }); } } catch (HttpRequestException) { JourneysAvailableEvent?.Invoke(this, new JourneysAvailableArgs { JourneysAvailable = false, NetworkProblem = true }); } catch (TaskCanceledException) { // This can be caused either from a user request or a network problem if (cancelToken.IsCancellationRequested == true) { JourneysAvailableEvent?.Invoke(this, new JourneysAvailableArgs { JourneysAvailable = false, RequestCancelled = true }); } else { JourneysAvailableEvent?.Invoke(this, new JourneysAvailableArgs { JourneysAvailable = false, NetworkProblem = true }); } } catch (OperationCanceledException) { JourneysAvailableEvent?.Invoke(this, new JourneysAvailableArgs { JourneysAvailable = false, NetworkProblem = true }); } }