Java tutorial
/* * 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; } } }