package de.tudarmstadt.informatik.hostage.ui2.fragment; import static com.google.android.gms.common.GooglePlayServicesUtil.getErrorDialog; import static com.google.android.gms.common.GooglePlayServicesUtil.isGooglePlayServicesAvailable; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; import android.app.FragmentManager; import android.content.DialogInterface; 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 android.widget.TextView; 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.BitmapDescriptor; import com.google.android.gms.maps.model.BitmapDescriptorFactory; 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.tudarmstadt.informatik.hostage.R; import de.tudarmstadt.informatik.hostage.commons.HelperUtils; import de.tudarmstadt.informatik.hostage.logging.Record; import de.tudarmstadt.informatik.hostage.persistence.HostageDBOpenHelper; import de.tudarmstadt.informatik.hostage.ui.LogFilter; import de.tudarmstadt.informatik.hostage.ui2.activity.MainActivity; /** * 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 recordOverviewFragment = new RecordOverviewFragment(); recordOverviewFragment.setFilter(filter); recordOverviewFragment.setGroupKey("ESSID"); MainActivity.getInstance().injectFragment(recordOverviewFragment, false); //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() { HostageDBOpenHelper dbh = new HostageDBOpenHelper(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); BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromResource(R.drawable.wifi_marker); 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 ? getResources() .getString(R.string.attack) : getResources().getString(R.string.attacks))).position( center)); marker.setIcon(bitmapDescriptor); 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 the inflater * @param container the container * @param savedInstanceState the savedInstanceState * @return the view */ @Override public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); final Activity activity = getActivity(); if (activity != null) { activity.setTitle(getResources().getString(R.string.drawer_threat_map)); } 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()) { final FragmentManager fragmentManager = getFragmentManager(); if (fragmentManager != null) { final MapFragment mapFragment = (MapFragment) getFragmentManager() .findFragmentById(R.id.threatmapfragment); if (mapFragment != null) { sMap = mapFragment.getMap(); populateMap(); } } } } catch (InflateException e) { // map already exists //e.printStackTrace(); } if (sMap != null) { sMap.setOnInfoWindowClickListener(this); // custom info window layout sMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() { @Override public View getInfoWindow(Marker marker) { return null; } @Override public View getInfoContents(Marker marker) { View view = inflater.inflate(R.layout.fragment_threatmap_infowindow, null); if (view != null) { TextView titleTextView = (TextView)view.findViewById(R.id.threatmap_infowindow_title); if (titleTextView != null) { titleTextView.setText(marker.getTitle()); } } return view; } }); } // tell the user to enable wifi so map data can be streamed if (activity != null && !HelperUtils.isWifiConnected(activity)) { new AlertDialog.Builder(activity) .setTitle(R.string.information) .setMessage(R.string.no_network_connection_threatmap_msg) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }) .setIcon(android.R.drawable.ic_dialog_info).show(); } 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(); } } }