diff --git a/.gitignore b/.gitignore index 5b9a46fbb..760423474 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ local.properties .classpath .project .settings +libs diff --git a/src/com/seafile/seadroid2/SeafConnection.java b/src/com/seafile/seadroid2/SeafConnection.java index 3155305e9..42f0cff22 100644 --- a/src/com/seafile/seadroid2/SeafConnection.java +++ b/src/com/seafile/seadroid2/SeafConnection.java @@ -1,36 +1,27 @@ package com.seafile.seadroid2; -import java.io.BufferedWriter; -import java.io.DataOutputStream; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.ProtocolException; -import java.net.URL; import java.net.URLEncoder; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSession; -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicNameValuePair; import org.json.JSONException; import org.json.JSONObject; import android.util.Log; +import com.github.kevinsawicki.http.HttpRequest; +import com.github.kevinsawicki.http.HttpRequest.HttpRequestException; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.data.DataManager; import com.seafile.seadroid2.data.DataManager.ProgressMonitor; @@ -54,166 +45,58 @@ public Account getAccount() { return account; } - class MyHostnameVerifier implements javax.net.ssl.HostnameVerifier { - public boolean verify(String urlHostName, String certHostName){ - return true; - } - - public boolean verify(String urlHost, SSLSession sslSession){ - return true; - } - } - - private void prepareSSL(HttpURLConnection conn, boolean secure) { - if (conn instanceof HttpsURLConnection) { - try { - HttpsURLConnection sc = (HttpsURLConnection) conn; - SSLContext context; - - context = SSLContext.getInstance("TLS"); - // if (secure) - // context.init(null, TrustManagerFactory.getTrustManagers(), null); - // else { - // MyHostnameVerifier verifier = new MyHostnameVerifier(); - // sc.setHostnameVerifier(verifier); - // context.init(null, TrustManagerFactory.getUnsecureTrustManagers(), null); - // } - - /** - * XXX: For convience, all SSL ceritificates are trusted - */ - MyHostnameVerifier verifier = new MyHostnameVerifier(); - sc.setHostnameVerifier(verifier); - context.init(null, TrustManagerFactory.getUnsecureTrustManagers(), null); - - sc.setSSLSocketFactory(context.getSocketFactory()); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (KeyManagementException e) { - e.printStackTrace(); - } - } - } - - private HttpURLConnection prepareGet(String apiPath, boolean withToken) - throws IOException { - URL url = new URL(account.server + apiPath); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - prepareSSL(conn, true); - conn.setReadTimeout(30000); - conn.setConnectTimeout(15000); - - conn.setRequestMethod("GET"); - conn.setDoInput(true); - if (withToken) - conn.addRequestProperty("Authorization", "Token " + account.token); - - //Log.d(DEBUG_TAG, "get from " + url.getPath()); - return conn; - } - - private HttpURLConnection prepareGet(String apiPath) throws IOException { - return prepareGet(apiPath, true); + private HttpRequest prepareApiGetRequest(String apiPath, Map params) throws IOException { + return HttpRequest.get(account.server + apiPath, params, true). + trustAllCerts().trustAllHosts(). + readTimeout(30000).connectTimeout(15000). + header("Authorization", "Token " + account.token); } - private HttpURLConnection prepareFileGet(String urlString) - throws IOException { - URL url = new URL(urlString); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - prepareSSL(conn, false); - conn.setConnectTimeout(15000); - conn.setRequestMethod("GET"); - conn.setDoInput(true); - return conn; + private HttpRequest prepareApiGetRequest(String apiPath) throws IOException { + return prepareApiGetRequest(apiPath, null); } - private HttpURLConnection preparePost(String apiPath, boolean withToken) - throws IOException { - URL url = new URL(account.server + apiPath); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - prepareSSL(conn, true); - - conn.setReadTimeout(10000 /* milliseconds */); - conn.setConnectTimeout(15000 /* milliseconds */); - - conn.setRequestMethod("POST"); - conn.setDoInput(true); - conn.setDoOutput(true); - if (withToken) - conn.addRequestProperty("Authorization", "Token " + account.token); - return conn; - } - - private HttpURLConnection preparePost(String apiPath) throws IOException { - return preparePost(apiPath, true); - } - - private String encodePostParams(List params) - throws UnsupportedEncodingException { - StringBuilder result = new StringBuilder(); - boolean first = true; + private HttpRequest prepareApiPostRequest(String apiPath, boolean withToken, Map params) + throws IOException { + HttpRequest req = HttpRequest.post(account.server + apiPath, params, true). + trustAllCerts().trustAllHosts(). + connectTimeout(15000); - for (NameValuePair pair : params) { - if (first) - first = false; - else - result.append("&"); - - result.append(URLEncoder.encode(pair.getName(), "UTF-8")); - result.append("="); - result.append(URLEncoder.encode(pair.getValue(), "UTF-8")); + if (withToken) { + req.header("Authorization", "Token " + account.token); } - return result.toString(); + return req; } - private void doPost(HttpURLConnection conn, List params) - throws IOException, ProtocolException, UnsupportedEncodingException { - OutputStream os = null; - try { - os = conn.getOutputStream(); - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( - os, "UTF-8")); - writer.write(encodePostParams(params)); - writer.close(); // must call this to actually write to the output - conn.connect(); - } finally { - try { - if (os != null) - os.close(); - } catch (Exception e) { - // ignore - } - } + private HttpRequest prepareApiFileGetRequest(String url) throws IOException { + return HttpRequest.get(url). + trustAllCerts().trustAllHosts(). + connectTimeout(15000); } - /** * Login into the server * @return true if login success, false otherwise - * @throws IOException + * @throws SeafException */ private boolean realLogin() throws SeafException { - InputStream is = null; - try { - HttpURLConnection conn = preparePost("api2/auth-token/", false); - + HttpRequest req = prepareApiPostRequest("api2/auth-token/", false, null); Log.d(DEBUG_TAG, "Login to " + account.server + "api2/auth-token/"); - List params = new ArrayList(); - params.add(new BasicNameValuePair("username", account.email)); - params.add(new BasicNameValuePair("password", account.passwd)); - doPost(conn, params); - if (conn.getResponseCode() != 200) { - if (conn.getResponseMessage() == null) + req.form("username", account.email); + req.form("password", account.passwd); + + if (req.code() != 200) { + if (req.message() == null) { throw SeafException.networkException; - else - throw new SeafException(conn.getResponseCode(), conn.getResponseMessage()); + } else { + throw new SeafException(req.code(), req.message()); + } } - - is = conn.getInputStream(); - String contentAsString = Utils.readIt(is); + + String contentAsString = new String(req.bytes(), "UTF-8"); JSONObject obj = Utils.parseJsonObject(contentAsString); if (obj == null) return false; @@ -222,26 +105,17 @@ private boolean realLogin() throws SeafException { } catch (SeafException e) { throw e; } catch (SSLException e) { - //Log.d("test", e.getMessage()); throw SeafException.sslException; + } catch (HttpRequestException e) { + throw SeafException.networkException; } catch (IOException e) { e.printStackTrace(); throw SeafException.networkException; } catch (JSONException e) { throw SeafException.illFormatException; - } finally { - // Makes sure that the InputStream is closed after the app is - // finished using it. - try { - if (is != null) { - is.close(); - } - } catch (Exception e) { - // ignore - } } } - + public boolean doLogin() throws SeafException { try { return realLogin(); @@ -251,125 +125,44 @@ public boolean doLogin() throws SeafException { } } - public boolean authPing() throws SeafException { - InputStream is = null; - try { - - HttpURLConnection conn = prepareGet("api2/auth/ping/"); - - // Starts the query - conn.connect(); - int response = conn.getResponseCode(); - if (response != 200) { - if (conn.getResponseMessage() == null) - throw SeafException.networkException; - else - throw new SeafException(response, conn.getResponseMessage()); - } - - is = conn.getInputStream(); - String result = Utils.readIt(is); - if (result.equals("\"pong\"")) - return true; - else - return false; - } catch (SeafException e) { - throw e; - } catch (Exception e) { - Log.d(DEBUG_TAG, "Other exception in authPing"); - return false; - } finally { - try { - if (is != null) { - is.close(); - } - } catch (Exception e) { - // ignore - } - } - } - - public boolean ping() throws SeafException { - InputStream is = null; + public String getRepos() throws SeafException { try { - HttpURLConnection conn = prepareGet("api2/ping/"); - conn.connect(); - int response = conn.getResponseCode(); - if (response != 200) - if (conn.getResponseMessage() == null) + HttpRequest req = prepareApiGetRequest("api2/repos/"); + if (req.code() != 200) { + if (req.message() == null) { throw SeafException.networkException; - else - throw new SeafException(response, conn.getResponseMessage()); - - is = conn.getInputStream(); - String result = Utils.readIt(is); - if (result.equals("\"pong\"")) - return true; - else - return false; - } catch (SeafException e) { - throw e; - } catch (Exception e) { - Log.d(DEBUG_TAG, "Other exception in ping"); - return false; - } finally { - try { - if (is != null) { - is.close(); + } else { + throw new SeafException(req.code(), req.message()); } - } catch (Exception e) { - // ignore } - } - } - - - public String getRepos() throws SeafException { - InputStream is = null; - try { - HttpURLConnection conn = prepareGet("api2/repos/"); - conn.connect(); - int response = conn.getResponseCode(); - if (response != 200) - if (conn.getResponseMessage() == null) - throw SeafException.networkException; - else - throw new SeafException(response, conn.getResponseMessage()); - is = conn.getInputStream(); - String result = Utils.readIt(is); + String result = new String(req.bytes(), "UTF-8"); return result; } catch (SeafException e) { throw e; + } catch (HttpRequestException e) { + throw SeafException.networkException; } catch (IOException e) { throw SeafException.networkException; - } finally { - try { - if (is != null) { - is.close(); - } - } catch (Exception e) { - // ignore - } } } public TwoTuple getDirents(String repoID, String path) throws SeafException { InputStream is = null; try { - String encPath = URLEncoder.encode(path, "UTF-8"); - HttpURLConnection conn = prepareGet("api2/repos/" + repoID + "/dir/" + "?p=" + encPath); - conn.connect(); - int response = conn.getResponseCode(); - if (response != 200) - if (conn.getResponseMessage() == null) + String apiPath = String.format("api2/repos/%s/dir/", repoID); + Map params = new HashMap(); + params.put("p", path); + HttpRequest req = prepareApiGetRequest(apiPath, params); + if (req.code() != 200) + if (req.message() == null) throw SeafException.networkException; else - throw new SeafException(response, conn.getResponseMessage()); + throw new SeafException(req.code(), req.message()); - is = conn.getInputStream(); - String content = Utils.readIt(is); - String dirID = conn.getHeaderField("oid"); + byte[] rawBytes = req.bytes(); + String content = new String(rawBytes, "UTF-8"); + String dirID = req.header("oid"); if (content == null || dirID == null) { throw SeafException.unknownException; } @@ -380,6 +173,8 @@ public TwoTuple getDirents(String repoID, String path) throws Se throw e; } catch (UnsupportedEncodingException e) { throw SeafException.encodingException; + } catch (HttpRequestException e) { + throw SeafException.networkException; } catch (IOException e) { throw SeafException.networkException; } finally { @@ -394,21 +189,19 @@ public TwoTuple getDirents(String repoID, String path) throws Se } private String getDownloadLink(String repoID, String path) throws SeafException { - InputStream is = null; try { - String encPath = URLEncoder.encode(path, "UTF-8"); - HttpURLConnection conn = prepareGet("api2/repos/" + repoID + "/file/" + "?p=" - + encPath + "&op=download"); - conn.connect(); - int response = conn.getResponseCode(); - if (response != 200) - if (conn.getResponseMessage() == null) + String apiPath = String.format("api2/repos/%s/file/", repoID); + Map params = new HashMap(); + params.put("p", path); + params.put("op", "download"); + HttpRequest req = prepareApiGetRequest(apiPath, params); + if (req.code() != 200) + if (req.message() == null) throw SeafException.networkException; else - throw new SeafException(response, conn.getResponseMessage()); - - is = conn.getInputStream(); - String result = Utils.readIt(is); + throw new SeafException(req.code(), req.message()); + + String result = new String(req.bytes(), "UTF-8"); // should return "\"http://gonggeng.org:8082/...\"" or "\"https://gonggeng.org:8082/...\" if (result.startsWith("\"http")) { return result.substring(1, result.length()-1); @@ -420,66 +213,39 @@ private String getDownloadLink(String repoID, String path) throws SeafException throw SeafException.encodingException; } catch (IOException e) { throw SeafException.networkException; - } finally { - try { - if (is != null) { - is.close(); - } - } catch (Exception e) { - // ignore - } + } catch (HttpRequestException e) { + throw SeafException.networkException; } } - - private File getFileFromLink(String dlink, String path, String localPath, - String oid, ProgressMonitor monitor) - throws SeafException { + + private File getFileFromLink(String dlink, String path, String localPath, + String oid, ProgressMonitor monitor) + throws SeafException { if (dlink == null) return null; File file = new File(localPath); - InputStream is = null; - OutputStream os = null; - HttpURLConnection conn = null; try { int i = dlink.lastIndexOf('/'); String quoted = dlink.substring(0, i) + "/" + URLEncoder.encode(dlink.substring(i+1), "UTF-8"); - conn = prepareFileGet(quoted); - conn.connect(); - int response = conn.getResponseCode(); - if (response != 200) - if (conn.getResponseMessage() == null) + + HttpRequest req = prepareApiFileGetRequest(quoted); + if (req.code() != 200) { + if (req.message() == null) { throw SeafException.networkException; - else - throw new SeafException(response, conn.getResponseMessage()); - + } else { + throw new SeafException(req.code(), req.message()); + } + } + File tmp = DataManager.getTempFile(path, oid); // Log.d(DEBUG_TAG, "write to " + tmp.getAbsolutePath()); - - is = conn.getInputStream(); - os = new FileOutputStream(tmp); - long nextUpdate = System.currentTimeMillis() + 1000; - long total = 0; - byte[] data = new byte[1024]; - while (true) { - int len = is.read(data, 0, 1024); - if (Thread.currentThread().isInterrupted()) - return null; - - if (len == -1) - break; - os.write(data, 0, len); - total += len; - if (monitor != null) - if (monitor.isCancelled()) - return null; - - if (System.currentTimeMillis() > nextUpdate) { - if (monitor != null) monitor.onProgressNotify(total); - nextUpdate = System.currentTimeMillis() + 1000; - } + if (monitor != null) { + req.receive(tmp); + } else { + req.receive(new MonitoredFileOutputStream(tmp, monitor)); } if (tmp.renameTo(file) == false) { @@ -487,6 +253,7 @@ private File getFileFromLink(String dlink, String path, String localPath, return null; } return file; + } catch (SeafException e) { throw e; } catch (UnsupportedEncodingException e) { @@ -494,10 +261,13 @@ private File getFileFromLink(String dlink, String path, String localPath, } catch (IOException e) { e.printStackTrace(); throw SeafException.networkException; - } finally { - if (conn != null) - conn.disconnect(); - // do not need to close is and os + } catch (HttpRequestException e) { + if (e.getCause() instanceof MonitorCancelledException) { + Log.d(DEBUG_TAG, "download is cancelled by user"); + return null; + } else { + throw SeafException.networkException; + } } } @@ -516,16 +286,17 @@ public File getFile(String repoID, String path, String localPath, String oid, Pr // set password for an encrypted repo public void setPassword(String repoID, String passwd) throws SeafException { try { - HttpURLConnection conn = preparePost("api2/repos/" + repoID + "/", true); + HttpRequest req = prepareApiPostRequest("api2/repos/" + repoID + "/", true, null); - List params = new ArrayList(); - params.add(new BasicNameValuePair("password", passwd)); - doPost(conn, params); - if (conn.getResponseCode() != 200) - if (conn.getResponseMessage() == null) + req.form("password", passwd); + + if (req.code() != 200) { + if (req.message() == null) { throw SeafException.networkException; - else - throw new SeafException(conn.getResponseCode(), conn.getResponseMessage()); + } else { + throw new SeafException(req.code(), req.message()); + } + } } catch (SeafException e) { Log.d(DEBUG_TAG, "Set Password err: " + e.getCode()); throw e; @@ -537,27 +308,25 @@ public void setPassword(String repoID, String passwd) throws SeafException { } private String getUploadLink(String repoID, boolean isUpdate) throws SeafException { - InputStream is = null; try { - String url; + String apiPath; if (isUpdate) { - url = "api2/repos/" + repoID + "/update-link/"; + apiPath = "api2/repos/" + repoID + "/update-link/"; } else { - url = "api2/repos/" + repoID + "/upload-link/"; + apiPath = "api2/repos/" + repoID + "/upload-link/"; } - HttpURLConnection conn = prepareGet(url); - conn.connect(); - int response = conn.getResponseCode(); - if (response != 200) { - Log.d("Upload", "Failed to get upload link " + response); - if (conn.getResponseMessage() == null) + + HttpRequest req = prepareApiGetRequest(apiPath); + if (req.code() != 200) { + Log.d("Upload", "Failed to get upload link " + req.code()); + if (req.message() == null) { throw SeafException.networkException; - else - throw new SeafException(conn.getResponseCode(), conn.getResponseMessage()); + } else { + throw new SeafException(req.code(), req.message()); + } } - - is = conn.getInputStream(); - String result = Utils.readIt(is); + + String result = new String(req.bytes(), "UTF-8"); // should return "\"http://gonggeng.org:8082/...\"" or "\"https://gonggeng.org:8082/...\" if (result.startsWith("\"http")) { // remove the starting and trailing quote @@ -573,22 +342,9 @@ private String getUploadLink(String repoID, boolean isUpdate) throws SeafExcepti else Log.d(DEBUG_TAG, "get upload link error"); throw SeafException.unknownException; - } finally { - try { - if (is != null) { - is.close(); - } - } catch (Exception e) { - // ignore - } } } - String attachmentName = "file"; - String crlf = "\r\n"; - String twoHyphens = "--"; - String boundary = "----SeafileAndroidBound$_$"; - /** * Upload a file to update an existing file */ @@ -620,161 +376,95 @@ public void uploadFile(String repoID, String dir, String filePath, ProgressMonit } private String uploadFileCommon(String link, String repoID, String dir, - String filePath, ProgressMonitor monitor, boolean isUpdate) + String filePath, ProgressMonitor monitor, boolean isUpdate) throws SeafException { - DataOutputStream request = null; - HttpURLConnection conn = null; - File file = new File(filePath); - if (!file.exists()) - throw new SeafException(SeafException.OTHER_EXCEPTION, "File not exists"); try { - URL url = new URL(link); - Log.d(DEBUG_TAG, "Upload to " + link); - - conn = (HttpURLConnection) url.openConnection(); - prepareSSL(conn, false); - conn.setConnectTimeout(15000); - conn.setRequestMethod("POST"); - //conn.setChunkedStreamingMode(0); - - int totalLen = 0; - - // write the parent dir - StringBuilder builder = new StringBuilder(); - builder.append(this.twoHyphens + this.boundary + this.crlf); // line 1, ------SeafileAndroidBound$_$ - builder.append("Content-Disposition: form-data; name=\"parent_dir\"" + this.crlf); // line 2 - builder.append(this.crlf); // line 3, an empty line - builder.append(dir + this.crlf); // line 4 - byte[] dirParam = builder.toString().getBytes("UTF-8"); - totalLen += dirParam.length; - - byte[] targetFileParam = {}; - if (isUpdate) { - builder = new StringBuilder(); - builder.append(this.twoHyphens + this.boundary + this.crlf); // line 1, ------SeafileAndroidBound$_$ - builder.append("Content-Disposition: form-data; name=\"target_file\"" + this.crlf); // line 2 - builder.append(this.crlf); // line 3, an empty line - String targetFilePath = Utils.pathJoin(dir, file.getName()); - builder.append(targetFilePath + this.crlf); // line 4 - targetFileParam = builder.toString().getBytes("UTF-8"); - totalLen += targetFileParam.length; + File file = new File(filePath); + if (!file.exists()) { + throw new SeafException(SeafException.OTHER_EXCEPTION, "File not exists"); } - String l1 = this.twoHyphens + this.boundary + this.crlf; // line 1 - byte[] l2 = new String("Content-Disposition: form-data; name=\"file\";filename=\"" - + file.getName() + "\"" + this.crlf).getBytes("UTF-8"); // line 2, - String l3 = "Content-Type: text/plain" + this.crlf; // line 3 - String l4 = this.crlf; // line 4 - totalLen += l1.length() + l2.length + l3.length() + l4.length() + file.length() + 2; - - String end = this.twoHyphens + this.boundary + this.twoHyphens + this.crlf; - totalLen += end.length(); + HttpRequest req = HttpRequest.post(link). + trustAllCerts().trustAllHosts(). + connectTimeout(15000); - Log.d(DEBUG_TAG, "Total len is " + totalLen); - conn.setFixedLengthStreamingMode(totalLen); - conn.setDoInput(true); - conn.setDoOutput(true); - // disable keep-alive, otherwise read the returned file ID will be failed. - conn.setRequestProperty("Connection", "close"); - conn.setRequestProperty("Cache-Control", "no-cache"); - conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + this.boundary); - - request = new DataOutputStream(conn.getOutputStream()); - request.write(dirParam); if (isUpdate) { - request.write(targetFileParam); + String targetFilePath = Utils.pathJoin(dir, file.getName()); + req.part("target_file", targetFilePath); + } else { + req.part("parent_dir", dir); } - request.writeBytes(l1); - request.write(l2); - request.writeBytes(l3); - request.writeBytes(l4); - FileInputStream in = new FileInputStream(file); - int total = 0; - long nextUpdate = System.currentTimeMillis() + 1000; - while (true) { - byte[] buffer = new byte[4096]; - int len = in.read(buffer, 0, 4096); - if (len == -1) - break; - request.write(buffer, 0, len); - total += len; - if (System.currentTimeMillis() > nextUpdate) { - if (monitor != null) monitor.onProgressNotify(total); - nextUpdate = System.currentTimeMillis() + 1000; - request.flush(); // seems to have to call this to prevent buffer in android 2.2 - } + + String contentType = "text/plain"; + if (monitor != null) { + req.bufferSize(MonitoredFileInputStream.BUFFER_SIZE); + req.part("file", file.getName(), contentType, + new MonitoredFileInputStream(file, monitor)); + } else { + req.part("file", file.getName(), contentType, + new FileInputStream(file)); } - request.writeBytes(this.crlf); - request.writeBytes(end); - request.flush(); - request.close(); - Log.d(DEBUG_TAG, "finish write"); + if (req.code() != 200) + if (req.message() == null) { + throw SeafException.networkException; + } else { + throw new SeafException(req.code(), req.message()); + } - // if we use https, only when we read input the data will be sent out - InputStream is = conn.getInputStream(); - if (!isUpdate) { - is.close(); + if (isUpdate) { + return new String(req.bytes(), "UTF-8"); + } else { + return null; + } + } catch (IOException e) { + throw SeafException.networkException; + + } catch (HttpRequestException e) { + if (e.getCause() instanceof MonitorCancelledException) { + Log.d(DEBUG_TAG, "upload is cancelled by user"); return null; } else { - String new_file_id = Utils.readIt(is); - is.close(); - return new_file_id; + throw SeafException.networkException; } - } catch (Exception e) { - e.printStackTrace(); - String msg = e.getMessage(); - if (msg != null) - Log.d(DEBUG_TAG, msg); - else - msg = ""; - throw new SeafException(SeafException.OTHER_EXCEPTION, msg); - } finally { - if (conn != null) conn.disconnect(); } } - public List createNewDir(String repoID, - String parentDir, - String dirName) throws SeafException { + public TwoTuple createNewDir(String repoID, + String parentDir, + String dirName) throws SeafException { - InputStream is = null; - HttpURLConnection conn = null; try { String fullPath = Utils.pathJoin(parentDir, dirName); - String encPath = URLEncoder.encode(fullPath, "UTF-8"); - conn = preparePost("api2/repos/" + repoID + - "/dir/" + "?p=" + encPath + - "&reloaddir=true"); - - List params = new ArrayList(); - params.add(new BasicNameValuePair("operation", "mkdir")); - doPost(conn, params); - - int response = conn.getResponseCode(); - if (response != 200) - if (conn.getResponseMessage() == null) + Map params = new HashMap(); + params.put("p", fullPath); + params.put("reloaddir", "true"); + + HttpRequest req = prepareApiPostRequest("api2/repos/" + repoID + "/dir/", true, params); + + req.form("operation", "mkdir"); + + if (req.code() != 200) { + if (req.message() == null) { throw SeafException.networkException; - else - throw new SeafException(response, conn.getResponseMessage()); + } else { + throw new SeafException(req.code(), req.message()); + } + } - String newDirID = conn.getHeaderField("oid"); + String newDirID = req.header("oid"); if (newDirID == null) { return null; } - is = conn.getInputStream(); - String content = Utils.readIt(is); - if (content == null) { + String content = new String(req.bytes(), "UTF-8"); + if (content.length() == 0) { return null; } - - List ret = new ArrayList(); - ret.add(newDirID); - ret.add(content); - return ret; + + return TwoTuple.newInstance(newDirID, content); } catch (SeafException e) { throw e; @@ -782,17 +472,111 @@ public List createNewDir(String repoID, throw SeafException.encodingException; } catch (IOException e) { throw SeafException.networkException; - } finally { - if (is != null) { - try { - is.close(); - } catch (Exception e) { + } catch (HttpRequestException e) { + throw SeafException.networkException; + } + } + + private class MonitoredFileInputStream extends InputStream { + public static final int BUFFER_SIZE = 4096; + + private ProgressMonitor monitor; + private InputStream src; + private long bytesRead = 0; + private long nextUpdate = 0; + + public MonitoredFileInputStream(File file, ProgressMonitor monitor) throws IOException { + this.src = new FileInputStream(file); + this.monitor = monitor; + } + + @Override + public int read(byte[] buffer) throws IOException { + int read = src.read(buffer); + if (read != -1) { + bytesRead += read; + checkMonitor(); + } + + return read; + } + + @Override + public int read() throws IOException { + int ret = src.read(); + if (ret != -1) { + ++bytesRead; + if (bytesRead % BUFFER_SIZE == 0) { + checkMonitor(); } } - if (conn != null) { - conn.disconnect(); + return ret; + } + + private void checkMonitor() throws MonitorCancelledException { + if (monitor.isCancelled() || + Thread.currentThread().isInterrupted()) { + throw new MonitorCancelledException(); } + + if (System.currentTimeMillis() > nextUpdate) { + monitor.onProgressNotify(bytesRead); + nextUpdate = System.currentTimeMillis() + 1000; + } + } + } + + private class MonitoredFileOutputStream extends OutputStream { + public static final int BUFFER_SIZE = 4096; + + private ProgressMonitor monitor; + private OutputStream dst; + private long bytesWritten = 0; + private long nextUpdate = 0; + + public MonitoredFileOutputStream(File file, ProgressMonitor monitor) throws IOException { + this.dst = new FileOutputStream(file); + this.monitor = monitor; + } + + @Override + public void write(byte[] buffer) throws IOException { + dst.write(buffer); + bytesWritten += buffer.length; + checkMonitor(); + } + + @Override + public void write(int b) throws IOException { + dst.write(b); + ++bytesWritten; + if (bytesWritten % BUFFER_SIZE == 0) { + checkMonitor(); + } + } + + private void checkMonitor() throws MonitorCancelledException { + if (monitor.isCancelled() || + Thread.currentThread().isInterrupted()) { + throw new MonitorCancelledException(); + } + + if (System.currentTimeMillis() > nextUpdate) { + monitor.onProgressNotify(bytesWritten); + nextUpdate = System.currentTimeMillis() + 1000; + } + + } + } + + + private class MonitorCancelledException extends IOException { + private static final long serialVersionUID = -1170466989781746232L; + + @Override + public String toString() { + return "the upload/download task has been cancelled"; } } -} \ No newline at end of file +} diff --git a/src/com/seafile/seadroid2/data/DataManager.java b/src/com/seafile/seadroid2/data/DataManager.java index ffeb9e553..88877fbd2 100644 --- a/src/com/seafile/seadroid2/data/DataManager.java +++ b/src/com/seafile/seadroid2/data/DataManager.java @@ -522,15 +522,15 @@ private String removeDirIDFromCache(String repoID, String path) { } public void createNewDir(String repoID, String parentDir, String dirName) throws SeafException { - List ret = sc.createNewDir(repoID, parentDir, dirName); + TwoTuple ret = sc.createNewDir(repoID, parentDir, dirName); if (ret == null) { return; } invalidateCache(repoID, parentDir); - String newDirID = ret.get(0); - String response = ret.get(1); + String newDirID = ret.getFirst(); + String response = ret.getSecond(); // The response is the dirents of the parentDir after creating // the new dir. We save it to avoid request it again File cache = getFileForDirentsCache(newDirID);