//*****************************************************************************
// This file is part of CheckIn4Me. Copyright � 2010 David Ivins
//
// CheckIn4Me 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.
//
// CheckIn4Me 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 CheckIn4Me. If not, see <http://www.gnu.org/licenses/>.
//*****************************************************************************
package com.davidivins.checkin4me.activities;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.*;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView;
import android.widget.Toast;
import com.davidivins.checkin4me.adapters.LocaleAdapter;
import com.davidivins.checkin4me.core.Ad;
import com.davidivins.checkin4me.core.GeneratedResources;
import com.davidivins.checkin4me.core.Locale;
import com.davidivins.checkin4me.core.Services;
import com.davidivins.checkin4me.listeners.interfaces.CleanableProgressDialogListener;
import com.davidivins.checkin4me.listeners.interfaces.GPSTimeoutListener;
import com.davidivins.checkin4me.listeners.interfaces.LocationsRetrieverListener;
import com.davidivins.checkin4me.listeners.interfaces.NetworkTimeoutListener;
import com.davidivins.checkin4me.threads.GPSTimeoutMonitor;
import com.davidivins.checkin4me.threads.LocationsRetriever;
import com.davidivins.checkin4me.threads.NetworkTimeoutMonitor;
import com.davidivins.checkin4me.util.CleanableProgressDialog;
import java.util.List;
/**
* NearbyPlaces
*
* @author david ivins
*/
public class NearbyPlaces extends ListActivity
implements OnItemClickListener, LocationsRetrieverListener, GPSTimeoutListener,
NetworkTimeoutListener, CleanableProgressDialogListener, LocationListener
{
private static final String TAG = NearbyPlaces.class.getSimpleName();
private static ProgressDialog progress_dialog = null;
private LocationsRetriever locations_retriever = null;
private GPSTimeoutMonitor gps_timeout_monitor = null;
private NetworkTimeoutMonitor network_timeout_monitor = null;
private Runnable display_progress_dialog = null;
private Handler handler = null;
private List<Locale> current_locations = null;
private String current_longitude = null;
private String current_latitude = null;
private String current_query = null;
/**
* onCreate
*
* @param saved_instance_state
*/
@Override
public void onCreate(Bundle saved_instance_state)
{
super.onCreate(saved_instance_state);
GeneratedResources.generate(this);
// temp fix until this is turned into an async task
handler = new Handler();
// if no services are connected, display error message, otherwise, process intent
if (Services.getInstance(this).atLeastOneConnected())
{
String pending_query = null;
boolean query_changed = false;
// check if latest query is a change from the previous query
if (null != getParent())
{
pending_query = getParent().getIntent().getStringExtra("query");
query_changed = !queriesAreTheSame(current_query, pending_query);
}
// store pending query as current query
if (null != pending_query)
current_query = pending_query.trim();
Log.i(TAG, "current_query = " + current_query);
// if a list doesn't exist yet
if (null == getListAdapter() || query_changed)
{
// if we don't have coordinates, request them. otherwise, just build the location list
if (null == current_locations || null == current_longitude || null == current_latitude || query_changed)
requestCoordinates(query_changed);
else
setLocationsList();
// set up gui
setContentView(GeneratedResources.getLayout("nearby_places"));
getListView().setTextFilterEnabled(true);
getListView().setOnItemClickListener(this);
}
}
else
{
setContentView(GeneratedResources.getLayout("nearby_places"));
TextView empty_list = (TextView)findViewById(android.R.id.empty);
empty_list.setText("No services connected!");
}
}
/**
* onStop
*/
public void onStop()
{
super.onStop();
cleanUp();
}
/**
* onPause
*/
public void onPause()
{
super.onPause();
cleanUp();
}
/**
* onDestroy
*/
public void onDestroy()
{
super.onDestroy();
cleanUp();
}
/**
* cleanUp
*/
private void cleanUp()
{
// kill any running timeout threads
killTimeouts();
// stop the gps when pausing the activity
stopLocationListener();
// cancel location thread
if (null != locations_retriever)
{
locations_retriever.cancel(true);
locations_retriever = null;
}
if (null != display_progress_dialog)
{
if (null != handler)
handler.removeCallbacks(display_progress_dialog);
display_progress_dialog = null;
}
if (null != handler)
handler = null;
// cancel any dialogs showing
cancelProgressDialog();
}
/**
* killTimeouts
*/
private void killTimeouts()
{
// cancel and delete gps timeout monitor thread if it exists
if (null != gps_timeout_monitor)
{
gps_timeout_monitor.cancel(true);
gps_timeout_monitor = null;
}
// cancel and delete network timeout monitor thread if it exists
if (null != network_timeout_monitor)
{
network_timeout_monitor.cancel(true);
network_timeout_monitor = null;
}
}
/**
* requestCoordinates
*/
public void requestCoordinates(boolean query_changed)
{
// cancel acquiring location dialog
cancelProgressDialog();
// check that gps location is available
if (((LocationManager)this.getSystemService(Context.LOCATION_SERVICE)).isProviderEnabled(
LocationManager.GPS_PROVIDER))
{
// start timeout thread for gps coordinates
gps_timeout_monitor = new GPSTimeoutMonitor(this);
gps_timeout_monitor.execute();
// Register the listener with the Location Manager to receive location updates
stopLocationListener();
startGPSLocationListener();
// display acquiring gps progress dialog
if (current_query == null || !query_changed)
{
startProgressDialog("Acquiring GPS Location...");
}
else
{
// delay displaying the progress dialog by 1 second to allow for a soft
// keyboard (from a possible search) to be hidden first. if the progress
// dialog shows up before the keyboard is hidden, the dialog will be killed
// upon hiding the keyboard.
display_progress_dialog = new Runnable()
{
public void run()
{
startProgressDialog("Acquiring GPS Location...");
}
};
handler.postDelayed(display_progress_dialog, 700);
}
}
else
{
GPSTimeout();
}
}
/**
* GPSTimeout
*/
public void GPSTimeout()
{
Log.i(TAG, "GPSTimeout");
// cancel acquiring location dialog
cancelProgressDialog();
// cancel updates for gps
stopLocationListener();
// get network info
NetworkInfo mobile_network_info =
((ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE)).getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
NetworkInfo wifi_network_info =
((ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE)).getNetworkInfo(ConnectivityManager.TYPE_WIFI);
// check that network location is available
if (((LocationManager)this.getSystemService(Context.LOCATION_SERVICE)).isProviderEnabled(
LocationManager.NETWORK_PROVIDER) &&
(mobile_network_info.isConnectedOrConnecting() || wifi_network_info.isConnectedOrConnecting()))
{
// start network location timeout
network_timeout_monitor = new NetworkTimeoutMonitor(this);
network_timeout_monitor.execute();
// Register the listener with the Location Manager to receive location updates
startNetworkLocationListener();
// show acquiring network dialog
startProgressDialog("Acquiring Network Location...");
}
else
{
NetworkTimeout();
}
}
/**
* NetworkTimeout
*/
public void NetworkTimeout()
{
Log.i(TAG, "NetworkTimeout");
// cancel acquiring location dialog
cancelProgressDialog();
// remove location updates
stopLocationListener();
// show error
Toast.makeText(this, "GPS and Network Connection Unavailable.", Toast.LENGTH_SHORT).show();
}
/**
* onLocationChanged
*
* @param location
*/
public void onLocationChanged(Location location)
{
// kill any running timeout threads
killTimeouts();
// set current location in ads
Ad.setLocation(location);
// cancel further updates
((LocationManager)getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
// cancel acquiring location dialog and start retrieving locations dialog
startProgressDialog("Retrieving Locations...");
// get longitude and latitude
current_longitude = Double.toString(location.getLongitude());
current_latitude = Double.toString(location.getLatitude());
// get preferences for retrieving locations
SharedPreferences persistent_storage = PreferenceManager.getDefaultSharedPreferences(this);
Editor persistent_storage_editor = persistent_storage.edit();
// store user's current longitude and latitude
persistent_storage_editor.putString("current_longitude", current_longitude);
persistent_storage_editor.putString("current_latitude", current_latitude);
persistent_storage_editor.commit();
// create locations retriever thread
locations_retriever = new LocationsRetriever(this, this, current_query, current_longitude, current_latitude, persistent_storage);
locations_retriever.execute();
}
/**
* updateLocations
*
* @param locations
*/
public void updateLocations(List<Locale> locations)
{
Log.i(TAG, "received new location data.");
// setup list for retrieved locations
current_locations = locations;
// set locations list
setLocationsList();
// cancel loading dialog
cancelProgressDialog();
}
/**
* displayProgressDialog
*
* @param text
*/
public void startProgressDialog(String text)
{
// cancel any existing dialog
cancelProgressDialog();
// create new dialog and display
progress_dialog = new CleanableProgressDialog(this, this, "", text, true);
progress_dialog.show();
}
/**
* cancelProgressDialog
*/
public void cancelProgressDialog()
{
// check if a progress dialog exists
if (null != progress_dialog)
{
// cancel the dialog if it is showing
if (progress_dialog.isShowing())
progress_dialog.cancel();
progress_dialog = null;
}
}
/**
* setLocationsList
*/
private void setLocationsList()
{
if (1 == current_locations.size())
{
LocaleAdapter adapter = new LocaleAdapter(
this, GeneratedResources.getLayout("nearby_place_row"), current_locations);
setListAdapter(adapter);
storeLocationAndViewDetails(0);
}
else if (0 < current_locations.size())
{
LocaleAdapter adapter = new LocaleAdapter(
this, GeneratedResources.getLayout("nearby_place_row"), current_locations);
setListAdapter(adapter);
}
else
{
this.setListAdapter(null);
}
}
/**
* onCreateOptionsMenu
*
* @param menu
*/
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
boolean qr_scanner_installed = false;
try
{
getPackageManager().getPackageInfo("com.google.zxing.client.android", 0);
qr_scanner_installed = true;
}
catch (Exception e)
{
Log.d(TAG, "QR scanner not installed");
}
// if no services are connected, don't display menu options
if (Services.getInstance(this).atLeastOneConnected())
{
if (qr_scanner_installed)
inflater.inflate(GeneratedResources.getMenu("nearby_places_with_qr_scanner"), menu);
else
inflater.inflate(GeneratedResources.getMenu("nearby_places"), menu);
}
else
{
inflater.inflate(GeneratedResources.getMenu("feedback_only"), menu);
}
return true;
}
/**
* onOptionsItemSelected
*
* @param item
*/
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
boolean result = false;
int id = item.getItemId();
if (GeneratedResources.getId("refresh") == id)
{
// check if a query already exists and prompt user to clear it
if (current_query != null)
displayClearQueryAlert();
else
requestCoordinates(false);
result = true;
}
else if (GeneratedResources.getId("search") == id)
{
// this has to go through the tabbed container
getParent().onSearchRequested();
result = true;
}
else if (GeneratedResources.getId("feedback") == id)
{
startActivity(new Intent(this, Feedback.class));
result = true;
}
else if (GeneratedResources.getId("qr_code") == id)
{
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.setPackage("com.google.zxing.client.android");
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
startActivityForResult(intent, 0);
result = true;
}
else
{
// do nothing
}
return result;
}
/**
* onActivityResult
*
* @param request_code
* @param result_code
* @param intent
*/
@Override
public void onActivityResult(int request_code, int result_code, Intent intent)
{
super.onActivityResult(request_code, result_code, intent);
Log.i(TAG, "request_code = " + request_code);
Log.i(TAG, "result_code = " + result_code);
if (request_code == 0)
{
if (result_code == RESULT_OK)
{
String format = intent.getStringExtra("SCAN_RESULT_FORMAT");
Log.i(TAG, "format = " + format);
if (format.equals("QR_CODE"))
{
current_query = intent.getStringExtra("SCAN_RESULT");
requestCoordinates(true);
}
else
{
Log.i(TAG, "not a QR_CODE. should not be hit.");
}
}
else if (result_code == RESULT_CANCELED)
{
Log.i(TAG, "cancelled");
}
}
else
{
Log.i(TAG, "something strange happened");
}
}
/**
* onItemClick
*
* @param adapter_view
* @param view
* @param position
* @param arg3
*/
public void onItemClick(AdapterView<?> adapter_view, View view, int position, long arg3)
{
storeLocationAndViewDetails(position);
}
/**
* storeLocationAndViewDetails
*
* @param location_index
*/
public void storeLocationAndViewDetails(int location_index)
{
// get the settings and editor
SharedPreferences persistent_storage = PreferenceManager.getDefaultSharedPreferences(this);
if (0 != current_locations.size() && 0 <= location_index && current_locations.size() > location_index)
{
// store selected location
Locale location = current_locations.get(location_index);
location.store(persistent_storage);
// load location details activity
startActivity(new Intent(this, LocationDetails.class));
}
else
{
// weird case showing up in crash logs. refresh current state with new list
// FIXED by changing current locations to not be static
// leaving check in for sanity's sake
requestCoordinates(false);
}
}
/**
* onKeyDown
*
* @param key_code
* @param event
* @return boolean
*/
@Override
public boolean onKeyDown(int key_code, KeyEvent event)
{
// kill threads, timeouts, and location services on home button
if (event.getAction() == KeyEvent.ACTION_DOWN)
{
switch (key_code)
{
case KeyEvent.KEYCODE_HOME:
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_SEARCH:
cleanUp();
}
}
return super.onKeyDown(key_code, event);
}
/**
* onDialogInterruptedByBackButton
*/
public void onDialogInterruptedByBackButton()
{
cleanUp();
}
/**
* onDialogInterruptedBySearchButton
*/
public void onDialogInterruptedBySearchButton()
{
cleanUp();
}
/**
* displayClearQueryAlert
*/
public void displayClearQueryAlert()
{
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setMessage("Would you like to clear your current search of \"" + current_query + "\" before refreshing?");
alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
current_query = null;
requestCoordinates(false);
}
});
alert.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
requestCoordinates(false);
}
});
alert.show();
}
/**
* queriesAreTheSame
*/
private boolean queriesAreTheSame(String query1, String query2)
{
boolean result = false;
if ((null == query1 && null == query2) ||
(null != query1 && null != query2 && query1.equals(query2.trim())))
result = true;
return result;
}
/**
* startGPSLocationListener
*/
public void startGPSLocationListener()
{
((LocationManager)this.getSystemService(Context.LOCATION_SERVICE)).requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, this);
}
/**
* startNetworkLocationListener
*/
public void startNetworkLocationListener()
{
((LocationManager)this.getSystemService(Context.LOCATION_SERVICE)).requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, this);
}
/**
* stopLocationListener
*/
public void stopLocationListener()
{
((LocationManager)this.getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
}
/**
* unused interface methods of GPS
*/
public void onStatusChanged(String provider, int status, Bundle extras) { }
public void onProviderEnabled(String provider) { }
public void onProviderDisabled(String provider) { }
}