/* * Copyright 2011 Marcy Gordon * * 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 * * 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 edu.usf.cutr.opentripplanner.android.tasks; import org.opentripplanner.api.model.error.PlannerError; import org.opentripplanner.api.ws.Message; import org.opentripplanner.api.ws.Request; import org.opentripplanner.api.model.Itinerary; import org.opentripplanner.api.ws.Response; import org.opentripplanner.routing.core.TraverseMode; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.AsyncTask; import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; import android.widget.Toast; import java.io.IOException; import java.lang.ref.WeakReference; import java.net.HttpURLConnection; import java.net.URL; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import edu.usf.cutr.opentripplanner.android.OTPApp; import edu.usf.cutr.opentripplanner.android.R; import edu.usf.cutr.opentripplanner.android.listeners.TripRequestCompleteListener; import edu.usf.cutr.opentripplanner.android.model.Server; import edu.usf.cutr.opentripplanner.android.util.JacksonConfig; /** * AsyncTask that invokes a trip planning request to the OTP Server * * @author Khoa Tran * @author Sean Barbeau (conversion to Jackson) */ public class TripRequest extends AsyncTask<Request, Integer, Long> { private Response response; private ProgressDialog progressDialog; private WeakReference<Activity> activity; private Context context; private Resources resources; private String currentRequestString = ""; private Server selectedServer; private TripRequestCompleteListener callback; public TripRequest(WeakReference<Activity> activity, Context context, Resources resources, Server selectedServer, TripRequestCompleteListener callback) { this.activity = activity; this.context = context; this.selectedServer = selectedServer; this.callback = callback; this.resources = resources; if (activity != null) { Activity activityRetrieved = activity.get(); progressDialog = new ProgressDialog(activityRetrieved); } } protected void onPreExecute() { if (activity.get() != null) { progressDialog.setIndeterminate(true); progressDialog.setCancelable(true); Activity activityRetrieved = activity.get(); if (activityRetrieved != null) { progressDialog = ProgressDialog.show(activityRetrieved, "", resources.getText(R.string.task_progress_tripplanner_progress), true); } } } protected Long doInBackground(Request... reqs) { long totalSize = 0; if (selectedServer == null) { Toast.makeText(context, resources.getString(R.string.toast_no_server_selected_error), Toast.LENGTH_SHORT).show(); return null; } else{ String prefix = PreferenceManager.getDefaultSharedPreferences(context) .getString(OTPApp.PREFERENCE_KEY_FOLDER_STRUCTURE_PREFIX , OTPApp.FOLDER_STRUCTURE_PREFIX_NEW); String baseURL = selectedServer.getBaseURL(); for (Request req : reqs) { response = requestPlan(req, prefix, baseURL); } } return totalSize; } protected void onCancelled(Long result) { try { if (progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } } catch (Exception e) { Log.e(OTPApp.TAG, "Error in TripRequest Cancelled dismissing dialog: " + e); } Activity activityRetrieved = activity.get(); if (activityRetrieved != null) { AlertDialog.Builder geocoderAlert = new AlertDialog.Builder(activityRetrieved); geocoderAlert.setTitle(R.string.tripplanner_results_title) .setMessage(R.string.tripplanner_error_request_timeout) .setCancelable(false) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } }); AlertDialog alert = geocoderAlert.create(); alert.show(); } Log.e(OTPApp.TAG, "No route to display!"); } protected void onPostExecute(Long result) { if (activity.get() != null) { try { if (progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } } catch (Exception e) { Log.e(OTPApp.TAG, "Error in TripRequest PostExecute dismissing dialog: " + e); } } if (response != null && response.getPlan() != null && response.getPlan().getItinerary().get(0) != null) { List<Itinerary> itineraries = response.getPlan().getItinerary(); callback.onTripRequestComplete(itineraries, currentRequestString); } else { Activity activityRetrieved = activity.get(); if (activityRetrieved != null) { AlertDialog.Builder feedback = new AlertDialog.Builder(activityRetrieved); feedback.setTitle(resources .getString(R.string.tripplanner_error_dialog_title)); feedback.setNeutralButton(resources.getString(android.R.string.ok), null); String msg = resources .getString(R.string.tripplanner_error_not_defined); PlannerError error = response.getError(); if (error != null) { int errorCode = error.getId(); if (response != null && response.getError() != null && errorCode != Message.PLAN_OK .getId()) { msg = getErrorMessage(response.getError().getId()); if (msg == null) { msg = response.getError().getMsg(); } } } feedback.setMessage(msg); feedback.create().show(); } Log.e(OTPApp.TAG, "No route to display!"); } } protected String getErrorMessage(int errorCode) { if (errorCode == Message.SYSTEM_ERROR.getId()) { return (resources.getString(R.string.tripplanner_error_system)); } else if (errorCode == Message.OUTSIDE_BOUNDS.getId()) { return (resources.getString(R.string.tripplanner_error_outside_bounds)); } else if (errorCode == Message.PATH_NOT_FOUND.getId()) { return (resources.getString(R.string.tripplanner_error_path_not_found)); } else if (errorCode == Message.NO_TRANSIT_TIMES.getId()) { return (resources.getString(R.string.tripplanner_error_no_transit_times)); } else if (errorCode == Message.REQUEST_TIMEOUT.getId()) { return (resources.getString(R.string.tripplanner_error_request_timeout)); } else if (errorCode == Message.BOGUS_PARAMETER.getId()) { return (resources.getString(R.string.tripplanner_error_bogus_parameter)); } else if (errorCode == Message.GEOCODE_FROM_NOT_FOUND.getId()) { return (resources .getString(R.string.tripplanner_error_geocode_from_not_found)); } else if (errorCode == Message.GEOCODE_TO_NOT_FOUND.getId()) { return (resources .getString(R.string.tripplanner_error_geocode_to_not_found)); } else if (errorCode == Message.GEOCODE_FROM_TO_NOT_FOUND.getId()) { return (resources .getString(R.string.tripplanner_error_geocode_from_to_not_found)); } else if (errorCode == Message.TOO_CLOSE.getId()) { return (resources.getString(R.string.tripplanner_error_too_close)); } else if (errorCode == Message.LOCATION_NOT_ACCESSIBLE.getId()) { return (resources .getString(R.string.tripplanner_error_location_not_accessible)); } else if (errorCode == Message.GEOCODE_FROM_AMBIGUOUS.getId()) { return (resources .getString(R.string.tripplanner_error_geocode_from_ambiguous)); } else if (errorCode == Message.GEOCODE_TO_AMBIGUOUS.getId()) { return (resources .getString(R.string.tripplanner_error_geocode_to_ambiguous)); } else if (errorCode == Message.GEOCODE_FROM_TO_AMBIGUOUS.getId()) { return (resources .getString(R.string.tripplanner_error_geocode_from_to_ambiguous)); } else if (errorCode == Message.UNDERSPECIFIED_TRIANGLE.getId() || errorCode == Message.TRIANGLE_NOT_AFFINE.getId() || errorCode == Message.TRIANGLE_OPTIMIZE_TYPE_NOT_SET.getId() || errorCode == Message.TRIANGLE_VALUES_NOT_SET.getId()) { return (resources.getString(R.string.tripplanner_error_triangle)); } else { return null; } } protected Response requestPlan(Request requestParams, String prefix, String baseURL) { HashMap<String, String> tmp = requestParams.getParameters(); Collection c = tmp.entrySet(); Iterator itr = c.iterator(); String params = ""; boolean first = true; while (itr.hasNext()) { if (first) { params += "?" + itr.next(); first = false; } else { params += "&" + itr.next(); } } if (requestParams.getBikeRental()) { String updatedString; if (prefix.equals(OTPApp.FOLDER_STRUCTURE_PREFIX_NEW)){ updatedString = params.replace(TraverseMode.BICYCLE.toString(), TraverseMode.BICYCLE.toString() + OTPApp.OTP_RENTAL_QUALIFIER); } else{ updatedString = params.replace(TraverseMode.BICYCLE.toString(), TraverseMode.BICYCLE.toString() + ", " + TraverseMode.WALK.toString()); } params = updatedString; } if (requestParams.getModes().getTrainish()) { // TraverseModeSet.toString() enumerates activated modes, which might not be supported by server // so we filter them out. Should be solved differently, e.g. by decoupling and introducing // an interface with version dependent implementations SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); if (prefs.getInt(OTPApp.PREFERENCE_KEY_API_VERSION, OTPApp.API_VERSION_V3) >= OTPApp.API_VERSION_V3) { String updatedString; updatedString = params.replace(TraverseMode.TRAINISH.toString(), ""); updatedString = updatedString.replace(TraverseMode.BUSISH.toString(), ""); params = updatedString; } } String u = baseURL + prefix + OTPApp.PLAN_LOCATION + params; Log.d(OTPApp.TAG, "URL: " + u); currentRequestString = u; HttpURLConnection urlConnection = null; URL url; Response plan = null; try { url = new URL(u); disableConnectionReuseIfNecessary(); // For bugs in HttpURLConnection pre-Froyo urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestProperty("Accept", "application/json"); urlConnection.setConnectTimeout(OTPApp.HTTP_CONNECTION_TIMEOUT); urlConnection.setReadTimeout(OTPApp.HTTP_SOCKET_TIMEOUT); plan = JacksonConfig.getObjectReaderInstance() .readValue(urlConnection.getInputStream()); } catch (java.net.SocketTimeoutException e) { Log.e(OTPApp.TAG, "Timeout fetching JSON or XML: " + e); e.printStackTrace(); cancel(true); } catch (IOException e) { Log.e(OTPApp.TAG, "Error fetching JSON or XML: " + e); e.printStackTrace(); cancel(true); // Reset timestamps to show there was an error // requestStartTime = 0; // requestEndTime = 0; } finally { if (urlConnection != null) { urlConnection.disconnect(); } } return plan; } /** * Disable HTTP connection reuse which was buggy pre-froyo */ private void disableConnectionReuseIfNecessary() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { System.setProperty("http.keepAlive", "false"); } } }