Java tutorial
/** Cycle Philly, Copyright 2014 Code for Philly * * @author Lloyd Emelle <lloyd@codeforamerica.org> * @author Christopher Le Dantec <ledantec@gatech.edu> * @author Anhong Guo <guoanhong15@gmail.com> * * Updated/Modified for Philly's app deployment. Based on the * CycleTracks codebase for SFCTA and Cycle Atlanta. * * CycleTracks, Copyright 2009,2010 San Francisco County Transportation Authority * San Francisco, CA, USA * * @author Billy Charlton <billy.charlton@sfcta.org> * * This file is part of CycleTracks. * * CycleTracks 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. * * CycleTracks 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 CycleTracks. If not, see <http://www.gnu.org/licenses/>. */ package org.opensmc.mytracks.cyclesmc; import android.app.ActionBar; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.database.Cursor; import android.database.SQLException; import android.graphics.Typeface; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.support.v4.app.FragmentActivity; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.Button; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.SimpleCursorAdapter; import android.widget.TextView; import android.widget.Toast; import com.firebase.client.*; import com.firebase.geofire.GeoFire; import com.firebase.geofire.GeoLocation; import com.firebase.geofire.GeoQuery; import com.firebase.geofire.GeoQueryEventListener; import com.firebase.geofire.LocationCallback; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.maps.model.LatLng; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; public class MainInput extends ActionBarActivity { private Toolbar toolbar; private final static int MENU_USER_INFO = 0; private final static int MENU_CONTACT_US = 1; private final static int MENU_MAP = 2; private final static int MENU_LEGAL_INFO = 3; public final static int PREF_ANONID = 13; final String DEGREE = "\u00b0"; public final static String FIREBASE_REF = "https://org.opensmc.mytracks.cyclesmc.firebaseio.com"; //Firebase indegoRef; //Firebase indegoGeofireRef; private final static int CONTEXT_RETRY = 0; private final static int CONTEXT_DELETE = 1; private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000; private LocationManager locationManager = null; private LatLng mySpot = null; private ValueEventListener connectedListener; TextView weatherText; private TextView debugLocation; Typeface weatherFont; //private RecyclerView nearbyStations; //private List<IndegoStation> indegoList = Collections.emptyList(); //private DataSnapshot indegoDataList; //private RideIndegoAdapter indegoAdapter; //TODO: Add floating action button to start trip //Once trip started, add progress. Miles/time/calories/recenter button etc //Choose which methods of transport used //Final survey/add notes/etc //Trip manager button DbAdapter mDb; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case CONNECTION_FAILURE_RESOLUTION_REQUEST: switch (resultCode) { case Activity.RESULT_OK: //TODO: ...try the request again? break; } } } @Override public void onResume() { super.onResume(); // Check that Google Play services is available int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); if (ConnectionResult.SUCCESS == resultCode) { Log.d("Location Updates", "Google Play services is available."); return; // Google Play services was not available for some reason } else { // Get the error dialog from Google Play services Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(resultCode, this, CONNECTION_FAILURE_RESOLUTION_REQUEST); // If Google Play services can provide an error dialog if (errorDialog != null) { ErrorDialogFragment errorFragment = new ErrorDialogFragment(); errorFragment.setDialog(errorDialog); errorFragment.show(getSupportFragmentManager(), "Location Updates"); } } } /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Firebase.setAndroidContext(this); final Firebase ref = new Firebase("https://org.opensmc.mytracks.cyclesmc.firebaseio.com"); final Firebase phlref = new Firebase("https://phl.firebaseio.com"); // Let's handle some launcher lifecycle issues: // If we're recording or saving right now, jump to the existing activity. // (This handles user who hit BACK button while recording) setContentView(R.layout.main); weatherFont = Typeface.createFromAsset(getAssets(), "cyclesmc.ttf"); weatherText = (TextView) findViewById(R.id.weatherView); weatherText.setTypeface(weatherFont); weatherText.setText(R.string.cloudy); Intent rService = new Intent(this, RecordingService.class); ServiceConnection sc = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { } public void onServiceConnected(ComponentName name, IBinder service) { IRecordService rs = (IRecordService) service; int state = rs.getState(); if (state > RecordingService.STATE_IDLE) { if (state == RecordingService.STATE_FULL) { startActivity(new Intent(MainInput.this, SaveTrip.class)); } else { // RECORDING OR PAUSED: startActivity(new Intent(MainInput.this, RecordingActivity.class)); } MainInput.this.finish(); } else { // Idle. First run? Switch to user prefs screen if there are no prefs stored yet SharedPreferences settings = getSharedPreferences("PREFS", 0); String anon = settings.getString("" + PREF_ANONID, "NADA"); if (settings.getAll().isEmpty()) { showWelcomeDialog(); } else if (anon == "NADA") { showWelcomeDialog(); } // Not first run - set up the list view of saved trips ListView listSavedTrips = (ListView) findViewById(R.id.ListSavedTrips); populateList(listSavedTrips); } MainInput.this.unbindService(this); // race? this says we no longer care } }; // This needs to block until the onServiceConnected (above) completes. // Thus, we can check the recording status before continuing on. bindService(rService, sc, Context.BIND_AUTO_CREATE); // And set up the record button final Button startButton = (Button) findViewById(R.id.ButtonStart); final Intent i = new Intent(this, RecordingActivity.class); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd", Locale.US); SharedPreferences settings = getSharedPreferences("PREFS", 0); final String anon = settings.getString("" + PREF_ANONID, "NADA"); Firebase weatherRef = new Firebase("https://publicdata-weather.firebaseio.com/philadelphia"); Firebase tempRef = new Firebase("https://publicdata-weather.firebaseio.com/philadelphia/currently"); tempRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Object val = dataSnapshot.getValue(); String cardinal = null; TextView tempState = (TextView) findViewById(R.id.temperatureView); // TextView liveTemp = (TextView) findViewById(R.id.warning); String apparentTemp = ((Map) val).get("apparentTemperature").toString(); String windSpeed = ((Map) val).get("windSpeed").toString(); Double windValue = (Double) ((Map) val).get("windSpeed"); Long windBearing = (Long) ((Map) val).get("windBearing"); // liveTemp.setText(" "+apparentTemp.toString()+DEGREE); WindDirection[] windDirections = WindDirection.values(); for (int i = 0; i < windDirections.length; i++) { if (windDirections[i].startDegree < windBearing && windDirections[i].endDegree > windBearing) { //Get Cardinal direction cardinal = windDirections[i].cardinal; } } if (windValue > 4) { tempState.setTextColor(0xFFDC143C); tempState.setText("winds " + cardinal + " at " + windSpeed + " mph. Ride with caution."); } else { tempState.setTextColor(0xFFFFFFFF); tempState.setText("winds " + cardinal + " at " + windSpeed + " mph."); } } @Override public void onCancelled(FirebaseError firebaseError) { } }); connectedListener = ref.getRoot().child(".info/connected").addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { boolean connected = (Boolean) dataSnapshot.getValue(); if (connected) { System.out.println("connected " + dataSnapshot.toString()); // Firebase cycleRef = new Firebase(FIREBASE_REF+"/"+anon+"/connections"); // cycleRef.setValue(Boolean.TRUE); // cycleRef.onDisconnect().removeValue(); } else { System.out.println("disconnected"); } } @Override public void onCancelled(FirebaseError error) { // No-op } }); weatherRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { Object value = snapshot.getValue(); Object hourly = ((Map) value).get("currently"); String alert = ((Map) hourly).get("summary").toString(); // TextView weatherAlert = (TextView) findViewById(R.id.weatherAlert); // weatherAlert.setText(alert); } @Override public void onCancelled(FirebaseError firebaseError) { } }); // Acquire a reference to the system Location Manager // Define a listener that responds to location updates LocationListener locationListener = new LocationListener() { public void onLocationChanged(Location location) { // Called when a new location is found by the network location provider. mySpot = new LatLng(location.getLatitude(), location.getLongitude()); makeUseOfNewLocation(location); } public void onStatusChanged(String provider, int status, Bundle extras) { } public void onProviderEnabled(String provider) { } public void onProviderDisabled(String provider) { } }; //nearbyStations = (RecyclerView) findViewById(R.id.nearbyStationList); //nearbyStations.setLayoutManager(new LinearLayoutManager(getApplicationContext())); //Listener for Indego Changes /*indegoRef = new Firebase("https://phl.firebaseio.com/indego/kiosks"); indegoRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { //Updates! Add them to indego data list indegoDataList = dataSnapshot; } @Override public void onCancelled(FirebaseError firebaseError) { } });*/ // Register the listener with the Location Manager to receive location updates //indegoGeofireRef = new Firebase("https://phl.firebaseio.com/indego/_geofire"); //GeoFire geoFire = new GeoFire(indegoGeofireRef); locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener); mySpot = myCurrentLocation(); //indegoList = new ArrayList<IndegoStation>(); System.out.println("lo: " + mySpot.toString()); /* GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(mySpot.latitude,mySpot.longitude), 0.5); geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() { @Override public void onKeyEntered(String key, GeoLocation location) { System.out.println(String.format("Key %s entered the search area at [%f,%f]", key, location.latitude, location.longitude)); //Create Indego Station object. To-do: check if object exists // IndegoStation station = new IndegoStation(); //station.kioskId = key; //station.location = location; /* if(indegoDataList != null){ //get latest info from list station.name = (String) indegoDataList.child(key).child("properties").child("name").getValue(); } System.out.println(station.name); //indegoList.add(station); //To-do: Add indego station info to RideIndegoAdapter } @Override public void onKeyExited(String key) { } @Override public void onKeyMoved(String key, GeoLocation location) { } @Override /* public void onGeoQueryReady() { //System.out.println("GEO READY :"+indegoList.toString()); // indegoAdapter = new RideIndegoAdapter(getApplicationContext(),indegoList); //nearbyStations.setAdapter(indegoAdapter); } @Override public void onGeoQueryError(FirebaseError error) { System.out.println("GEO error"); } });*/ startButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { // Before we go to record, check GPS status final LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { buildAlertMessageNoGps(); } else { startActivity(i); MainInput.this.finish(); } } }); toolbar = (Toolbar) findViewById(R.id.dashboard_bar); toolbar.setTitle(getString(R.string.app_name)); setSupportActionBar(toolbar); getSupportActionBar().setDisplayShowTitleEnabled(true); } private LatLng myCurrentLocation() { Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); Location loc = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false)); if (loc != null) { return new LatLng(loc.getLatitude(), loc.getLongitude()); } else { // try with coarse accuracy criteria.setAccuracy(Criteria.ACCURACY_FINE); loc = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false)); if (loc == null) { return new LatLng(39.952451, -75.163664); // city hall by default } } return null; } private void makeUseOfNewLocation(Location location) { System.out.println(location.toString()); System.out.println("Here."); //((RideIndegoAdapter)nearbyStations.getAdapter()).removeAll(); // debugLocation = (TextView) findViewById(R.id.locationDebug); // debugLocation.setText(location.toString()); } private void buildAlertMessageNoGps() { final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(String.format( "Your phone's GPS is disabled. %s needs GPS to determine your location.\n\nGo to System Settings now to enable GPS?", getString(R.string.app_name))).setCancelable(false) .setPositiveButton("GPS Settings...", new DialogInterface.OnClickListener() { public void onClick(final DialogInterface dialog, final int id) { final Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivityForResult(intent, 0); } }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(final DialogInterface dialog, final int id) { dialog.cancel(); } }); final AlertDialog alert = builder.create(); alert.show(); } private void showWelcomeDialog() { final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(String.format( "Please update your personal details so we can learn a bit about you.\n\nThen, try to use %s every time you ride. Your trip routes will be sent to regional transportation planners to improve biking in the %s area!\n\nThanks,\nThe %s team", getString(R.string.app_name), getString(R.string.geographicName), getString(R.string.app_name))) .setCancelable(false).setTitle(getString(R.string.welcomeMessage)) .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(final DialogInterface dialog, final int id) { startActivity(new Intent(MainInput.this, UserInfoActivity.class)); } }); final AlertDialog alert = builder.create(); alert.show(); } void populateList(ListView lv) { // Get list from the real phone database. W00t! DbAdapter mDb = new DbAdapter(MainInput.this); mDb.open(); // Clean up any bad trips & coords from crashes int cleanedTrips = mDb.cleanTables(); if (cleanedTrips > 0) { Toast.makeText(getBaseContext(), "" + cleanedTrips + " bad trip(s) removed.", Toast.LENGTH_SHORT) .show(); } try { Cursor allTrips = mDb.fetchAllTrips(); SimpleCursorAdapter sca = new SimpleCursorAdapter(this, R.layout.twolinelist, allTrips, new String[] { "purp", "fancystart", "fancyinfo" }, new int[] { R.id.TextView01, R.id.TextView03, R.id.TextInfo }); lv.setAdapter(sca); TextView counter = (TextView) findViewById(R.id.TextViewPreviousTrips); int numtrips = allTrips.getCount(); switch (numtrips) { case 0: counter.setText("No saved trips."); break; case 1: counter.setText("1 saved trip:"); break; default: counter.setText("" + numtrips + " saved trips:"); } // allTrips.close(); } catch (SQLException sqle) { // Do nothing, for now! } mDb.close(); lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int pos, long id) { Intent i = new Intent(MainInput.this, ShowMap.class); i.putExtra("showtrip", id); startActivity(i); } }); registerForContextMenu(lv); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, CONTEXT_RETRY, 0, "Retry Upload"); menu.add(0, CONTEXT_DELETE, 0, "Delete"); } @Override public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); switch (item.getItemId()) { case CONTEXT_RETRY: retryTripUpload(info.id); return true; case CONTEXT_DELETE: deleteTrip(info.id); return true; default: return super.onContextItemSelected(item); } } private void retryTripUpload(long tripId) { TripUploader uploader = new TripUploader(MainInput.this); uploader.execute(tripId); } private void deleteTrip(long tripId) { DbAdapter mDbHelper = new DbAdapter(MainInput.this); mDbHelper.open(); mDbHelper.deleteAllCoordsForTrip(tripId); mDbHelper.deleteTrip(tripId); mDbHelper.close(); ListView listSavedTrips = (ListView) findViewById(R.id.ListSavedTrips); listSavedTrips.invalidate(); populateList(listSavedTrips); } /* Creates the menu items */ @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, MENU_CONTACT_US, 0, "Contact Us").setIcon(android.R.drawable.ic_dialog_email); menu.add(0, MENU_USER_INFO, 0, "Edit User Info").setIcon(android.R.drawable.ic_menu_edit); menu.add(0, MENU_MAP, 0, "Cycling Map").setIcon(android.R.drawable.ic_menu_compass); menu.add(0, MENU_LEGAL_INFO, 0, "Legal Information").setIcon(android.R.drawable.ic_menu_info_details); return true; } /* Handles item selections */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_USER_INFO: startActivity(new Intent(this, UserInfoActivity.class)); return true; case MENU_CONTACT_US: Intent myIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "org.opensmc.mytracks.cyclesmc@gmail.com", null)); myIntent.putExtra(Intent.EXTRA_SUBJECT, String.format("%s Android App", getString(R.string.app_name))); startActivity(Intent.createChooser(myIntent, "Send email...")); return true; /*case MENU_MAP: startActivity(new Intent(this, ShowMapNearby.class)); return true;*/ case MENU_LEGAL_INFO: startActivity(new Intent(this, LicenseActivity.class)); return true; } return false; } } class FakeAdapter extends SimpleAdapter { public FakeAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) { super(context, data, resource, from, to); } }