package com.samsunghack.apps.android.noq; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.SharedPreferences; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesClient; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.location.LocationClient; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; import com.samsunghack.apps.android.utils.ImageDownloader; import com.samsunghack.apps.android.utils.LocationUtils; import com.samsunghack.apps.apis.GooglePlacesData; import com.samsunghack.apps.apis.GooglePlacesData.GooglePlaces; import com.samsunghack.apps.apis.GooglePlacesIfc; public class NearbyActivity extends FragmentActivity implements LocationListener, GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener { private static final String TAG="NearbyActivity"; // A request to connect to Location Services private LocationRequest mLocationRequest; // Stores the current instantiation of the location client in this object private LocationClient mLocationClient; // Handles to UI widgets private TextView mLatLng; private TextView mAddress; private ProgressBar mProgressBar; private TextView mConnectionState; private TextView mConnectionStatus; private ListView mRestaurantsListV; private EditText mSearchField; private Button mSearchButton; private String mSearchData; // Handle to SharedPreferences for this app SharedPreferences mPrefs; // Handle to a SharedPreferences editor SharedPreferences.Editor mEditor; /* * Note if updates have been turned on. Starts out as "false"; is set to * "true" in the method handleRequestSuccess of LocationUpdateReceiver. */ boolean mUpdatesRequested = false; GooglePlacesData mGooglePlacesData; ArrayList<GooglePlaces> mGooglePlacesList; private GooglePlacesAdapter mGooglePlacesAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_nearby); // Show the Up button in the action bar. getActionBar().setDisplayHomeAsUpEnabled(true); mRestaurantsListV = (ListView) findViewById(R.id.restaurants_list); mRestaurantsListV.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View view, int position, long id) { // Launch Reservations activity GooglePlaces restaurantPlace = mGooglePlacesList.get(position); Intent intent = new Intent(NearbyActivity.this,ReservationFormActivity.class); intent.putExtra(AppConstants.RESTAURANT_NAME, restaurantPlace.getName()); intent.putExtra(AppConstants.RESTAURANT_ADDRESS, restaurantPlace.getVicinity()); startActivity(intent); } }); mSearchField = (EditText) findViewById(R.id.search_field); mSearchButton = (Button) findViewById(R.id.search_button); mSearchButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mSearchData = mSearchField.getText().toString(); String[] params = new String[3];; params[0] = "37.37677240"; // Lat params[1] = "-121.92164020"; // Long params[2] = mSearchData; new FindNearbyRestaurantsTask().execute(params); } }); // Get handles to the UI view objects mLatLng = (TextView) findViewById(R.id.lat_lng); mProgressBar = (ProgressBar) findViewById(R.id.progress_bar); mConnectionState = (TextView) findViewById(R.id.text_connection_state); mConnectionStatus = (TextView) findViewById(R.id.text_connection_status); // Create a new global location parameters object mLocationRequest = LocationRequest.create(); /* * Set the update interval */ mLocationRequest .setInterval(LocationUtils.UPDATE_INTERVAL_IN_MILLISECONDS); // Use high accuracy mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // Set the interval ceiling to one minute mLocationRequest .setFastestInterval(LocationUtils.FAST_INTERVAL_CEILING_IN_MILLISECONDS); // Note that location updates are off until the user turns them on mUpdatesRequested = false; // Open Shared Preferences mPrefs = getSharedPreferences(LocationUtils.SHARED_PREFERENCES, Context.MODE_PRIVATE); // Get an editor mEditor = mPrefs.edit(); /* * Create a new location client, using the enclosing class to handle * callbacks. */ mLocationClient = new LocationClient(this, this, this); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action buttons switch (item.getItemId()) { case android.R.id.home: this.finish(); break; default: return super.onOptionsItemSelected(item); } return false; } /* * Called when the Activity is no longer visible at all. Stop updates and * disconnect. */ @Override public void onStop() { // If the client is connected if (mLocationClient.isConnected()) { stopPeriodicUpdates(); } // After disconnect() is called, the client is considered "dead". mLocationClient.disconnect(); super.onStop(); } /* * Called when the Activity is going into the background. Parts of the UI * may be visible, but the Activity is inactive. */ @Override public void onPause() { // Save the current setting for updates mEditor.putBoolean(LocationUtils.KEY_UPDATES_REQUESTED, mUpdatesRequested); mEditor.commit(); super.onPause(); } /* * Called when the Activity is restarted, even before it becomes visible. */ @Override public void onStart() { super.onStart(); /* * Connect the client. Don't re-start any requests here; instead, wait * for onResume() */ mLocationClient.connect(); } /* * Called when the system detects that this Activity is now visible. */ @Override public void onResume() { super.onResume(); // If the app already has a setting for getting location updates, get it if (mPrefs.contains(LocationUtils.KEY_UPDATES_REQUESTED)) { mUpdatesRequested = mPrefs.getBoolean( LocationUtils.KEY_UPDATES_REQUESTED, false); // Otherwise, turn off location updates until requested } else { mEditor.putBoolean(LocationUtils.KEY_UPDATES_REQUESTED, false); mEditor.commit(); } String[] params = new String[3]; params[0] = "37.37677240"; params[1] = "-121.92164020"; params[2] = "pizza"; // Find Nearby Restaurants new FindNearbyRestaurantsTask().execute(params); } /* * Handle results returned to this Activity by other Activities started with * startActivityForResult(). In particular, the method onConnectionFailed() * in LocationUpdateRemover and LocationUpdateRequester may call * startResolutionForResult() to start an Activity that handles Google Play * services problems. The result of this call returns here, to * onActivityResult. */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { // Choose what to do based on the request code switch (requestCode) { // If the request code matches the code sent in onConnectionFailed case LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST: switch (resultCode) { // If Google Play services resolved the problem case Activity.RESULT_OK: // Log the result Log.d(LocationUtils.APPTAG, getString(R.string.resolved)); // Display the result mConnectionState.setText(R.string.connected); mConnectionStatus.setText(R.string.resolved); break; // If any other result was returned by Google Play services default: // Log the result Log.d(LocationUtils.APPTAG, getString(R.string.no_resolution)); // Display the result mConnectionState.setText(R.string.disconnected); mConnectionStatus.setText(R.string.no_resolution); break; } // If any other request code was received default: // Report that this Activity received an unknown requestCode Log.d(LocationUtils.APPTAG, getString(R.string.unknown_activity_request_code, requestCode)); break; } } /** * Verify that Google Play services is available before making a request. * * @return true if Google Play services is available, otherwise false */ private boolean servicesConnected() { // Check that Google Play services is available int resultCode = GooglePlayServicesUtil .isGooglePlayServicesAvailable(this); // If Google Play services is available if (ConnectionResult.SUCCESS == resultCode) { // In debug mode, log the status Log.d(LocationUtils.APPTAG, getString(R.string.play_services_available)); // Continue return true; // Google Play services was not available for some reason } else { // Display an error dialog Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0); if (dialog != null) { ErrorDialogFragment errorFragment = new ErrorDialogFragment(); errorFragment.setDialog(dialog); errorFragment.show(getSupportFragmentManager(), LocationUtils.APPTAG); } return false; } } /** * Invoked by the "Get Location" button. * * Calls getLastLocation() to get the current location * * @param v * The view object associated with this method, in this case a * Button. */ public void getLocation(View v) { // If Google Play Services is available if (servicesConnected()) { // Get the current location Location currentLocation = mLocationClient.getLastLocation(); // Display the current location in the UI mLatLng.setText(LocationUtils.getLatLng(this, currentLocation)); } } /** * Invoked by the "Get Address" button. Get the address of the current * location, using reverse geocoding. This only works if a geocoding service * is available. * * @param v * The view object associated with this method, in this case a * Button. */ // For Eclipse with ADT, suppress warnings about Geocoder.isPresent() @SuppressLint("NewApi") public void getAddress(View v) { // In Gingerbread and later, use Geocoder.isPresent() to see if a // geocoder is available. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD && !Geocoder.isPresent()) { // No geocoder is present. Issue an error message Toast.makeText(this, R.string.no_geocoder_available, Toast.LENGTH_LONG).show(); return; } if (servicesConnected()) { // Get the current location Location currentLocation = mLocationClient.getLastLocation(); // Turn the indefinite activity indicator on mProgressBar.setVisibility(View.VISIBLE); // Start the background task (new NearbyActivity.GetAddressTask(this)).execute(currentLocation); } } /** * Invoked by the "Start Updates" button Sends a request to start location * updates * * @param v * The view object associated with this method, in this case a * Button. */ public void startUpdates(View v) { mUpdatesRequested = true; if (servicesConnected()) { startPeriodicUpdates(); } } /** * Invoked by the "Stop Updates" button Sends a request to remove location * updates request them. * * @param v * The view object associated with this method, in this case a * Button. */ public void stopUpdates(View v) { mUpdatesRequested = false; if (servicesConnected()) { stopPeriodicUpdates(); } } /* * Called by Location Services when the request to connect the client * finishes successfully. At this point, you can request the current * location or start periodic updates */ @Override public void onConnected(Bundle bundle) { mConnectionStatus.setText(R.string.connected); if (mUpdatesRequested) { startPeriodicUpdates(); } } /* * Called by Location Services if the connection to the location client * drops because of an error. */ @Override public void onDisconnected() { mConnectionStatus.setText(R.string.disconnected); } /* * Called by Location Services if the attempt to Location Services fails. */ @Override public void onConnectionFailed(ConnectionResult connectionResult) { /* * Google Play services can resolve some errors it detects. If the error * has a resolution, try sending an Intent to start a Google Play * services activity that can resolve error. */ if (connectionResult.hasResolution()) { try { // Start an Activity that tries to resolve the error connectionResult.startResolutionForResult(this, LocationUtils.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()); } } /** * Report location updates to the UI. * * @param location * The updated location. */ @Override public void onLocationChanged(Location location) { // Report to the UI that the location was updated mConnectionStatus.setText(R.string.location_updated); // In the UI, set the latitude and longitude to the value received mLatLng.setText(LocationUtils.getLatLng(this, location)); } /** * In response to a request to start updates, send a request to Location * Services */ private void startPeriodicUpdates() { mLocationClient.requestLocationUpdates(mLocationRequest, this); mConnectionState.setText(R.string.location_requested); } /** * In response to a request to stop updates, send a request to Location * Services */ private void stopPeriodicUpdates() { mLocationClient.removeLocationUpdates(this); mConnectionState.setText(R.string.location_updates_stopped); } /** * An AsyncTask that calls getFromLocation() in the background. The class * uses the following generic types: Location - A * {@link android.location.Location} object containing the current location, * passed as the input parameter to doInBackground() Void - indicates that * progress units are not used by this subclass String - An address passed * to onPostExecute() */ protected class GetAddressTask extends AsyncTask<Location, Void, String> { // Store the context passed to the AsyncTask when the system // instantiates it. Context localContext; // Constructor called by the system to instantiate the task public GetAddressTask(Context context) { // Required by the semantics of AsyncTask super(); // Set a Context for the background task localContext = context; } /** * Get a geocoding service instance, pass latitude and longitude to it, * format the returned address, and return the address to the UI thread. */ @Override protected String doInBackground(Location... params) { /* * Get a new geocoding service instance, set for localized * addresses. This example uses android.location.Geocoder, but other * geocoders that conform to address standards can also be used. */ Geocoder geocoder = new Geocoder(localContext, Locale.getDefault()); // Get the current location from the input parameter list Location location = params[0]; // Create a list to contain the result address List<Address> addresses = null; // Try to get an address for the current location. Catch IO or // network problems. try { /* * Call the synchronous getFromLocation() method with the * latitude and longitude of the current location. Return at * most 1 address. */ addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1); // Catch network or other I/O problems. } catch (IOException exception1) { // Log an error and return an error message Log.e(LocationUtils.APPTAG, getString(R.string.IO_Exception_getFromLocation)); // print the stack trace exception1.printStackTrace(); // Return an error message return (getString(R.string.IO_Exception_getFromLocation)); // Catch incorrect latitude or longitude values } catch (IllegalArgumentException exception2) { // Construct a message containing the invalid arguments String errorString = getString( R.string.illegal_argument_exception, location.getLatitude(), location.getLongitude()); // Log the error and print the stack trace Log.e(LocationUtils.APPTAG, errorString); exception2.printStackTrace(); // return errorString; } // If the reverse geocode returned an address if (addresses != null && addresses.size() > 0) { // Get the first address Address address = addresses.get(0); // Format the first line of address String addressText = getString( R.string.address_output_string, // If there's a street address, add it address.getMaxAddressLineIndex() > 0 ? address .getAddressLine(0) : "", // Locality is usually a city address.getLocality(), // The country of the address address.getCountryName()); // Return the text return addressText; // If there aren't any addresses, post a message } else { return getString(R.string.no_address_found); } } /** * A method that's called once doInBackground() completes. Set the text * of the UI element that displays the address. This method runs on the * UI thread. */ @Override protected void onPostExecute(String address) { // Turn off the progress bar mProgressBar.setVisibility(View.GONE); // Set the address in the UI mAddress.setText(address); } } /** * Show a dialog returned by Google Play services for the connection error * code * * @param errorCode * An error code returned from onConnectionFailed */ private void showErrorDialog(int errorCode) { // Get the error dialog from Google Play services Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(errorCode, this, LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST); // If Google Play services can provide an error dialog if (errorDialog != null) { // Create a new DialogFragment in which to show the error dialog ErrorDialogFragment errorFragment = new ErrorDialogFragment(); // Set the dialog in the DialogFragment errorFragment.setDialog(errorDialog); // Show the error dialog in the DialogFragment errorFragment.show(getSupportFragmentManager(), LocationUtils.APPTAG); } } /** * Define a DialogFragment to display the error dialog generated in * showErrorDialog. */ public static class ErrorDialogFragment extends DialogFragment { // Global field to contain the error dialog private Dialog mDialog; /** * Default constructor. Sets the dialog field to null */ public ErrorDialogFragment() { super(); mDialog = null; } /** * Set the dialog to display * * @param dialog * An error dialog */ public void setDialog(Dialog dialog) { mDialog = dialog; } /* * This method must return a Dialog to the DialogFragment. */ @Override public Dialog onCreateDialog(Bundle savedInstanceState) { return mDialog; } } class GooglePlacesAdapter extends ArrayAdapter<GooglePlaces> { private final LayoutInflater mLayoutInflater; private final ImageDownloader imageDownloader = new ImageDownloader(); GooglePlacesAdapter(NearbyActivity activity,ArrayList<GooglePlaces> GooglePlacesList) { super(activity, 0); mLayoutInflater = LayoutInflater.from(activity); } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = mLayoutInflater.inflate(R.layout.list_item_nearby_restaurants, parent, false); holder = new ViewHolder(); holder.name = (TextView) convertView.findViewById(R.id.name); holder.vicinity = (TextView) convertView.findViewById(R.id.vicinity); holder.imageView = (ImageView) convertView.findViewById(R.id.image); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } final GooglePlaces googlePlaces = getItem(position); imageDownloader.download(googlePlaces.getIcon(), (ImageView) holder.imageView); holder.name.setText(googlePlaces.getName()); holder.vicinity.setText(googlePlaces.getVicinity()); return convertView; } } class ViewHolder { ImageView imageView; TextView name; TextView vicinity; } private class FindNearbyRestaurantsTask extends AsyncTask<String, String, GooglePlacesData> { protected GooglePlacesData doInBackground(String... params) { // Turn the indefinite activity indicator on // FIXME - Progress Bar is causing some issues // if(mProgressBar!=null) { // mProgressBar.setVisibility(View.VISIBLE); // } return GooglePlacesIfc.getPlaces(getApplicationContext(),params[0],params[1],params[2],"food"); } protected void onProgressUpdate(String... progress) { } protected void onPostExecute(GooglePlacesData googlePlacesData) { if(googlePlacesData!=null) { displayGooglePlaces(googlePlacesData); } // if(mProgressBar!=null) { // mProgressBar.setVisibility(View.GONE); // } } protected void onPreExecute() { } } private void displayGooglePlaces(GooglePlacesData googlePlacesData) { mGooglePlacesData = googlePlacesData; if (mGooglePlacesData != null) { mGooglePlacesList = mGooglePlacesData.getFeatuersData(); if (mGooglePlacesList != null && mGooglePlacesList.isEmpty() == false) { if (mGooglePlacesAdapter == null) { mGooglePlacesAdapter = new GooglePlacesAdapter(NearbyActivity.this,mGooglePlacesList); } mGooglePlacesAdapter.clear(); Iterator<GooglePlaces> it = mGooglePlacesList.iterator(); while(it.hasNext()){ GooglePlaces GooglePlaces = it.next(); mGooglePlacesAdapter.add(GooglePlaces); } if (mGooglePlacesAdapter.isEmpty()) { Toast.makeText(this,R.string.fatal_error, Toast.LENGTH_SHORT).show(); } else { mRestaurantsListV.setAdapter(mGooglePlacesAdapter); } } else{ Toast.makeText(getApplicationContext(), "No GooglePlaces available.", Toast.LENGTH_SHORT).show(); } } else { Log.e(TAG,"displayGooglePlaces: mGooglePlacesData = null"); } } }