Browse Source

Merge branch 'master' of https://git.tk.informatik.tu-darmstadt.de/scm-ssi-student-hostagev2

Daniel Lazar 10 years ago
parent
commit
449023520b

BIN
res/drawable-hdpi/ic_action_new.png


BIN
res/drawable-hdpi/ic_menu_records.png


BIN
res/drawable-mdpi/ic_action_new.png


BIN
res/drawable-mdpi/ic_menu_records.png


BIN
res/drawable-xhdpi/ic_action_new.png


BIN
res/drawable-xhdpi/ic_menu_records.png


BIN
res/drawable-xxhdpi/ic_menu_records.png


+ 7 - 0
res/menu/profile_manager_actions.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:id="@+id/profile_manager_action_add"
+		  android:icon="@drawable/ic_action_new"
+		  android:title="New profile" />
+</menu>

+ 1 - 0
res/values/strings.xml

@@ -43,6 +43,7 @@
     <string name="drawer_settings">Settings</string>
     <string name="drawer_profile_manager">Profile Manager</string>
     <string name="drawer_app_info">Application info</string>
+	<string name="drawer_statistics">Statistics</string>
 
     <string name="button_title_apply">Apply</string>
     <string name="button_title_cancel">Cancel</string>

+ 5 - 5
res/xml/preferences.xml

@@ -38,18 +38,18 @@
             android:showSilent="true"
             android:summary="@string/pref_alarm_summ"
             android:title="@string/pref_alarm" />
-    </PreferenceCategory>    
+    </PreferenceCategory>
     <PreferenceCategory android:title="@string/pref_connection_settings" >
         <EditTextPreference
             android:key="pref_max_connections"
             android:defaultValue="@string/pref_max_connections_default"
             android:title="@string/pref_max_connections" />
-               
+
           <EditTextPreference
             android:key="pref_timeout"
             android:defaultValue="@string/pref_timeout_default"
             android:title="@string/pref_timeout" />
-          
+
           <EditTextPreference
             android:key="pref_sleeptime"
             android:defaultValue="@string/pref_sleeptime_default"
@@ -60,12 +60,12 @@
             android:key="pref_location_time"
             android:defaultValue="@string/pref_location_time_default"
             android:title="@string/pref_location_time" />
-               
+
           <EditTextPreference
             android:key="pref_location_retries"
             android:defaultValue="@string/pref_location_retries_default"
             android:title="@string/pref_location_retries" />
-          
+
     </PreferenceCategory>
 
 

+ 85 - 0
res/xml/settings_preferences.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
+
+	<CheckBoxPreference
+		android:defaultValue="false"
+	    android:key="pref_general_expert"
+	    android:summary="This will activate some expert functionalities."
+	    android:title="Expert Mode"/>
+
+
+	<PreferenceCategory android:title="@string/pref_notification" >
+		<CheckBoxPreference
+				android:defaultValue="true"
+				android:key="pref_vibration"
+				android:summary="@string/pref_vibration_summ"
+				android:title="@string/pref_vibration" />
+
+		<RingtonePreference
+				android:defaultValue="content://settings/system/notification_sound"
+				android:key="pref_notification_sound"
+				android:ringtoneType="notification"
+				android:showDefault="true"
+				android:showSilent="true"
+				android:summary="@string/pref_alarm_summ"
+				android:title="@string/pref_alarm" />
+	</PreferenceCategory>
+
+	<PreferenceCategory android:title="Advanced Settings">
+		<PreferenceScreen
+				android:key="pref_advanced settings"
+				android:title="Advanced Settings"
+				android:persistent="false">
+			<PreferenceCategory android:title="@string/pref_storage" >
+				<CheckBoxPreference
+						android:defaultValue="false"
+						android:key="pref_external_storage"
+						android:summary="@string/pref_external_storage_summ"
+						android:title="@string/pref_external_storage_title" />
+
+				<EditTextPreference
+						android:key="pref_external_location"
+						android:dependency="pref_external_storage"
+						android:defaultValue="/HosTaGe/LogFiles/"
+						android:title="@string/pref_external_location_title"
+						/>
+
+			</PreferenceCategory>
+			<PreferenceCategory android:title="@string/pref_upload" >
+				<EditTextPreference
+						android:key="pref_upload_server"
+						android:defaultValue="https://ssi.cased.de"
+						android:title="@string/pref_upload_server" />
+
+			</PreferenceCategory>
+			<PreferenceCategory android:title="@string/pref_connection_settings" >
+				<EditTextPreference
+						android:key="pref_max_connections"
+						android:defaultValue="@string/pref_max_connections_default"
+						android:title="@string/pref_max_connections" />
+
+				<EditTextPreference
+						android:key="pref_timeout"
+						android:defaultValue="@string/pref_timeout_default"
+						android:title="@string/pref_timeout" />
+
+				<EditTextPreference
+						android:key="pref_sleeptime"
+						android:defaultValue="@string/pref_sleeptime_default"
+						android:title="@string/pref_sleeptime" />
+			</PreferenceCategory>
+			<PreferenceCategory android:title="@string/pref_location_settings" >
+				<EditTextPreference
+						android:key="pref_location_time"
+						android:defaultValue="@string/pref_location_time_default"
+						android:title="@string/pref_location_time" />
+
+				<EditTextPreference
+						android:key="pref_location_retries"
+						android:defaultValue="@string/pref_location_retries_default"
+						android:title="@string/pref_location_retries" />
+
+			</PreferenceCategory>
+		</PreferenceScreen>
+	</PreferenceCategory>
+</PreferenceScreen>

+ 6 - 0
src/de/tudarmstadt/informatik/hostage/model/Profile.java

@@ -22,6 +22,12 @@ public class Profile {
 	public boolean mIsBackVisible = false;
 	public boolean mEditable = false;
 
+	public Profile(){
+		this.mEditable = true;
+		this.mActivated = false;
+		this.mId = -1;
+	}
+
 	public Profile(int id, String label, String text, Bitmap icon, boolean editable){
 		this.mId = id;
 		this.mLabel = text;

+ 10 - 5
src/de/tudarmstadt/informatik/hostage/ui2/activity/MainActivity.java

@@ -34,6 +34,7 @@ import de.tudarmstadt.informatik.hostage.ui2.fragment.HomeFragment;
 import de.tudarmstadt.informatik.hostage.ui2.fragment.ProfileManagerFragment;
 import de.tudarmstadt.informatik.hostage.ui2.fragment.RecordOverviewFragment;
 import de.tudarmstadt.informatik.hostage.ui2.fragment.ServicesFragment;
+import de.tudarmstadt.informatik.hostage.ui2.fragment.SettingsFragment;
 import de.tudarmstadt.informatik.hostage.ui2.fragment.StatisticsFragment;
 import de.tudarmstadt.informatik.hostage.ui2.fragment.ThreatMapFragment;
 import de.tudarmstadt.informatik.hostage.ui2.fragment.opengl.ThreatIndicatorGLRenderer;
@@ -155,7 +156,8 @@ public class MainActivity extends Activity {
 		mDrawerItems = new ArrayList<DrawerListItem>();
 		mDrawerItems.add(new DrawerListItem(R.string.drawer_overview, R.drawable.ic_menu_home));
 		mDrawerItems.add(new DrawerListItem(R.string.drawer_threat_map, R.drawable.ic_menu_mapmode));
-		mDrawerItems.add(new DrawerListItem(R.string.drawer_records, R.drawable.ic_action_stats));
+		mDrawerItems.add(new DrawerListItem(R.string.drawer_records, R.drawable.ic_menu_records));
+		mDrawerItems.add(new DrawerListItem(R.string.drawer_statistics, R.drawable.ic_menu_stats));
 		mDrawerItems.add(new DrawerListItem(R.string.drawer_services, R.drawable.ic_menu_set_as));
 		mDrawerItems.add(
 				new DrawerListItem(R.string.drawer_profile_manager, R.drawable.ic_menu_allfriends));
@@ -286,7 +288,7 @@ public class MainActivity extends Activity {
 				fragment = new RecordOverviewFragment();
 				break;
             }
-            case STATISTICS:{
+            case STATISTICS: {
                 Intent intent = this.getIntent();
                 intent.removeExtra(LogFilter.LOG_FILTER_INTENT_KEY);
                 fragment = new StatisticsFragment();
@@ -298,6 +300,9 @@ public class MainActivity extends Activity {
 			case PROFILE_MANAGER:
 				fragment = new ProfileManagerFragment();
 				break;
+			case SETTINGS:
+				fragment = new SettingsFragment();
+				break;
 			default:
 				break;
 		}
@@ -385,9 +390,9 @@ public class MainActivity extends Activity {
         HOME(0),
         THREAT_MAP(1),
         RECORDS(2),
-        SERVICES(3),
-        PROFILE_MANAGER(4),
-        STATISTICS(5),
+		STATISTICS(3),
+        SERVICES(4),
+        PROFILE_MANAGER(5),
         SETTINGS(6),
         APPLICATION_INFO(7);
 

+ 26 - 10
src/de/tudarmstadt/informatik/hostage/ui2/fragment/ProfileEditFragment.java

@@ -54,12 +54,18 @@ public class ProfileEditFragment extends PreferenceFragment implements
 				ProfileManager pmanager = ProfileManager.getInstance();
 
 				Profile profile = getProfile();
+
 				boolean createNew = false;
 
-				if(!profile.isEditable()){
-					profile = profile.cloneProfile();
-					profile.mEditable = true;
+				if(profile == null){
+					profile = new Profile();
 					createNew = true;
+				} else {
+					if(!profile.isEditable()){
+						profile = profile.cloneProfile();
+						profile.mEditable = true;
+						createNew = true;
+					}
 				}
 
 				profile.mLabel = prefs.getString("pref_profile_general_name", profile.mLabel);
@@ -87,14 +93,22 @@ public class ProfileEditFragment extends PreferenceFragment implements
 		Profile profile = getProfile();
 		prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()).edit();
 
-		if(profile != null){
-			prefs.putString("pref_profile_general_name", profile.mLabel);
-			prefs.putString("pref_profile_general_image", profile.mIconPath);
-			prefs.putString("pref_profile_general_description", profile.mText);
+		String pname = "",
+			   pimage = "",
+			   pdesc = "";
 
-			prefs.commit();
+		if(profile != null){
+			pname = profile.mLabel;
+			pimage = profile.mIconPath;
+			pdesc = profile.mText;
 		}
 
+		prefs.putString("pref_profile_general_name", pname);
+		prefs.putString("pref_profile_general_image", pimage);
+		prefs.putString("pref_profile_general_description", pdesc);
+
+		prefs.commit();
+
 		addPreferencesFromResource(R.xml.profile_preferences);
 
 		Preference pref = findPreference("pref_profile_general_image");
@@ -119,8 +133,10 @@ public class ProfileEditFragment extends PreferenceFragment implements
 				}
 		);
 
-		findPreference("pref_profile_general_name").setSummary(profile.mLabel);
-		findPreference("pref_profile_general_description").setSummary(profile.mText);
+		if(profile != null){
+			findPreference("pref_profile_general_name").setSummary(profile.mLabel);
+			findPreference("pref_profile_general_description").setSummary(profile.mText);
+		}
 	}
 
 	public Profile getProfile(){

+ 25 - 0
src/de/tudarmstadt/informatik/hostage/ui2/fragment/ProfileManagerFragment.java

@@ -1,8 +1,12 @@
 package de.tudarmstadt.informatik.hostage.ui2.fragment;
 
 import android.app.Fragment;
+import android.content.Intent;
 import android.os.Bundle;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -14,6 +18,7 @@ import java.util.List;
 import de.tudarmstadt.informatik.hostage.R;
 import de.tudarmstadt.informatik.hostage.dao.ProfileManager;
 import de.tudarmstadt.informatik.hostage.model.Profile;
+import de.tudarmstadt.informatik.hostage.ui2.activity.ProfileEditActivity;
 import de.tudarmstadt.informatik.hostage.ui2.adapter.ProfileManagerListAdapter;
 
 /**
@@ -30,6 +35,7 @@ public class ProfileManagerFragment extends Fragment {
                              Bundle savedInstanceState) {
 
 	    super.onCreateView(inflater, container, savedInstanceState);
+	    setHasOptionsMenu(true);
 
         View rootView = inflater.inflate(R.layout.fragment_profile_manager, container, false);
 	    SwipeListView list = (SwipeListView) rootView.findViewById(R.id.profile_manager_listview);
@@ -62,4 +68,23 @@ public class ProfileManagerFragment extends Fragment {
 		super.onResume();
 		mAdapter.notifyDataSetChanged();
 	}
+
+	@Override
+	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+		// Inflate the menu items for use in the action bar
+		inflater.inflate(R.menu.profile_manager_actions, menu);
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		switch(item.getItemId()){
+			case R.id.profile_manager_action_add:
+				Intent intent = new Intent(getActivity(), ProfileEditActivity.class);
+				intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+				getActivity().startActivity(intent);
+				return true;
+		}
+
+		return false;
+	}
 }

+ 43 - 6
src/de/tudarmstadt/informatik/hostage/ui2/fragment/RecordOverviewFragment.java

@@ -69,6 +69,8 @@ public class RecordOverviewFragment extends Fragment implements ChecklistDialog.
 
     UglyDbHelper dbh;
 
+    private String sectionToOpen = "";
+
 
     public RecordOverviewFragment(){}
 
@@ -182,6 +184,15 @@ public class RecordOverviewFragment extends Fragment implements ChecklistDialog.
 		//return super.onOptionsItemSelected(item);
 	}
 
+
+
+
+    public void onStart() {
+        super.onStart();
+        //this.populateListViewFromDB(this.expListView);
+        this.setSectionToOpen(this.sectionToOpen);
+    }
+
 	/*****************************
 	 *
 	 * 			Public API
@@ -193,9 +204,14 @@ public class RecordOverviewFragment extends Fragment implements ChecklistDialog.
 	 *
 	 * @param SSID the SSID
 	 */
-	public void showDetailsForSSID(String SSID) {
+	public void showDetailsForSSID(Context context,  String SSID) {
 		Log.e("RecordOverviewFragment", "Implement showDetailsForSSID!!");
-	}
+        this.clearFilter();
+        int ESSID_INDEX = 2;
+        ArrayList<String> ssids = new ArrayList<String>();
+        this.sectionToOpen = SSID;
+        this.groupingKey = this.groupingTitles(context).get(ESSID_INDEX);
+  	}
 
 
 	/*****************************
@@ -258,6 +274,17 @@ public class RecordOverviewFragment extends Fragment implements ChecklistDialog.
         mylist.setAdapter(adapter);
 	}
 
+    private void setSectionToOpen(String s){
+        this.sectionToOpen = s;
+        if (this.sectionToOpen != null && this.sectionToOpen.length() != 0){
+            if (this.getGroupTitle().contains(this.sectionToOpen)){
+                int section = this.getGroupTitle().indexOf(this.sectionToOpen);
+                this.expListView.expandGroup(section);
+                this.sectionToOpen = "";
+            }
+        }
+    }
+
 	private Context getBaseContext(){
 		return this.getActivity().getBaseContext();
 	}
@@ -482,6 +509,14 @@ public class RecordOverviewFragment extends Fragment implements ChecklistDialog.
     	this.filter.clear();
 	}
 
+    public ArrayList<String> groupingTitles(Context context){
+        ArrayList<String> titles = new ArrayList<String>();
+        for (String groupTitle : context.getResources().getStringArray(
+                R.array.Grouping)) {
+            titles.add(groupTitle);
+        }
+        return titles;
+    }
     public ArrayList<String> groupingTitles(){
         ArrayList<String> titles = new ArrayList<String>();
         for (String groupTitle : this.getResources().getStringArray(
@@ -678,15 +713,17 @@ public class RecordOverviewFragment extends Fragment implements ChecklistDialog.
 
 		LatLng tudarmstadtLoc = new LatLng(49.86923, 8.6632768);
 
-		final int numSSIDs = 30;
+		final int numSSIDs = 10;
+		final int numUniqueSSIDs = 6;
 		final double ssidRadius = 0.1;
 		final double bssidRadius = 0.004;
-		int id = 0;
+		int id = 0; // global id
 		for (int ssid = 0; ssid < numSSIDs; ssid++) {
 			LatLng ssidLocation = new LatLng(tudarmstadtLoc.latitude - ssidRadius + 2.0 * ssidRadius * Math.random(), tudarmstadtLoc.longitude - ssidRadius + 2.0 * ssidRadius * Math.random());
 
-			String ssidName = "WiFi" + ssid;
-			int numBSSIDs = (random.nextInt() % 10) + 10;
+			String ssidName = "WiFi" + ((ssid % numUniqueSSIDs) + 1);
+
+			int numBSSIDs = (Math.abs(random.nextInt()) % 20) + 1;
 			for (int bssid = 0; bssid < numBSSIDs; bssid++) {
 				Record record = new Record();
 				record.setId(id);

+ 20 - 0
src/de/tudarmstadt/informatik/hostage/ui2/fragment/SettingsFragment.java

@@ -0,0 +1,20 @@
+package de.tudarmstadt.informatik.hostage.ui2.fragment;
+
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+
+import de.tudarmstadt.informatik.hostage.R;
+
+/**
+ * @author Alexander Brakowski
+ * @created 24.02.14 23:37
+ */
+public class SettingsFragment extends PreferenceFragment {
+
+	@Override
+	public void onCreate(Bundle savedInstanceState){
+		super.onCreate(savedInstanceState);
+
+		addPreferencesFromResource(R.xml.settings_preferences);
+	}
+}

+ 6 - 39
src/de/tudarmstadt/informatik/hostage/ui2/fragment/StatisticsFragment.java

@@ -38,6 +38,7 @@ import de.tudarmstadt.informatik.hostage.logging.UglyDbHelper;
 import de.tudarmstadt.informatik.hostage.ui.LogFilter;
 import de.tudarmstadt.informatik.hostage.ui2.adapter.StatisticListAdapter;
 import de.tudarmstadt.informatik.hostage.ui2.dialog.ChecklistDialog;
+import de.tudarmstadt.informatik.hostage.ui2.helper.ColorSequenceGenerator;
 import de.tudarmstadt.informatik.hostage.ui2.model.PlotComparisonItem;
 import de.tudarmstadt.informatik.hostage.ui2.popup.AbstractPopup;
 import de.tudarmstadt.informatik.hostage.ui2.popup.AbstractPopupItem;
@@ -935,45 +936,11 @@ public class StatisticsFragment extends Fragment implements ChecklistDialog.Chec
 
 
     public int getOtherColor(){
-        return Color.argb(255,0,0,0);
-    }
-
-     public ArrayList<Integer> getColorList(){
-        if (this.colorList == null){
-            this.colorList = new ArrayList<Integer>();
-            for (int i =0; i < 255; i++){
-                Integer color = this.generateColorForIndex(i);
-                this.colorList.add(color);
-            }
-        }
-        return this.colorList;
-    }
-
-    public Integer getColor(int index){
-        if (this.colorList == null) this.colorList = new ArrayList<Integer>();
-        if (index >= this.colorList.size()) {
-            for (int i = this.colorList.size(); i<= index; i++){
-                Integer color = this.generateColorForIndex(i);
-                this.colorList.add(color);
-            }
-            return this.generateColorForIndex(index);
-        }
-        return this.colorList.get(index);
-    }
-
-    public Integer generateColorForIndex(int index){
-        int r[] = new int[]{0,0,0,1,1,1,1};
-        int g[] = new int[]{0,1,1,1,1,0,0};
-        int b[] = new int[]{1,1,0,0,1,1,0};
-
-        int a = index % 16;
-        int n = index % 7;
-
-        int R = Math.max( (r[n] * 255) - ((Math.max(0,(a ))) * 16), 0);
-        int G = Math.max( (g[n] * 255) - ((Math.max(0,(a ))) * 16), 0);
-        int B = Math.max( (b[n] * 255) - ((Math.max(0,(a ))) * 16), 0);
-
-        return Color.argb(255,R,G,B);
+        return Color.argb(255, 80, 80, 80); // grey
+	}
+	
+    public Integer getColor(int index) {
+		return ColorSequenceGenerator.getColorForIndex(index);
     }
 
 

+ 190 - 51
src/de/tudarmstadt/informatik/hostage/ui2/fragment/ThreatMapFragment.java

@@ -4,13 +4,16 @@ import android.app.Fragment;
 import android.graphics.Color;
 import android.location.Location;
 import android.os.Bundle;
-import android.util.Log;
 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;
@@ -31,11 +34,25 @@ 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 {
-	private GoogleMap map = null;
-	private static View view = null;
+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<String, String> sMarkerIDToSSID = new HashMap<String, String>();
+
+	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
@@ -51,14 +68,50 @@ public class ThreatMapFragment extends Fragment implements GoogleMap.OnInfoWindo
 		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();
-		recordOverviewFragment.showDetailsForSSID(marker.getId());
-		Log.i("MARKER", ""+marker.getId());
+		if (recordOverviewFragment != null) {
+			String ssid = sMarkerIDToSSID.get(marker.getId());
+			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) {
@@ -67,86 +120,172 @@ public class ThreatMapFragment extends Fragment implements GoogleMap.OnInfoWindo
 		}
 	}
 
+	/**
+	 * 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<Record> records = dbh.getAllRecords();
 
-		HashMap<String, ArrayList<Point>> threatLocations = new HashMap<String, ArrayList<Point>>();
+		HashMap<String, ArrayList<SSIDArea>> threadAreas = new HashMap<String, ArrayList<SSIDArea>>();
 
 		for (Record record : records) {
 			LatLng location = new LatLng(record.getLatitude(), record.getLongitude());
-			ArrayList<Point> points;
-			if (threatLocations.containsKey(record.getSsid())) {
-				points = threatLocations.get(record.getSsid());
+			ArrayList<SSIDArea> 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 {
-				points = new ArrayList<Point>();
-				threatLocations.put(record.getSsid(), points);
+				areas = new ArrayList<SSIDArea>();
+				areas.add(new SSIDArea(location));
+				threadAreas.put(record.getSsid(), areas);
 			}
-			points.add(new Point(location.latitude, location.longitude));
 		}
 
-		final int maxNumAttacks = 20;
 		CircleOptions circleOptions = new CircleOptions().radius(200.0).fillColor(Color.argb(127, 240, 80, 60)).strokeWidth(0.0f);
-		for (Map.Entry<String, ArrayList<Point>> entry : threatLocations.entrySet()) {
+		for (Map.Entry<String, ArrayList<SSIDArea>> entry : threadAreas.entrySet()) {
 			String ssid = entry.getKey();
-			ArrayList<Point> points = entry.getValue();
-
-			// color
-			int threatLevel = points.size();
-			if (threatLevel > maxNumAttacks) threatLevel = maxNumAttacks;
-			float alpha = 1.0f - (float)(threatLevel-1) / (float)(maxNumAttacks-1);
-			int color = Color.argb(127, (int) (240.0 + 15.0 * alpha), (int) (80.0 + 175.0 * alpha), 60);
-
-			// radius
-			Point minimum = new Point(360.0, 360.0), maximum = new Point(-360.0, -360.0);
-			for (Point point : points) {
-				if (point.x < minimum.x) minimum.x = point.x;
-				if (point.x > maximum.x) maximum.x = point.x;
-				if (point.y < minimum.y) minimum.y = point.y;
-				if (point.y > maximum.y) maximum.y = point.y;
+			ArrayList<SSIDArea> 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);
 			}
-			LatLng center = new LatLng(0.5 * (minimum.x + maximum.x), 0.5 * (minimum.y + maximum.y));
-			float[] result = new float[1];
-			Location.distanceBetween(minimum.x, minimum.y, maximum.x, maximum.y, result);
-			float radius = 0.5f * result[0];
-			map.addCircle(circleOptions.center(center).radius(100.0 + radius).fillColor(color));
-			map.addMarker(new MarkerOptions().title(ssid + ": " + points.size() + (points.size() == 1 ? " attack" : " attacks")).position(
-					center));
 		}
 
-		map.setMyLocationEnabled(true);
-		map.setOnInfoWindowClickListener(this);
+		sMap.setMyLocationEnabled(true);
 
-		LatLng tudarmstadt = new LatLng(49.86923, 8.6632768);
-		//LatLng mapCenter = new LatLng(41.889, -87.622);
-
-		//Location myLocation = map.getMyLocation();
-		map.moveCamera(CameraUpdateFactory.newLatLngZoom(tudarmstadt, 13));
+		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 (view != null) {
-			ViewGroup parent = (ViewGroup) view.getParent();
+		if (sView != null) {
+			ViewGroup parent = (ViewGroup) sView.getParent();
 			if (parent != null)
-				parent.removeView(view);
+				parent.removeView(sView);
 		}
 		
 		try {
-			view = inflater.inflate(R.layout.fragment_threatmap, container, false);
+			sView = inflater.inflate(R.layout.fragment_threatmap, container, false);
 			if (isGooglePlay()) {
-				map = ((MapFragment) getFragmentManager()
+				sMap = ((MapFragment) getFragmentManager()
 						.findFragmentById(R.id.threatmapfragment)).getMap();
 				populateMap();
 			}
 		} catch (InflateException e) {
         	// map already exists
-			e.printStackTrace();
+			//e.printStackTrace();
+		}
+		if (sMap != null) {
+			sMap.setOnInfoWindowClickListener(this);
 		}
 
-		return view;
+		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();
+		}
 	}
 }

+ 27 - 0
src/de/tudarmstadt/informatik/hostage/ui2/helper/ColorSequenceGenerator.java

@@ -0,0 +1,27 @@
+package de.tudarmstadt.informatik.hostage.ui2.helper;
+
+import android.graphics.Color;
+
+/**
+ * Idea from http://ridiculousfish.com/blog/posts/colors.html
+ * Created by Fabio Arnold on 25.02.14.
+ */
+public class ColorSequenceGenerator {
+	private static final int BIT_COUNT = 31; // sadly there is no unsigned type in java
+	public static int getColorForIndex(int index) {
+		int reverseIndex = 0;
+		for (int i = 0; i  < BIT_COUNT; i++) {
+			reverseIndex = (reverseIndex << 1) | (index & 1);
+			index >>= 1;
+		}
+		float hue = ((float)reverseIndex / (float)(1 << BIT_COUNT) + 0.6f) % 1.0f;
+
+
+		float[] hsv = new float[3];
+		hsv[0] = 360.0f * hue;
+		hsv[1] = 0.7f; // not fully saturated
+		hsv[2] = 1.0f;
+
+		return Color.HSVToColor(hsv);
+	}
+}