diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 91dd332..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "ServerProxy"]
- path = ServerProxy
- url = https://github.com/daneren2005/ServerProxy.git
diff --git a/ServerProxy b/ServerProxy
deleted file mode 160000
index a4d9573..0000000
--- a/ServerProxy
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit a4d957353db2634906e0d5099d7a078a111bfab9
diff --git a/app/build.gradle b/app/build.gradle
index 05e590b..2e15b66 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -46,7 +46,6 @@ android {
}
dependencies {
- compile project(':Server Proxy')
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:support-v4:23.4.+'
compile 'com.android.support:appcompat-v7:23.4.+'
diff --git a/app/src/main/java/github/nvllsvm/audinaut/service/DownloadFile.java b/app/src/main/java/github/nvllsvm/audinaut/service/DownloadFile.java
index c5305d3..3ef02b4 100644
--- a/app/src/main/java/github/nvllsvm/audinaut/service/DownloadFile.java
+++ b/app/src/main/java/github/nvllsvm/audinaut/service/DownloadFile.java
@@ -31,12 +31,12 @@ import android.os.PowerManager;
import android.util.Log;
import net.nullsum.audinaut.domain.MusicDirectory;
-import net.nullsum.audinaut.util.Constants;
-import net.nullsum.audinaut.util.SilentBackgroundTask;
-import net.nullsum.audinaut.util.FileUtil;
-import net.nullsum.audinaut.util.Util;
+import net.nullsum.audinaut.util.BufferFile;
import net.nullsum.audinaut.util.CacheCleaner;
-import github.daneren2005.serverproxy.BufferFile;
+import net.nullsum.audinaut.util.Constants;
+import net.nullsum.audinaut.util.FileUtil;
+import net.nullsum.audinaut.util.SilentBackgroundTask;
+import net.nullsum.audinaut.util.Util;
import org.apache.http.Header;
diff --git a/app/src/main/java/github/nvllsvm/audinaut/service/DownloadService.java b/app/src/main/java/github/nvllsvm/audinaut/service/DownloadService.java
index 9be5a1c..9d338d4 100644
--- a/app/src/main/java/github/nvllsvm/audinaut/service/DownloadService.java
+++ b/app/src/main/java/github/nvllsvm/audinaut/service/DownloadService.java
@@ -36,17 +36,17 @@ import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.PlayerState;
import net.nullsum.audinaut.domain.RepeatMode;
import net.nullsum.audinaut.receiver.MediaButtonIntentReceiver;
+import net.nullsum.audinaut.util.BufferProxy;
+import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.ImageLoader;
import net.nullsum.audinaut.util.Notifications;
-import net.nullsum.audinaut.util.SilentBackgroundTask;
-import net.nullsum.audinaut.util.Constants;
import net.nullsum.audinaut.util.ShufflePlayBuffer;
+import net.nullsum.audinaut.util.SilentBackgroundTask;
import net.nullsum.audinaut.util.SimpleServiceBinder;
import net.nullsum.audinaut.util.UpdateHelper;
import net.nullsum.audinaut.util.Util;
import net.nullsum.audinaut.util.tags.BastpUtil;
import net.nullsum.audinaut.view.UpdateView;
-import github.daneren2005.serverproxy.BufferProxy;
import java.io.File;
import java.io.IOException;
diff --git a/app/src/main/java/github/nvllsvm/audinaut/util/BufferFile.java b/app/src/main/java/github/nvllsvm/audinaut/util/BufferFile.java
new file mode 100644
index 0000000..4b0f93f
--- /dev/null
+++ b/app/src/main/java/github/nvllsvm/audinaut/util/BufferFile.java
@@ -0,0 +1,28 @@
+/*
+ This file is part of ServerProxy.
+ SocketProxy is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ Subsonic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with Subsonic. If not, see .
+ Copyright 2014 (C) Scott Jackson
+*/
+
+package net.nullsum.audinaut.util;
+
+import java.io.File;
+
+public interface BufferFile {
+ File getFile();
+ Long getContentLength();
+ long getEstimatedSize();
+ boolean isWorkDone();
+ void onStart();
+ void onStop();
+ void onResume();
+}
diff --git a/app/src/main/java/github/nvllsvm/audinaut/util/BufferProxy.java b/app/src/main/java/github/nvllsvm/audinaut/util/BufferProxy.java
new file mode 100644
index 0000000..b4e947c
--- /dev/null
+++ b/app/src/main/java/github/nvllsvm/audinaut/util/BufferProxy.java
@@ -0,0 +1,82 @@
+/*
+This file is part of ServerProxy.
+SocketProxy is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+Subsonic is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+You should have received a copy of the GNU General Public License
+along with Subsonic. If not, see .
+Copyright 2014 (C) Scott Jackson
+*/
+
+package net.nullsum.audinaut.util;
+
+import java.io.File;
+import java.net.Socket;
+
+import android.content.Context;
+
+import net.nullsum.audinaut.util.FileProxy;
+
+public class BufferProxy extends FileProxy {
+ private static final String TAG = BufferProxy.class.getSimpleName();
+ protected BufferFile progress;
+
+ public BufferProxy(Context context) {
+ super(context);
+ }
+
+ protected ProxyTask getTask(Socket client) {
+ return new BufferFileTask(client);
+ }
+
+ public void setBufferFile(BufferFile progress) {
+ this.progress = progress;
+ }
+
+ protected class BufferFileTask extends StreamFileTask {
+ public BufferFileTask(Socket client) {
+ super(client);
+ }
+
+ @Override
+ File getFile(String path) {
+ return progress.getFile();
+ }
+
+ @Override
+ Long getContentLength() {
+ Long contentLength = progress.getContentLength();
+ if(contentLength == null && progress.isWorkDone()) {
+ contentLength = file.length();
+ }
+ return contentLength;
+ }
+ @Override
+ long getFileSize() {
+ return progress.getEstimatedSize();
+ }
+
+ @Override
+ public void onStart() {
+ progress.onStart();
+ }
+ @Override
+ public void onStop() {
+ progress.onStop();
+ }
+ @Override
+ public void onResume() {
+ progress.onResume();
+ }
+
+ @Override
+ public boolean isWorkDone() {
+ return progress.isWorkDone() && cbSkip >= file.length();
+ }
+ }
+}
diff --git a/app/src/main/java/github/nvllsvm/audinaut/util/FileProxy.java b/app/src/main/java/github/nvllsvm/audinaut/util/FileProxy.java
new file mode 100644
index 0000000..388e488
--- /dev/null
+++ b/app/src/main/java/github/nvllsvm/audinaut/util/FileProxy.java
@@ -0,0 +1,219 @@
+/*
+ This file is part of ServerProxy.
+ SocketProxy is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ Subsonic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with Subsonic. If not, see .
+ Copyright 2014 (C) Scott Jackson
+*/
+
+package net.nullsum.audinaut.util;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.SocketException;
+
+import android.content.Context;
+import android.util.Log;
+
+import net.nullsum.audinaut.util.ServerProxy;
+
+public class FileProxy extends ServerProxy {
+ private static final String TAG = FileProxy.class.getSimpleName();
+
+ public FileProxy(Context context) {
+ super(context);
+ }
+
+ protected ProxyTask getTask(Socket client) {
+ return new StreamFileTask(client);
+ }
+
+ protected class StreamFileTask extends ProxyTask {
+ File file;
+
+ public StreamFileTask(Socket client) {
+ super(client);
+ }
+
+ @Override
+ public boolean processRequest() {
+ if(!super.processRequest()) {
+ return false;
+ }
+
+ Log.i(TAG, "Processing request for file " + path);
+ file = getFile(path);
+ if (!file.exists()) {
+ Log.e(TAG, "File " + path + " does not exist");
+ return false;
+ }
+
+ // Make sure to not try to read past where the file is downloaded
+ if(cbSkip != 0 && cbSkip >= file.length()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ File getFile(String path) {
+ return new File(path);
+ }
+
+ Long getContentLength() {
+ return file.length();
+ }
+ long getFileSize() {
+ return file.length();
+ }
+
+ @Override
+ public void run() {
+ Long contentLength = getContentLength();
+
+ // Create HTTP header
+ String headers;
+ if(cbSkip == 0) {
+ headers = "HTTP/1.0 200 OK\r\n";
+ } else {
+ headers = "HTTP/1.0 206 OK\r\n";
+ headers += "Content-Range: bytes " + cbSkip + "-" + (file.length() - 1) + "/";
+ if(contentLength == null) {
+ headers += "*";
+ } else {
+ headers += contentLength;
+ }
+ headers += "\r\n";
+
+ Log.i(TAG, "Streaming starts from: " + cbSkip);
+ }
+
+ String name = file.getPath();
+ int index = name.lastIndexOf('.');
+ String ext = "";
+ if(index != -1) {
+ ext = name.substring(index + 1).toLowerCase();
+ }
+ if("mp3".equals(ext)) {
+ headers += "Content-Type: audio/mpeg\r\n";
+ } else {
+ headers += "Content-Type: " + "application/octet-stream" + "\r\n";
+ }
+
+ long fileSize;
+ if(contentLength == null) {
+ fileSize = getFileSize();
+ } else {
+ fileSize = contentLength;
+ if(cbSkip > 0) {
+ headers += "Content-Length: " + (fileSize - cbSkip) + "\r\n";
+ } else {
+ headers += "Content-Length: " + fileSize + "\r\n";
+ }
+ headers += "Accept-Ranges: bytes \r\n";
+ }
+ Log.i(TAG, "Streaming fileSize: " + fileSize);
+
+ headers += "Connection: close\r\n";
+ headers += "\r\n";
+
+ long cbToSend = fileSize - cbSkip;
+ OutputStream output = null;
+ byte[] buff = new byte[64 * 1024];
+ try {
+ output = new BufferedOutputStream(client.getOutputStream(), 32*1024);
+ output.write(headers.getBytes());
+
+ // Make sure to have file lock
+ onStart();
+
+ // Loop as long as there's stuff to send
+ while (isRunning && !client.isClosed()) {
+ onResume();
+
+ // See if there's more to send
+ int cbSentThisBatch = 0;
+ if (file.exists()) {
+ FileInputStream input = new FileInputStream(file);
+ input.skip(cbSkip);
+ int cbToSendThisBatch = input.available();
+ while (cbToSendThisBatch > 0) {
+ int cbToRead = Math.min(cbToSendThisBatch, buff.length);
+ int cbRead = input.read(buff, 0, cbToRead);
+ if (cbRead == -1) {
+ break;
+ }
+ cbToSendThisBatch -= cbRead;
+ cbToSend -= cbRead;
+ output.write(buff, 0, cbRead);
+ output.flush();
+ cbSkip += cbRead;
+ cbSentThisBatch += cbRead;
+ }
+ input.close();
+ }
+
+ // Done regardless of whether or not it thinks it is
+ if(isWorkDone()) {
+ break;
+ }
+
+ // If we did nothing this batch, block for a second
+ if (cbSentThisBatch == 0) {
+ Log.d(TAG, "Blocking until more data appears (" + cbToSend + ")");
+ Thread.sleep(1000);
+ }
+ }
+
+ // Release file lock, use of stream proxy means nothing else is using it
+ onStop();
+ }
+ catch (SocketException socketException) {
+ Log.e(TAG, "SocketException() thrown, proxy client has probably closed. This can exit harmlessly");
+
+ // Release file lock, use of stream proxy means nothing else is using it
+ onStop();
+ }
+ catch (Exception e) {
+ Log.e(TAG, "Exception thrown from streaming task:");
+ Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
+ }
+
+ // Cleanup
+ try {
+ if (output != null) {
+ output.close();
+ }
+ client.close();
+ }
+ catch (IOException e) {
+ Log.e(TAG, "IOException while cleaning up streaming task:");
+ Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
+ }
+ }
+
+ public void onStart() {
+
+ }
+ public void onStop() {
+
+ }
+ public void onResume() {
+
+ }
+ public boolean isWorkDone() {
+ return cbSkip >= file.length();
+ }
+ }
+}
diff --git a/app/src/main/java/github/nvllsvm/audinaut/util/ServerProxy.java b/app/src/main/java/github/nvllsvm/audinaut/util/ServerProxy.java
new file mode 100644
index 0000000..86bdba6
--- /dev/null
+++ b/app/src/main/java/github/nvllsvm/audinaut/util/ServerProxy.java
@@ -0,0 +1,231 @@
+/*
+ This file is part of ServerProxy.
+ SocketProxy is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ Subsonic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with Subsonic. If not, see .
+ Copyright 2014 (C) Scott Jackson
+*/
+
+package net.nullsum.audinaut.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.net.UnknownHostException;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.util.Log;
+
+public abstract class ServerProxy implements Runnable {
+ private static final String TAG = ServerProxy.class.getSimpleName();
+
+ private Thread thread;
+ protected boolean isRunning;
+ private ServerSocket socket;
+ private int port;
+ private Context context;
+
+ public ServerProxy(Context context) {
+ // Create listening socket
+ try {
+ socket = new ServerSocket(0);
+ socket.setSoTimeout(5000);
+ port = socket.getLocalPort();
+ this.context = context;
+ } catch (UnknownHostException e) { // impossible
+ } catch (IOException e) {
+ Log.e(TAG, "IOException initializing server", e);
+ }
+ }
+
+ public void start() {
+ if(socket.isBound()) {
+ thread = new Thread(this, "Socket Proxy");
+ thread.start();
+ } else {
+ Log.e(TAG, "Attempting to start a non-initialized proxy");
+ }
+ }
+
+ public void stop() {
+ isRunning = false;
+ if(thread != null) {
+ thread.interrupt();
+ }
+ }
+
+ public String getPrivateAddress(String request) {
+ return getAddress("127.0.0.1", request);
+ }
+ public String getPublicAddress(String request) {
+ WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ int ipAddress = wifiManager.getConnectionInfo().getIpAddress();
+
+ if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
+ ipAddress = Integer.reverseBytes(ipAddress);
+ }
+
+ byte[] ipByteArray = BigInteger.valueOf(ipAddress).toByteArray();
+ String ipAddressString = null;
+ try {
+ ipAddressString = InetAddress.getByAddress(ipByteArray).getHostAddress();
+ } catch(UnknownHostException ex) {
+ Log.e(TAG, "Unable to get host address.");
+ }
+
+ return getAddress(ipAddressString, request);
+ }
+ private String getAddress(String host, String request) {
+ try {
+ return String.format("http://%s:%d/%s", host, port, URLEncoder.encode(request, "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public void run() {
+ isRunning = true;
+ while (isRunning) {
+ try {
+ Socket client = socket.accept();
+ if (client == null) {
+ continue;
+ }
+ Log.i(TAG, "client connected");
+
+ ProxyTask task = getTask(client);
+ if (task.processRequest()) {
+ new Thread(task, "ProxyTask").start();
+ }
+
+ } catch (SocketTimeoutException e) {
+ // Do nothing
+ } catch (IOException e) {
+ Log.e(TAG, "Error connecting to client", e);
+ }
+ }
+ Log.i(TAG, "Proxy interrupted. Shutting down.");
+ }
+
+ abstract ProxyTask getTask(Socket client);
+
+ protected abstract class ProxyTask implements Runnable {
+ protected Socket client;
+ protected String path;
+ protected int cbSkip = 0;
+ protected Map requestHeaders = new HashMap<>();
+
+ public ProxyTask(Socket client) {
+ this.client = client;
+ }
+
+ protected boolean readRequest() {
+ InputStream is;
+ String firstLine;
+ BufferedReader reader;
+ try {
+ is = client.getInputStream();
+ reader = new BufferedReader(new InputStreamReader(is), 8192);
+ firstLine = reader.readLine();
+ } catch (IOException e) {
+ Log.e(TAG, "Error parsing request", e);
+ return false;
+ }
+
+ if (firstLine == null) {
+ Log.i(TAG, "Proxy client closed connection without a request.");
+ return false;
+ }
+
+ StringTokenizer st = new StringTokenizer(firstLine);
+ if(!st.hasMoreTokens()) {
+ Log.w(TAG, "Unknown request with no tokens");
+ return false;
+ } else if(st.countTokens() < 2) {
+ Log.w(TAG, "Unknown request with no uri: \"" + firstLine + '"');
+ return false;
+ }
+ String method = st.nextToken();
+ String uri = st.nextToken();
+ String realUri = uri.substring(1);
+
+ // Process path
+ try {
+ path = URLDecoder.decode(realUri, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Unsupported encoding", e);
+ return false;
+ }
+
+ // Get all of the headers
+ try {
+ String line;
+ while((line = reader.readLine()) != null && !"".equals(line)) {
+ int index = line.indexOf(':');
+ // Ignore headers without ':' or where ':' is the last thing in the string
+ if(index != -1 && (index + 2) < line.length()) {
+ String headerName = line.substring(0, index);
+ String headerValue = line.substring(index + 2);
+
+ requestHeaders.put(headerName, headerValue);
+ }
+ }
+ } catch(IOException e) {
+ // Don't really care once past first line
+ } catch(Exception e) {
+ Log.w(TAG, "Exception reading request", e);
+ }
+
+ return true;
+ }
+
+ public boolean processRequest() {
+ if (!readRequest()) {
+ return false;
+ }
+ Log.i(TAG, "Processing request for " + path);
+
+ // Try to get range requested
+ String range = requestHeaders.get("Range");
+ if(range != null) {
+ int index = range.indexOf("=");
+ if(index >= 0) {
+ range = range.substring(index + 1);
+
+ index = range.indexOf("-");
+ if(index > 0) {
+ range = range.substring(0, index);
+ }
+
+ cbSkip = Integer.parseInt(range);
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 430c775..e7b4def 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1 @@
-include ':app', ':Server Proxy'
-project(':Server Proxy').projectDir = new File('ServerProxy')
+include ':app'