com.jeremyhaberman.playgrounds.Playgrounds.java Source code

Java tutorial

Introduction

Here is the source code for com.jeremyhaberman.playgrounds.Playgrounds.java

Source

/*
 * Copyright 2011 Jeremy Haberman
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package com.jeremyhaberman.playgrounds;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.ListPreference;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

/**
 * Playgrounds is the entry and primary Activity. It displays the map, the
 * user's current location and all playgrounds
 * 
 * @author jeremyhaberman
 * 
 */
public class Playgrounds extends MapActivity {

    private static Context context;
    private MapView map;
    private MapController controller;
    private MyLocationOverlay overlay;
    protected GeoPoint myLocation = null;
    private List<Overlay> mapOverlays;
    private PlaygroundsLayer playgroundsLayer;
    private ProgressDialog progressDialog;
    private AlertDialog alertDialog;
    private static final String LOADING_NEARBY_PLAYGROUNDS = "Loading nearby playgrounds";
    protected static final int ERROR_LOADING_PLAYGROUNDS = 1;
    protected static final CharSequence ERROR_LOADING_PLAYGROUNDS_STRING = "Error loading playgrounds.";
    protected static final int ERROR_FINDING_LOCATION = 0;
    DownloadPlaygroundsTask downloadPlaygroundsTask;
    Timer timer;
    TimerTask getMyLocationTask;

    /**
     * Instantiate the playgroundDAO for retrieving playground data and display
     * the map. The current location and playground data is loaded by
     * onResume().
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.map);
        initMapView();
    }

    /**
     * Display the map, allowing zoom controls
     */
    private void initMapView() {
        map = (MapView) findViewById(R.id.map);
        controller = map.getController();
        map.setBuiltInZoomControls(true);
    }

    /**
     * During onResume(), show the current location and nearby playgrounds
     */
    @Override
    protected void onResume() {
        super.onResume();
        showMyLocationAndPlaygrounds();
    }

    /**
     * Show the current location on the map, zoom in to level 13 and then
     * display nearby playgrounds
     */
    private void showMyLocationAndPlaygrounds() {
        if (overlay == null) {
            overlay = new MyLocationOverlay(this, map);
        }

        overlay.enableMyLocation();

        // Once the location is known, zoom in to it
        overlay.runOnFirstFix(new Runnable() {
            @Override
            public void run() {
                controller.setZoom(getZoom());
                myLocation = overlay.getMyLocation();
                controller.animateTo(myLocation);
                mHandler.post(showPlaygrounds);
            }
        });

        map.getOverlays().add(overlay);
    }

    /**
     * Gets the zoom level for the map based on the range preference
     * 
     * @return
     */
    protected int getZoom() {
        int range = getRange();
        switch (range) {
        case 1:
            return 15;
        case 2:
            return 14;
        case 5:
            return 13;
        case 10:
            return 12;
        default:
            return 13;
        }
    }

    /**
     * Gets the range preference
     * @return
     */
    private int getRange() {
        return Integer.parseInt(Settings.getRange(getApplicationContext()));
    }

    final Runnable showPlaygrounds = new Runnable() {
        public void run() {
            showNearbyPlaygrounds();
        }
    };

    /**
     * Handler for callbacks to the UI thread
     */
    final Handler mHandler = new Handler() {
        public void handleMessage(Message message) {

            AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());

            switch (message.what) {
            case ERROR_FINDING_LOCATION:
                progressDialog.dismiss();
                builder.setMessage(getString(R.string.unable_to_get_current_location));
                break;
            case ERROR_LOADING_PLAYGROUNDS:
                progressDialog.dismiss();
                builder.setMessage(ERROR_LOADING_PLAYGROUNDS_STRING);
                break;
            }
            progressDialog.dismiss();
        }
    };
    private int range;

    /**
     * Get playground data via background task and add it to the map
     */
    private void showNearbyPlaygrounds() {
        downloadPlaygroundsTask = new DownloadPlaygroundsTask();
        downloadPlaygroundsTask.execute(myLocation);
    }

    /**
     * Add playground items to the map. If <code>playgrounds</code> is
     * <code>null</code>, send a message to the UI handler to display the error
     * and allow the user to try again.
     * 
     * @param playgrounds
     */
    public void addPlaygroundsToMap(Collection<Playground> playgrounds) {

        if (playgrounds == null) {
            mHandler.sendEmptyMessage(ERROR_LOADING_PLAYGROUNDS);
            return;
        }

        mapOverlays = map.getOverlays();
        Drawable drawable = this.getResources().getDrawable(R.drawable.pin2);
        playgroundsLayer = new PlaygroundsLayer(this, drawable);

        Collection<PlaygroundItem> playgroundItems = PlaygroundItemCreator.createItems(playgrounds);

        for (PlaygroundItem item : playgroundItems) {
            playgroundsLayer.addOverlayItem(item);
        }

        mapOverlays.add(playgroundsLayer);
        map.postInvalidate();
    }

    @Override
    protected void onPause() {
        if (progressDialog != null && progressDialog.isShowing()) {
            progressDialog.cancel();
        }
        if (alertDialog != null && alertDialog.isShowing()) {
            alertDialog.cancel();
        }
        removePlaygroundsOnMap();
        overlay.disableMyLocation();
        overlay = null;
        downloadPlaygroundsTask.cancel(true);
        super.onPause();
    }

    /**
     * Clear the playgrounds from the map
     */
    private void removePlaygroundsOnMap() {
        mapOverlays = map.getOverlays();
        if (playgroundsLayer != null) {
            mapOverlays.remove(playgroundsLayer);
        }
    }

    /**
     * Display a "Loading nearby playgrounds..." dialog while the background
     * task is retrieving playground data
     */
    private void displayLoadingPlaygroundsProgressDialog() {
        progressDialog = ProgressDialog.show(Playgrounds.this, "", LOADING_NEARBY_PLAYGROUNDS, true);
        progressDialog.show();
    }

    /**
     * Display an error about failing to load playground data. Called by
     * displayErrorTask
     */
    protected void displayLocationError() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        // builder.setTitle("ERROR");

        builder.setMessage(getString(R.string.unable_to_get_current_location));
        builder.setCancelable(false);
        builder.setPositiveButton("Try Again", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                alertDialog.dismiss();
                mHandler.post(showPlaygrounds);
            }
        });
        builder.setNegativeButton("Close", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                alertDialog.dismiss();
            }
        });
        alertDialog = builder.create();
        alertDialog.show();
        mHandler.sendEmptyMessage(0);
    }

    /**
     * Displays an error if the playground data was not successfully loaded by
     * the thread in showPlaygrounds().
     */
    final Runnable displayPlaygroundLoadErrorTask = new Runnable() {
        public void run() {
            displayPlaygroundLoadError();
        }
    };

    /**
     * Display an error about failing to load playground data. Called by
     * displayErrorTask
     */
    protected void displayPlaygroundLoadError() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        // builder.setTitle("ERROR");

        builder.setMessage("Error loading playgrounds");
        builder.setCancelable(false);
        builder.setPositiveButton("Try Again", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                alertDialog.dismiss();
                mHandler.post(showPlaygrounds);
            }
        });
        builder.setNegativeButton("Close", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                alertDialog.dismiss();
            }
        });
        alertDialog = builder.create();
        alertDialog.show();
        mHandler.sendEmptyMessage(0);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        switch (item.getItemId()) {
        case R.id.add_current_location_button:
            startActivity(new Intent(this, AddCurrentLocation.class));
            return true;
        case R.id.add_by_address_button:
            startActivity(new Intent(this, AddByAddress.class));
            return true;
        case R.id.settings:
            startActivity(new Intent(this, Settings.class));
            return true;
        case R.id.about:
            startActivity(new Intent(this, About.class));
        }
        return false;
    }

    protected MapView getMap() {
        return map;
    }

    protected Overlay getPlaygroundOverlay() {
        return playgroundsLayer;
    }

    private class DownloadPlaygroundsTask extends AsyncTask<GeoPoint, Void, Collection<Playground>> {

        private static final String TAG = "DownloadPlaygroundsTask";
        private static final String LATITUDE_PARAM = "latitude";
        private static final String LONGITUDE_PARAM = "longitude";
        private static final String MAX_PARAM = "max";
        private static final String TYPE_PARAM = "type";
        private static final String NEARBY = "nearby";
        private static final int MAX_QUANTITY = 5;
        private static final String WITHIN = "within";
        private static final String RANGE = "range";
        private static final String RANGE_PARAM = "range";

        protected void onPreExecute() {
            displayLoadingPlaygroundsProgressDialog();
        }

        @Override
        protected Collection<Playground> doInBackground(GeoPoint... params) {
            GeoPoint location = params[0];
            Collection<Playground> playgrounds = new ArrayList<Playground>();
            HttpURLConnection httpConnection = null;
            Log.d(TAG, "getPlaygrounds()");

            try {
                // Build query
                URL url = new URL("http://swingsetweb.appspot.com/playground?" + TYPE_PARAM + "=" + RANGE + "&"
                        + LATITUDE_PARAM + "=" + location.getLatitudeE6() / 1E6 + "&" + LONGITUDE_PARAM + "="
                        + location.getLongitudeE6() / 1E6 + "&" + RANGE_PARAM + "=" + getRange());
                httpConnection = (HttpURLConnection) url.openConnection();
                httpConnection.setConnectTimeout(10000);
                httpConnection.setReadTimeout(10000);
                StringBuilder response = new StringBuilder();

                if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    // Read results from the query
                    BufferedReader input = new BufferedReader(
                            new InputStreamReader(httpConnection.getInputStream(), "UTF-8"));
                    String strLine = null;
                    while ((strLine = input.readLine()) != null) {
                        response.append(strLine);
                    }
                    input.close();

                }

                // Parse to get translated text
                JSONArray jsonPlaygrounds = new JSONArray(response.toString());
                int numOfPlaygrounds = jsonPlaygrounds.length();

                JSONObject jsonPlayground = null;

                for (int i = 0; i < numOfPlaygrounds; i++) {
                    jsonPlayground = jsonPlaygrounds.getJSONObject(i);
                    playgrounds.add(toPlayground(jsonPlayground));
                }

            } catch (Exception e) {
                Log.e(TAG, "Exception", e);
            } finally {
                if (httpConnection != null) {
                    httpConnection.disconnect();
                }
            }

            return playgrounds;
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
        }

        @Override
        protected void onPostExecute(Collection<Playground> playgrounds) {
            if (playgrounds.size() == 0) {
                mHandler.post(displayPlaygroundLoadErrorTask);
            } else {
                addPlaygroundsToMap(playgrounds);
                mHandler.sendEmptyMessage(0);
            }
        }

        private Playground toPlayground(JSONObject jsonPlayground) throws JSONException {
            String name = jsonPlayground.getString("name");
            String description = jsonPlayground.getString("description");
            int latitude = jsonPlayground.getInt("latitude");
            int longitude = jsonPlayground.getInt("longitude");

            return new Playground(name, description, latitude, longitude);
        }

    }

    @Override
    protected boolean isRouteDisplayed() {
        return false;
    }

    protected static Context getContext() {
        return context;
    }
}