diff --git a/core/.classpath b/core/.classpath index 427541e8..340040a3 100644 --- a/core/.classpath +++ b/core/.classpath @@ -10,6 +10,6 @@ - + diff --git a/core/AndroidManifest.xml b/core/AndroidManifest.xml index 15c7bfd5..a2adf6b2 100644 --- a/core/AndroidManifest.xml +++ b/core/AndroidManifest.xml @@ -17,8 +17,8 @@ --> + android:versionCode="216" + android:versionName="2.3.0" > +Transdroid 2.3.0\n +- Aria2 support\n +\n Transdroid 2.2.0\n - Dropped legacy Android support; Android 4.0.3 is the new minimum\n +- Added Default Server option\n - Fixed list selection quirks\n - Another language-related qBittorrent fix\n +- Copy RSS item or torrent file name to clipboard\n - Properly disable FTP download option when not supported\n - Support KitKat file picker and more file managers\n - Many updated translations\n diff --git a/core/res/values/strings.xml b/core/res/values/strings.xml index eaa7d429..d2491257 100644 --- a/core/res/values/strings.xml +++ b/core/res/values/strings.xml @@ -244,7 +244,8 @@ Port number User name Password - Deluge web password + Deluge web password + Secret token Advanced settings Local IP or host When connected to the specified local network @@ -350,6 +351,7 @@ + Aria2 BitComet Bitflu 1.2+ BitTorrent 6+ @@ -366,6 +368,7 @@ Vuze + daemon_aria2 daemon_bitcomet daemon_bitflu daemon_bittorrent diff --git a/core/src/org/transdroid/core/gui/settings/ServerSettingsActivity.java b/core/src/org/transdroid/core/gui/settings/ServerSettingsActivity.java index 1284ec6d..5864b329 100644 --- a/core/src/org/transdroid/core/gui/settings/ServerSettingsActivity.java +++ b/core/src/org/transdroid/core/gui/settings/ServerSettingsActivity.java @@ -117,6 +117,8 @@ public class ServerSettingsActivity extends KeyBoundPreferencesActivity { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); Daemon daemonType = Daemon.fromCode(prefs.getString("server_type_" + key, null)); extraPass.setEnabled(Daemon.supportsExtraPassword(daemonType)); + extraPass.setTitle(getString(daemonType == Daemon.Deluge ? R.string.pref_extrapassword : R.string.pref_secret)); + extraPass.setDialogTitle(extraPass.getTitle()); folder.setEnabled(daemonType == null ? false : Daemon.supportsCustomFolder(daemonType)); downloadDir.setEnabled(daemonType == null ? false : Daemon.needsManualPathSpecified(daemonType)); // sslTrustKey.setEnabled(sslValue && !sslTAValue); diff --git a/full/AndroidManifest.xml b/full/AndroidManifest.xml index cc02f828..2c5feef7 100644 --- a/full/AndroidManifest.xml +++ b/full/AndroidManifest.xml @@ -18,8 +18,8 @@ + android:versionCode="216" + android:versionName="2.3.0" > + * + * Transdroid 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. + * + * Transdroid 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 Transdroid. If not, see . + * + */ +package org.transdroid.daemon.Aria2c; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.base64.android.Base64; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.DaemonException; +import org.transdroid.daemon.DaemonException.ExceptionType; +import org.transdroid.daemon.DaemonSettings; +import org.transdroid.daemon.IDaemonAdapter; +import org.transdroid.daemon.Priority; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentDetails; +import org.transdroid.daemon.TorrentFile; +import org.transdroid.daemon.TorrentStatus; +import org.transdroid.daemon.task.AddByFileTask; +import org.transdroid.daemon.task.AddByMagnetUrlTask; +import org.transdroid.daemon.task.AddByUrlTask; +import org.transdroid.daemon.task.DaemonTask; +import org.transdroid.daemon.task.DaemonTaskFailureResult; +import org.transdroid.daemon.task.DaemonTaskResult; +import org.transdroid.daemon.task.DaemonTaskSuccessResult; +import org.transdroid.daemon.task.GetFileListTask; +import org.transdroid.daemon.task.GetFileListTaskSuccessResult; +import org.transdroid.daemon.task.GetTorrentDetailsTask; +import org.transdroid.daemon.task.GetTorrentDetailsTaskSuccessResult; +import org.transdroid.daemon.task.PauseTask; +import org.transdroid.daemon.task.RemoveTask; +import org.transdroid.daemon.task.ResumeTask; +import org.transdroid.daemon.task.RetrieveTask; +import org.transdroid.daemon.task.RetrieveTaskSuccessResult; +import org.transdroid.daemon.task.SetTransferRatesTask; +import org.transdroid.daemon.util.DLog; +import org.transdroid.daemon.util.HttpHelper; + +import android.net.Uri; +import android.text.TextUtils; + +/** + * The daemon adapter from the Aria2 torrent client. Documentation available at + * http://aria2.sourceforge.net/manual/en/html/aria2c.html + * @author erickok + */ +public class Aria2Adapter implements IDaemonAdapter { + + private static final String LOG_NAME = "Aria2 daemon"; + + private DaemonSettings settings; + private DefaultHttpClient httpclient; + + public Aria2Adapter(DaemonSettings settings) { + this.settings = settings; + } + + @Override + public DaemonTaskResult executeTask(DaemonTask task) { + + try { + JSONArray params = new JSONArray(); + + switch (task.getMethod()) { + case Retrieve: + + // Request all torrents from server + // NOTE Since there is no aria2.tellAll (or something) we have to use batch requests + JSONArray fields = new JSONArray().put("gid").put("status").put("totalLength").put("completedLength") + .put("uploadLength").put("downloadSpeed").put("uploadSpeed").put("numSeeders").put("dir") + .put("connections").put("errorCode").put("bittorrent").put("files"); + JSONObject active = buildRequest("aria2.tellActive", new JSONArray().put(fields)); + JSONObject waiting = buildRequest("aria2.tellWaiting", new JSONArray().put(0).put(9999).put(fields)); + JSONObject stopped = buildRequest("aria2.tellStopped", new JSONArray().put(0).put(9999).put(fields)); + params.put(active).put(waiting).put(stopped); + + List torrents = new ArrayList(); + JSONArray lists = makeRequestForArray(params.toString()); + for (int i = 0; i < lists.length(); i++) { + torrents.addAll(parseJsonRetrieveTorrents(lists.getJSONObject(i).getJSONArray("result"))); + } + return new RetrieveTaskSuccessResult((RetrieveTask) task, torrents, null); + + case GetTorrentDetails: + + // Request file listing of a torrent + params.put(task.getTargetTorrent().getUniqueID()); // gid + params.put(new JSONArray().put("bittorrent").put("errorCode")); + + JSONObject dinfo = makeRequest(buildRequest("aria2.tellStatus", params).toString()); + return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, + parseJsonTorrentDetails(dinfo.getJSONObject("result"))); + + case GetFileList: + + // Request file listing of a torrent + params.put(task.getTargetTorrent().getUniqueID()); // torrent_id + + JSONObject finfo = makeRequest(buildRequest("aria2.getFiles", params).toString()); + return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFileListing( + finfo.getJSONArray("result"), task.getTargetTorrent())); + + case AddByFile: + + // Encode the .torrent file's data + String file = ((AddByFileTask) task).getFile(); + InputStream in = new Base64.InputStream(new FileInputStream(new File(URI.create(file))), Base64.ENCODE); + StringWriter writer = new StringWriter(); + int c; + while ((c = in.read()) != -1) { + writer.write(c); + } + in.close(); + + // Request to add a torrent by local .torrent file + params.put(writer.toString()); + makeRequest(buildRequest("aria2.addTorrent", params).toString()); + return new DaemonTaskSuccessResult(task); + + case AddByUrl: + + // Request to add a torrent by URL + String url = ((AddByUrlTask) task).getUrl(); + params.put(new JSONArray().put(url)); + + makeRequest(buildRequest("aria2.addUri", params).toString()); + return new DaemonTaskSuccessResult(task); + + case AddByMagnetUrl: + + // Request to add a magnet link by URL + String magnet = ((AddByMagnetUrlTask) task).getUrl(); + params.put(new JSONArray().put(magnet)); + + makeRequest(buildRequest("aria2.addUri", params).toString()); + return new DaemonTaskSuccessResult(task); + + case Remove: + + // Remove a torrent + RemoveTask removeTask = (RemoveTask) task; + makeRequest(buildRequest(removeTask.includingData() ? "aria2.removeDownloadResult" : "aria2.remove", + params.put(removeTask.getTargetTorrent().getUniqueID())).toString()); + return new DaemonTaskSuccessResult(task); + + case Pause: + + // Pause a torrent + PauseTask pauseTask = (PauseTask) task; + makeRequest(buildRequest("aria2.pause", params.put(pauseTask.getTargetTorrent().getUniqueID())) + .toString()); + return new DaemonTaskSuccessResult(task); + + case PauseAll: + + // Resume all torrents + makeRequest(buildRequest("aria2.pauseAll", null).toString()); + return new DaemonTaskSuccessResult(task); + + case Resume: + + // Resume a torrent + ResumeTask resumeTask = (ResumeTask) task; + makeRequest(buildRequest("aria2.unpause", params.put(resumeTask.getTargetTorrent().getUniqueID())) + .toString()); + return new DaemonTaskSuccessResult(task); + + case ResumeAll: + + // Resume all torrents + makeRequest(buildRequest("aria2.unpauseAll", null).toString()); + return new DaemonTaskSuccessResult(task); + + case SetTransferRates: + + // Request to set the maximum transfer rates + SetTransferRatesTask ratesTask = (SetTransferRatesTask) task; + JSONObject options = new JSONObject(); + options.put("max-overall-download-limit", (ratesTask.getDownloadRate() == null ? -1 : ratesTask + .getDownloadRate().intValue())); + options.put("max-overall-upload-limit", (ratesTask.getUploadRate() == null ? -1 : ratesTask + .getUploadRate().intValue())); + + makeRequest(buildRequest("aria2.changeGlobalOption", params.put(options)).toString()); + return new DaemonTaskSuccessResult(task); + + default: + return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, + task.getMethod() + " is not supported by " + getType())); + } + } catch (JSONException e) { + return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString())); + } catch (DaemonException e) { + return new DaemonTaskFailureResult(task, e); + } catch (FileNotFoundException e) { + return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.FileAccessError, e.toString())); + } catch (IOException e) { + return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.FileAccessError, e.toString())); + } + } + + private JSONObject buildRequest(String sendMethod, JSONArray params) throws JSONException { + + // Build request for method + if (!TextUtils.isEmpty(settings.getExtraPassword())) { + JSONArray signed = new JSONArray(); + // Start with the secret token as parameter and then add the normal parameters + signed.put("token:" + settings.getExtraPassword()); + for (int i = 0; i < params.length(); i++) { + signed.put(params.get(i)); + } + params = signed; + } + JSONObject request = new JSONObject(); + request.put("id", "transdroid"); + request.put("jsonrpc", "2.0"); + request.put("method", sendMethod); + request.put("params", params); + return request; + + } + + private synchronized JSONObject makeRequest(String data) throws DaemonException { + String raw = makeRawRequest(data); + try { + return new JSONObject(raw); + } catch (JSONException e) { + DLog.d(LOG_NAME, "Error: " + e.toString()); + throw new DaemonException(ExceptionType.UnexpectedResponse, e.toString()); + } + } + + private synchronized JSONArray makeRequestForArray(String data) throws DaemonException { + String raw = makeRawRequest(data); + try { + return new JSONArray(raw); + } catch (JSONException e) { + DLog.d(LOG_NAME, "Error: " + e.toString()); + throw new DaemonException(ExceptionType.UnexpectedResponse, e.toString()); + } + } + + private synchronized String makeRawRequest(String data) throws DaemonException { + + try { + + // Initialise the HTTP client + if (httpclient == null) { + httpclient = HttpHelper.createStandardHttpClient(settings, !TextUtils.isEmpty(settings.getUsername())); + httpclient.addRequestInterceptor(HttpHelper.gzipRequestInterceptor); + httpclient.addResponseInterceptor(HttpHelper.gzipResponseInterceptor); + } + + // Set POST URL and data + String url = (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + + settings.getPort() + (settings.getFolder() == null ? "" : settings.getFolder()) + "/jsonrpc"; + HttpPost httppost = new HttpPost(url); + httppost.setEntity(new StringEntity(data)); + httppost.setHeader("Content-Type", "application/json"); + httppost.setHeader("Accept", "application/json"); + + // Execute + HttpResponse response = httpclient.execute(httppost); + + HttpEntity entity = response.getEntity(); + if (entity == null) + throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity in response object."); + + // Read JSON response + InputStream instream = entity.getContent(); + String result = HttpHelper.convertStreamToString(instream); + instream.close(); + + DLog.d(LOG_NAME, + "Success: " + + (result.length() > 300 ? result.substring(0, 300) + "... (" + result.length() + " chars)" + : result)); + return result; + + } catch (Exception e) { + DLog.d(LOG_NAME, "Error: " + e.toString()); + throw new DaemonException(ExceptionType.ConnectionError, e.toString()); + } + + } + + private ArrayList parseJsonRetrieveTorrents(JSONArray response) throws JSONException, DaemonException { + + // Parse response + ArrayList torrents = new ArrayList(); + for (int j = 0; j < response.length(); j++) { + + // Add the parsed torrent to the list + JSONObject tor = response.getJSONObject(j); + int downloadSpeed = tor.getInt("downloadSpeed"); + long totalLength = tor.getLong("totalLength"); + long completedLength = tor.getLong("completedLength"); + int numSeeders = tor.has("numSeeders") ? tor.getInt("numSeeders") : 0; + TorrentStatus status = convertAriaState(tor.getString("status"), completedLength == totalLength); + int errorCode = tor.optInt("errorCode", 0); + String error = errorCode > 0 ? convertAriaError(errorCode) : null; + String name = null; + JSONObject bittorrent = null; + if (tor.has("bittorrent")) { + // Get name form the bittorrent info object + bittorrent = tor.getJSONObject("bittorrent"); + if (bittorrent.has("info")) + name = bittorrent.getJSONObject("info").getString("name"); + } else if (tor.has("files")) { + // Get name from the first included file we can find + JSONArray files = tor.getJSONArray("files"); + if (files.length() > 0) { + name = Uri.parse(files.getJSONObject(0).getString("path")).getLastPathSegment(); + } + } + if (name == null) + name = tor.getString("gid"); // Fallback name + // @formatter:off + torrents.add(new Torrent( + j, + tor.getString("gid"), + name, + status, + tor.getString("dir"), + downloadSpeed, + tor.getInt("uploadSpeed"), + tor.getInt("connections"), + numSeeders , + tor.getInt("connections"), + numSeeders, + (downloadSpeed > 0? (int) (totalLength / downloadSpeed): -1), + completedLength, + tor.getLong("uploadLength"), + totalLength, + completedLength / (float) totalLength, // Percentage to [0..1] + 0f, // Not available + null, // Not available + null, // Not available + null, // Not available + error, + settings.getType())); + // @formatter:on + + } + return torrents; + + } + + private ArrayList parseJsonFileListing(JSONArray response, Torrent torrent) throws JSONException { + + // Parse response + ArrayList files = new ArrayList(); + for (int j = 0; j < response.length(); j++) { + + JSONObject file = response.getJSONObject(j); + // Add the parsed torrent to the list + // @formatter:off + String rel = file.getString("path"); + if (rel.startsWith(torrent.getLocationDir())) { + rel = rel.substring(torrent.getLocationDir().length()); + } + files.add(new TorrentFile( + Integer.toString(file.getInt("index")), + rel, + rel, + file.getString("path"), + file.getLong("length"), + file.getLong("completedLength"), + file.getBoolean("selected") ? Priority.Normal : Priority.Off)); + // @formatter:on + + } + return files; + + } + + private TorrentDetails parseJsonTorrentDetails(JSONObject response) throws JSONException { + + // Parse response + List trackers = new ArrayList(); + List errors = new ArrayList(); + + int error = response.optInt("errorCode", 0); + if (error > 0) + errors.add(convertAriaError(error)); + + if (response.has("bittorrent")) { + JSONObject bittorrent = response.getJSONObject("bittorrent"); + JSONArray announceList = bittorrent.getJSONArray("announceList"); + for (int i = 0; i < announceList.length(); i++) { + JSONArray announceUrlList = announceList.getJSONArray(i); + for (int j = 0; j < announceUrlList.length(); j++) { + trackers.add(announceUrlList.getString(j)); + } + } + } + + return new TorrentDetails(trackers, errors); + + } + + private TorrentStatus convertAriaState(String state, boolean isFinished) { + // Aria2 sends a string as status code + // (http://aria2.sourceforge.net/manual/en/html/aria2c.html#aria2.tellStatus) + if (state.equals("active")) { + return isFinished ? TorrentStatus.Seeding : TorrentStatus.Downloading; + } else if (state.equals("waiting")) { + return TorrentStatus.Queued; + } else if (state.equals("paused") || state.equals("complete")) { + return TorrentStatus.Paused; + } else if (state.equals("error")) { + return TorrentStatus.Error; + } else if (state.equals("removed")) { + return TorrentStatus.Checking; + } + return TorrentStatus.Unknown; + } + + private String convertAriaError(int errorCode) { + // Aria2 sends an exit code as error (http://aria2.sourceforge.net/manual/en/html/aria2c.html#id1) + String error = "Aria error #" + Integer.toString(errorCode); + switch (errorCode) { + case 3: + case 4: + return error + ": Resource was not found"; + case 5: + return error + ": Aborted because download speed was too slow"; + case 6: + return error + ": Network problem occurred"; + case 8: + return error + ": Remote server did not support resume when resume was required to complete download"; + case 9: + return error + ": There was not enough disk space available"; + case 11: + case 12: + return error + ": Duplicate file or info hash download"; + case 15: + case 16: + return error + ": Aria2 could not create new or open or truncate existing file"; + case 17: + case 18: + case 19: + return error + ": File I/O error occurred"; + case 20: + case 27: + return error + ": Aria2 could not parse Magnet URI or Metalink document"; + case 21: + return error + ": FTP command failed"; + case 22: + return error + ": HTTP response header was bad or unexpected"; + case 23: + return error + ": Too many redirects occurred"; + case 24: + return error + ": HTTP authorization failed"; + case 26: + return error + ": \".torrent\" file is corrupted or missing information that aria2 needs"; + default: + return error; + } + } + + @Override + public Daemon getType() { + return settings.getType(); + } + + @Override + public DaemonSettings getSettings() { + return this.settings; + } + +} diff --git a/lib/src/org/transdroid/daemon/Daemon.java b/lib/src/org/transdroid/daemon/Daemon.java index e6369b76..e4d5cc92 100644 --- a/lib/src/org/transdroid/daemon/Daemon.java +++ b/lib/src/org/transdroid/daemon/Daemon.java @@ -17,8 +17,12 @@ */ package org.transdroid.daemon; -import org.transdroid.daemon.Deluge.DelugeAdapter; +import org.transdroid.daemon.Aria2c.Aria2Adapter; +import org.transdroid.daemon.BitComet.BitCometAdapter; +import org.transdroid.daemon.Bitflu.BitfluAdapter; +import org.transdroid.daemon.BuffaloNas.BuffaloNasAdapter; import org.transdroid.daemon.DLinkRouterBT.DLinkRouterBTAdapter; +import org.transdroid.daemon.Deluge.DelugeAdapter; import org.transdroid.daemon.Ktorrent.KtorrentAdapter; import org.transdroid.daemon.Qbittorrent.QbittorrentAdapter; import org.transdroid.daemon.Rtorrent.RtorrentAdapter; @@ -27,9 +31,6 @@ import org.transdroid.daemon.Tfb4rt.Tfb4rtAdapter; import org.transdroid.daemon.Transmission.TransmissionAdapter; import org.transdroid.daemon.Utorrent.UtorrentAdapter; import org.transdroid.daemon.Vuze.VuzeAdapter; -import org.transdroid.daemon.Bitflu.BitfluAdapter; -import org.transdroid.daemon.BuffaloNas.BuffaloNasAdapter; -import org.transdroid.daemon.BitComet.BitCometAdapter; /** * Factory for new instances of server daemons, based on user settings. @@ -39,6 +40,11 @@ import org.transdroid.daemon.BitComet.BitCometAdapter; */ public enum Daemon { + Aria2 { + public IDaemonAdapter createAdapter(DaemonSettings settings) { + return new Aria2Adapter(settings); + } + }, Bitflu { public IDaemonAdapter createAdapter(DaemonSettings settings) { return new BitfluAdapter(settings); @@ -125,6 +131,8 @@ public enum Daemon { if (type == null) return null; switch (type) { + case Aria2: + return "daemon_aria2"; case BitComet: return "daemon_bitcomet"; case Bitflu: @@ -169,6 +177,9 @@ public enum Daemon { if (daemonCode == null) { return null; } + if (daemonCode.equals("daemon_aria2")) { + return Aria2; + } if (daemonCode.equals("daemon_bitcomet")) { return BitComet; } @@ -252,6 +263,8 @@ public enum Daemon { return 4081; case Vuze: return 6884; + case Aria2: + return 6800; } return 8080; } @@ -265,11 +278,11 @@ public enum Daemon { } public static boolean supportsFileListing(Daemon type) { - return type == Synology || type == Transmission || type == uTorrent || type == BitTorrent || type == KTorrent || type == Deluge || type == rTorrent || type == Vuze || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet || type == Dummy; + return type == Synology || type == Transmission || type == uTorrent || type == BitTorrent || type == KTorrent || type == Deluge || type == rTorrent || type == Vuze || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet || type == Aria2 || type == Dummy; } public static boolean supportsFineDetails(Daemon type) { - return type == uTorrent || type == BitTorrent || type == Daemon.Transmission || type == Deluge || type == rTorrent || type == qBittorrent || type == Dummy; + return type == uTorrent || type == BitTorrent || type == Daemon.Transmission || type == Deluge || type == rTorrent || type == qBittorrent || type == Aria2 || type == Dummy; } public static boolean needsManualPathSpecified(Daemon type) { @@ -277,7 +290,7 @@ public enum Daemon { } public static boolean supportsFilePaths(Daemon type) { - return type == uTorrent || type == BitTorrent || type == Vuze || type == Deluge || type == Transmission || type == rTorrent || type == KTorrent || type == BuffaloNas || type == Dummy; + return type == uTorrent || type == BitTorrent || type == Vuze || type == Deluge || type == Transmission || type == rTorrent || type == KTorrent || type == BuffaloNas || type == Aria2 || type == Dummy; } public static boolean supportsStoppingStarting(Daemon type) { @@ -293,7 +306,7 @@ public enum Daemon { } public static boolean supportsSetTransferRates(Daemon type) { - return type == Deluge || type == Transmission || type == uTorrent || type == BitTorrent || type == Deluge || type == rTorrent || type == Vuze || type == BuffaloNas || type == BitComet || type == Dummy; + return type == Deluge || type == Transmission || type == uTorrent || type == BitTorrent || type == Deluge || type == rTorrent || type == Vuze || type == BuffaloNas || type == BitComet || type == Aria2 || type == Dummy; } public static boolean supportsAddByFile(Daemon type) { @@ -302,11 +315,11 @@ public enum Daemon { } public static boolean supportsAddByMagnetUrl(Daemon type) { - return type == uTorrent || type == BitTorrent || type == Transmission || type == Synology || type == Deluge || type == Bitflu || type == KTorrent || type == rTorrent || type == qBittorrent || type == BitComet || type == Dummy; + return type == uTorrent || type == BitTorrent || type == Transmission || type == Synology || type == Deluge || type == Bitflu || type == KTorrent || type == rTorrent || type == qBittorrent || type == BitComet || type == Aria2 || type == Dummy; } public static boolean supportsRemoveWithData(Daemon type) { - return type == uTorrent || type == Vuze || type == Transmission || type == Deluge || type == BitTorrent || type == Tfb4rt || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet || type == rTorrent || type == Dummy; + return type == uTorrent || type == Vuze || type == Transmission || type == Deluge || type == BitTorrent || type == Tfb4rt || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet || type == rTorrent || type == Aria2 || type == Dummy; } public static boolean supportsFilePrioritySetting(Daemon type) { @@ -342,11 +355,11 @@ public enum Daemon { } public static boolean supportsExtraPassword(Daemon type) { - return type == Deluge; + return type == Deluge || type == Aria2; } public static boolean supportsUsernameForHttp(Daemon type) { - return type == Deluge; + return type == Deluge || type == Aria2; } } diff --git a/lib/src/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java b/lib/src/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java index 0b1dca96..f5d91ea0 100644 --- a/lib/src/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java +++ b/lib/src/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java @@ -469,7 +469,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { if (in.length() >= 3) { String part1 = in.substring(0, in.length() - 3); String part2 = in.substring(in.length() - 3); - return part1.replace("Ê", "").replace(" ", "").replace(",", "").replace(".", "") + part2.replace(",", "."); + return part1.replace("Ê", "").replace(" ", "").replace(",", "").replace(".", "") + part2.replace(",", "."); } return in.replace(",", "."); } diff --git a/lite/AndroidManifest.xml b/lite/AndroidManifest.xml index 1730ac61..40c2c1ad 100644 --- a/lite/AndroidManifest.xml +++ b/lite/AndroidManifest.xml @@ -18,8 +18,8 @@ + android:versionCode="216" + android:versionName="2.3.0" >