package de.tudarmstadt.informatik.hostage.ui2.fragment; import android.app.Fragment; import android.graphics.Color; import android.location.Location; import android.os.Bundle; import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesClient; import com.google.android.gms.location.LocationClient; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.MapFragment; 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 java.util.ArrayList; import java.util.HashMap; import java.util.Map; import de.tudarmstadt.informatik.hostage.R; import de.tudarmstadt.informatik.hostage.logging.Record; import de.tudarmstadt.informatik.hostage.logging.UglyDbHelper; import de.tudarmstadt.informatik.hostage.ui.LogFilter; import de.tudarmstadt.informatik.hostage.ui2.activity.MainActivity; import static com.google.android.gms.common.GooglePlayServicesUtil.*; /** * ThreatMapFragment * * Created by Fabio Arnold on 10.02.14. */ public class ThreatMapFragment extends Fragment implements GoogleMap.OnInfoWindowClickListener, GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener, LocationListener { private static GoogleMap sMap = null; private static View sView = null; private static HashMap sMarkerIDToSSID = new HashMap(); private LocationClient mLocationClient; private static final LocationRequest REQUEST = LocationRequest.create() .setExpirationDuration(5000) // 5 seconds .setInterval(5000) // 5 seconds .setFastestInterval(16) // 16ms = 60fps .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); /** * if google play services aren't available an error notification will be displayed * * @return true if the google play services are available */ private boolean isGooglePlay() { int status = isGooglePlayServicesAvailable(getActivity()); boolean result = status == ConnectionResult.SUCCESS; if (!result) { getErrorDialog(status, getActivity(), 10).show(); } return result; } /** * callback for when the info window of a marker gets clicked * open the RecordOverviewFragment and display all records belonging to an SSID * * @param marker this info window belongs to */ @Override public void onInfoWindowClick(Marker marker) { MainActivity.getInstance().displayView(MainActivity.MainMenuItem.RECORDS.getValue()); RecordOverviewFragment recordOverviewFragment = (RecordOverviewFragment)MainActivity.getInstance().getCurrentFragment(); if (recordOverviewFragment != null) { String ssid = sMarkerIDToSSID.get(marker.getId()); ArrayList ssids = new ArrayList(); ssids.add(ssid); LogFilter filter = new LogFilter(); filter.setESSIDs(ssids); recordOverviewFragment.setFilter(filter); recordOverviewFragment.setGroupKey("ESSID"); //recordOverviewFragment.showDetailsForSSID(getActivity(), ssid); } } /** * callbacks from LocationClient */ @Override public void onConnected(Bundle bundle) { mLocationClient.requestLocationUpdates(REQUEST, this); } @Override public void onDisconnected() { } @Override public void onConnectionFailed(ConnectionResult connectionResult) { } @Override public void onLocationChanged(Location location) { sMap.animateCamera(CameraUpdateFactory.newLatLng( new LatLng(location.getLatitude(), location.getLongitude()))); } /** * helper class * easier to use than LatLng */ private class Point { public double x, y; public Point(double sx, double sy) { x = sx; y = sy; } } /** * helper class * contains heuristic to split SSIDs by location * see MAX_DISTANCE */ private class SSIDArea { private Point mMinimum, mMaximum; public int numPoints; public static final int MAX_NUM_ATTACKS = 20; public static final float MAX_DISTANCE = 1000.0f; // 1km public SSIDArea(LatLng initialLocation) { //mMinimum = new Point(360.0, 360.0); //mMaximum = new Point(-360.0, -360.0); mMinimum = new Point(initialLocation.latitude, initialLocation.longitude); mMaximum = new Point(initialLocation.latitude, initialLocation.longitude); numPoints = 1; } public boolean doesLocationBelongToArea(LatLng location) { LatLng center = calculateCenterLocation(); float[] result = new float[1]; Location.distanceBetween(center.latitude, center.longitude, location.latitude, location.longitude, result); return result[0] < MAX_DISTANCE; } public void addLocation(LatLng location) { Point point = new Point(location.latitude, location.longitude); if (point.x < mMinimum.x) mMinimum.x = point.x; if (point.x > mMaximum.x) mMaximum.x = point.x; if (point.y < mMinimum.y) mMinimum.y = point.y; if (point.y > mMaximum.y) mMaximum.y = point.y; numPoints++; } public LatLng calculateCenterLocation() { return new LatLng(0.5 * (mMinimum.x + mMaximum.x), 0.5 * (mMinimum.y + mMaximum.y)); } public float calculateRadius() { float[] result = new float[1]; Location.distanceBetween(mMinimum.x, mMinimum.y, mMaximum.x, mMaximum.y, result); return 0.5f * result[0]; } public int calculateColor() { int threatLevel = numPoints; if (threatLevel > MAX_NUM_ATTACKS) threatLevel = MAX_NUM_ATTACKS; float alpha = 1.0f - (float)(threatLevel-1) / (float)(MAX_NUM_ATTACKS-1); return Color.argb(127, (int) (240.0 + 15.0 * alpha), (int) (80.0 + 175.0 * alpha), 60); } } /** * fills the map with markers and circle representing SSIDs */ private void populateMap() { UglyDbHelper dbh = new UglyDbHelper(getActivity()); ArrayList records = dbh.getAllRecords(); HashMap> threadAreas = new HashMap>(); for (Record record : records) { LatLng location = new LatLng(record.getLatitude(), record.getLongitude()); ArrayList areas; if (threadAreas.containsKey(record.getSsid())) { areas = threadAreas.get(record.getSsid()); boolean foundArea = false; for (SSIDArea area : areas) { if (area.doesLocationBelongToArea(location)) { area.addLocation(location); foundArea = true; break; } } if (!foundArea) { areas.add(new SSIDArea(location)); } } else { areas = new ArrayList(); areas.add(new SSIDArea(location)); threadAreas.put(record.getSsid(), areas); } } CircleOptions circleOptions = new CircleOptions().radius(200.0).fillColor(Color.argb(127, 240, 80, 60)).strokeWidth(0.0f); for (Map.Entry> entry : threadAreas.entrySet()) { String ssid = entry.getKey(); ArrayList areas = entry.getValue(); for (SSIDArea area : areas) { int color = area.calculateColor(); LatLng center = area.calculateCenterLocation(); float radius = area.calculateRadius(); sMap.addCircle(circleOptions.center(center).radius(100.0 + radius).fillColor(color)); Marker marker = sMap.addMarker(new MarkerOptions() .title(ssid + ": " + area.numPoints + (area.numPoints == 1 ? " attack" : " attacks")).position( center)); sMarkerIDToSSID.put(marker.getId(), ssid); } } sMap.setMyLocationEnabled(true); LatLng tudarmstadt = new LatLng(49.86923, 8.6632768); // default location sMap.moveCamera(CameraUpdateFactory.newLatLngZoom(tudarmstadt, 13)); } /** * performs initialization * checks if google play services are supported * view must be removed if this object has been created once before * that is why view is static * * @param inflater * @param container * @param savedInstanceState * @return the view */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); if (sView != null) { ViewGroup parent = (ViewGroup) sView.getParent(); if (parent != null) parent.removeView(sView); } try { sView = inflater.inflate(R.layout.fragment_threatmap, container, false); if (isGooglePlay()) { sMap = ((MapFragment) getFragmentManager() .findFragmentById(R.id.threatmapfragment)).getMap(); populateMap(); } } catch (InflateException e) { // map already exists //e.printStackTrace(); } if (sMap != null) { sMap.setOnInfoWindowClickListener(this); } return sView; } @Override public void onResume() { super.onResume(); if (mLocationClient == null) { mLocationClient = new LocationClient(MainActivity.getInstance().getApplicationContext(), this, this); } mLocationClient.connect(); } @Override public void onPause() { super.onPause(); if (mLocationClient != null) { mLocationClient.disconnect(); } } }