package fi.bitrite.android.ws.activity; import android.annotation.SuppressLint; import android.support.v7.app.AlertDialog; import android.app.Dialog; import android.app.SearchManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentSender; import android.content.SharedPreferences; import android.location.Location; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.DialogFragment; import android.text.Html; import android.util.Log; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; import android.widget.SearchView; import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.analytics.GoogleAnalytics; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.location.LocationServices; import com.google.android.gms.maps.CameraUpdate; 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.SupportMapFragment; import com.google.android.gms.maps.model.*; import com.google.maps.android.clustering.Cluster; import com.google.maps.android.clustering.ClusterManager; import com.google.maps.android.clustering.algo.PreCachingAlgorithmDecorator; import com.google.maps.android.clustering.view.DefaultClusterRenderer; import com.google.maps.android.ui.IconGenerator; import fi.bitrite.android.ws.R; import fi.bitrite.android.ws.WSAndroidApplication; import fi.bitrite.android.ws.api.RestClient; import fi.bitrite.android.ws.host.Search; import fi.bitrite.android.ws.host.impl.RestMapSearch; import fi.bitrite.android.ws.model.Host; import fi.bitrite.android.ws.model.HostBriefInfo; import fi.bitrite.android.ws.persistence.StarredHostDao; import fi.bitrite.android.ws.persistence.impl.StarredHostDaoImpl; import fi.bitrite.android.ws.util.Tools; import fi.bitrite.android.ws.util.WSNonHierarchicalDistanceBasedAlgorithm; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.concurrent.ConcurrentHashMap; public class Maps2Activity extends WSBaseActivity implements ClusterManager.OnClusterClickListener<HostBriefInfo>, ClusterManager.OnClusterInfoWindowClickListener<HostBriefInfo>, ClusterManager.OnClusterItemClickListener<HostBriefInfo>, ClusterManager.OnClusterItemInfoWindowClickListener<HostBriefInfo>, GoogleMap.OnCameraChangeListener, GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks, SharedPreferences.OnSharedPreferenceChangeListener, OnMapReadyCallback { private GoogleMap mMap; // Might be null if Google Play services APK is not available. private MapSearchTask searchTask; private ConcurrentHashMap<Integer, HostBriefInfo> mHosts = new ConcurrentHashMap<Integer, HostBriefInfo>(); private ClusterManager<HostBriefInfo> mClusterManager; private Cluster<HostBriefInfo> mLastClickedCluster; private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000; private static final int REQUEST_RESOLVE_ERROR = 1001; private static final String DIALOG_ERROR = "dialog_error"; private boolean mPlayServicesConnectionStatus = false; private static final String TAG = "Maps2Activity"; private CameraPosition mLastCameraPosition = null; private boolean mResolvingError = false; Location mLastDeviceLocation; String mDistanceUnit; private boolean mIsOffline = false; StarredHostDao starredHostDao = new StarredHostDaoImpl(); private List<HostBriefInfo> starredHosts; private GoogleApiClient mGoogleApiClient; enum ClusterStatus {none, some, all} @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_maps); if (!initView()) { finish(); return; } // Google analytics tracker ((WSAndroidApplication) getApplication()).getTracker(WSAndroidApplication.TrackerName.APP_TRACKER); PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); mDistanceUnit = PreferenceManager.getDefaultSharedPreferences(this) .getString("distance_unit", "km"); mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); setUpMapIfNeeded(); } // Immediately handle change to distance unit if requrired public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals("distance_unit")) { mDistanceUnit = PreferenceManager.getDefaultSharedPreferences(this) .getString("distance_unit", "km"); } } /** * This is where google play services gets connected and we can now find recent location. * <p/> * Note that all the complex stuff about connecting to Google Play Services (just to get location) * is from http://developer.android.com/training/location/retrieve-current.html and I don't actually * know how to test it. * * @param connectionHint */ @Override public void onConnected(Bundle connectionHint) { Log.i(TAG, "Connected to Google Play services mLastCameraPosition==" + (mLastCameraPosition != null)); mPlayServicesConnectionStatus = true; mLastDeviceLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); // If we are now connected, but still don't have a location, use a bogus default. if (mLastDeviceLocation == null) { mLastDeviceLocation = new Location("default"); mLastDeviceLocation.setLatitude( Double.parseDouble(getResources().getString(R.string.map_default_latitude))); mLastDeviceLocation.setLongitude(Double.parseDouble(getResources().getString(R.string.map_default_longitude))); } // mMap may not yet be initialized in some cases; Connect happens before map setup. if (mMap != null) { mMap.setMyLocationEnabled(true); if (getSavedCameraPosition() == null) { setMapToCurrentLocation(); } } } public void onConnectionSuspended(int i) { Log.i(TAG, "Disconnected from play services"); mPlayServicesConnectionStatus = false; Toast.makeText(this, getString(R.string.disconnected_location_services), Toast.LENGTH_SHORT).show(); } public void onConnectionFailed(ConnectionResult connectionResult) { if (connectionResult.hasResolution()) { try { // Start an Activity that tries to resolve the error connectionResult.startResolutionForResult( this, CONNECTION_FAILURE_RESOLUTION_REQUEST); /* * Thrown if Google Play services canceled the original * PendingIntent */ } catch (IntentSender.SendIntentException e) { // Log the error e.printStackTrace(); } } else { /* * If no resolution is available, display a dialog to the * user with the error. */ showErrorDialog(connectionResult.getErrorCode()); } } /** * Add the title and snippet to the marker so that infoWindow can be rendered. */ private class HostRenderer extends DefaultClusterRenderer<HostBriefInfo> { private final IconGenerator mSingleLocationClusterIconGenerator = new IconGenerator(getApplicationContext()); private final IconGenerator mSingleHostIconGenerator = new IconGenerator(getApplicationContext()); private SparseArray<BitmapDescriptor> mIcons = new SparseArray<BitmapDescriptor>(); private BitmapDescriptor mSingleHostBitmapDescriptor; public HostRenderer() { super(getApplicationContext(), mMap, mClusterManager); View sameLocationMultiHostClusterView = getLayoutInflater().inflate(R.layout.same_location_cluster_marker, null); View singleHostMarkerView = getLayoutInflater().inflate(R.layout.location_marker, null); mSingleLocationClusterIconGenerator.setContentView(sameLocationMultiHostClusterView); mSingleLocationClusterIconGenerator.setBackground(null); mSingleHostIconGenerator.setContentView(singleHostMarkerView); mSingleHostIconGenerator.setBackground(null); mSingleHostBitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mSingleHostIconGenerator.makeIcon()); } @Override protected void onBeforeClusterRendered(Cluster<HostBriefInfo> cluster, MarkerOptions markerOptions) { if (clusterLocationStatus(cluster) == ClusterStatus.all) { int size = cluster.getSize(); BitmapDescriptor descriptor = mIcons.get(size); if (descriptor == null) { // Cache new bitmaps descriptor = BitmapDescriptorFactory.fromBitmap(mSingleLocationClusterIconGenerator.makeIcon(String.valueOf(size))); mIcons.put(size, descriptor); } markerOptions.icon(descriptor); } else { super.onBeforeClusterRendered(cluster, markerOptions); } } @Override protected void onBeforeClusterItemRendered(HostBriefInfo host, MarkerOptions markerOptions) { String street = host.getStreet(); String snippet = host.getCity() + ", " + host.getProvince().toUpperCase(); if (street != null && street.length() > 0) { snippet = street + "<br/>" + snippet; } if (mLastDeviceLocation != null) { double distance = Tools.calculateDistanceBetween(host.getLatLng(), mLastDeviceLocation, mDistanceUnit); snippet += "<br/>" + getString(R.string.distance_from_current, (int) distance, mDistanceUnit); } markerOptions.title(host.getFullname()).snippet(snippet); markerOptions.icon(mSingleHostBitmapDescriptor); } @Override protected boolean shouldRenderAsCluster(Cluster<HostBriefInfo> cluster) { // Render as a cluster if all the items are at the exact same location, or if there are more than // min_cluster_size in the cluster. ClusterStatus status = clusterLocationStatus(cluster); boolean renderAsCluster = status == ClusterStatus.all || status == ClusterStatus.some || cluster.getSize() >= getResources().getInteger(R.integer.min_cluster_size); return renderAsCluster; } /** * Attempt to determine the location status of items in the cluster, whether all in one location * or in a variety of locations. * * @param cluster * @return */ protected ClusterStatus clusterLocationStatus(Cluster<HostBriefInfo> cluster) { HashSet<String> latLngs = new HashSet<String>(); for (HostBriefInfo item : cluster.getItems()) { latLngs.add(item.getLatLng().toString()); } // if cluster size and latLngs size are same, all are unique locations, so 'none' if (cluster.getSize() == latLngs.size()) { return ClusterStatus.none; } // If there is only one unique location, then all are in same location. else if (latLngs.size() == 1) { return ClusterStatus.all; } // Otherwise it's a mix of same and other location return ClusterStatus.some; } } class ClusterInfoWindowAdapter implements GoogleMap.InfoWindowAdapter { private View mPopup = null; private LayoutInflater mInflater = null; ClusterInfoWindowAdapter(LayoutInflater inflater) { this.mInflater = inflater; } @Override public View getInfoWindow(Marker marker) { return null; } @Override public View getInfoContents(Marker marker) { String hostList = ""; ArrayList<HostBriefInfo> hosts = new ArrayList<HostBriefInfo>(); if (mPopup == null) { mPopup = mInflater.inflate(R.layout.info_window, null); } TextView tv = (TextView) mPopup.findViewById(R.id.title); if (mLastClickedCluster != null) { if (mLastDeviceLocation != null) { double distance = Tools.calculateDistanceBetween(marker.getPosition(), mLastDeviceLocation, mDistanceUnit); TextView distance_tv = (TextView) mPopup.findViewById(R.id.distance_from_current); distance_tv.setText(Html.fromHtml(getString(R.string.distance_from_current, (int) distance, mDistanceUnit))); } hosts = (ArrayList<HostBriefInfo>) mLastClickedCluster.getItems(); if (mLastClickedCluster != null) { for (HostBriefInfo host : hosts) { hostList += host.getFullname() + "<br/>"; } hostList += getString(R.string.click_to_view_all); } String title = getString(R.string.hosts_at_location, hosts.size(), hosts.get(0).getLocation()); tv.setText(Html.fromHtml(title)); tv = (TextView) mPopup.findViewById(R.id.snippet); tv.setText(Html.fromHtml(hostList)); } return (mPopup); } } @Override protected void onResume() { super.onResume(); // We'll use the starred hosts when network is offline. starredHostDao.open(); starredHosts = starredHostDao.getAllBrief(); setUpMapIfNeeded(); } @Override protected void onPause() { starredHostDao.close(); super.onPause(); } @Override protected void onStop() { if (mLastCameraPosition != null) { saveMapLocation(mLastCameraPosition); } GoogleAnalytics.getInstance(this).reportActivityStop(this); super.onStop(); } protected void saveMapLocation(CameraPosition position) { SharedPreferences settings = getSharedPreferences("map_last_location", 0); SharedPreferences.Editor editor = settings.edit(); editor.putFloat("latitude", (float) position.target.latitude); editor.putFloat("longitude", (float) position.target.longitude); editor.putFloat("zoom", (float) position.zoom); editor.commit(); } /** * Retrieve map location and zoom from saved preference. Returns null if none existed. * * @return */ protected CameraPosition getSavedCameraPosition() { SharedPreferences settings = getSharedPreferences("map_last_location", 0); if (!settings.contains("latitude")) { return null; } float latitude = settings.getFloat("latitude", Float.parseFloat(getResources().getString(R.string.map_default_latitude))); float longitude = settings.getFloat("longitude", Float.parseFloat(getResources().getString(R.string.map_default_longitude))); float zoom = settings.getFloat("zoom", (float) getResources().getInteger(R.integer.map_initial_zoom)); CameraPosition position = new CameraPosition(new LatLng(latitude, longitude), zoom, 0, 0); return position; } @Override protected void onStart() { super.onStart(); GoogleAnalytics.getInstance(this).reportActivityStart(this); } /** * Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly * installed) and the map has not already been instantiated.. This will ensure that we only ever * call {@link #setUpMap()} once when {@link #mMap} is not null. * <p/> * If it isn't installed {@link SupportMapFragment} (and * {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to * install/update the Google Play services APK on their device. * <p/> * A user can return to this FragmentActivity after following the prompt and correctly * installing/updating/enabling the Google Play services. Since the FragmentActivity may not * have been completely destroyed during this process (it is likely that it would only be * stopped or paused), {@link #onCreate(android.os.Bundle)} may not be called again so we should call this * method in {@link #onResume()} to guarantee that it will be called. */ private void setUpMapIfNeeded() { // Do a null check to confirm that we have not already instantiated the map. if (mMap == null) { SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map_fragment); mapFragment.getMapAsync(this); } } public void onMapReady(GoogleMap map) { mMap = map; setUpMap(); mGoogleApiClient.connect(); // Can't connect until here because location will need map to act } private void setUpMap() { // Rotate gestures probably aren't needed here and can be disorienting for some of our users. mMap.getUiSettings().setRotateGesturesEnabled(false); mMap.setOnCameraChangeListener(this); CameraPosition position = null; // If we were launched with an intent asking us to zoom to a member Intent receivedIntent = getIntent(); if (receivedIntent.hasExtra("target_map_latlng")) { LatLng targetLatLng = receivedIntent.getParcelableExtra("target_map_latlng"); position = new CameraPosition(targetLatLng, getResources().getInteger(R.integer.map_showhost_zoom), 0, 0); } if (position == null) { position = getSavedCameraPosition(); } if (position != null) { mMap.moveCamera(CameraUpdateFactory.newCameraPosition(position)); // The move itself will end up setting the mlastCameraPosition. } mClusterManager = new ClusterManager<HostBriefInfo>(this, mMap); mClusterManager.setAlgorithm(new PreCachingAlgorithmDecorator<HostBriefInfo>(new WSNonHierarchicalDistanceBasedAlgorithm<HostBriefInfo>(this))); mMap.setOnMarkerClickListener(mClusterManager); mMap.setOnInfoWindowClickListener(mClusterManager); mClusterManager.setOnClusterClickListener(this); mClusterManager.setOnClusterInfoWindowClickListener(this); mClusterManager.setOnClusterItemClickListener(this); mClusterManager.setOnClusterItemInfoWindowClickListener(this); mClusterManager.setRenderer(new HostRenderer()); mMap.setInfoWindowAdapter(mClusterManager.getMarkerManager()); mClusterManager.getClusterMarkerCollection().setOnInfoWindowAdapter(new ClusterInfoWindowAdapter(getLayoutInflater())); mClusterManager.getMarkerCollection().setOnInfoWindowAdapter(new SingleHostInfoWindowAdapter(getLayoutInflater())); } /** * If we can get a location, go to it with default zoom. */ void setMapToCurrentLocation() { LatLng gotoLatLng = new LatLng(mLastDeviceLocation.getLatitude(), mLastDeviceLocation.getLongitude()); float zoom = (float) getResources().getInteger(R.integer.map_initial_zoom); // Default mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(gotoLatLng, zoom)); } @Override public void onCameraChange(CameraPosition position) { mLastCameraPosition = position; // If not connected, we'll switch to offline/starred hosts mode if (!Tools.isNetworkConnected(this)) { sendMessage(R.string.map_network_not_connected, false); // If we already knew we were offline, return if (mIsOffline) { return; } // Otherwise, set state to offline and load only offline hosts mIsOffline = true; loadOfflineHosts(); return; } // If we were offline, switch back on, but remove the offline markers if (mIsOffline) { mIsOffline = false; mClusterManager.clearItems(); mClusterManager.getMarkerCollection().clear(); mHosts.clear(); } // And get standard host list for region from server LatLngBounds curScreen = mMap.getProjection().getVisibleRegion().latLngBounds; Search search = new RestMapSearch(curScreen.northeast, curScreen.southwest); if (position.zoom < getResources().getInteger(R.integer.map_zoom_min_load)) { sendMessage(R.string.hosts_dont_load, false); } else { sendMessage(getResources().getString(R.string.loading_hosts), false); doMapSearch(search); } } private void loadOfflineHosts() { mClusterManager.clearItems(); mClusterManager.getMarkerCollection().clear(); mHosts.clear(); mClusterManager.addItems(starredHosts); mClusterManager.cluster(); } public void doMapSearch(Search search) { searchTask = new MapSearchTask(); searchTask.execute(search); } @Override /** * - Capture the clicked cluster so we can use it in custom infoWindow * - Check overall bounds of items in cluster * - If the bounds are empty (all hosts at same place) then let it pop the info window * - Otherwise, move the camera to show the bounds of the map */ public boolean onClusterClick(Cluster<HostBriefInfo> cluster) { mLastClickedCluster = cluster; // remember for use later in the Adapter // Find out the bounds of the hosts currently in cluster LatLngBounds.Builder builder = new LatLngBounds.Builder(); for (HostBriefInfo host : cluster.getItems()) { builder.include(host.getLatLng()); } LatLngBounds bounds = builder.build(); // If the hosts are not all at the same location, then change bounds of map. if (!bounds.southwest.equals(bounds.northeast)) { // Offset from edge of map in pixels when exploding cluster View mapView = findViewById(R.id.map_fragment); int padding_percent = getResources().getInteger(R.integer.cluster_explode_padding_percent); int padding = Math.min(mapView.getHeight(), mapView.getWidth()) * padding_percent / 100; CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, mapView.getWidth(), mapView.getHeight(), padding); mMap.animateCamera(cu); return true; } showMultihostSelectDialog((ArrayList<HostBriefInfo>) cluster.getItems()); return true; } @Override /** * Start the Search tab with the members we have at this exact location. */ public void onClusterInfoWindowClick(Cluster<HostBriefInfo> hostBriefInfoCluster) { Intent intent = new Intent(this, ListSearchTabActivity.class); intent.putParcelableArrayListExtra("search_results", (ArrayList<HostBriefInfo>) hostBriefInfoCluster.getItems()); startActivity(intent); } @Override public boolean onClusterItemClick(HostBriefInfo hostBriefInfo) { return false; } @Override public void onClusterItemInfoWindowClick(HostBriefInfo host) { Intent i = new Intent(this, HostInformationActivity.class); i.putExtra("host", Host.createFromBriefInfo(host)); i.putExtra("id", host.getId()); startActivityForResult(i, 0); } private class MapSearchTask extends AsyncTask<Search, Void, Object> { private static final String TAG = "MapSearchTask"; @Override protected Object doInBackground(Search... params) { Search search = params[0]; Object retObj = null; try { retObj = search.doSearch(); } catch (Exception e) { Log.e(TAG, e.getMessage(), e); retObj = e; } return retObj; } @SuppressWarnings("unchecked") @Override protected void onPostExecute(Object result) { if (result instanceof Exception) { RestClient.reportError(Maps2Activity.this, result); return; } ArrayList<HostBriefInfo> hosts = (ArrayList<HostBriefInfo>) result; if (hosts.isEmpty()) { sendMessage((String) getResources().getText(R.string.no_results), false); } for (HostBriefInfo host : hosts) { HostBriefInfo v = mHosts.putIfAbsent(host.getId(), host); // Only add to the cluster if it wasn't in mHosts before. if (v == null) { mClusterManager.addItem(host); } } mClusterManager.cluster(); } } /* Creates a dialog for an error message */ private void showErrorDialog(int errorCode) { // Create a fragment for the error dialog ErrorDialogFragment dialogFragment = new ErrorDialogFragment(); // Pass the error that should be displayed Bundle args = new Bundle(); args.putInt(DIALOG_ERROR, errorCode); dialogFragment.setArguments(args); dialogFragment.show(getSupportFragmentManager(), "errordialog"); } /* Called from ErrorDialogFragment when the dialog is dismissed. */ public void onDialogDismissed() { mResolvingError = false; } /* A fragment to display an error dialog */ public static class ErrorDialogFragment extends DialogFragment { public ErrorDialogFragment() { } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Get the error code and retrieve the appropriate dialog int errorCode = this.getArguments().getInt(DIALOG_ERROR); return GooglePlayServicesUtil.getErrorDialog(errorCode, this.getActivity(), REQUEST_RESOLVE_ERROR); } @Override public void onDismiss(DialogInterface dialog) { ((Maps2Activity) getActivity()).onDialogDismissed(); } } /** * InfoWindowAdapter to present info about a single host marker. * Implemented here so we can have multiple lines, which the maps-provided one prevents. */ class SingleHostInfoWindowAdapter implements GoogleMap.InfoWindowAdapter { private View mPopup = null; private LayoutInflater mInflater = null; SingleHostInfoWindowAdapter(LayoutInflater inflater) { this.mInflater = inflater; } @Override public View getInfoWindow(Marker marker) { return (null); } @SuppressLint("InflateParams") @Override public View getInfoContents(Marker marker) { if (mPopup == null) { mPopup = mInflater.inflate(R.layout.single_host_infowindow, null); } TextView titleView = (TextView) mPopup.findViewById(R.id.title); titleView.setText(marker.getTitle()); TextView snippetView = (TextView) mPopup.findViewById(R.id.snippet); snippetView.setText(Html.fromHtml(marker.getSnippet())); return (mPopup); } } public void showMultihostSelectDialog(final ArrayList<HostBriefInfo> hosts) { String[] mPossibleItems = new String[hosts.size()]; double distance = Tools.calculateDistanceBetween(hosts.get(0).getLatLng(), mLastDeviceLocation, mDistanceUnit); String distanceSummary = getString(R.string.distance_from_current, (int) distance, mDistanceUnit); LinearLayout customTitleView = (LinearLayout) getLayoutInflater().inflate(R.layout.multihost_dialog_header, null); TextView titleView = (TextView) customTitleView.findViewById(R.id.title); titleView.setText(getString(R.string.hosts_at_location, hosts.size(), hosts.get(0).getStreetCityAddress())); TextView distanceView = (TextView) customTitleView.findViewById(R.id.distance_from_current); distanceView.setText(distanceSummary); for (int i = 0; i < hosts.size(); i++) { mPossibleItems[i] = hosts.get(i).getFullname(); } AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); alertDialogBuilder.setCustomTitle(customTitleView); alertDialogBuilder .setNegativeButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { return; } } ) .setItems(mPossibleItems, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int index) { Intent intent = new Intent(Maps2Activity.this, HostInformationActivity.class); HostBriefInfo briefHost = hosts.get(index); Host host = Host.createFromBriefInfo(hosts.get(index)); intent.putExtra("host", host); intent.putExtra("id", briefHost.getId()); startActivity(intent); } }); AlertDialog alertDialog = alertDialogBuilder.create(); alertDialog.show(); } private Toast lastToast = null; private void sendMessage(int message_id, final boolean error) { String message = getString(message_id); sendMessage(message, error); } private void sendMessage(final String message, final boolean error) { Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT); if (lastToast != null) { lastToast.cancel(); } toast.show(); lastToast = toast; } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.map_actions, menu); // Get the SearchView and set the searchable configuration SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); android.support.v7.widget.SearchView searchView = (android.support.v7.widget.SearchView) menu.findItem(R.id.action_search).getActionView(); // Assumes current activity is the searchable activity searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.legal) { startActivity(new Intent(this, LegalNoticesActivity.class)); return(true); } return super.onOptionsItemSelected(item); } }