package uk.co.thomseddon.relay;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener;
public class MainActivity extends Activity {
private static String TAG = "RE";
private boolean mDiscovering = true;
public static final String PREFS_NAME = "RelayPrefs";
Button mButton;
Handler mHandler;
Thread mThread;
WifiManager.MulticastLock mLock;
JmDNS mJmDns;
ServiceListener mListener;
String mService = "_relay._tcp.local.";
ArrayList<String> mHostNames;
ArrayList<String> mHosts;
ArrayAdapter<String> mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "onCreate");
// Discover button
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.i(TAG, "Click: discovering=" + (mDiscovering ? "true" : "false"));
if (mDiscovering) {
stopDiscovery();
} else {
startDiscovery();
}
}
});
// Handler
mHandler = new Handler();
// List
mHostNames = new ArrayList<>();
mHosts = new ArrayList<>();
mAdapter = new ArrayAdapter<>(this, R.layout.list_item_host, mHostNames);
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(mAdapter);
final Context context = this;
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
// Start service
Intent intent = new Intent(context, SocketService.class);
String uri = mHosts.get(i);
intent.putExtra("uri", uri);
startService(intent);
// Save connection
addTrustedHost(uri);
// Stop discovery
stopDiscovery();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onPause() {
super.onPause();
if (mDiscovering)
stopDiscovery();
}
@Override
protected void onResume() {
super.onResume();
if (mDiscovering)
startDiscovery();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mDiscovering)
stopDiscovery();
}
private void startDiscovery() {
Log.i(TAG, "Start discovery");
mButton.setText("Stop discovery");
mDiscovering = true;
mHosts.clear();
mHostNames.clear();
if (mListener == null)
initListener();
mThread = new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "RUN");
WifiManager wifi = (WifiManager) getSystemService(WIFI_SERVICE);
mLock = wifi.createMulticastLock("relay");
mLock.setReferenceCounted(true);
mLock.acquire();
try {
mJmDns = JmDNS.create();
mJmDns.addServiceListener(mService, mListener);
Log.i(TAG, "Added service listener");
} catch (IOException e) {
e.printStackTrace();
}
}
});
mThread.start();
}
private void stopDiscovery() {
mButton.setEnabled(false);
new StopDiscovery().execute();
}
private void initListener() {
Log.i(TAG, "initListener");
mListener = new ServiceListener() {
@Override
public void serviceAdded(ServiceEvent serviceEvent) {
Log.i(TAG, "Service added");
mJmDns.requestServiceInfo(serviceEvent.getType(), serviceEvent.getName(), 1);
}
@Override
public void serviceRemoved(ServiceEvent serviceEvent) {
Log.i(TAG, "Service removed");
}
@Override
public void serviceResolved(ServiceEvent serviceEvent) {
Log.i(TAG, "Service resolved: " +
serviceEvent.getInfo().getInetAddresses()[0].getHostAddress());
ServiceInfo info = serviceEvent.getInfo();
String address = info.getInet4Addresses()[0].getHostAddress();
int port = info.getPort();
String uri = "ws://" + address + ":" + port;
if (trustHost(uri)) {
// Connect
Intent intent = new Intent(getApplicationContext(), SocketService.class);
intent.putExtra("uri", uri);
startService(intent);
// Stop discovery
stopDiscovery();
} else {
// Update UI
mHostNames.add(info.getName());
mHosts.add(uri);
mHandler.post(new Runnable() {
@Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
}
}
};
}
private boolean trustHost(String host) {
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, 0);
String trusted = prefs.getString("trustedHosts", "");
Log.i(TAG, "Trusted: " + trusted);
StringTokenizer tokens = new StringTokenizer(trusted, ",");
while (tokens.hasMoreTokens()) {
if (tokens.nextToken().equals(host))
return true;
}
return false;
}
private void addTrustedHost(String host) {
if (trustHost(host))
return;
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, 0);
String trusted = prefs.getString("trustedHosts", "");
trusted += (trusted.length() > 0 ? "," : "") + host;
SharedPreferences.Editor editor = prefs.edit();
editor.putString("trustedHosts", trusted);
editor.commit();
}
class StopDiscovery extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
Log.i(TAG, "Stop discovery");
if (mJmDns == null)
return null;
if (mListener != null) {
mJmDns.removeServiceListener(mService, mListener);
}
try {
mJmDns.close();
} catch (IOException e) {
e.printStackTrace();
}
mJmDns = null;
mLock.release();
mThread = null;
return null;
}
@Override
public void onPostExecute(Void voids) {
mDiscovering = false;
mButton.setEnabled(true);
mButton.setText("Discover");
}
}
}