package st.alr.mqttitude; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import st.alr.mqttitude.preferences.ActivityPreferences; import st.alr.mqttitude.services.ServiceBindable; import st.alr.mqttitude.services.ServiceLocator; import st.alr.mqttitude.support.Defaults; import st.alr.mqttitude.support.Events; import st.alr.mqttitude.support.Friend; import st.alr.mqttitude.support.GeocodableLocation; import st.alr.mqttitude.support.ReverseGeocodingTask; //import android.app.DialogFragment; //import android.app.Fragment; //import android.app.FragmentManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.support.v4.app.DialogFragment; import android.support.v4.widget.DrawerLayout; import android.text.format.DateFormat; import android.util.Config; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.model.BitmapDescriptor; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.Circle; import com.google.android.gms.maps.model.CircleOptions; 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 de.greenrobot.event.EventBus; public class ActivityMain extends android.support.v4.app.FragmentActivity { MenuItem publish; TextView location; TextView statusLocator; TextView statusLastupdate; TextView statusServer; private GoogleMap mMap; private TextView locationPrimary; private TextView locationMeta; private LinearLayout locationAvailable; private LinearLayout locationUnavailable; private Marker mMarker; private Circle mCircle; private ServiceLocator serviceLocator; private ServiceConnection locatorConnection; private static Handler handler; private SimpleAdapter mPeers; private ArrayList<HashMap<String, String>> mylist; private Map<String,Integer> userMappings = new HashMap<String,Integer>(); //private Map<String,BitmapDescriptor> userMarkerIcons = new HashMap<String,BitmapDescriptor>(); //private Map<String,BitmapDescriptor> userHistoryMarkerIcons = new HashMap<String,BitmapDescriptor>(); private DrawerLayout mDrawerLayout; private ListView mDrawerList; private Map<String,LinkedList<Marker>> userMarkerHistory = new HashMap<String,LinkedList<Marker>>(); private SharedPreferences sharedPreferences; private Map<String,Friend> friends = new HashMap<String,Friend>(); @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.menu_settings) { Intent intent1 = new Intent(this, ActivityPreferences.class); startActivity(intent1); return true; } else if (itemId == R.id.menu_status) { Intent intent1 = new Intent(this, ActivityStatus.class); startActivity(intent1); return true; } else if (itemId == R.id.menu_publish) { if(serviceLocator != null) serviceLocator.publishLastKnownLocation(); return true; } else if (itemId == R.id.menu_share) { if(serviceLocator != null) this.share(null); return true; } else { return super.onOptionsItemSelected(item); } } private void setUpMapIfNeeded() { if (mMap == null) { mMap = ((com.google.android.gms.maps.SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map)).getMap(); if (mMap != null) { setUpMap(); } } } private void setUpMap() { // Hide the zoom controls as the button panel will cover it. mMap.getUiSettings().setZoomControlsEnabled(false); mMap.setMyLocationEnabled(false); mMap.setTrafficEnabled(false); } @Override protected void onStart() { super.onStart(); Log.v(this.toString(), "binding"); locatorConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { serviceLocator = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.v(this.toString(), "bound"); serviceLocator = (ServiceLocator) ((ServiceBindable.ServiceBinder)service).getService(); } }; bindService(new Intent(this, App.getServiceLocatorClass()), locatorConnection, Context.BIND_AUTO_CREATE); EventBus.getDefault().register(this); if(serviceLocator != null) serviceLocator.enableForegroundMode(); } @Override public void onStop() { unbindService(locatorConnection); EventBus.getDefault().unregister(this); if(serviceLocator != null) serviceLocator.enableBackgroundMode(); super.onStop(); } @Override protected void onResume() { super.onResume(); setUpMapIfNeeded(); } @Override protected void onPause() { super.onPause(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); if (App.getInstance().isDebugBuild()) menu.findItem(R.id.menu_status).setVisible(true); return true; } /** * @category START */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setUpMapIfNeeded(); serviceLocator = null; locationAvailable = (LinearLayout) findViewById(R.id.locationAvailable); locationUnavailable = (LinearLayout) findViewById(R.id.locationUnavailable); locationPrimary = (TextView) findViewById(R.id.locationPrimary); locationMeta = (TextView) findViewById(R.id.locationMeta); // Handler for updating text fields on the UI like the lat/long and address. handler = new Handler() { public void handleMessage(Message msg) { onHandlerMessage(msg); } }; showLocationUnavailable(); //NAVIGATION DRAW for tracking peers String[] mPlanetTitles = getResources().getStringArray(R.array.planets_array); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerList = (ListView) findViewById(R.id.left_drawer); mylist = new ArrayList<HashMap<String, String>>(); HashMap<String, String> map = new HashMap<String, String>(); parseContacts(); for(Friend f: friends.values()){ map = new HashMap<String, String>(); map.put("Peer", f.getName()); map.put("Location", ""); map.put("Time", ""); map.put("Username", f.getMqqtUsername()); mylist.add(map); userMappings.put(f.getMqqtUsername(), mylist.size()-1); } //mylist.add(map); mPeers = new SimpleAdapter(this, mylist, R.layout.draw_list_item, new String[] {"Peer", "Location", "Time"}, new int[] {R.id.listviewmaintext, R.id.listviewsubtext, R.id.listviewminortext}); mDrawerList.setAdapter(mPeers); // Set the adapter for the list view //mDrawerList.setAdapter(new ArrayAdapter<String>(this, // R.layout.draw_list_item, mPlanetTitles)); // Set the list's click listener mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); } private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { selectItem(position); } } /** Swaps fragments in the main content view */ private void selectItem(int position) { // Create a new fragment and specify the planet to show based on position // Highlight the selected item, update the title, and close the drawer mDrawerList.setItemChecked(position, true); String peer = mylist.get(position).get("Peer"); setTitle(peer); //need to zoom to the peer //get the index into the hasmap for the named peer (we don't trust the drawer index as there could be future UI elements //int idx = userMappings.get("/mqttitude/"+peer); HashMap<String,String> peerhmap = mylist.get(position); try{ LatLng latlng = new LatLng(Double.parseDouble(peerhmap.get("Latitude")),Double.parseDouble(peerhmap.get("Longitude"))); CameraUpdate center = CameraUpdateFactory.newLatLng(latlng); CameraUpdate zoom = CameraUpdateFactory.zoomTo(15); mMap.moveCamera(center); mMap.animateCamera(zoom); } catch (NullPointerException e){ //we failed to find a valid long/lat } mDrawerLayout.closeDrawer(mDrawerList); } @Override public void setTitle(CharSequence title) { //mTitle = title; getActionBar().setTitle(title); } private void onHandlerMessage(Message msg) { switch (msg.what) { case ReverseGeocodingTask.GEOCODER_RESULT: Log.v(this.toString(), "Geocoder result_ " + ((GeocodableLocation) msg.obj).getGeocoder()); locationPrimary.setText(((GeocodableLocation) msg.obj).getGeocoder()); break; case ReverseGeocodingTask.GEOCODER_NORESULT: break; } } public void onEvent(Events.LocationUpdated e) { setLocation(e.getGeocodableLocation()); } Marker peer = null; //handle new peer location public void onEventMainThread(Events.UpdatedPeerLocation e){ LatLng latlong = new LatLng(Double.parseDouble(e.getLat()),Double.parseDouble(e.getLng())); setPeerLocation(latlong,e.getUsr(),e.getGca(),e.getTst()); } public void setPeerLocation(LatLng latlong, String user, String geoAddress, String time){ //need to put pin on the UI or update //TODO: should put double parsing into event code //BitmapFactory.decodeResource(getResources(), R.drawable.marker); LinkedList<Marker> thisUserMarkerHistory = null; //first lets see if there is a existng marker and turn in grey if (userMarkerHistory.containsKey(user)){ //get the old marker and change it's icon thisUserMarkerHistory = userMarkerHistory.get(user); Marker lastMarker = thisUserMarkerHistory.removeLast(); lastMarker.setIcon(friends.get(user).getStaleMarkerImage()); // userHistoryMarkerIcons.get(user)); thisUserMarkerHistory.add(lastMarker); }else{ //the user doesn't have a history so create a blank one //userMarkerHistory.put(user, new LinkedList<Marker>()); thisUserMarkerHistory = new LinkedList<Marker>(); } MarkerOptions ms = new MarkerOptions(); ms.icon(friends.get(user).getMarkerImage()); //userMarkerIcons.get(user)); ms.title(user); ms.position(latlong); ms.snippet("Some Text"); peer = mMap.addMarker(ms); thisUserMarkerHistory.add(peer); //update the table userMarkerHistory.put(user,thisUserMarkerHistory); //need to update the hashmap int idx = userMappings.get(user); HashMap<String,String> elem = mylist.get(idx); long millis = Long.parseLong(time); millis = millis * 1000; SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy HH:mm"); // Create a calendar object that will convert the date and time value in milliseconds to date. Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(millis); String dateFormatted = formatter.format(calendar.getTime()); elem.put("Location", geoAddress);//"Lat/Lng: " + latlong.latitude + " " + latlong.longitude); elem.put("Time", dateFormatted); elem.put("Latitude",""+latlong.latitude); elem.put("Longitude",""+ latlong.longitude); mylist.set(idx, elem); mPeers = new SimpleAdapter(this, mylist, R.layout.draw_list_item, new String[] {"Peer", "Location", "Time"}, new int[] {R.id.listviewmaintext, R.id.listviewsubtext, R.id.listviewminortext}); mDrawerList.setAdapter(mPeers); } public void setLocation(GeocodableLocation location) { Location l = location.getLocation(); if(l == null) { showLocationUnavailable(); return; } LatLng latlong = new LatLng(l.getLatitude(), l.getLongitude()); CameraUpdate center = CameraUpdateFactory.newLatLng(latlong); CameraUpdate zoom = CameraUpdateFactory.zoomTo(15); if (mMarker != null) mMarker.remove(); if (mCircle != null) mCircle.remove(); mMarker = mMap.addMarker(new MarkerOptions().position(latlong).icon(BitmapDescriptorFactory.fromResource(R.drawable.marker))); if(l.getAccuracy() >= 50) { mCircle = mMap.addCircle(new CircleOptions().center(latlong).radius(l.getAccuracy()).strokeColor(0xff1082ac).fillColor(0x1c15bffe).strokeWidth(3)); } mMap.moveCamera(center); mMap.animateCamera(zoom); locationPrimary.setText(l.getLatitude() + " / " + l.getLongitude()); locationMeta.setText(App.getInstance().formatDate(new Date())); showLocationAvailable(); if (Geocoder.isPresent()) (new ReverseGeocodingTask(this, handler)).execute(new GeocodableLocation[] {location}); } private void showLocationAvailable() { locationUnavailable.setVisibility(View.GONE); if(!locationAvailable.isShown()) locationAvailable.setVisibility(View.VISIBLE); } private void showLocationUnavailable(){ locationAvailable.setVisibility(View.GONE); if(!locationUnavailable.isShown()) locationUnavailable.setVisibility(View.VISIBLE); } public void share(View view) { GeocodableLocation l = serviceLocator.getLastKnownLocation(); Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra( Intent.EXTRA_TEXT, "http://maps.google.com/?q=" + Double.toString(l.getLatitude()) + "," + Double.toString(l.getLongitude())); sendIntent.setType("text/plain"); startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.shareLocation))); } public void upload(View view) { serviceLocator.publishLastKnownLocation(); } /* * Here we parse the contacts and build a list of known MQTTITUDE users * we then use this elsewhere to a) produce UI and b) to pass to the service to * create subscriptions */ public void parseContacts(){ //new friends user ids by using android contacts, //TODO: put in fn ContentResolver cr = getContentResolver(); Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); if (cur.getCount() > 0) { while (cur.moveToNext()) { String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID)); String name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); if (Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) { //Query IM details String imWhere = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; String[] imWhereParams = new String[]{id, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE}; Cursor imCur = cr.query(ContactsContract.Data.CONTENT_URI, null, imWhere, imWhereParams, null); if (imCur.moveToFirst()) { String imName = imCur.getString(imCur.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)); String imType; imType = imCur.getString(imCur.getColumnIndex(ContactsContract.CommonDataKinds.Im.TYPE)); String label = imCur.getString(imCur.getColumnIndex(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL)); if(imType.equalsIgnoreCase("3")){ //TODO: CHange hard coded string if(label.equalsIgnoreCase("MQTTITUDE")){ Events.NewPeerAdded msg = new Events.NewPeerAdded(imName); EventBus.getDefault().post(msg); //create a friend object Friend friend = new Friend(); friend.setMqqtUsername(imName); friend.setName(name); friend.setMarkerImage(createCustomMarker(friends.size()-1,1.0f)); friend.setStaleMarkerImage(createCustomMarker(friends.size()-1,0.5f)); friends.put(imName, friend); } } } imCur.close(); } } } } /* * We use this to generate markers for each of the different peers/friends * we can use a solid colour for each then alter the apha for historical markers */ private BitmapDescriptor createCustomMarker(int colour, float alpha){ float[] hsv = new float[3]; hsv[0] = (colour * 50) % 360; //mod 365 so we get variation hsv[1] = 1; hsv[2] = alpha; Bitmap bm = Bitmap.createBitmap(40, 40, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(); c.setBitmap(bm); Paint p = new Paint(); p.setColor(Color.HSVToColor(hsv)); c.drawCircle(20, 20, 10, p); return BitmapDescriptorFactory.fromBitmap(bm); } }