/*
BostonBusMap
Copyright (C) 2009 George Schneeloch
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package boston.Bus.Map.main;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import boston.Bus.Map.R;
import com.commonsware.android.mapsv2.popups.AbstractMapActivity;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.LatLng;
import com.schneeloch.bostonbusmap_library.data.BusLocation;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.schneeloch.bostonbusmap_library.data.GroupKey;
import com.schneeloch.bostonbusmap_library.data.ITransitDrawables;
import com.schneeloch.bostonbusmap_library.data.Location;
import com.schneeloch.bostonbusmap_library.data.Locations;
import com.schneeloch.bostonbusmap_library.data.IntersectionLocation;
import com.schneeloch.bostonbusmap_library.data.RouteTitles;
import com.schneeloch.bostonbusmap_library.data.Selection;
import com.schneeloch.bostonbusmap_library.data.StopLocation;
import boston.Bus.Map.data.TransitDrawables;
import boston.Bus.Map.data.UpdateArguments;
import boston.Bus.Map.provider.DatabaseAgent;
import com.schneeloch.bostonbusmap_library.data.VehicleLocations;
import com.schneeloch.bostonbusmap_library.database.Schema;
import com.schneeloch.bostonbusmap_library.provider.IDatabaseAgent;
import boston.Bus.Map.provider.TransitContentProvider;
import com.schneeloch.bostonbusmap_library.transit.ITransitSystem;
import com.schneeloch.bostonbusmap_library.transit.TransitSystem;
import boston.Bus.Map.tutorials.IntroTutorial;
import boston.Bus.Map.tutorials.Tutorial;
import boston.Bus.Map.ui.MapManager;
import boston.Bus.Map.ui.ModeAdapter;
import boston.Bus.Map.util.SearchHelper;
import com.schneeloch.bostonbusmap_library.util.Constants;
import com.google.common.collect.Lists;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.app.SearchManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.SearchRecentSuggestions;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.Toast;
import android.widget.AdapterView.OnItemSelectedListener;
import com.google.android.gms.maps.SupportMapFragment;
/**
* The main activity
*
*/
public class Main extends AbstractMapActivity {
private static final String selectedRouteIndexKey = "selectedRouteIndex";
private static final String selectedBusPredictionsKey = "selectedBusPredictions";
private static final String centerLatKey = "centerLat";
private static final String centerLonKey = "centerLon";
private static final String zoomLevelKey = "zoomLevel";
private static final String selectedIntersectionKey = "selectedIntersection";
//private static final String gpsAlwaysOn = "gpsAlwaysOn";
private static final String markUpdatedStops = "markUpdatedStops";
private static final String introScreenKey = "introScreen";
public static final String tutorialStepKey = "tutorialStep";
private EditText searchView;
/**
* Used to make updateBuses run every 10 seconds or so
*/
private UpdateHandler handler;
/**
* This is used to indicate to the mode spinner to ignore the first time we set it, so we don't update every time the screen changes
*/
private boolean firstRunMode;
/**
* Is location overlay supposed to be enabled? Used mostly for onResume()
*/
private boolean locationEnabled;
private Spinner toggleButton;
private Button chooseAPlaceButton;
private Button chooseAFavoriteButton;
/**
* The list of routes that's selectable in the routes dropdown list
*/
private RouteTitles dropdownRouteKeysToTitles;
private AlertDialog routeChooserDialog;
private ListView drawerList;
private DrawerLayout drawerLayout;
private UpdateArguments arguments;
public static final int UPDATE_INTERVAL_INVALID = 9999;
public static final int UPDATE_INTERVAL_SHORT = 15;
public static final int UPDATE_INTERVAL_MEDIUM = 50;
public static final int UPDATE_INTERVAL_LONG = 100;
public static final int UPDATE_INTERVAL_NONE = 0;
public static final String ROUTE_KEY = "route";
public static final String STOP_KEY = "stop";
public static final String MODE_KEY = "mode";
private final static int DRAWER_INTERSECTIONS_MENU_ITEM_POS = 0;
private final static int DRAWER_CHOOSE_STOP_POS = 1;
private final static int DRAWER_CENTER_ON_CITY_POS = 2;
private final static int DRAWER_ROUTES_POS = 3;
private final static int DRAWER_SETTINGS_POS = 4;
private static final String[] drawerOptions = new String[5];
static {
drawerOptions[DRAWER_INTERSECTIONS_MENU_ITEM_POS] = "Places";
drawerOptions[DRAWER_CHOOSE_STOP_POS] = "Favorite Stops";
drawerOptions[DRAWER_CENTER_ON_CITY_POS] = "Center on Boston";
drawerOptions[DRAWER_ROUTES_POS] = "Routes";
drawerOptions[DRAWER_SETTINGS_POS] = "Settings";
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
firstRunMode = true;
TransitSystem.setDefaultTimeFormat(this);
//get widgets
SupportMapFragment fragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
fragment.setRetainInstance(true);
fragment.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap map) {
toggleButton = (Spinner) findViewById(R.id.predictionsOrLocations);
chooseAPlaceButton = (Button) findViewById(R.id.chooseAPlaceButton);
chooseAFavoriteButton = (Button) findViewById(R.id.chooseFavoriteButton);
searchView = (EditText) findViewById(R.id.searchTextView);
final ProgressBar progress = (ProgressBar) findViewById(R.id.progress);
ImageButton searchButton = (ImageButton) findViewById(R.id.searchButton);
ImageButton refreshButton = (ImageButton) findViewById(R.id.refreshButton);
Button skipTutorialButton = (Button) findViewById(R.id.mapViewTutorialSkipButton);
Button routesButton = (Button) findViewById(R.id.routes_button);
routesButton.setVisibility(View.GONE);
Button vehiclesButton = (Button) findViewById(R.id.vehicles_button);
vehiclesButton.setVisibility(View.GONE);
// TODO: find a better place for this
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
drawerList = (ListView) findViewById(R.id.left_drawer);
drawerList.setAdapter(new ArrayAdapter<String>(Main.this,
R.layout.drawer_list_item,
drawerOptions));
drawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectMenuItem(position);
drawerLayout.closeDrawer(drawerList);
}
});
ImageButton drawerButton = (ImageButton) findViewById(R.id.drawerButton);
drawerButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (drawerLayout.isDrawerOpen(GravityCompat.END)) {
drawerLayout.closeDrawer(GravityCompat.END);
} else {
drawerLayout.openDrawer(GravityCompat.END);
}
}
});
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(Main.this);
final IDatabaseAgent databaseAgent = new DatabaseAgent(getContentResolver());
final ProgressDialog progressDialog = new ProgressDialog(Main.this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setCancelable(true);
progress.setVisibility(View.INVISIBLE);
searchView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onSearchRequested();
}
});
searchButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onSearchRequested();
}
});
chooseAPlaceButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showIntersectionsDialog();
}
});
chooseAFavoriteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showChooseStopDialog();
}
});
refreshButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
boolean b = handler.instantRefresh();
if (b == false) {
Toast.makeText(Main.this, "Please wait 10 seconds before clicking Refresh again", Toast.LENGTH_LONG).show();
}
}
});
final ITransitSystem transitSystem = new TransitSystem();
{
ITransitDrawables busDrawables = new TransitDrawables(
R.drawable.busstop_intersect, R.drawable.busstop_intersect_selected,
R.drawable.busstop, R.drawable.busstop_selected,
R.drawable.busstop_updated, R.drawable.busstop_selected
);
ITransitDrawables subwayDrawables = new TransitDrawables(
R.drawable.busstop_intersect, R.drawable.busstop_intersect_selected,
R.drawable.busstop, R.drawable.busstop_selected,
R.drawable.busstop_updated, R.drawable.busstop_selected
);
ITransitDrawables commuterRailDrawables = new TransitDrawables(
R.drawable.busstop_intersect, R.drawable.busstop_intersect_selected,
R.drawable.busstop, R.drawable.busstop_selected,
R.drawable.busstop_updated, R.drawable.busstop_selected
);
ITransitDrawables hubwayDrawables = new TransitDrawables(
R.drawable.busstop_intersect, R.drawable.busstop_intersect_selected,
R.drawable.busstop_bike, R.drawable.busstop_bike_selected,
R.drawable.busstop_bike_updated, R.drawable.busstop_bike_selected
);
transitSystem.setDefaultTransitSource(busDrawables, subwayDrawables, commuterRailDrawables, hubwayDrawables,
databaseAgent);
}
SpinnerAdapter modeSpinnerAdapter = new ModeAdapter(Main.this, Arrays.asList(Selection.modesSupported));
toggleButton.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
if (firstRunMode) {
firstRunMode = false;
} else if (arguments != null && handler != null) {
if (position >= 0 && position < Selection.modesSupported.length) {
setMode(Selection.modesSupported[position], false, true);
}
}
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
//do nothing
}
});
toggleButton.setAdapter(modeSpinnerAdapter);
dropdownRouteKeysToTitles = transitSystem.getRouteKeysToTitles();
{
String[] routeTitles = dropdownRouteKeysToTitles.titleArray();
AlertDialog.Builder builder = new AlertDialog.Builder(Main.this);
builder.setTitle(getString(R.string.chooseRouteInBuilder));
builder.setItems(routeTitles, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
String route = dropdownRouteKeysToTitles.getTagUsingIndex(item);
setNewRoute(route, true, true);
}
});
routeChooserDialog = builder.create();
}
//get the busLocations variable if it already exists. We need to do that step here since handler
long lastUpdateTime = 0;
int previousUpdateConstantlyInterval = UPDATE_INTERVAL_NONE;
RefreshAsyncTask majorHandler = null;
Selection selection;
Locations busLocations = null;
{
locationEnabled = prefs.getBoolean(getString(R.string.alwaysShowLocationCheckbox), true);
int selectedRouteIndex = prefs.getInt(selectedRouteIndexKey, 0);
int modeInt = prefs.getInt(selectedBusPredictionsKey, Selection.Mode.BUS_PREDICTIONS_ALL.modeInt);
selection = new Selection(Selection.Mode.VEHICLE_LOCATIONS_ALL, null);
for (Selection.Mode mode : Selection.Mode.values()) {
if (mode.modeInt == modeInt) {
String route = dropdownRouteKeysToTitles.getTagUsingIndex(selectedRouteIndex);
selection = new Selection(mode, route);
break;
}
}
}
if (locationEnabled) {
if (ActivityCompat.checkSelfPermission(Main.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(Main.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(Main.this, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
}, 0);
} else {
map.setMyLocationEnabled(true);
}
}
if (busLocations == null) {
busLocations = new Locations(databaseAgent, transitSystem, selection);
}
Button reportButton = (Button) findViewById(R.id.report_problem_button);
reportButton.setVisibility(View.GONE);
Button moreInfoButton = (Button) findViewById(R.id.moreinfo_button);
moreInfoButton.setVisibility(View.GONE);
Button alertsButton = (Button) findViewById(R.id.alerts_button);
alertsButton.setVisibility(View.GONE);
Button editButton = (Button)findViewById(R.id.edit_button);
editButton.setVisibility(View.GONE);
Button deleteButton = (Button)findViewById(R.id.delete_button);
deleteButton.setVisibility(View.GONE);
MapManager manager = new MapManager(Main.this, map, transitSystem,
busLocations, reportButton, moreInfoButton, alertsButton, routesButton,
vehiclesButton, editButton, deleteButton);
arguments = new UpdateArguments(progress, progressDialog,
map, databaseAgent, manager,
majorHandler, busLocations, transitSystem, Main.this);
handler = new UpdateHandler(arguments);
manager.setHandler(handler);
PopupAdapter popupAdapter = new PopupAdapter(Main.this, manager, dropdownRouteKeysToTitles);
map.setInfoWindowAdapter(popupAdapter);
populateHandlerSettings();
{
int centerLat = prefs.getInt(centerLatKey, Integer.MAX_VALUE);
int centerLon = prefs.getInt(centerLonKey, Integer.MAX_VALUE);
int zoomLevel = prefs.getInt(zoomLevelKey, Integer.MAX_VALUE);
setMode(selection.getMode(), true, false);
updateSearchText(selection);
if (centerLat != Integer.MAX_VALUE && centerLon != Integer.MAX_VALUE && zoomLevel != Integer.MAX_VALUE) {
LatLng latLng = new LatLng(centerLat * Constants.InvE6, centerLon * Constants.InvE6);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoomLevel));
} else {
LatLng latLng = new LatLng(TransitSystem.getCenterLat(), TransitSystem.getCenterLon());
//move maps widget to center of transit network
map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14));
}
//make the textView blank
}
handler.setLastUpdateTime(lastUpdateTime);
//show all icons if there are any
handler.triggerUpdate();
if (handler.getUpdateConstantlyInterval() != UPDATE_INTERVAL_NONE &&
previousUpdateConstantlyInterval == UPDATE_INTERVAL_NONE) {
handler.instantRefresh();
}
//enable plus/minus zoom buttons in map
map.getUiSettings().setZoomControlsEnabled(true);
// if app is started with selection information, use it
Intent intent = getIntent();
if (intent != null) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
String route = bundle.getString(ROUTE_KEY);
String stop = bundle.getString(STOP_KEY);
String modeString = bundle.getString(MODE_KEY);
Selection.Mode modeInt = Selection.Mode.BUS_PREDICTIONS_ALL;
if (modeString != null) {
for (Selection.Mode mode : Selection.Mode.values()) {
if (modeString.equals(mode.modeString)) {
modeInt = mode;
break;
}
}
}
if (route != null && stop != null) {
setNewStop(route, stop);
setMode(modeInt, true, true);
} else if (route != null) {
setNewRoute(route, false, false);
setMode(modeInt, true, true);
}
}
// from http://stackoverflow.com/questions/13372326/how-to-get-getintent-to-return-null-after-activity-called-with-an-intent-set
intent.setData(null);
}
}
});
}
@SuppressLint("MissingPermission")
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
boolean requestedCoarse = false;
boolean requestedFine = false;
if (arguments == null || arguments.getMapView() == null) {
return;
}
GoogleMap map = arguments.getMapView();
for (int i = 0; i < permissions.length; i++) {
String permission = permissions[i];
int result = grantResults[i];
if (permission.equals(Manifest.permission.ACCESS_COARSE_LOCATION) && result == PackageManager.PERMISSION_GRANTED) {
requestedCoarse = true;
}
if (permission.equals(Manifest.permission.ACCESS_FINE_LOCATION) && result == PackageManager.PERMISSION_GRANTED) {
requestedFine = true;
}
}
if (requestedCoarse && requestedFine) {
try {
map.setMyLocationEnabled(true);
} catch (SecurityException e) {
// pass, should not happen
}
}
}
/**
* Updates search text depending on current mode
*/
private void updateSearchText(Selection selection) {
if (searchView != null)
{
if (arguments != null && arguments.getOverlayGroup().isAlwaysFocusRoute()) {
String route = selection.getRoute();
String routeTitle = dropdownRouteKeysToTitles.getTitle(route);
searchView.setText("Route " + routeTitle);
searchView.setHint("Search routes");
}
else if (selection.getMode() == Selection.Mode.VEHICLE_LOCATIONS_ALL ||
selection.getMode() == Selection.Mode.BUS_PREDICTIONS_ALL ||
selection.getMode() == Selection.Mode.BUS_PREDICTIONS_STAR) {
searchView.setText("");
searchView.setHint("Search routes");
}
else {
String route = selection.getRoute();
String routeTitle = dropdownRouteKeysToTitles.getTitle(route);
searchView.setText("Route " + routeTitle);
searchView.setHint("Search routes");
}
}
else
{
Log.i("BostonBusMap", "ERROR: search view is null");
}
}
/**
* This should be called only by SearchHelper
*
* @param route
* @param saveNewQuery save a search term in the search history as if user typed it in
*/
public void setNewRoute(String route, boolean saveNewQuery, boolean updateMode)
{
if (arguments != null && handler != null)
{
Locations locations = arguments.getBusLocations();
Selection selection = locations.getSelection();
locations.setSelection(selection.withDifferentRoute(route));
String routeTitle = dropdownRouteKeysToTitles.getTitle(route);
if (routeTitle == null)
{
routeTitle = route;
}
updateSearchText(locations.getSelection());
if (saveNewQuery)
{
final SearchRecentSuggestions suggestions = new SearchRecentSuggestions(Main.this, TransitContentProvider.AUTHORITY,
TransitContentProvider.MODE);
suggestions.saveRecentQuery("route " + routeTitle, null);
}
if (updateMode) {
if (selection.getMode() == Selection.Mode.BUS_PREDICTIONS_STAR ||
selection.getMode() == Selection.Mode.BUS_PREDICTIONS_ALL) {
setMode(Selection.Mode.BUS_PREDICTIONS_ONE, true, false);
}
else if (selection.getMode() == Selection.Mode.VEHICLE_LOCATIONS_ALL) {
setMode(Selection.Mode.VEHICLE_LOCATIONS_ONE, true, false);
}
}
handler.immediateRefresh();
handler.triggerUpdate();
}
}
@Override
protected void onPause() {
if (arguments != null)
{
final GoogleMap mapView = arguments.getMapView();
LatLng point = mapView.getCameraPosition().target;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = prefs.edit();
Selection selection = arguments.getBusLocations().getSelection();
editor.putInt(selectedBusPredictionsKey, selection.getMode().modeInt);
editor.putInt(selectedRouteIndexKey, arguments.getBusLocations().getRouteAsIndex(selection.getRoute()));
editor.putInt(centerLatKey, (int)(point.latitude * Constants.E6));
editor.putInt(centerLonKey, (int)(point.longitude * Constants.E6));
editor.putInt(zoomLevelKey, (int)mapView.getCameraPosition().zoom);
editor.apply();
}
if (handler != null)
{
handler.removeAllMessages();
handler.nullifyProgress();
}
if (arguments != null) {
if (arguments.getProgressDialog() != null) {
arguments.getProgressDialog().dismiss();
}
}
super.onPause();
}
@Override
protected void onDestroy() {
handler = null;
arguments = null;
searchView = null;
toggleButton = null;
super.onDestroy();
}
private void selectMenuItem(int selection) {
//when the menu button is clicked, a menu comes up
switch (selection)
{
case DRAWER_SETTINGS_POS:
startActivity(new Intent(this, Preferences.class));
break;
case DRAWER_CENTER_ON_CITY_POS:
if (arguments != null)
{
LatLng point = new LatLng(TransitSystem.getCenterLat(), TransitSystem.getCenterLon());
arguments.getMapView().moveCamera(CameraUpdateFactory.newLatLng(point));
handler.triggerUpdate(1500);
}
break;
case DRAWER_ROUTES_POS:
routeChooserDialog.show();
break;
case DRAWER_INTERSECTIONS_MENU_ITEM_POS:
showIntersectionsDialog();
break;
case DRAWER_CHOOSE_STOP_POS:
showChooseStopDialog();
break;
default:
throw new RuntimeException("Unable to find selection " + selection);
}
}
private void showChooseStopDialog() {
if (arguments != null)
{
ImmutableList<StopLocation> favoriteStops = arguments.getBusLocations().getCurrentFavorites();
ImmutableList.Builder<Location> locationBuilder = ImmutableList.builder();
locationBuilder.addAll(favoriteStops);
final ImmutableList<ImmutableList<Location>> groups = Locations.groupLocations(locationBuilder.build(), favoriteStops.size());
String[] titles = new String[groups.size()];
for (int i = 0; i < groups.size(); i++)
{
ImmutableList<Location> stops = groups.get(i);
int routeCount = 0;
for (Location location : stops) {
StopLocation stop = (StopLocation)location;
routeCount += stop.getRoutes().size();
}
StopLocation stop = (StopLocation)groups.get(i).get(0);
String routeTag = stop.getFirstRoute();
String routeTitle = arguments.getBusLocations().getRouteTitle(routeTag);
String title;
if (routeCount > 1) {
title = stop.getTitle() + " (route " + routeTitle + ", ...)";
} else {
title = stop.getTitle() + " (route " + routeTitle + ")";
}
titles[i] = title;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.chooseStopInBuilder));
builder.setItems(titles, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which >= 0 && which < groups.size()) {
StopLocation stop = (StopLocation)groups.get(which).get(0);
String route = stop.getFirstRoute();
setNewStop(route, stop.getStopTag());
setMode(Selection.Mode.BUS_PREDICTIONS_STAR, true, true);
}
}
});
AlertDialog stopChooserDialog = builder.create();
stopChooserDialog.show();
}
}
private void showIntersectionsDialog() {
if (arguments != null) {
Collection<String> unsortedTitles = arguments.getBusLocations().getIntersectionNames();
List<String> titles = Lists.newArrayList(unsortedTitles);
Collections.sort(titles);
final String[] titlesArray = new String[titles.size() + 1];
titlesArray[0] = "Add new place...";
for (int i = 0; i < titles.size(); i++) {
titlesArray[i+1] = titles.get(i);
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.places));
builder.setItems(titlesArray, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
Toast.makeText(Main.this, "Tap a spot on the map to create a place", Toast.LENGTH_LONG).show();
arguments.getOverlayGroup().setNextClickListener(new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(final LatLng latLng) {
AlertDialog.Builder builder = new AlertDialog.Builder(Main.this);
builder.setTitle("New place name");
final EditText textView = new EditText(Main.this);
textView.setHint("Place name (ie, Home or Work)");
builder.setView(textView);
builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String newName = textView.getText().toString();
if (newName.length() == 0) {
Toast.makeText(Main.this, "Place name cannot be empty", Toast.LENGTH_LONG).show();
} else {
float latitudeAsDegrees = (float) latLng.latitude;
float longitudeAsDegrees = (float) latLng.longitude;
IntersectionLocation.Builder builder = new IntersectionLocation.Builder(newName, latitudeAsDegrees, longitudeAsDegrees);
Locations locations = arguments.getBusLocations();
locations.addIntersection(builder);
Toast.makeText(Main.this, "New place created!", Toast.LENGTH_LONG).show();
setNewIntersection(newName);
}
dialog.dismiss();
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.create().show();
}
});
} else if (which >= 1 && which < titlesArray.length) {
setNewIntersection(titlesArray[which]);
}
}
});
AlertDialog stopChooserDialog = builder.create();
stopChooserDialog.show();
}
}
@Override
protected void onResume() {
super.onResume();
if (arguments != null && handler != null) {
//check the result
populateHandlerSettings();
updateSearchText(arguments.getBusLocations().getSelection());
handler.resume();
// workaround for bad design decisions
if (arguments.getProgressDialog() == null) {
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setCancelable(true);
arguments.setProgressDialog(progressDialog);
}
if (arguments.getProgress() == null) {
final ProgressBar progress = (ProgressBar) findViewById(R.id.progress);
arguments.setProgress(progress);
}
}
Tutorial tutorial = new Tutorial(IntroTutorial.populate());
tutorial.start(this);
}
private void populateHandlerSettings() {
if (handler == null || arguments == null) {
return;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
int updateInterval = getUpdateInterval(prefs);
handler.setUpdateConstantlyInterval(updateInterval);
boolean showTraffic = prefs.getBoolean("showTraffic", false);
MapManager manager = arguments.getOverlayGroup();
manager.setShowTraffic(showTraffic);
boolean allRoutesBlue = prefs.getBoolean(getString(R.string.allRoutesBlue), TransitSystem.isDefaultAllRoutesBlue());
manager.setAllRoutesBlue(allRoutesBlue);
boolean changeRouteIfSelected = prefs.getBoolean("showLinesOnSelected", true);
manager.setChangeRouteIfSelected(changeRouteIfSelected);
boolean alwaysFocusRoute = prefs.getBoolean("alwaysFocusRoute", false);
manager.setAlwaysFocusRoute(alwaysFocusRoute);
manager.setAllRoutesBlue(allRoutesBlue);
manager.setDrawLine(prefs.getBoolean("showRouteLineCheckbox2", true));
locationEnabled = prefs.getBoolean(getString(R.string.alwaysShowLocationCheckbox), true);
String intervalString = Integer.valueOf(updateInterval).toString();
//since the default value for this flag is true, make sure we let the preferences know of this
prefs.edit().
putBoolean(getString(R.string.alwaysShowLocationCheckbox), locationEnabled).
putString(getString(R.string.updateContinuouslyInterval), intervalString).
putBoolean("showRouteLineCheckbox2", arguments.getOverlayGroup().isShowLine()).
putBoolean(getString(R.string.allRoutesBlue), allRoutesBlue).
putBoolean("showTraffic", showTraffic).
putBoolean("showLinesOnSelected", changeRouteIfSelected).
apply();
}
private int getUpdateInterval(SharedPreferences prefs) {
String intervalString = prefs.getString(getString(R.string.updateContinuouslyInterval), "");
int interval;
if (intervalString.length() == 0) {
interval = prefs.getBoolean(getString(R.string.runInBackgroundCheckbox), true) ? UPDATE_INTERVAL_SHORT : UPDATE_INTERVAL_NONE;
}
else
{
interval = Integer.parseInt(intervalString);
}
return interval;
}
@Override
public void onNewIntent(Intent newIntent) {
//since Main is marked singletop, it only uses one activity and onCreate won't get called. Use this to handle search requests
if (Intent.ACTION_SEARCH.equals(newIntent.getAction()))
{
String query = newIntent.getStringExtra(SearchManager.QUERY);
if (query == null)
{
return;
}
final SearchHelper helper = new SearchHelper(this, dropdownRouteKeysToTitles, arguments, query, new DatabaseAgent(getContentResolver()));
helper.runSearch(new Runnable()
{
@Override
public void run() {
//search is finished
String suggestionsQuery = helper.getSuggestionsQuery();
if (suggestionsQuery != null)
{
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(Main.this, TransitContentProvider.AUTHORITY,
TransitContentProvider.MODE);
suggestions.saveRecentQuery(suggestionsQuery, null);
if (handler != null)
{
handler.triggerUpdate();
}
}
}
});
}
}
public void setMode(Selection.Mode mode, boolean updateIcon, boolean triggerRefresh)
{
Selection.Mode setTo = Selection.Mode.VEHICLE_LOCATIONS_ALL;
for (int i = 0; i < Selection.modesSupported.length; i++)
{
if (Selection.modesSupported[i] == mode)
{
setTo = mode;
break;
}
}
if (updateIcon)
{
for (int i = 0; i < Selection.modesSupported.length; i++)
{
if (Selection.modesSupported[i] == mode)
{
toggleButton.setSelection(i);
break;
}
}
}
Locations locations = arguments.getBusLocations();
Selection oldSelection = locations.getSelection();
Selection newSelection = oldSelection.withDifferentMode(setTo);
locations.setSelection(newSelection);
if (triggerRefresh) {
handler.triggerUpdate();
handler.immediateRefresh();
}
updateSearchText(newSelection);
updateButtonVisibility(newSelection);
}
private void updateButtonVisibility(Selection selection) {
Selection.Mode mode = selection.getMode();
if (mode == Selection.Mode.BUS_PREDICTIONS_STAR) {
chooseAFavoriteButton.setVisibility(View.VISIBLE);
chooseAPlaceButton.setVisibility(View.GONE);
}
else
{
chooseAFavoriteButton.setVisibility(View.GONE);
chooseAPlaceButton.setVisibility(View.GONE);
}
}
public void setNewIntersection(String name) {
if (arguments != null) {
Locations locations = arguments.getBusLocations();
setMode(Selection.Mode.BUS_PREDICTIONS_ALL, true, false);
IntersectionLocation newLocation = locations.getIntersection(name);
if (newLocation != null) {
LatLng latlng = new LatLng(newLocation.getLatitudeAsDegrees(), newLocation.getLongitudeAsDegrees());
arguments.getMapView().moveCamera(CameraUpdateFactory.newLatLng(latlng));
arguments.getMapView().moveCamera(CameraUpdateFactory.scrollBy(0, -100));
handler.triggerUpdateThenSelect(Optional.of(newLocation.makeGroupKey()));
}
}
}
/**
* Sets the current selected stop to stopTag, moves map over it, sets route to route, sets mode to stops for one route
* @param route
* @param stopTag
*/
public void setNewStop(String route, String stopTag)
{
StopLocation stopLocation = arguments.getBusLocations().setSelectedStop(route, stopTag);
if (stopLocation == null)
{
Log.e("BostonBusMap", "Error: stopLocation was null");
return;
}
handler.triggerUpdateThenSelect(Optional.of(stopLocation.makeGroupKey()));
if (route != null)
{
if (stopLocation.getRoutes().contains(route) == false)
{
//only set it if some route which contains this stop isn't already set
setNewRoute(route, false, false);
}
}
setMode(Selection.Mode.BUS_PREDICTIONS_ONE, true, true);
LatLng latlng = new LatLng(stopLocation.getLatitudeAsDegrees(), stopLocation.getLongitudeAsDegrees());
arguments.getMapView().moveCamera(CameraUpdateFactory.newLatLng(latlng));
arguments.getMapView().moveCamera(CameraUpdateFactory.scrollBy(0, -100));
}
public void highlightVehicle(String vehicleId, String route, Schema.Routes.SourceId sourceId) {
VehicleLocations vehicleLocations = arguments.getBusLocations().getVehicleLocations();
VehicleLocations.Key key = new VehicleLocations.Key(sourceId, route, vehicleId);
final BusLocation location = vehicleLocations.get(key);
if (location != null) {
handler.triggerUpdateThen(new Runnable() {
@Override
public void run() {
arguments.getOverlayGroup().setSelectedBusId(Optional.of(location.makeGroupKey()));
handler.triggerUpdateThen(new Runnable() {
@Override
public void run() {
if (!arguments.getOverlayGroup().getSelectedBusId().isPresent()) {
Toast.makeText(Main.this, "Unable to locate vehicle to highlight", Toast.LENGTH_LONG).show();
}
}
});
}
});
LatLng latlng = new LatLng(location.getLatitudeAsDegrees(), location.getLongitudeAsDegrees());
arguments.getMapView().moveCamera(CameraUpdateFactory.newLatLng(latlng));
arguments.getMapView().moveCamera(CameraUpdateFactory.scrollBy(0, -100));
}
else {
Toast.makeText(Main.this, "Unable to locate vehicle to highlight", Toast.LENGTH_LONG).show();
}
}
}