Added basic WebSocket functionality (no decoding yet)
This commit is contained in:
@@ -1,4 +0,0 @@
|
|||||||
package com.example.fitbot.processing;
|
|
||||||
|
|
||||||
public class MotionProcessor {
|
|
||||||
}
|
|
@@ -1,4 +1,4 @@
|
|||||||
package com.example.fitbot.processing;
|
package com.example.fitbot.util.processing;
|
||||||
|
|
||||||
public class MotionData {
|
public class MotionData {
|
||||||
|
|
@@ -1,4 +1,6 @@
|
|||||||
package com.example.fitbot.processing;
|
package com.example.fitbot.util.processing;
|
||||||
|
|
||||||
|
import com.example.fitbot.util.server.WebSocket;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@@ -17,9 +19,14 @@ public class MotionInputStream extends InputStream {
|
|||||||
*/
|
*/
|
||||||
public static MotionInputStream startListening()
|
public static MotionInputStream startListening()
|
||||||
{
|
{
|
||||||
// Create server
|
// Create socket server
|
||||||
|
WebSocket socket = WebSocket.createServer(80);
|
||||||
|
|
||||||
|
// Check if the socket
|
||||||
|
if ( socket != null )
|
||||||
|
{
|
||||||
|
socket.startListening();
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@@ -0,0 +1,4 @@
|
|||||||
|
package com.example.fitbot.util.processing;
|
||||||
|
|
||||||
|
public class MotionProcessor {
|
||||||
|
}
|
@@ -0,0 +1,15 @@
|
|||||||
|
package com.example.fitbot.util.server;
|
||||||
|
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for handling WebSocket events.
|
||||||
|
*/
|
||||||
|
public interface IWebSocketHandler {
|
||||||
|
|
||||||
|
// Function for handling the connection of the WebSocket.
|
||||||
|
default void onConnected(Socket socket) {}
|
||||||
|
default void onDisconnected(Socket socket) {}
|
||||||
|
default void onMessageReceived(String message) {}
|
||||||
|
default void onError(Socket socket, String error) {}
|
||||||
|
}
|
@@ -0,0 +1,66 @@
|
|||||||
|
package com.example.fitbot.util.server;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class WebSocket {
|
||||||
|
|
||||||
|
private ServerSocket serverSocket;
|
||||||
|
private WebSocketConnectionHandler connectionHandler;
|
||||||
|
private final Set<Socket> clients = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
protected IWebSocketHandler eventHandler = new IWebSocketHandler() {}; // NO-OP event handler.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for creating a new WebSocket server.
|
||||||
|
*/
|
||||||
|
private WebSocket() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for creating a new WebSocket server given the provided port.
|
||||||
|
* @param port The port to configure the server to.
|
||||||
|
* @return A WebSocket connection, or null if something went wrong.
|
||||||
|
*/
|
||||||
|
public static @Nullable WebSocket createServer(int port) {
|
||||||
|
try {
|
||||||
|
WebSocket socket = new WebSocket();
|
||||||
|
socket.serverSocket = new ServerSocket(port);
|
||||||
|
return socket;
|
||||||
|
} catch (IOException error)
|
||||||
|
{
|
||||||
|
String cause = error.getMessage() == null ? "Unknown reason" : error.getMessage();
|
||||||
|
Log.e("WebSocket -- Creating new WebSocket server", cause);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for listening for incoming connections.
|
||||||
|
*/
|
||||||
|
public void startListening() {
|
||||||
|
this.connectionHandler = new WebSocketConnectionHandler(this);
|
||||||
|
this.connectionHandler.listen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for getting the ServerSocket connection
|
||||||
|
* @return The ServerSocket connection.
|
||||||
|
*/
|
||||||
|
public ServerSocket getSocket() {
|
||||||
|
return this.serverSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for checking whether this WebSocket connection is connected.
|
||||||
|
* @return The connection status of the WebSocket.
|
||||||
|
*/
|
||||||
|
public boolean isConnected() {
|
||||||
|
return !this.serverSocket.isClosed();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,88 @@
|
|||||||
|
package com.example.fitbot.util.server;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class WebSocketConnectionHandler implements Runnable {
|
||||||
|
|
||||||
|
private final WebSocket theSocket;
|
||||||
|
private Thread thread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for WebSocketConnectionHandler.
|
||||||
|
* This class handles all new incoming Socket connections.
|
||||||
|
* @param webSocket The socket to check for new connections.
|
||||||
|
*/
|
||||||
|
protected WebSocketConnectionHandler(WebSocket webSocket) {
|
||||||
|
this.theSocket = webSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (theSocket.isConnected()) {
|
||||||
|
try {
|
||||||
|
Socket newSocket = this.theSocket.getSocket().accept();
|
||||||
|
this.theSocket.eventHandler.onConnected(newSocket);
|
||||||
|
|
||||||
|
Scanner scanner = new Scanner(newSocket.getInputStream(), "UTF-8");
|
||||||
|
String data = scanner.useDelimiter("\\r\\n\\r\\n").next();
|
||||||
|
Matcher header = Pattern.compile("^GET").matcher(data);
|
||||||
|
|
||||||
|
// Check if the header contains the GET keyword
|
||||||
|
// If this is the case, upgrade the HTTP connection to WebSocket.
|
||||||
|
if (header.find()) {
|
||||||
|
Matcher match = Pattern.compile("Sec-WebSocket-Key: (.*)").matcher(data);
|
||||||
|
match.find();
|
||||||
|
|
||||||
|
String SECAccept = Base64.getEncoder().encodeToString(
|
||||||
|
MessageDigest.getInstance("SHA-1").digest((match.group(1) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes(StandardCharsets.UTF_8)));
|
||||||
|
|
||||||
|
byte[] response = (
|
||||||
|
"HTTP/1.1 101 Switching Protocols\r\n" +
|
||||||
|
"Connection: Upgrade\r\n" +
|
||||||
|
"Upgrade: websocket\r\n" +
|
||||||
|
"Sec-WebSocket-Accept: " +
|
||||||
|
SECAccept + "\r\n\r\n").getBytes(StandardCharsets.UTF_8);
|
||||||
|
newSocket.getOutputStream().write(response, 0, response.length);
|
||||||
|
|
||||||
|
// TODO: Read data from socket and provide in onMessageReceived.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException | NoSuchAlgorithmException error) {
|
||||||
|
String reason = error.getMessage() == null ? "Unknown reason" : error.getMessage();
|
||||||
|
Log.e("WebSocketConnectionHandler", "Error listening to Socket connections: " + reason);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for checking whether the connection handler is actively listening.
|
||||||
|
* @return Whether or not it's listening.
|
||||||
|
*/
|
||||||
|
public boolean isActive() {
|
||||||
|
return this.thread.isAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for listening to all new incoming socket connections.
|
||||||
|
*/
|
||||||
|
public void listen() {
|
||||||
|
this.thread = new Thread(this);
|
||||||
|
this.thread.start();
|
||||||
|
Log.i("WebSocketConnectionHandler", "Listening started.");
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user