Java tutorial
/* * Copyright (C) 2009-2014 Johan Nilsson <http://markupartist.com> * * 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 com.markupartist.sthlmtraveling; import android.app.AlertDialog; import android.app.Dialog; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.res.Configuration; import android.database.Cursor; import android.location.Location; import android.location.LocationManager; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.text.TextUtils; import android.text.format.Time; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager.BadTokenException; import android.widget.Adapter; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.SimpleAdapter.ViewBinder; import android.widget.TextView; import android.widget.Toast; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; import com.markupartist.sthlmtraveling.MyLocationManager.MyLocationFoundListener; import com.markupartist.sthlmtraveling.SectionedAdapter.Section; import com.markupartist.sthlmtraveling.provider.JourneysProvider.Journey.Journeys; import com.markupartist.sthlmtraveling.provider.planner.JourneyQuery; import com.markupartist.sthlmtraveling.provider.planner.Planner; import com.markupartist.sthlmtraveling.provider.planner.Planner.BadResponse; import com.markupartist.sthlmtraveling.provider.planner.Planner.Response; import com.markupartist.sthlmtraveling.provider.planner.Planner.Trip2; import com.markupartist.sthlmtraveling.provider.site.Site; import com.markupartist.sthlmtraveling.ui.view.SmsTicketDialog; import com.markupartist.sthlmtraveling.ui.view.TripView; import com.markupartist.sthlmtraveling.utils.Analytics; import com.markupartist.sthlmtraveling.utils.ViewHelper; import org.json.JSONException; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; /** * Routes activity * * Accepts a routes data URI in the format: * * <pre> * <code>journeyplanner://routes?start_point=STARTPOINT&end_point=ENDPOINT&time=TIME</code> * </pre> * * All parameters needs to be url encoded. Time is optional, but if provided it must be in * RFC 2445 format. */ public class RoutesActivity extends BaseListActivity implements MyLocationFoundListener { /** * The Journey */ static final String EXTRA_JOURNEY_QUERY = "sthlmtraveling.intent.action.JOURNEY_QUERY"; /** * The trip. */ @Deprecated static final String EXTRA_TRIP = "com.markupartist.sthlmtraveling.trip"; /** * The start point for the search. */ @Deprecated static final String EXTRA_START_POINT = "com.markupartist.sthlmtraveling.start_point"; /** * The end point for the search. */ @Deprecated static final String EXTRA_END_POINT = "com.markupartist.sthlmtraveling.end_point"; /** * Departure time in RFC 2445 format. */ static final String EXTRA_TIME = "com.markupartist.sthlmtraveling.time"; /** * Indicates if the time is the departure or arrival time. */ static final String EXTRA_IS_TIME_DEPARTURE = "com.markupartist.sthlmtraveling.is_time_departure"; private final String TAG = "RoutesActivity"; private static final int DIALOG_ILLEGAL_PARAMETERS = 0; private static final int DIALOG_SEARCH_ROUTES_NETWORK_PROBLEM = 1; private static final int DIALOG_GET_EARLIER_ROUTES_NETWORK_PROBLEM = 2; private static final int DIALOG_GET_LATER_ROUTES_NETWORK_PROBLEM = 3; private static final int DIALOG_GET_ROUTES_SESSION_TIMEOUT = 4; private static final int DIALOG_SEARCH_ROUTES_NO_RESULT = 5; private static final int DIALOG_START_POINT_ALTERNATIVES = 6; private static final int DIALOG_END_POINT_ALTERNATIVES = 7; private static final int DIALOG_SEARCH_ROUTES_ERROR = 8; private static final int DIALOG_BUY_SMS_TICKET = 9; private static final int ADAPTER_EARLIER = 0; private static final int ADAPTER_ROUTES = 1; private static final int ADAPTER_LATER = 2; private final int SECTION_CHANGE_TIME = 1; private final int SECTION_ROUTES = 2; protected static final int REQUEST_CODE_CHANGE_TIME = 0; protected static final int REQUEST_CODE_POINT_ON_MAP_START = 1; protected static final int REQUEST_CODE_POINT_ON_MAP_END = 2; /** * Key to identify if the instance of SearchRoutesTask is in progress. */ private static final String STATE_SEARCH_ROUTES_IN_PROGRESS = "com.markupartist.sthlmtraveling.searchroutes.inprogress"; private static final String STATE_GET_EARLIER_ROUTES_IN_PROGRESS = "com.markupartist.sthlmtraveling.getearlierroutes.inprogress"; private static final String STATE_GET_LATER_ROUTES_IN_PROGRESS = "com.markupartist.sthlmtraveling.getlaterroutes.inprogress"; private static final String STATE_ROUTE_ERROR_CODE = "com.markupartist.sthlmtraveling.state.routeerrorcode"; private RoutesAdapter mRouteAdapter; private MultipleListAdapter mMultipleListAdapter; private ArrayList<HashMap<String, String>> mDateAdapterData; private MyLocationManager mMyLocationManager; private SearchRoutesTask mSearchRoutesTask; private GetEarlierRoutesTask mGetEarlierRoutesTask; private GetLaterRoutesTask mGetLaterRoutesTask; private Toast mToast; private Response mPlannerResponse; private JourneyQuery mJourneyQuery; private String mRouteErrorCode; private Bundle mSavedState; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.routes_list); registerScreen("Routes"); LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); mMyLocationManager = new MyLocationManager(locationManager); // Get the journey query. mJourneyQuery = getJourneyQueryFromIntent(getIntent()); if (mJourneyQuery == null || (mJourneyQuery.origin.name == null || mJourneyQuery.destination.name == null)) { showDialog(DIALOG_ILLEGAL_PARAMETERS); // If passed with bad parameters, break the execution. return; } View headerView = getLayoutInflater().inflate(R.layout.empty, null); getListView().addHeaderView(headerView, null, false); getListView().setHeaderDividersEnabled(false); initActionBar(); updateStartAndEndPointViews(mJourneyQuery); updateJourneyHistory(); initRoutes(mJourneyQuery); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getSupportMenuInflater(); inflater.inflate(R.menu.actionbar_routes, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { MenuItem starItem = menu.findItem(R.id.actionbar_item_star); if (isStarredJourney(mJourneyQuery)) { starItem.setIcon(R.drawable.ic_action_star_on); } else { starItem.setIcon(R.drawable.ic_action_star_off); } if (mPlannerResponse != null && mPlannerResponse.canBuySmsTicket()) { MenuItem smsItem = menu.findItem(R.id.actionbar_item_sms); smsItem.setVisible(true); } return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionbar_item_reverse: reverseJourneyQuery(); return true; case R.id.actionbar_item_star: handleStarAction(); supportInvalidateOptionsMenu(); return true; case R.id.actionbar_item_sms: Analytics.getInstance(this).event("Ticket", "Click on ab"); showDialog(DIALOG_BUY_SMS_TICKET); return true; } return super.onOptionsItemSelected(item); } private JourneyQuery getJourneyQueryFromIntent(Intent intent) { JourneyQuery journeyQuery; if (intent.hasExtra(EXTRA_JOURNEY_QUERY)) { journeyQuery = intent.getExtras().getParcelable(EXTRA_JOURNEY_QUERY); } else { journeyQuery = getJourneyQueryFromUri(intent.getData()); } return journeyQuery; } private JourneyQuery getJourneyQueryFromUri(Uri uri) { JourneyQuery jq = new JourneyQuery(); jq.origin = new Planner.Location(); jq.origin.name = uri.getQueryParameter("start_point"); if (!TextUtils.isEmpty(uri.getQueryParameter("start_point_id"))) { jq.origin.id = Integer.parseInt(uri.getQueryParameter("start_point_id")); } if (!TextUtils.isEmpty(uri.getQueryParameter("start_point_lat")) && !TextUtils.isEmpty(uri.getQueryParameter("start_point_lng"))) { jq.origin.latitude = (int) (Double.parseDouble(uri.getQueryParameter("start_point_lat")) * 1E6); jq.origin.longitude = (int) (Double.parseDouble(uri.getQueryParameter("start_point_lng")) * 1E6); } jq.destination = new Planner.Location(); jq.destination.name = uri.getQueryParameter("end_point"); if (!TextUtils.isEmpty(uri.getQueryParameter("end_point_id"))) { jq.destination.id = Integer.parseInt(uri.getQueryParameter("end_point_id")); } if (!TextUtils.isEmpty(uri.getQueryParameter("end_point_lat")) && !TextUtils.isEmpty(uri.getQueryParameter("end_point_lng"))) { jq.destination.latitude = (int) (Double.parseDouble(uri.getQueryParameter("end_point_lat")) * 1E6); jq.destination.longitude = (int) (Double.parseDouble(uri.getQueryParameter("end_point_lng")) * 1E6); } jq.isTimeDeparture = true; if (!TextUtils.isEmpty(uri.getQueryParameter("isTimeDeparture"))) { jq.isTimeDeparture = Boolean.parseBoolean(uri.getQueryParameter("isTimeDeparture")); } jq.time = new Time(); String timeString = uri.getQueryParameter("time"); if (!TextUtils.isEmpty(timeString)) { jq.time.parse(timeString); } else { jq.time.setToNow(); } return jq; } /** * Search for routes. Will first check if we already have data stored. * @param journeyQuery The journey query */ private void initRoutes(JourneyQuery journeyQuery) { final Planner.Response savedResponse = (Planner.Response) getLastNonConfigurationInstance(); if (savedResponse != null) { onSearchRoutesResult(savedResponse); } else { if (journeyQuery.origin.isMyLocation() || journeyQuery.destination.isMyLocation()) { Location location = mMyLocationManager.getLastKnownLocation(); if (mMyLocationManager.shouldAcceptLocation(location)) { onMyLocationFound(location); } else { mMyLocationManager.requestLocationUpdates(this); mToast = Toast.makeText(this, getText(R.string.determining_your_position), Toast.LENGTH_LONG); mToast.show(); } } else { mSearchRoutesTask = new SearchRoutesTask(); //mSearchRoutesTask.setOnSearchRoutesResultListener(this); mSearchRoutesTask.execute(mJourneyQuery); } } } /** * Called before this activity is destroyed, returns the previous details. * This data is used if the screen is rotated. Then we don't need to ask for the data again. * @return the trip */ @Override public Object onRetainNonConfigurationInstance() { return mPlannerResponse; } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); restoreLocalState(savedInstanceState); mSavedState = null; } /** * Restores the local state. * @param savedInstanceState the bundle containing the saved state */ private void restoreLocalState(Bundle savedInstanceState) { restoreJourneyQuery(savedInstanceState); restoreSearchRoutesTask(savedInstanceState); restoreGetEarlierRoutesTask(savedInstanceState); restoreGetLaterRoutesTask(savedInstanceState); if (savedInstanceState.containsKey(STATE_ROUTE_ERROR_CODE)) { mRouteErrorCode = savedInstanceState.getString(STATE_ROUTE_ERROR_CODE); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); saveJourneyQuery(outState); saveSearchRoutesTask(outState); saveGetEarlierRoutesTask(outState); saveGetLaterRoutesTask(outState); if (!TextUtils.isEmpty(mRouteErrorCode)) { outState.putString(STATE_ROUTE_ERROR_CODE, mRouteErrorCode); } mSavedState = outState; } @Override protected void onResume() { super.onResume(); if (mSavedState != null) restoreLocalState(mSavedState); } @Override protected void onDestroy() { super.onDestroy(); onCancelSearchRoutesTask(); onCancelGetEarlierRoutesTask(); onCancelGetLaterRoutesTask(); mMyLocationManager.removeUpdates(); dismissProgress(); } @Override protected void onPause() { super.onPause(); onCancelSearchRoutesTask(); onCancelGetEarlierRoutesTask(); onCancelGetLaterRoutesTask(); mMyLocationManager.removeUpdates(); dismissProgress(); } /** * Restores the search routes task. * @param savedInstanceState the saved state */ private void restoreJourneyQuery(Bundle savedInstanceState) { if (savedInstanceState.containsKey(EXTRA_JOURNEY_QUERY)) { mJourneyQuery = savedInstanceState.getParcelable(EXTRA_JOURNEY_QUERY); } } /** * If there is any running search for routes, save it and process it later * on. * @param outState the out state */ private void saveJourneyQuery(Bundle outState) { outState.putParcelable(EXTRA_JOURNEY_QUERY, mJourneyQuery); } /** * Cancels a search routes task if it is running. */ private void onCancelSearchRoutesTask() { if (mSearchRoutesTask != null && mSearchRoutesTask.getStatus() == AsyncTask.Status.RUNNING) { Log.i(TAG, "Cancels the search routes task."); mSearchRoutesTask.cancel(true); mSearchRoutesTask = null; } } /** * Restores the search routes task. * @param savedInstanceState the saved state */ private void restoreSearchRoutesTask(Bundle savedInstanceState) { if (savedInstanceState.getBoolean(STATE_SEARCH_ROUTES_IN_PROGRESS)) { Log.d(TAG, "restoring SearchRoutesTask"); mSearchRoutesTask = new SearchRoutesTask(); mSearchRoutesTask.execute(mJourneyQuery); } } /** * If there is any running search for routes, save it and process it later * on. * @param outState the out state */ private void saveSearchRoutesTask(Bundle outState) { final SearchRoutesTask task = mSearchRoutesTask; if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) { Log.d(TAG, "saving SearchRoutesTask"); task.cancel(true); mSearchRoutesTask = null; outState.putBoolean(STATE_SEARCH_ROUTES_IN_PROGRESS, true); } } /** * Cancel the get earlier routes task if it is running. */ private void onCancelGetEarlierRoutesTask() { if (mGetEarlierRoutesTask != null && mGetEarlierRoutesTask.getStatus() == AsyncTask.Status.RUNNING) { Log.i(TAG, "Cancels the get earlier routes task."); mGetEarlierRoutesTask.cancel(true); mGetEarlierRoutesTask = null; } } /** * Restores the task for getting earlier routes task. * @param savedInstanceState the saved state */ private void restoreGetEarlierRoutesTask(Bundle savedInstanceState) { if (savedInstanceState.getBoolean(STATE_GET_EARLIER_ROUTES_IN_PROGRESS)) { Log.d(TAG, "restoring GetEarlierRoutesTask"); mGetEarlierRoutesTask = new GetEarlierRoutesTask(); mGetEarlierRoutesTask.execute(mJourneyQuery); } } /** * Save the state for the task for getting earlier routes. * @param outState the out state */ private void saveGetEarlierRoutesTask(Bundle outState) { final GetEarlierRoutesTask task = mGetEarlierRoutesTask; if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) { Log.d(TAG, "saving GetEarlierRoutesTas"); task.cancel(true); mGetEarlierRoutesTask = null; outState.putBoolean(STATE_GET_EARLIER_ROUTES_IN_PROGRESS, true); } } /** * Cancel the get later routes task if it is running. */ private void onCancelGetLaterRoutesTask() { if (mGetLaterRoutesTask != null && mGetLaterRoutesTask.getStatus() == AsyncTask.Status.RUNNING) { Log.i(TAG, "Cancels the get later routes task."); mGetLaterRoutesTask.cancel(true); mGetLaterRoutesTask = null; } } /** * Restores the task for getting earlier routes task. * @param savedInstanceState the saved state */ private void restoreGetLaterRoutesTask(Bundle savedInstanceState) { if (savedInstanceState.getBoolean(STATE_GET_LATER_ROUTES_IN_PROGRESS)) { Log.d(TAG, "restoring GetLaterRoutesTask"); mGetLaterRoutesTask = new GetLaterRoutesTask(); mGetLaterRoutesTask.execute(mJourneyQuery); } } /** * Save the state for the task for getting earlier routes. * @param outState the out state */ private void saveGetLaterRoutesTask(Bundle outState) { final GetLaterRoutesTask task = mGetLaterRoutesTask; if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) { Log.d(TAG, "saving GetLaterRoutesTas"); task.cancel(true); mGetLaterRoutesTask = null; outState.putBoolean(STATE_GET_LATER_ROUTES_IN_PROGRESS, true); } } private String buildDateString() { String timeString = mJourneyQuery.time.format("%R"); String dateString = mJourneyQuery.time.format("%e %B"); if (mJourneyQuery.isTimeDeparture) { return getString(R.string.departing_on, timeString, dateString); } else { return getString(R.string.arriving_by, timeString, dateString); } } private void createSections() { // Date and time adapter. mDateAdapterData = new ArrayList<HashMap<String, String>>(1); HashMap<String, String> item = new HashMap<String, String>(); item.put("title", buildDateString()); mDateAdapterData.add(item); SimpleAdapter dateTimeAdapter = new SimpleAdapter(this, mDateAdapterData, R.layout.date_and_time, new String[] { "title" }, new int[] { R.id.date_time }); // Earlier routes SimpleAdapter earlierAdapter = createEarlierLaterAdapter(R.drawable.arrow_up_float); // Later routes SimpleAdapter laterAdapter = createEarlierLaterAdapter(R.drawable.arrow_down_float); mMultipleListAdapter = new MultipleListAdapter(); mMultipleListAdapter.addAdapter(ADAPTER_EARLIER, earlierAdapter); mMultipleListAdapter.addAdapter(ADAPTER_ROUTES, mRouteAdapter); mMultipleListAdapter.addAdapter(ADAPTER_LATER, laterAdapter); mSectionedAdapter.addSection(SECTION_CHANGE_TIME, getString(R.string.date_and_time_label), dateTimeAdapter); mSectionedAdapter.addSection(SECTION_ROUTES, getString(R.string.route_alternatives_label), mMultipleListAdapter); setListAdapter(mSectionedAdapter); ViewHelper.crossfade(getListView().getEmptyView(), getListView()); } SectionedAdapter mSectionedAdapter = new SectionedAdapter() { protected View getHeaderView(Section section, int index, View convertView, ViewGroup parent) { TextView result = (TextView) convertView; if (convertView == null) { result = (TextView) getLayoutInflater().inflate(R.layout.header, null); } result.setText(section.caption); return (result); } }; /** * Helper to create earlier or later adapter. * @param resource the image resource to show in the list * @return a prepared adapter */ private SimpleAdapter createEarlierLaterAdapter(int resource) { ArrayList<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); Map<String, Object> map = new HashMap<String, Object>(); map.put("image", resource); list.add(map); SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.earlier_later_routes_row, new String[] { "image" }, new int[] { R.id.earlier_later, }); adapter.setViewBinder(new ViewBinder() { @Override public boolean setViewValue(View view, Object data, String textRepresentation) { switch (view.getId()) { case R.id.earlier_later: ImageView imageView = (ImageView) view; imageView.setImageResource((Integer) data); return true; } return false; } }); return adapter; } @Override protected void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); int headerViewsCount = getListView().getHeaderViewsCount(); // Compensate for the added header views. Is this how we do it? position -= headerViewsCount; Section section = mSectionedAdapter.getSection(position); int sectionId = section.id; int innerPosition = mSectionedAdapter.getSectionIndex(position); Adapter adapter = section.adapter; switch (sectionId) { case SECTION_ROUTES: MultipleListAdapter multipleListAdapter = (MultipleListAdapter) adapter; int adapterId = multipleListAdapter.getAdapterId(innerPosition); switch (adapterId) { case ADAPTER_EARLIER: mGetEarlierRoutesTask = new GetEarlierRoutesTask(); mGetEarlierRoutesTask.execute(mJourneyQuery); break; case ADAPTER_LATER: mGetLaterRoutesTask = new GetLaterRoutesTask(); mGetLaterRoutesTask.execute(mJourneyQuery); break; case ADAPTER_ROUTES: Trip2 trip = (Trip2) mSectionedAdapter.getItem(position); findRouteDetails(trip); break; } break; case SECTION_CHANGE_TIME: Intent i = new Intent(this, ChangeRouteTimeActivity.class); i.putExtra(EXTRA_JOURNEY_QUERY, mJourneyQuery); startActivityForResult(i, REQUEST_CODE_CHANGE_TIME); break; } } public void onSearchRoutesResult(Planner.Response response) { mPlannerResponse = response; //mTrip = trip; //updateStartAndEndPointViews(trip.getStartPoint(), trip.getEndPoint()); mJourneyQuery.ident = response.ident; mJourneyQuery.seqnr = response.seqnr; if (mRouteAdapter == null) { mRouteAdapter = new RoutesAdapter(this, response.trips); createSections(); } else { // TODO: Scroll and animate to the new result. mRouteAdapter.refill(response.trips); mSectionedAdapter.notifyDataSetChanged(); } supportInvalidateOptionsMenu(); } /* public void onSiteAlternatives(Trip trip) { // TODO: Handle alternatives... if (trip.getStartPoint().getSiteId() == 0 && trip.getStartPointAlternatives().size() > 1) { Log.d(TAG, "show start alternatives..."); showDialog(DIALOG_START_POINT_ALTERNATIVES); } else if (trip.getEndPoint().getSiteId() == 0 && trip.getEndPointAlternatives().size() > 1) { Log.d(TAG, "show end alternatives..."); showDialog(DIALOG_END_POINT_ALTERNATIVES); } else { mSearchRoutesTask = new SearchRoutesTask(); mSearchRoutesTask.execute(mTrip); } } */ @Override public void onMyLocationFound(Location location) { Log.d(TAG, "onMyLocationFound: " + location); mMyLocationManager.removeUpdates(); if (mToast != null) mToast.cancel(); Planner.Location startPoint = mJourneyQuery.origin; Planner.Location endPoint = mJourneyQuery.destination; Site tmpStop = new Site(); tmpStop.setLocation(location); if (startPoint.isMyLocation()) { if (!mMyLocationManager.shouldAcceptLocation(location)) { Intent i = new Intent(this, PointOnMapActivity.class); i.putExtra(PointOnMapActivity.EXTRA_STOP, tmpStop); i.putExtra(PointOnMapActivity.EXTRA_HELP_TEXT, getString(R.string.tap_your_location_on_map)); startActivityForResult(i, REQUEST_CODE_POINT_ON_MAP_START); } else { startPoint.latitude = (int) (location.getLatitude() * 1E6); startPoint.longitude = (int) (location.getLongitude() * 1E6); } } if (endPoint.isMyLocation()) { if (!mMyLocationManager.shouldAcceptLocation(location)) { Intent i = new Intent(this, PointOnMapActivity.class); i.putExtra(PointOnMapActivity.EXTRA_STOP, tmpStop); i.putExtra(PointOnMapActivity.EXTRA_HELP_TEXT, getString(R.string.tap_your_location_on_map)); startActivityForResult(i, REQUEST_CODE_POINT_ON_MAP_END); } else { endPoint.latitude = (int) (location.getLatitude() * 1E6); endPoint.longitude = (int) (location.getLongitude() * 1E6); } } updateStartAndEndPointViews(mJourneyQuery); // TODO: Maybe need to set start and end points to the trip again here? mSearchRoutesTask = new SearchRoutesTask(); mSearchRoutesTask.execute(mJourneyQuery); } /** * Find route details. Will start {@link RouteDetailActivity}. * @param trip the route to find details for */ private void findRouteDetails(final Trip2 trip) { // TODO: Change to pass the trip later on instead. Intent i = new Intent(RoutesActivity.this, RouteDetailActivity.class); i.putExtra(RouteDetailActivity.EXTRA_JOURNEY_QUERY, mJourneyQuery); i.putExtra(RouteDetailActivity.EXTRA_JOURNEY_TRIP, trip); startActivity(i); } /** * This method is called when the sending activity has finished, with the * result it supplied. * * @param requestCode The original request code as given to startActivity(). * @param resultCode From sending activity as per setResult(). * @param data From sending activity as per setResult(). */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQUEST_CODE_CHANGE_TIME: if (resultCode == RESULT_CANCELED) { Log.d(TAG, "Change time activity cancelled."); } else { mJourneyQuery = data.getParcelableExtra(EXTRA_JOURNEY_QUERY); HashMap<String, String> item = mDateAdapterData.get(0); item.put("title", buildDateString()); mSearchRoutesTask = new SearchRoutesTask(); mSearchRoutesTask.execute(mJourneyQuery); } break; case REQUEST_CODE_POINT_ON_MAP_START: if (resultCode == RESULT_CANCELED) { Log.d(TAG, "action canceled"); finish(); return; } else { Site startPoint = data.getParcelableExtra(PointOnMapActivity.EXTRA_STOP); Log.d(TAG, "Got Stop " + startPoint); mJourneyQuery.origin.name = Planner.Location.TYPE_MY_LOCATION; mJourneyQuery.origin.latitude = (int) (startPoint.getLocation().getLatitude() * 1E6); mJourneyQuery.origin.longitude = (int) (startPoint.getLocation().getLongitude() * 1E6); mSearchRoutesTask = new SearchRoutesTask(); mSearchRoutesTask.execute(mJourneyQuery); // TODO: Is this call really needed? updateStartAndEndPointViews(mJourneyQuery); } break; case REQUEST_CODE_POINT_ON_MAP_END: if (resultCode == RESULT_CANCELED) { Log.d(TAG, "action canceled"); finish(); return; } else { Site endPoint = data.getParcelableExtra(PointOnMapActivity.EXTRA_STOP); Log.d(TAG, "Got Stop " + endPoint); mJourneyQuery.destination.name = Planner.Location.TYPE_MY_LOCATION; mJourneyQuery.destination.latitude = (int) (endPoint.getLocation().getLatitude() * 1E6); mJourneyQuery.destination.longitude = (int) (endPoint.getLocation().getLongitude() * 1E6); mSearchRoutesTask = new SearchRoutesTask(); mSearchRoutesTask.execute(mJourneyQuery); // TODO: Is this call really needed? updateStartAndEndPointViews(mJourneyQuery); } break; } } protected void reverseJourneyQuery() { Planner.Location tmpStartPoint = new Planner.Location(mJourneyQuery.destination); Planner.Location tmpEndPoint = new Planner.Location(mJourneyQuery.origin); mJourneyQuery.origin = tmpStartPoint; mJourneyQuery.destination = tmpEndPoint; /* * Note: To launch a new intent won't work because sl.se would * need to have a new ident generated to be able to search for * route details in the next step. */ mSearchRoutesTask = new SearchRoutesTask(); mSearchRoutesTask.execute(mJourneyQuery); updateStartAndEndPointViews(mJourneyQuery); } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_ILLEGAL_PARAMETERS: return new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert) .setTitle(getText(R.string.attention_label)) .setMessage(getText(R.string.bad_routes_parameters_message)).setCancelable(true) .setNeutralButton(getText(android.R.string.ok), null).create(); case DIALOG_SEARCH_ROUTES_NETWORK_PROBLEM: return DialogHelper.createNetworkProblemDialog(this, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mSearchRoutesTask = new SearchRoutesTask(); mSearchRoutesTask.execute(mJourneyQuery); } }); case DIALOG_GET_EARLIER_ROUTES_NETWORK_PROBLEM: return DialogHelper.createNetworkProblemDialog(this, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mGetEarlierRoutesTask = new GetEarlierRoutesTask(); mGetEarlierRoutesTask.execute(mJourneyQuery); } }); case DIALOG_GET_LATER_ROUTES_NETWORK_PROBLEM: return DialogHelper.createNetworkProblemDialog(this, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mGetLaterRoutesTask = new GetLaterRoutesTask(); mGetLaterRoutesTask.execute(mJourneyQuery); } }); case DIALOG_GET_ROUTES_SESSION_TIMEOUT: return new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert) .setTitle(getText(R.string.attention_label)) .setMessage(getText(R.string.session_timeout_message)) .setNeutralButton(getText(android.R.string.ok), null).create(); case DIALOG_SEARCH_ROUTES_NO_RESULT: return new AlertDialog.Builder(this).setTitle(getText(R.string.no_routes_found_label)) .setMessage(getText(R.string.no_routes_found_message)) .setPositiveButton(getText(R.string.back), new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }).setNegativeButton(getText(R.string.cancel), null).create(); case DIALOG_SEARCH_ROUTES_ERROR: return new AlertDialog.Builder(this).setTitle(R.string.planner_error_title) .setMessage(Planner.plannerErrorCodeToStringRes(mRouteErrorCode)) .setPositiveButton(getText(R.string.back), new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }).setNegativeButton(getText(R.string.cancel), null).create(); case DIALOG_START_POINT_ALTERNATIVES: /* ArrayAdapter<Site> startAlternativesAdapter = new ArrayAdapter<Site>(this, android.R.layout.simple_dropdown_item_1line, mTrip.getStartPointAlternatives()); return new AlertDialog.Builder(this) .setTitle(R.string.did_you_mean) .setAdapter(startAlternativesAdapter, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Site site = mTrip.getStartPointAlternatives().get(which); Stop startPoint = mTrip.getStartPoint(); startPoint.setSiteId(site.getId()); startPoint.setName(site.getName()); onSiteAlternatives(mTrip); } }) .create(); */ break; case DIALOG_END_POINT_ALTERNATIVES: /* ArrayAdapter<Site> endAlternativesAdapter = new ArrayAdapter<Site>(this, android.R.layout.simple_dropdown_item_1line, mTrip.getEndPointAlternatives()); return new AlertDialog.Builder(this) .setTitle(R.string.did_you_mean) .setAdapter(endAlternativesAdapter, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Site site = mTrip.getEndPointAlternatives().get(which); Stop endPoint = mTrip.getEndPoint(); endPoint.setSiteId(site.getId()); endPoint.setName(site.getName()); onSiteAlternatives(mTrip); } }) .create(); */ break; case DIALOG_BUY_SMS_TICKET: return SmsTicketDialog.createDialog(this, mPlannerResponse.getTariffZones()); } return null; } /** * Constructs a search routes data URI. * @param startPoint the start point * @param endPoint the end point * @param time the time, pass null for now * @param isTimeDeparture true if the time is departure time, false if arrival * @return the data uri */ public static Uri createRoutesUri(Site startPoint, Site endPoint, Time time, boolean isTimeDeparture) { Uri routesUri; String timeString = ""; String startLat = ""; String startLng = ""; String endLat = ""; String endLng = ""; if (time != null) { timeString = time.format2445(); } if (startPoint.getLocation() != null) { startLat = String.valueOf(startPoint.getLocation().getLatitude()); startLng = String.valueOf(startPoint.getLocation().getLongitude()); } if (endPoint.getLocation() != null) { endLat = String.valueOf(endPoint.getLocation().getLatitude()); endLng = String.valueOf(endPoint.getLocation().getLongitude()); } routesUri = Uri.parse(String.format( "journeyplanner://routes?" + "start_point=%s" + "&start_point_id=%s" + "&start_point_lat=%s" + "&start_point_lng=%s" + "&end_point=%s" + "&end_point_id=%s" + "&end_point_lat=%s" + "&end_point_lng=%s" + "&time=%s" + "&isTimeDeparture=%s", Uri.encode(startPoint.getName()), startPoint.getId(), startLat, startLng, Uri.encode(endPoint.getName()), endPoint.getId(), endLat, endLng, timeString, isTimeDeparture)); return routesUri; } @Override public boolean onSearchRequested() { Intent i = new Intent(this, StartActivity.class); i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); return true; } private class RoutesAdapter extends BaseAdapter { private Context mContext; private ArrayList<Trip2> mTrips; public RoutesAdapter(Context context, ArrayList<Trip2> trips) { mContext = context; mTrips = trips; } public void refill(ArrayList<Trip2> trips) { mTrips = trips; } @Override public int getCount() { return mTrips.size(); } @Override public Object getItem(int position) { return mTrips.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (!isEmpty()) { Trip2 trip = mTrips.get(position); TripView v = new TripView(mContext); if (position == getCount() - 1) { v.showDivider(false); } v.setTrip(trip); return v; } return new View(mContext); } } /** * Show progress dialog. */ private void showProgress() { setSupportProgressBarIndeterminateVisibility(true); } /** * Dismiss the progress dialog. */ private void dismissProgress() { setSupportProgressBarIndeterminateVisibility(false); } @Override public void onConfigurationChanged(Configuration newConfig) { onRotationChange(newConfig); super.onConfigurationChanged(newConfig); } private void onRotationChange(Configuration newConfig) { /* if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { } else { } */ } /** * Background task for searching for routes. */ private class SearchRoutesTask extends AsyncTask<JourneyQuery, Void, Planner.Response> { private boolean mWasSuccess = true; private String mErrorCode; @Override public void onPreExecute() { showProgress(); } @Override protected Planner.Response doInBackground(JourneyQuery... params) { try { return Planner.getInstance().findJourney(RoutesActivity.this, params[0]); } catch (IOException e) { mWasSuccess = false; // TODO: We should return the Trip here as well. return null; } catch (BadResponse e) { mWasSuccess = false; mErrorCode = e.errorCode; return null; } } @Override protected void onPostExecute(Planner.Response result) { dismissProgress(); if (result != null && !result.trips.isEmpty()) { onSearchRoutesResult(result); } else if (!mWasSuccess) { if (TextUtils.isEmpty(mErrorCode)) { getListView().getEmptyView().setVisibility(View.GONE); try { showDialog(DIALOG_SEARCH_ROUTES_NETWORK_PROBLEM); } catch (BadTokenException e) { Log.w(TAG, "Caught BadTokenException when trying to show network error dialog."); } } else { getListView().getEmptyView().setVisibility(View.GONE); mRouteErrorCode = mErrorCode; try { showDialog(DIALOG_SEARCH_ROUTES_ERROR); } catch (BadTokenException e) { Log.w(TAG, "Caught BadTokenException when trying to show routes error dialog."); } } } /* else if (result.hasAlternatives()) { onSiteAlternatives(result); }*/ else { getListView().getEmptyView().setVisibility(View.GONE); try { showDialog(DIALOG_SEARCH_ROUTES_NO_RESULT); } catch (BadTokenException e) { Log.w(TAG, "Caught BadTokenException when trying to no results dialog."); } } } } /** * Background task for getting earlier routes. */ private class GetEarlierRoutesTask extends AsyncTask<JourneyQuery, Void, Planner.Response> { private boolean mWasSuccess = true; private String mErrorCode; @Override public void onPreExecute() { showProgress(); } @Override protected Planner.Response doInBackground(JourneyQuery... params) { try { return Planner.getInstance().findPreviousJourney(RoutesActivity.this, params[0]); } catch (IOException e) { mWasSuccess = false; } catch (BadResponse e) { mWasSuccess = false; mErrorCode = e.errorCode; } return null; } @Override protected void onPostExecute(Planner.Response result) { dismissProgress(); if (result != null && !result.trips.isEmpty()) { //mTrip.setRoutes(result); onSearchRoutesResult(result); } else if (!mWasSuccess) { if (TextUtils.isEmpty(mErrorCode)) { try { showDialog(DIALOG_GET_EARLIER_ROUTES_NETWORK_PROBLEM); } catch (BadTokenException e) { Log.w(TAG, "Caught BadTokenException when trying to show network error dialog."); } } else { mRouteErrorCode = mErrorCode; try { showDialog(DIALOG_SEARCH_ROUTES_ERROR); } catch (BadTokenException e) { Log.w(TAG, "Caught BadTokenException when trying to show routes error dialog."); } } } else { try { showDialog(DIALOG_GET_ROUTES_SESSION_TIMEOUT); } catch (BadTokenException e) { Log.w(TAG, "Caught BadTokenException when trying to show session timeout dialog."); } } } } /** * Background task for getting later routes. */ private class GetLaterRoutesTask extends AsyncTask<JourneyQuery, Void, Planner.Response> { private boolean mWasSuccess = true; private String mErrorCode; @Override public void onPreExecute() { showProgress(); } @Override protected Planner.Response doInBackground(JourneyQuery... params) { try { return Planner.getInstance().findNextJourney(RoutesActivity.this, params[0]); } catch (IOException e) { mWasSuccess = false; } catch (BadResponse e) { mWasSuccess = false; mErrorCode = e.errorCode; } return null; } @Override protected void onPostExecute(Planner.Response result) { dismissProgress(); if (result != null && !result.trips.isEmpty()) { //mTrip.setRoutes(result); onSearchRoutesResult(result); } else if (!mWasSuccess) { if (TextUtils.isEmpty(mErrorCode)) { try { showDialog(DIALOG_GET_EARLIER_ROUTES_NETWORK_PROBLEM); } catch (BadTokenException e) { Log.w(TAG, "Caught BadTokenException when trying to show network error dialog."); } } else { mRouteErrorCode = mErrorCode; try { showDialog(DIALOG_SEARCH_ROUTES_ERROR); } catch (BadTokenException e) { Log.w(TAG, "Caught BadTokenException when trying to show routes error dialog."); } } } else { try { showDialog(DIALOG_GET_ROUTES_SESSION_TIMEOUT); } catch (BadTokenException e) { Log.w(TAG, "Caught BadTokenException when trying to show session timeout dialog."); } } } } private boolean isStarredJourney(JourneyQuery journeyQuery) { String json; try { json = mJourneyQuery.toJson(false).toString(); } catch (JSONException e) { Log.e(TAG, "Failed to convert journey to a json document."); return false; } String[] projection = new String[] { Journeys.JOURNEY_DATA, }; Uri uri = Journeys.CONTENT_URI; String selection = Journeys.STARRED + " = ? AND " + Journeys.JOURNEY_DATA + " = ?"; String[] selectionArgs = new String[] { "1", json }; Cursor cursor = managedQuery(uri, projection, selection, selectionArgs, null); startManagingCursor(cursor); boolean isStarred = cursor.getCount() > 0; stopManagingCursor(cursor); return isStarred; } private void handleStarAction() { String json; try { json = mJourneyQuery.toJson(false).toString(); } catch (JSONException e) { Log.e(TAG, "Failed to convert journey to a json document."); return; } ContentValues values = new ContentValues(); values.put(Journeys.JOURNEY_DATA, json); Uri uri = Journeys.CONTENT_URI; String where = Journeys.JOURNEY_DATA + "= ?"; String[] selectionArgs = new String[] { json }; // TODO: Replace button with a checkbox and check with that instead? if (isStarredJourney(mJourneyQuery)) { values.put(Journeys.STARRED, "0"); getContentResolver().update(uri, values, where, selectionArgs); } else { values.put(Journeys.STARRED, "1"); int affectedRows = getContentResolver().update(uri, values, where, selectionArgs); if (affectedRows <= 0) { getContentResolver().insert(Journeys.CONTENT_URI, values); } } } /** * Updates the journey history. */ private void updateJourneyHistory() { // TODO: Move to async task. String json; try { json = mJourneyQuery.toJson(false).toString(); } catch (JSONException e) { Log.e(TAG, "Failed to convert journey to a json document."); return; } String[] projection = new String[] { Journeys._ID, // 0 Journeys.JOURNEY_DATA, // 1 Journeys.STARRED, // 2 }; String selection = Journeys.JOURNEY_DATA + " = ?"; Cursor cursor = managedQuery(Journeys.CONTENT_URI, projection, selection, new String[] { json }, null); startManagingCursor(cursor); ContentValues values = new ContentValues(); Uri journeyUri; if (cursor.getCount() > 0) { cursor.moveToFirst(); journeyUri = ContentUris.withAppendedId(Journeys.CONTENT_URI, cursor.getInt(0)); getContentResolver().update(journeyUri, values, null, null); } else { // Not sure if this is the best way to do it, but the lack limit and // offset in on a content provider leaves us to fetch all and iterate. values.put(Journeys.JOURNEY_DATA, json); journeyUri = getContentResolver().insert(Journeys.CONTENT_URI, values); Cursor notStarredCursor = managedQuery(Journeys.CONTENT_URI, projection, Journeys.STARRED + " = ? OR " + Journeys.STARRED + " IS NULL", new String[] { "0" }, Journeys.DEFAULT_SORT_ORDER); startManagingCursor(notStarredCursor); // +1 because the position is zero-based. if (notStarredCursor.moveToPosition(Journeys.DEFAULT_HISTORY_SIZE + 1)) { do { Uri deleteUri = ContentUris.withAppendedId(Journeys.CONTENT_URI, notStarredCursor.getInt(0)); getContentResolver().delete(deleteUri, null, null); } while (notStarredCursor.moveToNext()); } stopManagingCursor(notStarredCursor); } stopManagingCursor(cursor); // TODO: Store created id and work on that while toggling if starred or not. } }