package net.wigle.wigleandroid;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import android.annotation.SuppressLint;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.location.Address;
import android.location.Location;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import net.wigle.wigleandroid.background.QueryThread;
import net.wigle.wigleandroid.model.ConcurrentLinkedHashMap;
import net.wigle.wigleandroid.model.Network;
import net.wigle.wigleandroid.model.QueryArgs;
public class DBResultActivity extends ActionBarActivity {
private static final int MENU_RETURN = 12;
private static final int MSG_QUERY_DONE = 2;
private static final int LIMIT = 50;
private static final int DEFAULT_ZOOM = 18;
private NetworkListAdapter listAdapter;
private MapView mapView;
private MapRender mapRender;
private final List<Network> resultList = new ArrayList<>();
private final ConcurrentLinkedHashMap<LatLng, Integer> obsMap = new ConcurrentLinkedHashMap<>();
@Override
public void onCreate( final Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
// set language
MainActivity.setLocale( this );
setContentView( R.layout.dbresult );
// force media volume controls
setVolumeControlStream( AudioManager.STREAM_MUSIC );
setupList();
QueryArgs queryArgs = ListFragment.lameStatic.queryArgs;
MainActivity.info("queryArgs: " + queryArgs);
final TextView tv = (TextView) findViewById( R.id.dbstatus );
if ( queryArgs != null ) {
tv.setText( getString(R.string.status_working) + "...");
Address address = queryArgs.getAddress();
LatLng center = MappingFragment.DEFAULT_POINT;
if ( address != null ) {
center = new LatLng(address.getLatitude(), address.getLongitude());
}
setupMap( center, savedInstanceState );
setupQuery( queryArgs );
}
else {
tv.setText(getString(R.string.status_fail) + "...");
}
}
private void setupList() {
// not set by nonconfig retain
listAdapter = new NetworkListAdapter( getApplicationContext(), R.layout.row );
final ListView listView = (ListView) findViewById( R.id.dblist );
ListFragment.setupListAdapter( listView, MainActivity.getMainActivity(), listAdapter, true );
}
private void setupMap( final LatLng center, final Bundle savedInstanceState ) {
mapView = new MapView( this );
mapView.onCreate(savedInstanceState);
MapsInitializer.initialize(this);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(final GoogleMap googleMap) {
mapRender = new MapRender(DBResultActivity.this, googleMap, true);
if (center != null) {
final CameraPosition cameraPosition = new CameraPosition.Builder()
.target(center).zoom(DEFAULT_ZOOM).build();
googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
}
});
final RelativeLayout rlView = (RelativeLayout) findViewById( R.id.db_map_rl );
rlView.addView( mapView );
}
@SuppressLint("HandlerLeak")
private void setupQuery( final QueryArgs queryArgs ) {
final Address address = queryArgs.getAddress();
// what runs on the gui thread
final Handler handler = new Handler() {
@Override
public void handleMessage( final Message msg ) {
final TextView tv = (TextView) findViewById( R.id.dbstatus );
if ( msg.what == MSG_QUERY_DONE ) {
tv.setText( getString(R.string.status_success) );
listAdapter.clear();
boolean first = true;
for ( final Network network : resultList ) {
listAdapter.add( network );
if ( address == null && first ) {
final LatLng center = MappingFragment.getCenter( DBResultActivity.this, network.getLatLng(), null );
MainActivity.info( "set center: " + center + " network: " + network.getSsid()
+ " point: " + network.getLatLng());
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(final GoogleMap googleMap) {
final CameraPosition cameraPosition = new CameraPosition.Builder()
.target(center).zoom(DEFAULT_ZOOM).build();
googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
});
first = false;
}
if (network.getLatLng() != null && mapRender != null) {
mapRender.addItem(network);
}
}
resultList.clear();
}
}
};
String sql = "SELECT bssid,lastlat,lastlon FROM " + DatabaseHelper.NETWORK_TABLE + " WHERE 1=1 ";
final String ssid = queryArgs.getSSID();
final String bssid = queryArgs.getBSSID();
boolean limit = false;
if ( ssid != null && ! "".equals(ssid) ) {
sql += " AND ssid like " + DatabaseUtils.sqlEscapeString(ssid);
limit = true;
}
if ( bssid != null && ! "".equals(bssid) ) {
sql += " AND bssid like " + DatabaseUtils.sqlEscapeString(bssid);
limit = true;
}
if ( address != null ) {
final double diff = 0.1d;
final double lat = address.getLatitude();
final double lon = address.getLongitude();
sql += " AND lastlat > '" + (lat - diff) + "' AND lastlat < '" + (lat + diff) + "'";
sql += " AND lastlon > '" + (lon - diff) + "' AND lastlon < '" + (lon + diff) + "'";
}
if ( limit ) {
sql += " LIMIT " + LIMIT;
}
final TreeMap<Float,String> top = new TreeMap<>();
final float[] results = new float[1];
final long[] count = new long[1];
final QueryThread.Request request = new QueryThread.Request( sql, new QueryThread.ResultHandler() {
@Override
public boolean handleRow( final Cursor cursor ) {
final String bssid = cursor.getString(0);
final float lat = cursor.getFloat(1);
final float lon = cursor.getFloat(2);
count[0]++;
if ( address == null ) {
top.put( (float) count[0], bssid );
}
else {
Location.distanceBetween( lat, lon, address.getLatitude(), address.getLongitude(), results );
final float meters = results[0];
if ( top.size() <= LIMIT ) {
putWithBackoff( top, bssid, meters );
}
else {
Float last = top.lastKey();
if ( meters < last ) {
top.remove( last );
putWithBackoff( top, bssid, meters );
}
}
}
return true;
}
@Override
public void complete() {
for ( final String bssid : top.values() ) {
final Network network = ListFragment.lameStatic.dbHelper.getNetwork( bssid );
resultList.add( network );
final LatLng point = network.getLatLng();
if (point != null) {
obsMap.put(point, 0);
}
}
handler.sendEmptyMessage( MSG_QUERY_DONE );
if ( mapView != null ) {
// force a redraw
mapView.postInvalidate();
}
}
});
// queue it up
ListFragment.lameStatic.dbHelper.addToQueue( request );
}
private static void putWithBackoff( TreeMap<Float,String> top, String s, float diff ) {
String old = top.put( diff, s );
// protect against infinite loops
int count = 0;
while ( old != null && count < 1000 ) {
// ut oh, two at the same difference away. add a slight bit and put it back
// info( "collision at diff: " + diff + " old: " + old.getCallsign() + " orig: " + s.getCallsign() );
diff += 0.0001f;
old = top.put( diff, old );
count++;
}
}
/* Creates the menu items */
@Override
public boolean onCreateOptionsMenu( final Menu menu ) {
MenuItem item = menu.add(0, MENU_RETURN, 0, getString(R.string.menu_return));
item.setIcon( android.R.drawable.ic_media_previous );
return true;
}
/* Handles item selections */
@Override
public boolean onOptionsItemSelected( final MenuItem item ) {
switch ( item.getItemId() ) {
case MENU_RETURN:
finish();
return true;
}
return false;
}
@Override
public void onDestroy() {
mapView.onDestroy();
super.onDestroy();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
if (mapRender != null) {
// save memory
mapRender.clear();
}
}
@Override
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
}