Initial commit of NsdChat application
Change-Id: I157ddfe38b79f01e72e8b52fa16d666c92f08254
This commit is contained in:
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.android.nsdchat;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
public class ChatConnection {
|
||||
|
||||
private Handler mUpdateHandler;
|
||||
private ChatServer mChatServer;
|
||||
private ChatClient mChatClient;
|
||||
|
||||
private static final String TAG = "ChatConnection";
|
||||
|
||||
private Socket mSocket;
|
||||
private int mPort = -1;
|
||||
|
||||
public ChatConnection(Handler handler) {
|
||||
mUpdateHandler = handler;
|
||||
mChatServer = new ChatServer(handler);
|
||||
}
|
||||
|
||||
public void tearDown() {
|
||||
mChatServer.tearDown();
|
||||
mChatClient.tearDown();
|
||||
}
|
||||
|
||||
public void connectToServer(InetAddress address, int port) {
|
||||
mChatClient = new ChatClient(address, port);
|
||||
}
|
||||
|
||||
public void sendMessage(String msg) {
|
||||
if (mChatClient != null) {
|
||||
mChatClient.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public int getLocalPort() {
|
||||
return mPort;
|
||||
}
|
||||
|
||||
public void setLocalPort(int port) {
|
||||
mPort = port;
|
||||
}
|
||||
|
||||
|
||||
public synchronized void updateMessages(String msg, boolean local) {
|
||||
Log.e(TAG, "Updating message: " + msg);
|
||||
|
||||
if (local) {
|
||||
msg = "me: " + msg;
|
||||
} else {
|
||||
msg = "them: " + msg;
|
||||
}
|
||||
|
||||
Bundle messageBundle = new Bundle();
|
||||
messageBundle.putString("msg", msg);
|
||||
|
||||
Message message = new Message();
|
||||
message.setData(messageBundle);
|
||||
mUpdateHandler.sendMessage(message);
|
||||
|
||||
}
|
||||
|
||||
private synchronized void setSocket(Socket socket) {
|
||||
Log.d(TAG, "setSocket being called.");
|
||||
if (socket == null) {
|
||||
Log.d(TAG, "Setting a null socket.");
|
||||
}
|
||||
if (mSocket != null) {
|
||||
if (mSocket.isConnected()) {
|
||||
try {
|
||||
mSocket.close();
|
||||
} catch (IOException e) {
|
||||
// TODO(alexlucas): Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
mSocket = socket;
|
||||
}
|
||||
|
||||
private Socket getSocket() {
|
||||
return mSocket;
|
||||
}
|
||||
|
||||
private class ChatServer {
|
||||
ServerSocket mServerSocket = null;
|
||||
Thread mThread = null;
|
||||
|
||||
public ChatServer(Handler handler) {
|
||||
mThread = new Thread(new ServerThread());
|
||||
mThread.start();
|
||||
}
|
||||
|
||||
public void tearDown() {
|
||||
mThread.interrupt();
|
||||
try {
|
||||
mServerSocket.close();
|
||||
} catch (IOException ioe) {
|
||||
Log.e(TAG, "Error when closing server socket.");
|
||||
}
|
||||
}
|
||||
|
||||
class ServerThread implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
// Since discovery will happen via Nsd, we don't need to care which port is
|
||||
// used. Just grab an available one and advertise it via Nsd.
|
||||
mServerSocket = new ServerSocket(0);
|
||||
setLocalPort(mServerSocket.getLocalPort());
|
||||
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
Log.d(TAG, "ServerSocket Created, awaiting connection");
|
||||
setSocket(mServerSocket.accept());
|
||||
Log.d(TAG, "Connected.");
|
||||
if (mChatClient == null) {
|
||||
int port = mSocket.getPort();
|
||||
InetAddress address = mSocket.getInetAddress();
|
||||
connectToServer(address, port);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error creating ServerSocket: ", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ChatClient {
|
||||
|
||||
private InetAddress mAddress;
|
||||
private int PORT;
|
||||
|
||||
private final String CLIENT_TAG = "ChatClient";
|
||||
|
||||
private Thread mSendThread;
|
||||
private Thread mRecThread;
|
||||
|
||||
public ChatClient(InetAddress address, int port) {
|
||||
|
||||
Log.d(CLIENT_TAG, "Creating chatClient");
|
||||
this.mAddress = address;
|
||||
this.PORT = port;
|
||||
|
||||
mSendThread = new Thread(new SendingThread());
|
||||
mSendThread.start();
|
||||
}
|
||||
|
||||
class SendingThread implements Runnable {
|
||||
|
||||
BlockingQueue<String> mMessageQueue;
|
||||
private int QUEUE_CAPACITY = 10;
|
||||
|
||||
public SendingThread() {
|
||||
mMessageQueue = new ArrayBlockingQueue<String>(QUEUE_CAPACITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (getSocket() == null) {
|
||||
setSocket(new Socket(mAddress, PORT));
|
||||
Log.d(CLIENT_TAG, "Client-side socket initialized.");
|
||||
|
||||
} else {
|
||||
Log.d(CLIENT_TAG, "Socket already initialized. skipping!");
|
||||
}
|
||||
|
||||
mRecThread = new Thread(new ReceivingThread());
|
||||
mRecThread.start();
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
Log.d(CLIENT_TAG, "Initializing socket failed, UHE", e);
|
||||
} catch (IOException e) {
|
||||
Log.d(CLIENT_TAG, "Initializing socket failed, IOE.", e);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
String msg = mMessageQueue.take();
|
||||
sendMessage(msg);
|
||||
} catch (InterruptedException ie) {
|
||||
Log.d(CLIENT_TAG, "Message sending loop interrupted, exiting");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReceivingThread implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
BufferedReader input;
|
||||
try {
|
||||
input = new BufferedReader(new InputStreamReader(
|
||||
mSocket.getInputStream()));
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
|
||||
String messageStr = null;
|
||||
messageStr = input.readLine();
|
||||
if (messageStr != null) {
|
||||
Log.d(CLIENT_TAG, "Read from the stream: " + messageStr);
|
||||
updateMessages(messageStr, false);
|
||||
} else {
|
||||
Log.d(CLIENT_TAG, "The nulls! The nulls!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
input.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(CLIENT_TAG, "Server loop error: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void tearDown() {
|
||||
try {
|
||||
getSocket().close();
|
||||
} catch (IOException ioe) {
|
||||
Log.e(CLIENT_TAG, "Error when closing server socket.");
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(String msg) {
|
||||
try {
|
||||
Socket socket = getSocket();
|
||||
if (socket == null) {
|
||||
Log.d(CLIENT_TAG, "Socket is null, wtf?");
|
||||
} else if (socket.getOutputStream() == null) {
|
||||
Log.d(CLIENT_TAG, "Socket output stream is null, wtf?");
|
||||
}
|
||||
|
||||
PrintWriter out = new PrintWriter(
|
||||
new BufferedWriter(
|
||||
new OutputStreamWriter(getSocket().getOutputStream())), true);
|
||||
out.println(msg);
|
||||
out.flush();
|
||||
updateMessages(msg, true);
|
||||
} catch (UnknownHostException e) {
|
||||
Log.d(CLIENT_TAG, "Unknown Host", e);
|
||||
} catch (IOException e) {
|
||||
Log.d(CLIENT_TAG, "I/O Exception", e);
|
||||
} catch (Exception e) {
|
||||
Log.d(CLIENT_TAG, "Error3", e);
|
||||
}
|
||||
Log.d(CLIENT_TAG, "Client sent message: " + msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.android.nsdchat;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.nsd.NsdServiceInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.example.android.nsdchat.NsdHelper;
|
||||
|
||||
public class NsdChatActivity extends Activity {
|
||||
|
||||
NsdHelper mNsdHelper;
|
||||
|
||||
private TextView mStatusView;
|
||||
private Handler mUpdateHandler;
|
||||
|
||||
public static final String TAG = "NsdChat";
|
||||
|
||||
ChatConnection mConnection;
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.main);
|
||||
mStatusView = (TextView) findViewById(R.id.status);
|
||||
|
||||
mUpdateHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
String chatLine = msg.getData().getString("msg");
|
||||
addChatLine(chatLine);
|
||||
}
|
||||
};
|
||||
|
||||
mConnection = new ChatConnection(mUpdateHandler);
|
||||
|
||||
mNsdHelper = new NsdHelper(this);
|
||||
mNsdHelper.initializeNsd();
|
||||
|
||||
}
|
||||
|
||||
public void clickAdvertise(View v) {
|
||||
// Register service
|
||||
if(mConnection.getLocalPort() > -1) {
|
||||
mNsdHelper.registerService(mConnection.getLocalPort());
|
||||
} else {
|
||||
Log.d(TAG, "ServerSocket isn't bound.");
|
||||
}
|
||||
}
|
||||
|
||||
public void clickDiscover(View v) {
|
||||
mNsdHelper.discoverServices();
|
||||
}
|
||||
|
||||
public void clickConnect(View v) {
|
||||
NsdServiceInfo service = mNsdHelper.getChosenServiceInfo();
|
||||
if (service != null) {
|
||||
Log.d(TAG, "Connecting.");
|
||||
mConnection.connectToServer(service.getHost(),
|
||||
service.getPort());
|
||||
} else {
|
||||
Log.d(TAG, "No service to connect to!");
|
||||
}
|
||||
}
|
||||
|
||||
public void clickSend(View v) {
|
||||
EditText messageView = (EditText) this.findViewById(R.id.chatInput);
|
||||
if (messageView != null) {
|
||||
String messageString = messageView.getText().toString();
|
||||
if (!messageString.isEmpty()) {
|
||||
mConnection.sendMessage(messageString);
|
||||
}
|
||||
messageView.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
public void addChatLine(String line) {
|
||||
mStatusView.append("\n" + line);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (mNsdHelper != null) {
|
||||
mNsdHelper.stopDiscovery();
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (mNsdHelper != null) {
|
||||
mNsdHelper.discoverServices();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
mNsdHelper.tearDown();
|
||||
mConnection.tearDown();
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.android.nsdchat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.nsd.NsdServiceInfo;
|
||||
import android.net.nsd.NsdManager;
|
||||
import android.util.Log;
|
||||
|
||||
public class NsdHelper {
|
||||
|
||||
Context mContext;
|
||||
|
||||
NsdManager mNsdManager;
|
||||
NsdManager.ResolveListener mResolveListener;
|
||||
NsdManager.DiscoveryListener mDiscoveryListener;
|
||||
NsdManager.RegistrationListener mRegistrationListener;
|
||||
|
||||
public static final String SERVICE_TYPE = "_http._tcp.";
|
||||
|
||||
public static final String TAG = "NsdHelper";
|
||||
public String mServiceName = "NsdChat";
|
||||
|
||||
NsdServiceInfo mService;
|
||||
|
||||
public NsdHelper(Context context) {
|
||||
mContext = context;
|
||||
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
|
||||
}
|
||||
|
||||
public void initializeNsd() {
|
||||
initializeResolveListener();
|
||||
initializeDiscoveryListener();
|
||||
initializeRegistrationListener();
|
||||
|
||||
//mNsdManager.init(mContext.getMainLooper(), this);
|
||||
|
||||
}
|
||||
|
||||
public void initializeDiscoveryListener() {
|
||||
mDiscoveryListener = new NsdManager.DiscoveryListener() {
|
||||
|
||||
@Override
|
||||
public void onDiscoveryStarted(String regType) {
|
||||
Log.d(TAG, "Service discovery started");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceFound(NsdServiceInfo service) {
|
||||
Log.d(TAG, "Service discovery success" + service);
|
||||
if (!service.getServiceType().equals(SERVICE_TYPE)) {
|
||||
Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
|
||||
} else if (service.getServiceName().equals(mServiceName)) {
|
||||
Log.d(TAG, "Same machine: " + mServiceName);
|
||||
} else if (service.getServiceName().contains(mServiceName)){
|
||||
mNsdManager.resolveService(service, mResolveListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceLost(NsdServiceInfo service) {
|
||||
Log.e(TAG, "service lost" + service);
|
||||
if (mService == service) {
|
||||
mService = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDiscoveryStopped(String serviceType) {
|
||||
Log.i(TAG, "Discovery stopped: " + serviceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
|
||||
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
|
||||
mNsdManager.stopServiceDiscovery(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
|
||||
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
|
||||
mNsdManager.stopServiceDiscovery(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void initializeResolveListener() {
|
||||
mResolveListener = new NsdManager.ResolveListener() {
|
||||
|
||||
@Override
|
||||
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
Log.e(TAG, "Resolve failed" + errorCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceResolved(NsdServiceInfo serviceInfo) {
|
||||
Log.e(TAG, "Resolve Succeeded. " + serviceInfo);
|
||||
|
||||
if (serviceInfo.getServiceName().equals(mServiceName)) {
|
||||
Log.d(TAG, "Same IP.");
|
||||
return;
|
||||
}
|
||||
mService = serviceInfo;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void initializeRegistrationListener() {
|
||||
mRegistrationListener = new NsdManager.RegistrationListener() {
|
||||
|
||||
@Override
|
||||
public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
|
||||
mServiceName = NsdServiceInfo.getServiceName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistrationFailed(NsdServiceInfo arg0, int arg1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceUnregistered(NsdServiceInfo arg0) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public void registerService(int port) {
|
||||
NsdServiceInfo serviceInfo = new NsdServiceInfo();
|
||||
serviceInfo.setPort(port);
|
||||
serviceInfo.setServiceName(mServiceName);
|
||||
serviceInfo.setServiceType(SERVICE_TYPE);
|
||||
|
||||
mNsdManager.registerService(
|
||||
serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
|
||||
|
||||
}
|
||||
|
||||
public void discoverServices() {
|
||||
mNsdManager.discoverServices(
|
||||
SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
|
||||
}
|
||||
|
||||
public void stopDiscovery() {
|
||||
mNsdManager.stopServiceDiscovery(mDiscoveryListener);
|
||||
}
|
||||
|
||||
public NsdServiceInfo getChosenServiceInfo() {
|
||||
return mService;
|
||||
}
|
||||
|
||||
public void tearDown() {
|
||||
mNsdManager.unregisterService(mRegistrationListener);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user