diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2476ab46e..d8f4b8e84 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,13 +21,12 @@ - - - + - + + diff --git a/app/src/main/java/com/seafile/seadroid2/SeafConnection.java b/app/src/main/java/com/seafile/seadroid2/SeafConnection.java index 9476da341..13b949ace 100644 --- a/app/src/main/java/com/seafile/seadroid2/SeafConnection.java +++ b/app/src/main/java/com/seafile/seadroid2/SeafConnection.java @@ -47,8 +47,6 @@ * @author plt */ public class SeafConnection { - public static final int HTTP_STATUS_REPO_PASSWORD_REQUIRED = 440; - private static final String DEBUG_TAG = "SeafConnection"; private static final int CONNECTION_TIMEOUT = 15000; private static final int READ_TIMEOUT = 30000; @@ -69,12 +67,6 @@ private HttpRequest prepareApiGetRequest(String apiPath, Map params) return req; } - private HttpRequest prepareApiPutRequest(String apiPath, Map params) throws IOException { - HttpRequest req = HttpRequest.put(account.server + apiPath, params, false); - setRequestCommon(req); - return req; - } - private void setRequestCommon(HttpRequest req) { req.readTimeout(READ_TIMEOUT) .connectTimeout(CONNECTION_TIMEOUT) @@ -99,338 +91,10 @@ private HttpRequest prepareHttpsCheck(HttpRequest req) { return req; } - private HttpRequest prepareApiGetRequest(String apiPath) throws IOException { - return prepareApiGetRequest(apiPath, null); - } - - private HttpRequest prepareApiFileGetRequest(String url) throws HttpRequestException { - HttpRequest req = HttpRequest.get(url).connectTimeout(CONNECTION_TIMEOUT).followRedirects(true); - - return prepareHttpsCheck(req); - } - - /** - * Prepare a post request. - * - * @param apiPath The path of the http request - * @param withToken - * @param params The query param to be appended to the request url - * @throws IOException - */ - private HttpRequest prepareApiPostRequest(String apiPath, boolean withToken, Map params) throws HttpRequestException { - return prepareApiPostRequest(apiPath, withToken, params, false); - } - - /** - * Prepare a post request. - * - * @param apiPath The path of the http request - * @param withToken - * @param params The query param to be appended to the request url - * @param encode true to encode the full URL - * @throws IOException - */ - private HttpRequest prepareApiPostRequest(String apiPath, boolean withToken, Map params, boolean encode) throws HttpRequestException { - HttpRequest req = HttpRequest.post(account.server + apiPath, params, encode) - .followRedirects(true) - .connectTimeout(CONNECTION_TIMEOUT); - - if (withToken) { - req.header("Authorization", "Token " + account.token); - } - - return prepareHttpsCheck(req); - } - - private HttpRequest prepareApiDeleteRequest(String apiPath, Map params) throws HttpRequestException { - HttpRequest req = HttpRequest.delete(account.server + apiPath, params, false) - .followRedirects(true) - .connectTimeout(CONNECTION_TIMEOUT); - - req.header("Authorization", "Token " + account.token); - - return prepareHttpsCheck(req); - } - - /** - * Login into the server - * - * @return true if login success, false otherwise - * @throws SeafException - */ - private boolean realLogin(String passwd, String authToken, boolean rememberDevice) throws SeafException { - boolean withAuthToken = false; - HttpRequest req = null; - try { - req = prepareApiPostRequest("api2/auth-token/", false, null); - // Log.d(DEBUG_TAG, "Login to " + account.server + "api2/auth-token/"); - - if (!TextUtils.isEmpty(authToken)) { - req.header("X-Seafile-OTP", authToken); - withAuthToken = true; - // Log.d(DEBUG_TAG, "authToken " + authToken); - } - if (!TextUtils.isEmpty(account.sessionKey)) { - req.header("X-SEAFILE-S2FA", account.sessionKey); - } - if (rememberDevice) { - req.header("X-SEAFILE-2FA-TRUST-DEVICE", 1); - } - req.form("username", account.email); - req.form("password", passwd); - - String appVersion = ""; - Context context = SeadroidApplication.getAppContext(); - try { - PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - appVersion = pInfo.versionName; - } catch (NameNotFoundException e) { - // ignore - } - - //TODO Secure.getString(context.getContentResolver(), Secure.ANDROID_ID); ? - String deviceId = DeviceIdManager.getInstance().getOrSet(); - - req.form("platform", "android"); - req.form("device_id", deviceId); - req.form("device_name", Build.MODEL); - req.form("client_version", appVersion); - req.form("platform_version", Build.VERSION.RELEASE); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK, withAuthToken); - String sessionKey = req.header("x-seafile-s2fa"); - if (!TextUtils.isEmpty(sessionKey)) { - account.sessionKey = sessionKey; - } - String contentAsString = new String(req.bytes(), "UTF-8"); - JSONObject obj = Utils.parseJsonObject(contentAsString); - if (obj == null) - return false; - account.token = obj.getString("token"); - return true; - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - e.printStackTrace(); - throw SeafException.networkException; - } catch (JSONException e) { - throw SeafException.illFormatException; - } - } - - /** - *

- * get Account info, which consists of three fields, usage, total and email. - *

- * use GET to send HTTP request. - * - * @return - * @throws SeafException - */ - public String getAccountInfo() throws SeafException { - - String result; - try { - HttpRequest req = prepareApiGetRequest("api2/account/info/"); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - result = new String(req.bytes(), "UTF-8"); - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - - return result; - } - - public String getServerInfo() throws SeafException { - - String result; - try { - HttpRequest req = prepareApiGetRequest("api2/server-info/"); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - result = new String(req.bytes(), "UTF-8"); - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - return result; - } - - public boolean doLogin(String passwd, String authToken, boolean rememberDevice) throws SeafException { - try { - return realLogin(passwd, authToken, rememberDevice); - } catch (Exception e) { - // do again - return realLogin(passwd, authToken, rememberDevice); - } - } - - public String getRepos() throws SeafException { - HttpRequest req = null; - try { - req = prepareApiGetRequest("api/v2.1/repos/"); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - String result = new String(req.bytes(), "UTF-8"); - return result; - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - } - - public String getEvents(int start, boolean useNewActivity) throws SeafException { - String apiPath; - try { - Map params = Maps.newHashMap(); - if (useNewActivity) { - apiPath = String.format("api/v2.1/activities/"); - if (start == 0) { - start = 1; - } - params.put("page", start); - } else { - apiPath = String.format("api2/events/"); - params.put("start", start); - } - - HttpRequest req = prepareApiGetRequest(apiPath, params); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - return new String(req.bytes(), "UTF-8"); - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - } - - public String getHistoryChanges(String repoID, String commitId) throws SeafException { - try { - String apiPath = String.format("api2/repo_history_changes/%s/", repoID); - Map params = Maps.newHashMap(); - params.put("commit_id", commitId); - HttpRequest req = prepareApiGetRequest(apiPath, params); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - String result = new String(req.bytes(), "UTF-8"); - - return result; - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - } - - public String getStarredFiles() throws SeafException { - try { - HttpRequest req = prepareApiGetRequest(ApiUrls.STAR_ITEMS); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - return new String(req.bytes(), "UTF-8"); - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - } - - public String getAvatar(String email, int size) throws SeafException { - try { - String apiPath = String.format("api2/avatars/user/%s/resized/%d", email, size); - HttpRequest req = prepareApiGetRequest(apiPath); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - String result = new String(req.bytes(), "UTF-8"); - return result; - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - } - - public String searchLibraries(String query, int page, int pageSize) throws SeafException { - - try { - Map params = Maps.newHashMap(); - params.put("q", encodeUriComponent(query)); - - if (pageSize < 0) { - page = 20; - } - params.put("per_page", pageSize); - params.put("page", page); - - HttpRequest req = prepareApiGetRequest("api2/search/", params); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - String result = new String(req.bytes(), "UTF-8"); - return result; - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - - } - private static String encodeUriComponent(String src) throws UnsupportedEncodingException { return URLEncoder.encode(src, "UTF-8"); } - /** - * Get the contents of a directory. - * - * @param repoID - * @param path - * @return A non-null Pair of (dirID, content). If the local cache is up to date, the "content" is null. - * @throws SeafException - */ - public String getDirents(String repoID, String path) throws SeafException { - try { - String apiPath = String.format(ApiUrls.REPOS_DIR, repoID); - Map params = Maps.newHashMap(); - params.put("p", encodeUriComponent(path)); - - HttpRequest req = prepareApiGetRequest(apiPath, params); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - byte[] rawBytes = req.bytes(); - if (rawBytes == null) { - throw SeafException.unknownException; - } - return new String(rawBytes, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw SeafException.encodingException; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - } - public Pair getDownloadLink(String repoID, String path, boolean isReUsed) throws SeafException { try { String apiPath = String.format("api2/repos/%s/file/", repoID); @@ -464,890 +128,49 @@ public Pair getDownloadLink(String repoID, String path, boolean } } - public String getBlockDownloadList(String repoID, String path) throws SeafException, IOException { - try { - String apiPath = String.format("api2/repos/%s/file/", repoID); - Map params = Maps.newHashMap(); - params.put("p", encodeUriComponent(path)); - params.put("op", "downloadblks"); - HttpRequest req = prepareApiGetRequest(apiPath, params); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - String result = new String(req.bytes(), "UTF-8"); - return result; - } catch (SeafException | IOException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - /** - * get file server link for downloading a block - * - * @param repoID - * @param fileId - * @param blockId - * @return - * @throws SeafException - * @throws IOException - */ - private String getBlockDownloadLink(String repoID, String fileId, String blockId) throws SeafException, IOException { - try { - String apiPath = String.format("api2/repos/%s/files/%s/blks/%s/download-link/", repoID, fileId, blockId); - HttpRequest req = prepareApiGetRequest(apiPath, null); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - return new String(req.bytes(), "UTF-8"); - } catch (SeafException | IOException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - /** - * Get the latest version of the file from server - * - * @param repoID - * @param fileBlocks - * @param blockId - * @param localPath - * @param monitor - * @return A two tuple of (fileID, file). If the local cached version is up to date, the returned file is null. - */ - public Pair getBlock(String repoID, - FileBlocks fileBlocks, - String blockId, - String localPath, - long fileSize, - ProgressMonitor monitor) throws SeafException, IOException, JSONException { - String dlink = getBlockDownloadLink(repoID, fileBlocks.getFileId(), blockId).replaceAll("\"", ""); - - File block = getBlockFromLink(dlink, fileBlocks, blockId, localPath, fileSize, monitor); - if (block != null) { - return new Pair<>(blockId, block); - } else { - throw SeafException.unknownException; - } + public String getReUsedFileLink(String repoID, String path) throws SeafException { + //Setting up links can be reused + Pair ret = getDownloadLink(repoID, path, true); + return ret.first; } + private void checkRequestResponseStatus(HttpRequest req, int expectedStatusCode) throws SeafException { + if (req.code() != expectedStatusCode) { + Log.d(DEBUG_TAG, "HTTP request failed : " + req.url() + ", " + req.code() + ", " + req.message()); - 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); - - try { - int i = dlink.lastIndexOf('/'); - String quoted = dlink.substring(0, i) + "/" + URLEncoder.encode(dlink.substring(i + 1), "UTF-8"); - - HttpRequest req = prepareApiFileGetRequest(quoted); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - if (monitor != null) { - Long size = -1L; - if (req.contentLength() > 0) { - size = Long.valueOf(req.contentLength()); + if (req.message() == null) { + throw SeafException.networkException; + } else if (req.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { + String wiped = req.header("X-Seafile-Wiped"); + if (wiped != null) { + throw SeafException.remoteWipedException; } else { - // The req.contentLength() returns an int, which has a max value of - // 2GB. So if a file size exceeds 2GB, request.contentLength() would - // return -1. In such case, we parse the content length from the raw - // header string directly. - // - // See https://github.com/kevinsawicki/http-request/blob/http-request-5.6/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java#L2519-L2521 - String contentLengthheader = req.header(HttpRequest.HEADER_CONTENT_LENGTH); - // The server may not send us the "Content-Length" header in the - // response, e.g. when the server is using chunked transfer encoding. - if (contentLengthheader != null) { - size = Long.parseLong(contentLengthheader); - } + throw new SeafException(req.code(), req.message()); } - - if (size > 0) { - monitor.onProgressNotify(size, false); + } else { + try { + String result = new String(req.bytes(), "UTF-8"); + if (result != null && Utils.parseJsonObject(result) != null) { + JSONObject json = Utils.parseJsonObject(result); + if (json.has("detail")) { + throw new SeafException(req.code(), json.optString("detail")); + } + throw new SeafException(req.code(), json.optString("error_msg")); + } else { + throw new SeafException(req.code(), req.message()); + } + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + throw new SeafException(req.code(), req.message()); } - } - File tmp = DataManager.createTempFile(); - // Log.d(DEBUG_TAG, "write to " + tmp.getAbsolutePath()); - if (monitor == null) { - req.receive(tmp); - } else { - req.bufferSize(MonitoredFileOutputStream.BUFFER_SIZE); - req.receive(new MonitoredFileOutputStream(tmp, monitor)); } + } else { + // Log.v(DEBUG_TAG, "HTTP request ok : " + req.url()); + } + } - if (!tmp.renameTo(file)) { - Log.w(DEBUG_TAG, "Rename file error"); - return null; - } - return file; - } catch (SeafException e) { - throw e; - } catch (UnsupportedEncodingException e) { - throw SeafException.encodingException; - } catch (IOException e) { - e.printStackTrace(); - throw SeafException.networkException; - } catch (HttpRequestException e) { - if (e.getCause() instanceof MonitorCancelledException) { - // Log.d(DEBUG_TAG, "download is cancelled"); - throw SeafException.userCancelledException; - } else { - throw getSeafExceptionFromHttpRequestException(e); - } - } - } - - private File getBlockFromLink(String dlink, FileBlocks fileBlocks, String blkId, String localPath, long fileSize, ProgressMonitor monitor) throws SeafException { - if (dlink == null) - return null; - - try { - - HttpRequest req = prepareApiFileGetRequest(dlink); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - if (monitor != null) { - /*if (req.header(HttpRequest.HEADER_CONTENT_LENGTH) == null) { - throw SeafException.illFormatException; - } - Long size = Long.parseLong(req.header(HttpRequest.HEADER_CONTENT_LENGTH));*/ - if (req.contentLength() > 0) { - monitor.onProgressNotify(fileSize, true); - } - } - - File block = new File(localPath); - // Log.d(DEBUG_TAG, "write to " + block.getAbsolutePath()); - if (monitor == null) { - req.receive(block); - } else { - req.bufferSize(DataManager.BUFFER_SIZE); - req.receive(new MonitoredFileOutputStream(fileBlocks, blkId, block, monitor)); - } - - return block; - - } catch (SeafException e) { - throw e; - } catch (UnsupportedEncodingException e) { - throw SeafException.encodingException; - } catch (IOException e) { - e.printStackTrace(); - throw SeafException.networkException; - } catch (HttpRequestException e) { - if (e.getCause() instanceof MonitorCancelledException) { - // Log.d(DEBUG_TAG, "download is cancelled"); - throw SeafException.userCancelledException; - } else { - throw getSeafExceptionFromHttpRequestException(e); - } - } - } - - public String getReUsedFileLink(String repoID, String path) throws SeafException { - //Setting up links can be reused - Pair ret = getDownloadLink(repoID, path, true); - return ret.first; - } - - /** - * Get the latest version of the file from server - * - * @param repoID - * @param path - * @param localPath - * @param cachedFileID The file id of the local cached version - * @param monitor - * @return A two tuple of (fileID, file). If the local cached version is up to date, the returned file is null. - */ - public Pair getFile(String repoID, String path, String localPath, String cachedFileID, ProgressMonitor monitor) throws SeafException { - Pair ret = getDownloadLink(repoID, path, false); - String dlink = ret.first; - String fileID = ret.second; - - if (fileID.equals(cachedFileID)) { - // cache is valid - // Log.d(DEBUG_TAG, String.format("file %s is cached", path)); - return new Pair(fileID, null); - } else { - /*Log.d(DEBUG_TAG, - String.format("file %s will be downloaded from server, latest %s, local cache %s", - path, fileID, cachedFileID != null ? cachedFileID : "null"));*/ - - File file = getFileFromLink(dlink, path, localPath, fileID, monitor); - if (file != null) { - return new Pair(fileID, file); - } else { - throw SeafException.unknownException; - } - } - } - - // get encrypted repo info - public String getEncryptRepo(String repoID) throws SeafException { - Response response = null; - try { - String url = account.server + "api2/repos/" + repoID; - Request request = new Request.Builder() - .url(url) - .header("Authorization", "Token " + account.token) - .build(); - response = new OkHttpClient().newCall(request).execute(); - if (response.code() == HttpURLConnection.HTTP_OK) { - return response.body().string(); - } else { - throw new SeafException(response.code(), response.message()); - } - } catch (IOException e) { - e.printStackTrace(); - } - return ""; - } - - // set password for an encrypted repo - public boolean setPassword(String repoID, String passwd) throws SeafException { - try { - HttpRequest req = prepareApiPostRequest("api2/repos/" + repoID + "/", true, null); - - req.form("password", passwd); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - return true; - } catch (SeafException e) { - Log.d(DEBUG_TAG, "Set Password err: " + e.getCode()); - throw e; - } catch (Exception e) { - Log.d(DEBUG_TAG, "Exception in setPassword "); - e.printStackTrace(); - } - return false; - } - - public void createNewRepo(String repoName, String description, String password) throws SeafException { - HttpRequest req = prepareApiPostRequest("api2/repos/", true, null); - req.form("name", repoName); - - if (description.length() > 0) { - req.form("desc", description); - } - - if (password.length() > 0) { - req.form("passwd", password); - } - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - } - - public Pair createNewDir(String repoID, String parentDir, String dirName) throws SeafException { - - HttpRequest req = null; - try { - String fullPath = Utils.pathJoin(parentDir, dirName); - final String encodeUriComponent = encodeUriComponent(fullPath).replaceAll("\\+", "%20"); - Map params = Maps.newHashMap(); - params.put("p", encodeUriComponent); - params.put("reloaddir", "true"); - - req = prepareApiPostRequest("api2/repos/" + repoID + "/dir/", true, params, false); - - req.form("operation", "mkdir"); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - String newDirID = req.header("oid"); - if (newDirID == null) { - return null; - } - - String content = new String(req.bytes(), "UTF-8"); - if (content.length() == 0) { - return null; - } - - return new Pair(newDirID, content); - - } catch (SeafException e) { - throw e; - } catch (UnsupportedEncodingException e) { - throw SeafException.encodingException; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - public Pair createNewFile(String repoID, String parentDir, String fileName) throws SeafException { - - try { - String fullPath = Utils.pathJoin(parentDir, fileName); - final String encodeUriComponent = encodeUriComponent(fullPath).replaceAll("\\+", "%20"); - Map params = Maps.newHashMap(); - params.put("p", encodeUriComponent); - params.put("reloaddir", "true"); - - HttpRequest req = prepareApiPostRequest("api2/repos/" + repoID + "/file/", true, params, false); - - req.form("operation", "create"); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - String newDirID = req.header("oid"); - if (newDirID == null) { - return null; - } - - String content = new String(req.bytes(), "UTF-8"); - if (content.length() == 0) { - return null; - } - - return new Pair(newDirID, content); - } catch (SeafException e) { - throw e; - } catch (UnsupportedEncodingException e) { - throw SeafException.encodingException; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - - /** - * Wrap a FileInputStream in a upload task. We publish the progress of the upload during the process, and if we detect the task has been cancelled by the user, we throw a {@link MonitorCancelledException} to indicate such a situation. - */ - private class MonitoredFileInputStream extends InputStream { - public static final int BUFFER_SIZE = 1024; - - - private static final long PROGRESS_UPDATE_INTERVAL = 1000; - private ProgressMonitor monitor; - private InputStream src; - private long bytesRead = 0; - private long nextUpdate = System.currentTimeMillis() + PROGRESS_UPDATE_INTERVAL; - - 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(); - } - } - - return ret; - } - - @Override - public void close() throws IOException { - src.close(); - } - - private void checkMonitor() throws MonitorCancelledException { - if (monitor.isCancelled() || - Thread.currentThread().isInterrupted()) { - throw new MonitorCancelledException(); - } - - if (System.currentTimeMillis() > nextUpdate) { - monitor.onProgressNotify(bytesRead, false); - nextUpdate = System.currentTimeMillis() + PROGRESS_UPDATE_INTERVAL; - } - } - } - - /** - * Wrap a FileOutputStream in a download task. We publish the upload progress during the process, and if we detect the task has been cancelled by the user, we throw a {@link MonitorCancelledException} to indicate such a situation. - */ - private class MonitoredFileOutputStream extends OutputStream { - public static final int BUFFER_SIZE = 4096; - - private static final long PROGRESS_UPDATE_INTERVAL = 500; - private ProgressMonitor monitor; - private OutputStream dst; - private long bytesWritten = 0; - private long nextUpdate = System.currentTimeMillis() + PROGRESS_UPDATE_INTERVAL; - - private FileBlocks fileBlocks; - private String blockId; - - public MonitoredFileOutputStream(File file, ProgressMonitor monitor) throws IOException { - this.dst = new FileOutputStream(file); - this.monitor = monitor; - } - - public MonitoredFileOutputStream(FileBlocks fileBlocks, String blockId, File file, ProgressMonitor monitor) throws IOException { - this.dst = new FileOutputStream(file); - this.monitor = monitor; - if (fileBlocks != null) { - this.fileBlocks = fileBlocks; - this.blockId = blockId; - } - } - - @Override - public void write(byte[] buffer, int off, int len) throws IOException { - dst.write(buffer, off, len); - bytesWritten += len; - checkMonitor(); - } - - @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(); - } - } - - @Override - public void close() throws IOException { - dst.close(); - } - - private void checkMonitor() throws MonitorCancelledException { - if (monitor.isCancelled() || - Thread.currentThread().isInterrupted()) { - throw new MonitorCancelledException(); - } - - if (System.currentTimeMillis() > nextUpdate) { - if (fileBlocks != null) { - fileBlocks.getBlock(blockId).finished = bytesWritten; - monitor.onProgressNotify(fileBlocks.getFinished(), false); - } else { - monitor.onProgressNotify(bytesWritten, false); - } - nextUpdate = System.currentTimeMillis() + PROGRESS_UPDATE_INTERVAL; - } - } - } - - private class MonitorCancelledException extends IOException { - private static final long serialVersionUID = -1170466989781746232L; - - @Override - public String toString() { - return "the upload/download task has been cancelled"; - } - } - - public void renameRepo(String repoID, String newName) throws SeafException { - Map params = Maps.newHashMap(); - params.put("op", "rename"); - - HttpRequest req = prepareApiPostRequest(String.format("api2/repos/%s/", repoID), true, params); - req.form("repo_name", newName); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - } - - public void deleteRepo(String repoID) throws SeafException { - try { - HttpRequest req = prepareApiDeleteRequest(String.format("api2/repos/%s/", repoID), null); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - - public boolean deleteShareLink(String token) throws SeafException { - try { - HttpRequest req = prepareApiDeleteRequest(String.format("api/v2.1/share-links/%s/", token), null); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - String result = new String(req.bytes(), "UTF-8"); - if (result != null && Utils.parseJsonObject(result) != null) { - JSONObject obj = Utils.parseJsonObject(result); - return obj.getBoolean("success"); - } else { - throw SeafException.illFormatException; - } - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } catch (JSONException e) { - e.printStackTrace(); - } - return false; - } - - - public String getShareLink(String repoID, String path) throws SeafException { - try { - Map params = Maps.newHashMap(); - params.put("repo_id", repoID); - params.put("path", path); - HttpRequest req = prepareApiGetRequest("api/v2.1/share-links", params); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - String result = new String(req.bytes(), "UTF-8"); - return result; - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (IOException e) { - throw SeafException.networkException; - } - } - - public String getShareLink(String repoID, String path, String password, String days) throws SeafException { - try { - Map params = Maps.newHashMap(); - HttpRequest req = prepareApiPostRequest("api/v2.1/share-links/", true, params, false); - req.form("repo_id", repoID); - req.form("path", path); - if (!TextUtils.isEmpty(password)) { - req.form("password", password); - } - if (!TextUtils.isEmpty(days)) { - req.form("expire_days", days); - } - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - String result = new String(req.bytes(), "UTF-8"); - if (result != null && Utils.parseJsonObject(result) != null) { - JSONObject obj = Utils.parseJsonObject(result); - return obj.getString("link"); - } else { - throw SeafException.illFormatException; - } - - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (UnsupportedEncodingException e) { - throw SeafException.illFormatException; - } catch (JSONException e) { - e.printStackTrace(); - throw SeafException.illFormatException; - } - } - - public void completeRemoteWipe(String token) throws SeafException { - try { - HttpRequest req = prepareApiPostRequest("api2/device-wiped/", true, null); - req.form("token", token); - checkRequestResponseStatus(req, HttpURLConnection.HTTP_CREATED); - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - public void star(String repoID, String path) throws SeafException { - try { - //api/v2.1/starred-items/ - HttpRequest req = prepareApiPostRequest("api2/starredfiles/", true, null); - - req.form("repo_id", repoID); - req.form("p", path); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_CREATED); - - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - public void starItems(String repoID, String path) throws SeafException { - try { - //api/v2.1/starred-items/ - HttpRequest req = prepareApiPostRequest(ApiUrls.STAR_ITEMS, true, null); - - req.form("repo_id", repoID); - req.form("path", path); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - public void unstar(String repoID, String path) throws SeafException { - try { - Map params = Maps.newHashMap(); - params.put("repo_id", repoID); - params.put("p", path); - HttpRequest req = prepareApiDeleteRequest("api2/starredfiles/", params); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - public void unstarItems(String repoID, String path) throws SeafException { - try { - Map params = Maps.newHashMap(); - params.put("repo_id", repoID); - params.put("path", path); - HttpRequest req = prepareApiDeleteRequest(ApiUrls.STAR_ITEMS, params); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - public Pair rename(String repoID, String path, String newName, boolean isdir) throws SeafException { - try { - Map params = Maps.newHashMap(); - params.put("p", encodeUriComponent(path).replaceAll("\\+", "%20")); - params.put("reloaddir", "true"); - String suffix = isdir ? "/dir/" : "/file/"; - HttpRequest req = prepareApiPostRequest("api2/repos/" + repoID + suffix, true, params); - - req.form("operation", "rename"); - req.form("newname", newName); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - String newDirID = req.header("oid"); - if (newDirID == null) { - return null; - } - - String content = new String(req.bytes(), "UTF-8"); - if (content.length() == 0) { - return null; - } - - return new Pair(newDirID, content); - } catch (SeafException e) { - throw e; - } catch (UnsupportedEncodingException e) { - throw SeafException.encodingException; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - public boolean delete(String repoID, String path, boolean isdir) throws SeafException { - try { - Map params = Maps.newHashMap(); - params.put("p", encodeUriComponent(path).replaceAll("\\+", "%20")); - String suffix = isdir ? "/dir/" : "/file/"; - HttpRequest req = prepareApiDeleteRequest(ApiUrls.DELETE_FILE_OR_DIR + repoID + suffix, params); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - String content = new String(req.bytes(), "UTF-8"); - if (content.length() == 0) { - return false; - } - - JSONObject jsonObject = new JSONObject(content); - return jsonObject.optBoolean("success"); - } catch (SeafException e) { - throw e; - } catch (UnsupportedEncodingException e) { - throw SeafException.encodingException; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (JSONException e) { - throw SeafException.illFormatException; - } - } - - /** - * Copy a file or multiple files, multiple file/folder names should be seperated by a ":". - * - * @param srcRepoId the source repo id - * @param srcDir the source folder in src_repo - * @param srcFn list of file/folder names to copy. Multiple file/folder names can be seperated by ":" - * @param dstRepoId the destination repo id - * @param dstDir the destination folder in dst_repo - * @throws SeafException - */ - public void copy(String srcRepoId, String srcDir, String srcFn, String dstRepoId, String dstDir) throws SeafException { - try { - Map params = Maps.newHashMap(); - params.put("p", encodeUriComponent(srcDir).replaceAll("\\+", "%20")); - - HttpRequest req = prepareApiPostRequest("api2/repos/" + srcRepoId + "/fileops/copy/", true, params); - - req.form("dst_repo", dstRepoId); - req.form("dst_dir", dstDir); - req.form("file_names", srcFn); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } catch (UnsupportedEncodingException e) { - throw SeafException.encodingException; - } - } - - /** - * Move multiple files - * - * @param srcRepoId the source repo id - * @param srcDir the source folder in src_repo - * @param srcFn list of file/folder names to move. Multiple file/folder names can be seperated by ":" - * @param dstRepoId the destination repo id - * @param dstDir the destination folder in dst_repo - * @throws SeafException - */ - public void move(String srcRepoId, String srcDir, String srcFn, String dstRepoId, String dstDir) throws SeafException { - try { - Map params = Maps.newHashMap(); - params.put("p", srcDir); - - HttpRequest req = prepareApiPostRequest("api2/repos/" + srcRepoId + "/fileops/move/", true, params); - - req.form("dst_repo", dstRepoId); - req.form("dst_dir", dstDir); - req.form("file_names", srcFn); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - } catch (SeafException e) { - throw e; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - /** - * Move a single file - * - * @param srcRepoId the source repo id - * @param srcPath the source file path - * @param dstRepoId the destination repo id - * @param dstDir the destination folder in dst_repo - * @return - * @throws SeafException - */ - public Pair move(String srcRepoId, String srcPath, String dstRepoId, String dstDir) throws SeafException { - try { - Map params = Maps.newHashMap(); - params.put("p", encodeUriComponent(srcPath).replaceAll("\\+", "%20")); - params.put("reloaddir", "true"); - String suffix = "/file/"; - HttpRequest req = prepareApiPostRequest("api2/repos/" + srcRepoId + suffix, true, params); - - req.form("operation", "move"); - req.form("dst_repo", dstRepoId); - req.form("dst_dir", dstDir); - - checkRequestResponseStatus(req, HttpURLConnection.HTTP_OK); - - String newDirID = req.header("oid"); - if (newDirID == null) { - return null; - } - - String content = new String(req.bytes(), "UTF-8"); - if (content.length() == 0) { - return null; - } - - return new Pair(newDirID, content); - } catch (SeafException e) { - throw e; - } catch (UnsupportedEncodingException e) { - throw SeafException.encodingException; - } catch (HttpRequestException e) { - throw getSeafExceptionFromHttpRequestException(e); - } - } - - private void checkRequestResponseStatus(HttpRequest req, int expectedStatusCode) throws SeafException { - if (req.code() != expectedStatusCode) { - Log.d(DEBUG_TAG, "HTTP request failed : " + req.url() + ", " + req.code() + ", " + req.message()); - - if (req.message() == null) { - throw SeafException.networkException; - } else if (req.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { - String wiped = req.header("X-Seafile-Wiped"); - if (wiped != null) { - throw SeafException.remoteWipedException; - } else { - throw new SeafException(req.code(), req.message()); - } - } else { - try { - String result = new String(req.bytes(), "UTF-8"); - if (result != null && Utils.parseJsonObject(result) != null) { - JSONObject json = Utils.parseJsonObject(result); - if (json.has("detail")) { - throw new SeafException(req.code(), json.optString("detail")); - } - throw new SeafException(req.code(), json.optString("error_msg")); - } else { - throw new SeafException(req.code(), req.message()); - } - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - throw new SeafException(req.code(), req.message()); - } - - } - } else { - // Log.v(DEBUG_TAG, "HTTP request ok : " + req.url()); - } - } - - private void checkRequestResponseStatus(HttpRequest req, int expectedStatusCode, boolean withAuthToken) throws SeafException { - if (req.code() != expectedStatusCode) { - Log.d(DEBUG_TAG, "HTTP request failed : " + req.url() + ", " + req.code() + ", " + req.message()); - - if (req.message() == null) { - throw SeafException.networkException; - } else if (req.header("X-Seafile-OTP") != null && req.header("X-Seafile-OTP").equals("required")) { - if (withAuthToken) - throw SeafException.twoFactorAuthTokenInvalid; - else - throw SeafException.twoFactorAuthTokenMissing; - } else { - throw new SeafException(req.code(), req.message()); - } - } else { - // Log.v(DEBUG_TAG, "HTTP request ok : " + req.url()); - } - } private SeafException getSeafExceptionFromHttpRequestException(HttpRequestException e) { if (e.getCause() instanceof SSLHandshakeException) { diff --git a/app/src/main/res/layout/layout_frame_swipe_rv.xml b/app/src/main/res/layout/layout_frame_swipe_rv.xml index f5f5d54a2..59739b80b 100644 --- a/app/src/main/res/layout/layout_frame_swipe_rv.xml +++ b/app/src/main/res/layout/layout_frame_swipe_rv.xml @@ -1,6 +1,5 @@