diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 565057b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: android -android: - components: - # Uncomment the lines below if you want to - # use the latest revision of Android SDK Tools - # - tools - # - platform-tools - - # The BuildTools version used by your project - #- build-tools-26.0.2 - - # The SDK version used to compile your project - - android-16 - - # Additional components - #- extra-google-google_play_services - #- extra-google-m2repository - #- extra-android-m2repository - - # Specify at least one system image, - # if you need to run emulator(s) during your tests - #- sys-img-x86-android-26 - #- sys-img-armeabi-v7a-android-17 -before_script: - - chmod +x ./DarkChatter/gradlew - -script: - - ./DarkChatter/gradlew tasks - - ./DarkChatter/gradlew build connectedCheck --stacktrace diff --git a/DarkChatter/.idea/misc.xml b/DarkChatter/.idea/misc.xml index e0d5b93..c0f68ed 100644 --- a/DarkChatter/.idea/misc.xml +++ b/DarkChatter/.idea/misc.xml @@ -5,31 +5,27 @@ - + diff --git a/DarkChatter/.idea/vcs.xml b/DarkChatter/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/DarkChatter/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/DarkChatter/app/src/main/AndroidManifest.xml b/DarkChatter/app/src/main/AndroidManifest.xml index 4b29d91..b959a0b 100644 --- a/DarkChatter/app/src/main/AndroidManifest.xml +++ b/DarkChatter/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + + + - + + \ No newline at end of file diff --git a/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/ChatActivity.java b/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/ChatActivity.java new file mode 100644 index 0000000..d41daf9 --- /dev/null +++ b/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/ChatActivity.java @@ -0,0 +1,164 @@ +/* + * 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 comdarkchatter.github.darkchatter; + +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 android.support.design.widget.Snackbar; +import comdarkchatter.github.darkchatter.NsdHelper; + +public class ChatActivity 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); + Log.d(TAG, "Creating chat activity"); + 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); + } + }; + + } + + public void clickAdvertise(View v) { + // Register service + if(mConnection.getLocalPort() > -1) { + mNsdHelper.registerService(mConnection.getLocalPort()); + } else { + //User-side warning when register fails + Snackbar registerError = Snackbar.make(v, "Unable to register!", Snackbar.LENGTH_SHORT); + registerError.show(); + 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 { + //User-side warning when connect fails + Snackbar connectError = Snackbar.make(v, "Unable to connect! There is a problem with the service.", Snackbar.LENGTH_SHORT); + connectError.show(); + 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()) { + boolean messageSent = mConnection.sendMessage(messageString); + if (!messageSent){ + //User-side warning when connect fails + Snackbar messageSentError = Snackbar.make(v, "Message was not sent. There is a problem with the service.", Snackbar.LENGTH_SHORT); + messageSentError.show(); + } + } + messageView.setText(""); + } + } + + public void addChatLine(String line) { + mStatusView.append("\n" + line); + } + + @Override + protected void onStart() { + Log.d(TAG, "Starting."); + mConnection = new ChatConnection(mUpdateHandler); + + mNsdHelper = new NsdHelper(this); + mNsdHelper.initializeNsd(); + super.onStart(); + } + + + @Override + protected void onPause() { + Log.d(TAG, "Pausing."); + if (mNsdHelper != null) { + mNsdHelper.stopDiscovery(); + } + super.onPause(); + } + + @Override + protected void onResume() { + Log.d(TAG, "Resuming."); + super.onResume(); + if (mNsdHelper != null) { + mNsdHelper.discoverServices(); + } + } + + + // For KitKat and earlier releases, it is necessary to remove the + // service registration when the application is stopped. There's + // no guarantee that the onDestroy() method will be called (we're + // killable after onStop() returns) and the NSD service won't remove + // the registration for us if we're killed. + + // In L and later, NsdService will automatically unregister us when + // our connection goes away when we're killed, so this step is + // optional (but recommended). + + @Override + protected void onStop() { + Log.d(TAG, "Being stopped."); + mNsdHelper.tearDown(); + mConnection.tearDown(); + mNsdHelper = null; + mConnection = null; + super.onStop(); + } + + @Override + protected void onDestroy() { + Log.d(TAG, "Being destroyed."); + super.onDestroy(); + } +} \ No newline at end of file diff --git a/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/ChatConnection.java b/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/ChatConnection.java new file mode 100644 index 0000000..98f7eb0 --- /dev/null +++ b/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/ChatConnection.java @@ -0,0 +1,301 @@ + +/* + * 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 comdarkchatter.github.darkchatter; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.support.design.widget.Snackbar; +import android.util.Log; +import android.view.View; + +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(); + if (mChatClient != null) { + mChatClient.tearDown(); + } + } + + public void connectToServer(InetAddress address, int port) { + mChatClient = new ChatClient(address, port); + } + + //send message returns true if sent, false if not sent + public boolean sendMessage(String msg) { + if (mChatClient != null) { + return mChatClient.sendMessage(msg); + } + return false; + } + + 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 mMessageQueue; + private int QUEUE_CAPACITY = 10; + + public SendingThread() { + mMessageQueue = new ArrayBlockingQueue(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 boolean sendMessage(String msg) { + //boolean to check if the message was sent without errors + boolean messageSent = true; + try { + Socket socket = getSocket(); + if (socket == null) { + Log.d(CLIENT_TAG, "Socket is null"); + } else if (socket.getOutputStream() == null) { + Log.d(CLIENT_TAG, "Socket output stream is null"); + } + + 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); + messageSent = false; + } catch (IOException e) { + Log.d(CLIENT_TAG, "I/O Exception", e); + messageSent = false; + } catch (Exception e) { + Log.d(CLIENT_TAG, "Error3", e); + messageSent = false; + } + + Log.d(CLIENT_TAG, "Client sent message: " + msg); + return messageSent; + } + } +} diff --git a/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/GroupActivity.java b/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/GroupActivity.java index 04f5acb..d7c0621 100644 --- a/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/GroupActivity.java +++ b/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/GroupActivity.java @@ -1,5 +1,9 @@ package comdarkchatter.github.darkchatter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.wifi.p2p.WifiP2pManager; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; @@ -11,6 +15,11 @@ public class GroupActivity extends AppCompatActivity { + private Context activity; + private WifiP2pManager manager; + private WifiP2pManager.Channel channel; + private BroadcastReceiver receiver; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -18,12 +27,17 @@ protected void onCreate(Bundle savedInstanceState) { Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); + activity = this.activity; + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { + @Override public void onClick(View view) { - Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) - .setAction("Action", null).show(); + Intent chat = new Intent(GroupActivity.this, ChatActivity.class); + //GroupActivity.this.startActivity(chat); + startActivity(chat); + } }); } @@ -48,4 +62,4 @@ public boolean onOptionsItemSelected(MenuItem item) { } return super.onOptionsItemSelected(item); } -} +} \ No newline at end of file diff --git a/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/NsdHelper.java b/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/NsdHelper.java new file mode 100644 index 0000000..354232d --- /dev/null +++ b/DarkChatter/app/src/main/java/comdarkchatter/github/darkchatter/NsdHelper.java @@ -0,0 +1,188 @@ +/* + * 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 comdarkchatter.github.darkchatter; + +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(); + + //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); + } + + @Override + public void onStopDiscoveryFailed(String serviceType, int errorCode) { + Log.e(TAG, "Discovery failed: Error code:" + errorCode); + } + }; + } + + 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(); + Log.d(TAG, "Service registered: " + mServiceName); + } + + @Override + public void onRegistrationFailed(NsdServiceInfo arg0, int arg1) { + Log.d(TAG, "Service registration failed: " + arg1); + } + + @Override + public void onServiceUnregistered(NsdServiceInfo arg0) { + Log.d(TAG, "Service unregistered: " + arg0.getServiceName()); + } + + @Override + public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + Log.d(TAG, "Service unregistration failed: " + errorCode); + } + + }; + } + + public void registerService(int port) { + tearDown(); // Cancel any previous registration request + initializeRegistrationListener(); + 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() { + stopDiscovery(); // Cancel any existing discovery request + initializeDiscoveryListener(); + mNsdManager.discoverServices( + SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener); + } + + public void stopDiscovery() { + if (mDiscoveryListener != null) { + try { + mNsdManager.stopServiceDiscovery(mDiscoveryListener); + } finally { + } + mDiscoveryListener = null; + } + } + + public NsdServiceInfo getChosenServiceInfo() { + return mService; + } + + public void tearDown() { + if (mRegistrationListener != null) { + try { + mNsdManager.unregisterService(mRegistrationListener); + } finally { + } + mRegistrationListener = null; + } + } +} \ No newline at end of file diff --git a/DarkChatter/app/src/main/res/layout/content_chat.xml b/DarkChatter/app/src/main/res/layout/content_chat.xml new file mode 100644 index 0000000..96c6252 --- /dev/null +++ b/DarkChatter/app/src/main/res/layout/content_chat.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/DarkChatter/app/src/main/res/layout/main.xml b/DarkChatter/app/src/main/res/layout/main.xml new file mode 100644 index 0000000..aeeff1d --- /dev/null +++ b/DarkChatter/app/src/main/res/layout/main.xml @@ -0,0 +1,67 @@ + + + + + + +