package net.wigle.wigleandroid; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Map; import android.annotation.SuppressLint; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Color; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.DialogFragment; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; import android.text.ClipboardManager; import android.text.InputType; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.ImageView; 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.CircleOptions; 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.NetworkType; import net.wigle.wigleandroid.model.OUI; @SuppressWarnings("deprecation") public class NetworkActivity extends ActionBarActivity implements DialogListener { private static final int MENU_EXIT = 11; private static final int MENU_COPY = 12; private static final int NON_CRYPTO_DIALOG = 130; private static final int MSG_OBS_UPDATE = 1; private static final int MSG_OBS_DONE = 2; private static final int DEFAULT_ZOOM = 18; private Network network; private MapView mapView; private int observations = 0; private boolean isDbResult = false; private final ConcurrentLinkedHashMap<LatLng, Integer> obsMap = new ConcurrentLinkedHashMap<>(512); // used for shutting extraneous activities down on an error public static NetworkActivity networkActivity; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { MainActivity.info("NET: onCreate"); super.onCreate(savedInstanceState); if (ListFragment.lameStatic.oui == null) { ListFragment.lameStatic.oui = new OUI(getAssets()); } final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } // set language MainActivity.setLocale( this ); setContentView(R.layout.network); networkActivity = this; final Intent intent = getIntent(); final String bssid = intent.getStringExtra( ListFragment.NETWORK_EXTRA_BSSID ); isDbResult = intent.getBooleanExtra(ListFragment.NETWORK_EXTRA_IS_DB_RESULT, false); MainActivity.info( "bssid: " + bssid + " isDbResult: " + isDbResult); network = MainActivity.getNetworkCache().get(bssid); SimpleDateFormat format = NetworkListAdapter.getConstructionTimeFormater(this); TextView tv = (TextView) findViewById( R.id.bssid ); tv.setText( bssid ); if ( network == null ) { MainActivity.info( "no network found in cache for bssid: " + bssid ); } else { // do gui work tv = (TextView) findViewById( R.id.ssid ); tv.setText( network.getSsid() ); final String ouiString = network.getOui(ListFragment.lameStatic.oui); tv = (TextView) findViewById( R.id.oui ); tv.setText( ouiString ); final int image = NetworkListAdapter.getImage( network ); final ImageView ico = (ImageView) findViewById( R.id.wepicon ); ico.setImageResource( image ); final ImageView ico2 = (ImageView) findViewById( R.id.wepicon2 ); ico2.setImageResource( image ); tv = (TextView) findViewById( R.id.na_signal ); final int level = network.getLevel(); tv.setTextColor( NetworkListAdapter.getSignalColor( level ) ); tv.setText( Integer.toString( level ) ); tv = (TextView) findViewById( R.id.na_type ); tv.setText( network.getType().name() ); tv = (TextView) findViewById( R.id.na_firsttime ); tv.setText( NetworkListAdapter.getConstructionTime(format, network ) ); tv = (TextView) findViewById( R.id.na_chan ); if ( ! NetworkType.WIFI.equals(network.getType()) ) { tv.setText( getString(R.string.na) ); } else { Integer chan = network.getChannel(); chan = chan != null ? chan : network.getFrequency(); tv.setText( " " + Integer.toString(chan) + " " ); } tv = (TextView) findViewById( R.id.na_cap ); tv.setText( " " + network.getCapabilities().replace("][", "]\n[") ); setupMap( network, savedInstanceState ); // kick off the query now that we have our map setupQuery(); setupButton( network ); } } @Override public void onDestroy() { MainActivity.info("NET: onDestroy"); networkActivity = null; if (mapView != null) { mapView.onDestroy(); } super.onDestroy(); } @Override public void onResume() { MainActivity.info("NET: onResume"); super.onResume(); if (mapView != null) { mapView.onResume(); } } @Override public void onPause() { MainActivity.info("NET: onPause"); super.onPause(); if (mapView != null) { mapView.onPause(); } } @Override public void onSaveInstanceState(final Bundle outState) { MainActivity.info("NET: onSaveInstanceState"); super.onSaveInstanceState(outState); if (mapView != null) { mapView.onSaveInstanceState(outState); } } @Override public void onLowMemory() { MainActivity.info("NET: onLowMemory"); super.onLowMemory(); if (mapView != null) { mapView.onLowMemory(); } } @SuppressLint("HandlerLeak") private void setupQuery() { // 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.na_observe ); if ( msg.what == MSG_OBS_UPDATE ) { tv.setText( " " + Integer.toString( observations ) + "..."); } else if ( msg.what == MSG_OBS_DONE ) { tv.setText( " " + Integer.toString( observations ) ); mapView.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(final GoogleMap googleMap) { int count = 0; for (Map.Entry<LatLng, Integer> obs : obsMap.entrySet()) { final LatLng latLon = obs.getKey(); final int level = obs.getValue(); if (count == 0 && network.getLatLng() == null) { final CameraPosition cameraPosition = new CameraPosition.Builder() .target(latLon).zoom(DEFAULT_ZOOM).build(); googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); } googleMap.addCircle(new CircleOptions() .center(latLon) .radius(4) .fillColor(NetworkListAdapter.getSignalColor(level, true)) .strokeWidth(0) .zIndex(level)); count++; } MainActivity.info("observation count: " + count); } }); } } }; final String sql = "SELECT level,lat,lon FROM " + DatabaseHelper.LOCATION_TABLE + " WHERE bssid = '" + network.getBssid() + "' limit " + obsMap.maxSize(); final QueryThread.Request request = new QueryThread.Request( sql, new QueryThread.ResultHandler() { @Override public boolean handleRow( final Cursor cursor ) { observations++; obsMap.put( new LatLng( cursor.getFloat(1), cursor.getFloat(2) ), cursor.getInt(0) ); if ( ( observations % 10 ) == 0 ) { // change things on the gui thread handler.sendEmptyMessage( MSG_OBS_UPDATE ); } return true; } @Override public void complete() { handler.sendEmptyMessage( MSG_OBS_DONE ); } }); ListFragment.lameStatic.dbHelper.addToQueue( request ); } private void setupMap( final Network network, final Bundle savedInstanceState ) { mapView = new MapView( this ); try { mapView.onCreate(savedInstanceState); } catch (NullPointerException ex) { MainActivity.error("npe in mapView.onCreate: " + ex, ex); } MapsInitializer.initialize( this ); if (network.getLatLng() != null) { mapView.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(final GoogleMap googleMap) { final CameraPosition cameraPosition = new CameraPosition.Builder() .target(network.getLatLng()).zoom(DEFAULT_ZOOM).build(); googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); googleMap.addCircle(new CircleOptions() .center(network.getLatLng()) .radius(5) .fillColor(Color.argb(128, 240, 240, 240)) .strokeColor(Color.argb(200, 255, 32, 32)) .strokeWidth(3f) .zIndex(100)); } }); } final RelativeLayout rlView = (RelativeLayout) findViewById( R.id.netmap_rl ); rlView.addView( mapView ); } private void setupButton( final Network network ) { final Button connectButton = (Button) findViewById( R.id.connect_button ); if ( ! NetworkType.WIFI.equals(network.getType()) ) { connectButton.setEnabled( false ); } connectButton.setOnClickListener( new OnClickListener() { @Override public void onClick( final View buttonView ) { if ( Network.CRYPTO_NONE == network.getCrypto() ) { MainActivity.createConfirmation( NetworkActivity.this, "You have permission to access this network?", 0, NON_CRYPTO_DIALOG); } else { final CryptoDialog cryptoDialog = CryptoDialog.newInstance(network); try { cryptoDialog.show(NetworkActivity.this.getSupportFragmentManager(), "crypto-dialog"); } catch (final IllegalStateException ex) { MainActivity.error("exception showing crypto dialog: " + ex, ex); } } } }); } private int getExistingSsid( final String ssid ) { final WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); final String quotedSsid = "\"" + ssid + "\""; int netId = -2; for ( final WifiConfiguration config : wifiManager.getConfiguredNetworks() ) { MainActivity.info( "bssid: " + config.BSSID + " ssid: " + config.SSID + " status: " + config.status + " id: " + config.networkId + " preSharedKey: " + config.preSharedKey + " priority: " + config.priority + " wepTxKeyIndex: " + config.wepTxKeyIndex + " allowedAuthAlgorithms: " + config.allowedAuthAlgorithms + " allowedGroupCiphers: " + config.allowedGroupCiphers + " allowedKeyManagement: " + config.allowedKeyManagement + " allowedPairwiseCiphers: " + config.allowedPairwiseCiphers + " allowedProtocols: " + config.allowedProtocols + " hiddenSSID: " + config.hiddenSSID + " wepKeys: " + Arrays.toString( config.wepKeys ) ); if ( quotedSsid.equals( config.SSID ) ) { netId = config.networkId; break; } } return netId; } @Override public void handleDialog(final int dialogId) { switch(dialogId) { case NON_CRYPTO_DIALOG: connectToNetwork( null ); break; default: MainActivity.warn("Network unhandled dialogId: " + dialogId); } } private void connectToNetwork( final String password ) { final int preExistingNetId = getExistingSsid( network.getSsid() ); final WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService( Context.WIFI_SERVICE ); int netId = -2; if ( preExistingNetId < 0 ) { final WifiConfiguration newConfig = new WifiConfiguration(); newConfig.SSID = "\"" + network.getSsid() + "\""; newConfig.hiddenSSID = false; if ( password != null ) { if ( Network.CRYPTO_WEP == network.getCrypto() ) { newConfig.wepKeys = new String[]{ "\"" + password + "\"" }; } else { newConfig.preSharedKey = "\"" + password + "\""; } } netId = wifiManager.addNetwork( newConfig ); } if ( netId >= 0 ) { final boolean disableOthers = true; wifiManager.enableNetwork(netId, disableOthers); } } public static class CryptoDialog extends DialogFragment { public static CryptoDialog newInstance(final Network network) { final CryptoDialog frag = new CryptoDialog(); Bundle args = new Bundle(); args.putString("ssid", network.getSsid()); args.putString("capabilities", network.getCapabilities()); args.putString("level", Integer.toString(network.getLevel())); frag.setArguments(args); return frag; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final Dialog dialog = getDialog(); View view = inflater.inflate(R.layout.cryptodialog, container); dialog.setTitle(getArguments().getString("ssid")); TextView text = (TextView) view.findViewById( R.id.security ); text.setText(getArguments().getString("capabilities")); text = (TextView) view.findViewById( R.id.signal ); text.setText(getArguments().getString("level")); final Button ok = (Button) view.findViewById( R.id.ok_button ); final EditText password = (EditText) view.findViewById( R.id.edit_password ); password.addTextChangedListener( new SettingsFragment.SetWatcher() { @Override public void onTextChanged( final String s ) { if ( s.length() > 0 ) { ok.setEnabled(true); } } }); final CheckBox showpass = (CheckBox) view.findViewById( R.id.showpass ); showpass.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged( final CompoundButton buttonView, final boolean isChecked ) { if ( isChecked ) { password.setInputType( InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ); password.setTransformationMethod( null ); } else { password.setInputType( InputType.TYPE_TEXT_VARIATION_PASSWORD ); password.setTransformationMethod( android.text.method.PasswordTransformationMethod.getInstance() ); } } }); ok.setOnClickListener( new OnClickListener() { @Override public void onClick( final View buttonView ) { try { final NetworkActivity networkActivity = (NetworkActivity) getActivity(); networkActivity.connectToNetwork( password.getText().toString() ); dialog.dismiss(); } catch ( Exception ex ) { // guess it wasn't there anyways MainActivity.info( "exception dismissing crypto dialog: " + ex ); } } } ); Button cancel = (Button) view.findViewById( R.id.cancel_button ); cancel.setOnClickListener( new OnClickListener() { @Override public void onClick( final View buttonView ) { try { dialog.dismiss(); } catch ( Exception ex ) { // guess it wasn't there anyways MainActivity.info( "exception dismissing crypto dialog: " + ex ); } } } ); return view; } } /* Creates the menu items */ @Override public boolean onCreateOptionsMenu( final Menu menu ) { MenuItem item = menu.add(0, MENU_COPY, 0, getString(R.string.menu_copy_network)); item.setIcon( android.R.drawable.ic_menu_save ); item = menu.add(0, MENU_EXIT, 0, getString(R.string.menu_return)); item.setIcon( android.R.drawable.ic_menu_revert ); return true; } /* Handles item selections */ @Override public boolean onOptionsItemSelected( final MenuItem item ) { switch ( item.getItemId() ) { case MENU_EXIT: // call over to finish finish(); return true; case MENU_COPY: // copy the netid if (network != null) { ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); clipboard.setText(network.getBssid()); } return true; case android.R.id.home: // MainActivity.info("NETWORK: actionbar back"); if (isDbResult) { // don't go back to main activity finish(); return true; } } return false; } }