Java tutorial
/* * Copyright (C) TernUp Research Labs * * This file is part of Caddisfly * * Caddisfly is free software: you can redistribute it and modify it under the terms of * the GNU Affero General Public License (AGPL) as published by the Free Software Foundation, * either version 3 of the License or any later version. * * Caddisfly 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 Affero General Public License included below for more details. * * The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>. */ package com.ternup.caddisfly.fragment; 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.ternup.caddisfly.R; import com.ternup.caddisfly.app.Globals; import com.ternup.caddisfly.app.MainApp; import com.ternup.caddisfly.util.LocationUtils; import com.ternup.caddisfly.util.NetworkUtils; import com.ternup.caddisfly.util.PreferencesUtils; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; import android.content.DialogInterface; 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.location.LocationManager; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import java.io.IOException; import java.util.List; import java.util.Locale; public class LocationFragment extends BaseFragment implements LocationListener, GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener { MainApp mainApp; // Handle to SharedPreferences for this app SharedPreferences mPrefs; /* * Note if updates have been turned on. Starts out as "false"; is set to "true" in the * method handleRequestSuccess of LocationUpdateReceiver. * */ boolean mUpdatesRequested = false; Button mLocationButton; // 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 LinearLayout mActivityIndicator; public LocationFragment() { } /** * Returns a new instance of this fragment for the given section * number. */ public static LocationFragment newInstance() { return new LocationFragment(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View rootView = inflater.inflate(R.layout.fragment_location, container, false); mainApp = (MainApp) getActivity().getApplicationContext(); mLocationButton = (Button) rootView.findViewById(R.id.locationButton); mLatLng = (TextView) rootView.findViewById(R.id.lat_lng); mActivityIndicator = (LinearLayout) rootView.findViewById(R.id.progressBar); mLocationButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //getAddress(); startUpdates(rootView); } }); // 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; /* * Create a new location client, using the enclosing class to * handle callbacks. */ mLocationClient = new LocationClient(getActivity(), this, this); return rootView; } public void stopUpdates() { Log.d(Globals.DEBUG_TAG, "stop location updates"); // If the client is connected if (mLocationClient.isConnected()) { stopPeriodicUpdates(); } // After disconnect() is called, the client is considered "dead". mLocationClient.disconnect(); // Turn off the progress bar mActivityIndicator.setVisibility(View.GONE); mLocationButton.setVisibility(View.VISIBLE); } /* * Called when the Activity is no longer visible at all. * Stop updates and disconnect. */ @Override public void onStop() { 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 PreferencesUtils.setBoolean(getActivity(), R.string.locationUpdatesRequested, mUpdatesRequested); 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 (PreferencesUtils.contains(getActivity(), R.string.locationUpdatesRequested)) { mUpdatesRequested = PreferencesUtils.getBoolean(getActivity(), R.string.locationUpdatesRequested, false); // Otherwise, turn off location updates until requested } else { PreferencesUtils.setBoolean(getActivity(), R.string.locationUpdatesRequested, false); } // if location coordinates already available then get address if (!mUpdatesRequested) { if (mainApp.address != null && (mainApp.address.getFeatureName() == null || mainApp.address.getFeatureName().isEmpty())) { if (mainApp.location != null && mainApp.location.getLatitude() > 0) { getAddress(mainApp.location); } } } } /* * 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 public 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(Globals.DEBUG_TAG, getString(R.string.resolved)); break; // If any other result was returned by Google Play services default: // Log the result Log.d(Globals.DEBUG_TAG, getString(R.string.no_resolution)); break; } // If any other request code was received default: // Report that this Activity received an unknown requestCode Log.d(Globals.DEBUG_TAG, 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(getActivity()); // If Google Play services is available if (ConnectionResult.SUCCESS == resultCode) { // In debug mode, log the status Log.d(Globals.DEBUG_TAG, 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, getActivity(), 0); if (dialog != null) { ErrorDialogFragment errorFragment = new ErrorDialogFragment(); errorFragment.setDialog(dialog); errorFragment.show(getFragmentManager(), Globals.DEBUG_TAG); } return false; } } /** * Invoked by the "Get Address" button. * Get the address of the current location, using reverse geo coding. This only works if * a geo coding service is available. */ public void getAddress(Location location) { if (NetworkUtils.checkInternetConnection(getActivity())) { // 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(getActivity(), R.string.no_geo_coder_available, Toast.LENGTH_LONG).show(); return; } if (servicesConnected()) { // Get the current location //Location currentLocation = mLocationClient.getLastLocation(); // Start the background task (new LocationFragment.GetAddressTask(getActivity())).execute(location); } } } /** * 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) { // Get Location Manager and check for GPS & Network location services LocationManager lm = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE); if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER) || !lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { // Build the alert dialog AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle("Location Services Not Active"); builder.setMessage("Please enable Location Services and GPS"); builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogInterface, int i) { // Show location settings when the user acknowledges the alert dialog Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivity(intent); } }); Dialog alertDialog = builder.create(); alertDialog.setCanceledOnTouchOutside(false); alertDialog.show(); } else { mUpdatesRequested = true; if (servicesConnected()) { // Turn the indefinite activity indicator on mActivityIndicator.setVisibility(View.VISIBLE); mLocationButton.setVisibility(View.GONE); 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); //mLocationClient.setMockMode(true); 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(getActivity(), 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(getActivity(), location)); stopUpdates(mLocationButton); mainApp.location = location; getAddress(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() { Log.d(Globals.DEBUG_TAG, "Stopped periodic Updates"); mLocationClient.removeLocationUpdates(this); //mConnectionState.setText(R.string.location_updates_stopped); } private void sendMessage() { Intent intent = new Intent("locationUpdated"); LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent); } /** * 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, getActivity(), 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(getFragmentManager(), Globals.DEBUG_TAG); } } /** * 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; } } /** * 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. final 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 geo coding 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; // 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(Globals.DEBUG_TAG, 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(Globals.DEBUG_TAG, errorString); exception2.printStackTrace(); // return errorString; } // If the reverse geocode returned an address if (addresses != null && addresses.size() > 0) { mainApp.address = addresses.get(0); Bundle bundle = mainApp.address.getExtras(); if (bundle == null) { bundle = new Bundle(); } bundle.putDouble("lat", location.getLatitude()); bundle.putDouble("lon", location.getLongitude()); mainApp.address.setExtras(bundle); sendMessage(); return null; } 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 mActivityIndicator.setVisibility(View.GONE); mLocationButton.setVisibility(View.VISIBLE); // Set the address in the UI //mAddress.setText(address); listener.onPageComplete(); } } }