us.socialgoodworking.mocklocation.MockLocationActivity.java Source code

Java tutorial

Introduction

Here is the source code for us.socialgoodworking.mocklocation.MockLocationActivity.java

Source

/*
 * Copyright (C) 2012 Paul Corriveau
 *
 * 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 us.socialgoodworking.mocklocation;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.DialogFragment;
import android.view.View;
import android.widget.Button;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import us.socialgoodworking.ezlocation.mock.MockLocation;
import us.socialgoodworking.mocklocation.R;
import us.socialgoodworking.utility.Logging;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.FieldNamingPolicy;

/**
 * Main activity used to set fake GPS locations returned by the GPS provider for testing purposes.
 * 
 * @author Paul Corriveau  <a href="mailto:paul@socialgoodworking.us?subject=MockLocation">paul@socialgoodworking.us</a>
 * @version 1.0.0
 *
 */
public class MockLocationActivity extends SherlockFragmentActivity
        implements OnMapClickListener, OnMarkerClickListener, SelectRouteDialog.SelectRouteDialogListener,
        RecordRouteDialog.RecordRouteDialogListener {

    static final String TAG = "MockLocation";
    static boolean SUPPORTS_JELLY_BEAN = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN;
    static boolean SUPPORTS_ICE_CREAM_SANDWICH = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;

    private static final String providerName = LocationManager.GPS_PROVIDER;
    private static final int TAP_MODE = 0;
    private static final int PLAYBACK_MODE = 1;
    private static final int RECORDING_MODE = 2;
    private static String routeDir = "routeList";
    private static String routeFile = "routeList.txt";

    boolean bSwitchButtonPlacement = !SUPPORTS_ICE_CREAM_SANDWICH;
    private int currentMode;
    private GoogleMap map = null;

    // Central park, Manhattan, NY
    private LatLng defaultLatLng = new LatLng(40.76793169992044, -73.98180484771729);
    private LatLng currentLatLng = defaultLatLng;
    private LatLng previousLatLng = defaultLatLng;
    private LatLng startingRecordPoint;
    private Marker marker;

    private MockLocation mockLocation;
    private Button btnStartStop;

    // Has either a playback or recording started?
    private boolean bStarted = false;
    private Handler handler;
    private long updateInterval = 5000; // every 5 seconds...

    private MenuItem menuItemPlayback;
    private MenuItem menuItemRecord;

    private Routes routeList;
    private String currentRoute;
    private int currentRouteLocationIndex;

    private boolean mockPermissionEnabled = true;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Logging.debug(TAG, TAG, "onCreate");

        getSupportActionBar().setDisplayShowTitleEnabled(true);
        getSupportActionBar().setDisplayUseLogoEnabled(true);
        Drawable d = getResources().getDrawable(R.drawable.socialgoodworking_ab_back1);
        getSupportActionBar().setBackgroundDrawable(d);

        btnStartStop = (Button) findViewById(R.id.btnStartStop);
        btnStartStop.setVisibility(View.GONE);

        btnStartStop.setOnClickListener(clickListener);
        btnStartStop.setTextColor(getResources().getColorStateList(R.color.button_colors));

        currentRouteLocationIndex = 0;

        if (savedInstanceState != null) {
            Logging.debug(TAG, "onCreate", "savedInstanceState != null");
            double lat;
            double lng;

            currentMode = savedInstanceState.getInt("mode", TAP_MODE);
            bStarted = savedInstanceState.getBoolean("started", false);

            lat = savedInstanceState.getDouble("currentLat", defaultLatLng.latitude);
            lng = savedInstanceState.getDouble("currentLng", defaultLatLng.longitude);
            currentLatLng = new LatLng(lat, lng);

            lat = savedInstanceState.getDouble("previousLat", defaultLatLng.latitude);
            lng = savedInstanceState.getDouble("previousLng", defaultLatLng.longitude);
            previousLatLng = new LatLng(lat, lng);

            currentRoute = savedInstanceState.getString("currentRoute");
            currentRouteLocationIndex = savedInstanceState.getInt("index", 0);
        } else {
            Logging.debug(TAG, "onCreate", "savedInstanceState =!= null");
            currentMode = TAP_MODE;
            currentRoute = "";
        }

        try {
            mockLocation = new MockLocation.MockLocationBuilder(getApplicationContext(), providerName).build();
            mockLocation.removeProvider();
            mockLocation.setUpMockProvider();
            Logging.debug(TAG, "onCreate", "mockLocation instance created");
        }

        catch (SecurityException se) {
            Logging.debug(TAG, "onCreate", se.getMessage());
            mockPermissionEnabled = false;
            showSecurityErrorDlg();
        }

        setUpMapIfNeeded();

        handler = new Handler();
    }

    @Override
    protected void onResume() {
        super.onResume();
        Logging.debug(TAG, TAG, "onResume");

        readRoutes();

        // readRoutes() creates routeList.
        if (currentRoute != null && !currentRoute.isEmpty()) {
            routeList.setCurrentIndex(currentRoute, currentRouteLocationIndex);
        }

    }

    @Override
    protected void onPause() {
        super.onPause();
        Logging.debug(TAG, TAG, "onPause");
        if (currentRoute != null && !currentRoute.isEmpty() && currentMode == RECORDING_MODE) {
            saveRoutes();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        Logging.debug(TAG, TAG, "onStop");
    }

    // Save the activity state in case there's an orientation change...
    @Override
    protected void onSaveInstanceState(Bundle saveState) {
        saveState.putInt("mode", currentMode);
        saveState.putDouble("currentLat", currentLatLng.latitude);
        saveState.putDouble("currentLng", currentLatLng.longitude);
        saveState.putDouble("previousLat", currentLatLng.latitude);
        saveState.putDouble("previousLng", currentLatLng.longitude);
        saveState.putString("currentRoute", currentRoute);
        saveState.putBoolean("started", bStarted);

        if (currentRoute != null && !currentRoute.isEmpty()) {
            saveState.putInt("index", routeList.getCurrentIndex(currentRoute));
        }

        super.onSaveInstanceState(saveState);
    }

    @Override
    protected void onDestroy() {
        Logging.debug(TAG, "onDestroy", "removing mock provider");
        mockLocation.removeProvider();

        switch (currentMode) {
        case TAP_MODE:
            handler.removeCallbacks(tapTask);
            break;

        case PLAYBACK_MODE:
            handler.removeCallbacks(playbackTask);
            if (bStarted) {
                // A bit of a kludge just to save the index of the next position when we're in the middle of a playback
                // so we can continue from where we left off...
                saveRoutes();
            }

            break;

        //         case RECORDING_MODE:
        //            if (currentRoute != null && !currentRoute.isEmpty()) {
        //               saveRoutes();
        //            }
        //            
        //            break;
        }

        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        Logging.debug(TAG, TAG, "onCreateOptionsMenu");
        getSupportMenuInflater().inflate(R.menu.menu_activity_main, menu);
        menuItemPlayback = menu.findItem(R.id.tap_or_playback);
        menuItemRecord = menu.findItem(R.id.record);

        // Restore the activity view here because we need access to the menu items in the action bar...
        restoreActivityView(currentMode);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Intent i;

        switch (item.getItemId()) {
        // Toggle for 'playback' mode...
        case R.id.tap_or_playback:
            onTapOrPlayback();
            break;

        case R.id.record:
            onRecord();
            break;

        case R.id.about:
            i = new Intent(this, AboutActivity.class);
            Logging.debug(TAG, TAG, "onOptionsItemSelected starting About...");
            startActivity(i);
            break;

        case R.id.policy:
            i = new Intent(this, PolicyActivity.class);
            Logging.debug(TAG, TAG, "onOptionsItemSelected starting About...");
            startActivity(i);
            break;

        //          case R.id.legal:
        //               String LicenseInfo = GooglePlayServicesUtil.getOpenSourceSoftwareLicenseInfo(getApplicationContext());
        //               AlertDialog.Builder LicenseDialog = new AlertDialog.Builder(this);
        //               LicenseDialog.setTitle("Legal Notices");
        //               LicenseDialog.setMessage(LicenseInfo);
        //               LicenseDialog.show();
        //               break;

        case R.id.contact_us:
            try {
                final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
                emailIntent.setType("plain/text");
                emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
                        new String[] { "paul@socialgoodworking.us" });
                emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "MockLocation Comment");
                emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,
                        "We want your input! Let us know what you think...");
                startActivity(Intent.createChooser(emailIntent, "Contact us via:"));
            } catch (Exception ex) {
                Logging.debug(TAG, "onOptionsItemSelected", ex.getMessage());
            }
            break;
        }

        return true;
    }

    private Runnable playbackTask = new Runnable() {
        public void run() {
            previousLatLng = currentLatLng;
            currentLatLng = routeList.getNext(currentRoute);

            float speed = mockLocation.getSpeed(previousLatLng.latitude, previousLatLng.longitude,
                    currentLatLng.latitude, currentLatLng.longitude, updateInterval);
            float bearing = mockLocation.getBearing(previousLatLng.latitude, previousLatLng.longitude,
                    currentLatLng.latitude, currentLatLng.longitude);

            //Logging.debug(TAG, "playbackTask: ", "speed = " + Float.toString(speed) + ", bearing = " + Double.toString(bearing) + ", update interval = " + updateInterval);

            mockLocation.setMockLocation(currentLatLng.latitude, currentLatLng.longitude, 0, speed, bearing);
            marker.setPosition(currentLatLng);
            map.moveCamera(CameraUpdateFactory.newLatLng(currentLatLng));
            handler.postDelayed(this, updateInterval);
        }
    };

    private Runnable tapTask = new Runnable() {
        public void run() {
            //Logging.debug(TAG, "tapTask: ", Double.toString(currentLatLng.latitude) + ", " +  Double.toString(currentLatLng.longitude));
            mockLocation.setMockLocation(currentLatLng.latitude, currentLatLng.longitude, 0, 0, 0);
            handler.postDelayed(this, 1000);
        }
    };

    private View.OnClickListener clickListener = new View.OnClickListener() {
        public void onClick(View v) {

            if (v.getId() == R.id.btnStartStop) {
                if (currentMode == PLAYBACK_MODE && mockPermissionEnabled) {
                    if (!bStarted) {
                        bStarted = true;
                        btnStartStop.setText("Stop Playback");
                        menuItemPlayback.setIcon(R.drawable.ic_action_play_on);
                        updateInterval = routeList.getUpdateInterval(currentRoute);
                        Logging.debug(TAG, "onClick", "PLAYBACK_MODE started");
                        handler.postDelayed(playbackTask, 0);
                    } else {
                        bStarted = false;
                        btnStartStop.setText("Start Playback");
                        handler.removeCallbacks(playbackTask);
                        menuItemPlayback.setIcon(R.drawable.ic_action_play);
                        marker.setPosition(currentLatLng);
                        map.animateCamera(CameraUpdateFactory.newLatLng(currentLatLng));
                        Logging.debug(TAG, "onClick", "PLAYBACK_MODE stopped");
                    }
                } else if (currentMode == RECORDING_MODE) {
                    if (bStarted) {
                        bStarted = false;
                        // Save the route to file???
                        btnStartStop.setText("Start Recording");
                        menuItemRecord.setIcon(R.drawable.ic_action_record);
                        marker.setPosition(startingRecordPoint);
                        map.animateCamera(CameraUpdateFactory.newLatLng(startingRecordPoint));
                    } else {
                        bStarted = true;
                        btnStartStop.setText("Stop Recording");
                        menuItemRecord.setIcon(R.drawable.ic_action_record_on);

                        updateInterval = routeList.getUpdateInterval(currentRoute);

                        // Add the current point as the start ????
                        routeList.addPoint(currentRoute, marker.getPosition());

                        // Save the start point 
                        startingRecordPoint = marker.getPosition();
                    }
                }
            }
        }
    };

    public void onMapClick(LatLng point) {
        Logging.debug(TAG, "onMapClick", "routeList = " + (routeList == null ? "null" : "not null"));

        switch (currentMode) {
        case TAP_MODE:
            Logging.debug(TAG, "onMapClick:",
                    Double.toString(point.latitude) + ", " + Double.toString(point.longitude));
            map.moveCamera(CameraUpdateFactory.newLatLng(point));
            marker.setTitle("MockLocation");
            marker.setSnippet(Double.toString(point.latitude) + ", " + Double.toString(point.longitude));
            marker.setPosition(point);
            if (mockPermissionEnabled) {
                mockLocation.setMockLocation(point.latitude, point.longitude);
            }
            currentLatLng = point;
            break;

        case PLAYBACK_MODE:
            // Nothing to do...
            break;

        case RECORDING_MODE:
            if (bStarted) {
                // Add the point to the route...
                routeList.addPoint(currentRoute, point);
                map.moveCamera(CameraUpdateFactory.newLatLng(point));
                marker.setTitle("MockLocation");
                marker.setSnippet(Double.toString(point.latitude) + ", " + Double.toString(point.longitude));
                marker.setPosition(point);
            } else {
                map.moveCamera(CameraUpdateFactory.newLatLng(point));
                marker.setTitle("MockLocation");
                marker.setSnippet(Double.toString(point.latitude) + ", " + Double.toString(point.longitude));
                marker.setPosition(point);
            }

            break;
        }
    }

    public boolean onMarkerClick(Marker marker) {
        if (marker.isInfoWindowShown())
            marker.hideInfoWindow();
        else
            marker.showInfoWindow();

        return false;
    }

    private void setUpMapIfNeeded() {
        if (map == null) {
            Logging.debug(TAG, "setUpMapIfNeeded", "map == null");
            map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.locationmap)).getMap();
            if (map != null) {
                map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                map.setOnMapClickListener(this);
            } else {
                Logging.debug(TAG, "setUpMapIfNeeded", "map == null; getSupportFragmentManager returned null");
                return;
            }
        }

        if (marker == null) {
            marker = map.addMarker(new MarkerOptions().position(defaultLatLng).title("Greenwich observatory")
                    .snippet("Airy transit circle")
                    .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)));
        }

        //if ( savedInstanceState != null ) {
        //   double lat = savedInstanceState.getDouble("lat");
        //   double lon = savedInstanceState.getDouble("lon");

        // Apparently affects click sensitivity around marker...
        if (marker.isDraggable()) {
            Logging.debug(TAG, "setUpMapIfNeeded", "marker.setDraggable(false)");
            marker.setDraggable(false);
        }

        marker.setTitle("MockLocation");
        marker.setSnippet(
                Double.toString(currentLatLng.latitude) + ", " + Double.toString(currentLatLng.longitude));
        marker.setPosition(currentLatLng);
        map.moveCamera(CameraUpdateFactory.newLatLng(currentLatLng));
        //}
    }

    private void onTapOrPlayback() {

        switch (currentMode) {
        case TAP_MODE:
            if (!mockPermissionEnabled) {
                return;
            }

            if (routeList == null || routeList.routes.isEmpty()) {
                AlertDialog.Builder dialog = new AlertDialog.Builder(this);
                dialog.setTitle("Select a Route");
                dialog.setMessage("There are no saved routes.");
                dialog.setCancelable(false).setPositiveButton(android.R.string.ok, null);
                AlertDialog alertDialog = dialog.create();
                alertDialog.show();
            } else {
                menuItemRecord.setVisible(false);
                btnStartStop.setText("Start Playback");
                currentMode = PLAYBACK_MODE;
                handler.removeCallbacks(tapTask);
                showPlaybackRouteSelectDlg();
            }

            break;

        case PLAYBACK_MODE:
            currentMode = TAP_MODE;
            handler.removeCallbacks(playbackTask);
            btnStartStop.setText("Start");
            //btnStartStop.setEnabled(false);
            btnStartStop.setVisibility(View.GONE);
            bStarted = false;
            menuItemRecord.setVisible(true);
            menuItemPlayback.setIcon(R.drawable.ic_action_play);
            marker.setPosition(currentLatLng);
            map.animateCamera(CameraUpdateFactory.newLatLng(currentLatLng));
            handler.postDelayed(tapTask, 0);
            return;
        }
    }

    private void onRecord() {

        switch (currentMode) {
        case TAP_MODE:
            currentMode = RECORDING_MODE;
            menuItemPlayback.setVisible(false);
            handler.removeCallbacks(tapTask);
            showRecordRouteDlg();
            break;

        case RECORDING_MODE:
            currentMode = TAP_MODE;
            menuItemRecord.setIcon(R.drawable.ic_action_record);
            menuItemPlayback.setVisible(true);
            bStarted = false;
            btnStartStop.setVisibility(View.GONE);

            if (routeList != null && routeList.routes.size() > 0 && !routeList.getRoute(currentRoute).isEmpty()) {
                saveRoutes();
            } else {
                Logging.debug(TAG, "onRecord",
                        "No route points in " + currentRoute + ". Removing route name from list.");
                routeList.deleteRoute(currentRoute);
            }

            handler.postDelayed(tapTask, 0);

            break;
        }
    }

    private void showRecordRouteDlg() {
        DialogFragment f = new RecordRouteDialog();
        f.show(getSupportFragmentManager(), "record");
    }

    private void showSecurityErrorDlg() {
        DialogFragment f = new SecurityErrorDialog();
        f.show(getSupportFragmentManager(), "error");
    }

    private void showPlaybackRouteSelectDlg() {
        DialogFragment f = new SelectRouteDialog();
        Bundle b = new Bundle();
        b.putStringArray("routes", routeList.getRoutes());
        f.setArguments(b);
        f.show(getSupportFragmentManager(), "routes");
    }

    public void onSelectRouteDialogPositiveClick(DialogInterface dialog, String selectedRoute) {
        if (selectedRoute == null || selectedRoute.isEmpty()) {
            dialog.dismiss();
            return;
        }

        currentRoute = selectedRoute;
        dialog.dismiss();
        Logging.debug(TAG, "onDialogPositiveClick", " currentRoute = " + currentRoute);
        btnStartStop.setVisibility(View.VISIBLE);
    }

    public void onSelectRouteDialogNegativeClick(DialogInterface dialog) {
        dialog.dismiss();
        Logging.debug(TAG, "onDialogNegativeClick", " Cancel");
        currentRoute = "";
        currentMode = TAP_MODE;
        handler.removeCallbacks(playbackTask);
        btnStartStop.setText("Start");
        btnStartStop.setVisibility(View.GONE);
        bStarted = false;
        menuItemRecord.setVisible(true);
        menuItemPlayback.setIcon(R.drawable.ic_action_play);
        marker.setPosition(currentLatLng);
        map.animateCamera(CameraUpdateFactory.newLatLng(currentLatLng));
    }

    public void onRecordRouteDialogPositiveClick(DialogInterface dialog, String routeName, int interval) {
        currentRoute = routeName;
        dialog.dismiss();
        routeList.setUpdateInterval(currentRoute, interval);
        btnStartStop.setText("Start Recording");
        btnStartStop.setVisibility(View.VISIBLE);
    }

    public void onRecordRouteDialogNegativeClick(DialogInterface dialog) {
        dialog.dismiss();
        menuItemRecord.setIcon(R.drawable.ic_action_record);
        menuItemPlayback.setVisible(true);
        currentMode = TAP_MODE;
        bStarted = false;
        btnStartStop.setVisibility(View.GONE);
    }

    public void saveRoutes() {
        final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .create();
        String json = gson.toJson(routeList);
        Logging.debug(TAG, "saveRoutes", json);
        IO.writeFile(getApplicationContext(), routeDir, routeFile, json, false);
    }

    public void readRoutes() {
        if (routeList == null)
            routeList = new Routes();

        String r = IO.readFile(getApplicationContext(), routeDir, routeFile);
        if (r.length() > 0) {
            Logging.debug(TAG, "readRoutes", r);
            final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                    .create();
            routeList = gson.fromJson(r, Routes.class);
        }
    }

    private void restoreActivityView(int mode) {
        Logging.debug(TAG, TAG, "restoreActivityView");
        switch (mode) {
        case TAP_MODE:
            if (mockPermissionEnabled) {
                Logging.debug(TAG, "restoreActivityView", "TAP_MODE start");
                handler.postDelayed(tapTask, 1000);
            }
            break;

        case PLAYBACK_MODE:
            menuItemRecord.setVisible(false);

            if (bStarted == false) {
                btnStartStop.setText("Start Playback");
                menuItemPlayback.setIcon(R.drawable.ic_action_play);
                btnStartStop.setVisibility(View.VISIBLE);
                marker.setPosition(currentLatLng);
                map.animateCamera(CameraUpdateFactory.newLatLng(currentLatLng));
            } else {
                btnStartStop.setText("Stop Playback");
                btnStartStop.setVisibility(View.VISIBLE);
                menuItemPlayback.setIcon(R.drawable.ic_action_play_on);
                handler.postDelayed(playbackTask, 1000); // delay a bit to let view get finished drawing?
            }
            break;

        case RECORDING_MODE:
            menuItemPlayback.setVisible(false);

            if (bStarted == false) {
                btnStartStop.setText("Start Recording");
                btnStartStop.setVisibility(View.VISIBLE);
                menuItemRecord.setIcon(R.drawable.ic_action_record);
            } else {
                btnStartStop.setText("Stop Recording");
                btnStartStop.setVisibility(View.VISIBLE);
                menuItemRecord.setIcon(R.drawable.ic_action_record_on);
                updateInterval = routeList.getUpdateInterval(currentRoute);
            }
            break;
        }
    }
}