Browse Source

Merge branch 'master' into dev

# Conflicts:
#	app/src/main/java/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java
pull/406/head
Eric Kok 7 years ago
parent
commit
ac6a66b19f
  1. 125
      app/src/main/java/de/timroes/axmlrpc/ResponseParser.java
  2. 8
      app/src/main/java/de/timroes/axmlrpc/XMLRPCClient.java
  3. 99
      app/src/main/java/de/timroes/axmlrpc/XMLUtil.java
  4. 38
      app/src/main/java/de/timroes/axmlrpc/serializer/ArraySerializer.java
  5. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/Base64Serializer.java
  6. 7
      app/src/main/java/de/timroes/axmlrpc/serializer/BooleanSerializer.java
  7. 15
      app/src/main/java/de/timroes/axmlrpc/serializer/DateTimeSerializer.java
  8. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/DoubleSerializer.java
  9. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/IntSerializer.java
  10. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/LongSerializer.java
  11. 6
      app/src/main/java/de/timroes/axmlrpc/serializer/NullSerializer.java
  12. 11
      app/src/main/java/de/timroes/axmlrpc/serializer/Serializer.java
  13. 189
      app/src/main/java/de/timroes/axmlrpc/serializer/SerializerHandler.java
  14. 13
      app/src/main/java/de/timroes/axmlrpc/serializer/StringSerializer.java
  15. 63
      app/src/main/java/de/timroes/axmlrpc/serializer/StructSerializer.java
  16. 2
      app/src/main/java/org/transdroid/core/app/settings/ApplicationSettings.java
  17. 48
      app/src/main/java/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java
  18. 48
      app/src/main/res/values-da/strings.xml

125
app/src/main/java/de/timroes/axmlrpc/ResponseParser.java

@ -1,12 +1,15 @@
package de.timroes.axmlrpc; package de.timroes.axmlrpc;
import de.timroes.axmlrpc.serializer.SerializerHandler; import de.timroes.axmlrpc.serializer.SerializerHandler;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Map; import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import org.apache.http.HttpEntity;
import org.w3c.dom.Document; import org.xmlpull.v1.XmlPullParser;
import org.w3c.dom.Element; import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
/** /**
* The ResponseParser parses the response of an XMLRPC server to an object. * The ResponseParser parses the response of an XMLRPC server to an object.
@ -18,6 +21,30 @@ class ResponseParser {
private static final String FAULT_CODE = "faultCode"; private static final String FAULT_CODE = "faultCode";
private static final String FAULT_STRING = "faultString"; private static final String FAULT_STRING = "faultString";
/**
* Deallocate Http Entity and close streams
*/
private static void consumeHttpEntity(InputStream response, HttpEntity entity) {
// Ideally we should use EntityUtils.consume(), introduced in apache http utils 4.1 - not available in
// Android yet
if (entity != null) {
try {
entity.consumeContent();
} catch (IOException e) {
// ignore exception (could happen if Content-Length is wrong)
}
}
if (response != null) {
try {
response.close();
} catch (Exception e) {
// ignore exception
}
}
}
/** /**
* The given InputStream must contain the xml response from an xmlrpc server. * The given InputStream must contain the xml response from an xmlrpc server.
* This method extract the content of it as an object. * This method extract the content of it as an object.
@ -27,70 +54,50 @@ class ResponseParser {
* @throws XMLRPCException Will be thrown whenever something fails. * @throws XMLRPCException Will be thrown whenever something fails.
* @throws XMLRPCServerException Will be thrown, if the server returns an error. * @throws XMLRPCServerException Will be thrown, if the server returns an error.
*/ */
public Object parse(InputStream response) throws XMLRPCException { public Object parse(InputStream response, HttpEntity entity) throws XMLRPCException {
try { try {
XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); pullParser.setInput(response, "UTF-8");
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder(); pullParser.nextTag();
Document dom = builder.parse(response); pullParser.require(XmlPullParser.START_TAG, null, XMLRPCClient.METHOD_RESPONSE);
Element e = dom.getDocumentElement();
pullParser.nextTag(); // either TAG_PARAMS (<params>) or TAG_FAULT (<fault>)
// Check for root tag String tag = pullParser.getName();
if(!e.getNodeName().equals(XMLRPCClient.METHOD_RESPONSE)) { if (tag.equals(XMLRPCClient.PARAMS)) {
throw new XMLRPCException("MethodResponse root tag is missing."); // normal response
} pullParser.nextTag(); // TAG_PARAM (<param>)
pullParser.require(XmlPullParser.START_TAG, null, XMLRPCClient.PARAM);
e = XMLUtil.getOnlyChildElement(e.getChildNodes()); pullParser.nextTag(); // TAG_VALUE (<value>)
// no parser.require() here since its called in XMLRPCSerializer.deserialize() below
if(e.getNodeName().equals(XMLRPCClient.PARAMS)) { // deserialize result
Object obj = SerializerHandler.getDefault().deserialize(pullParser);
e = XMLUtil.getOnlyChildElement(e.getChildNodes()); consumeHttpEntity(response, entity);
return obj;
if(!e.getNodeName().equals(XMLRPCClient.PARAM)) { } else if (tag.equals(XMLRPCClient.FAULT)) {
throw new XMLRPCException("The params tag must contain a param tag."); // fault response
pullParser.nextTag(); // TAG_VALUE (<value>)
Map<String, Object> map = (Map<String, Object>) SerializerHandler.getDefault().deserialize(pullParser);
consumeHttpEntity(response, entity);
//Check that required tags are in the response
if (!map.containsKey(FAULT_STRING) || !map.containsKey(FAULT_CODE)) {
throw new XMLRPCException("Bad XMLRPC Fault response received - <faultCode> and/or <faultString> missing!");
} }
throw new XMLRPCServerException((String) map.get(FAULT_STRING), (Integer) map.get(FAULT_CODE));
return getReturnValueFromElement(e); } else {
throw new XMLRPCException("Bad tag <" + tag + "> in XMLRPC response - neither <params> nor <fault>");
} else if(e.getNodeName().equals(XMLRPCClient.FAULT)) {
@SuppressWarnings("unchecked")
Map<String,Object> o = (Map<String,Object>)getReturnValueFromElement(e);
throw new XMLRPCServerException((String)o.get(FAULT_STRING), (Integer)o.get(FAULT_CODE));
} }
throw new XMLRPCException("The methodResponse tag must contain a fault or params tag."); } catch (XmlPullParserException ex) {
consumeHttpEntity(response, entity);
throw new XMLRPCException("Error parsing response.", ex);
} catch (Exception ex) { } catch (Exception ex) {
consumeHttpEntity(response, entity);
if(ex instanceof XMLRPCServerException) if(ex instanceof XMLRPCServerException)
throw (XMLRPCServerException)ex; throw (XMLRPCServerException)ex;
else else
throw new XMLRPCException("Error getting result from server.", ex); throw new XMLRPCException("Error getting result from server.", ex);
} }
} }
/**
* This method takes an element (must be a param or fault element) and
* returns the deserialized object of this param tag.
*
* @param element An param element.
* @return The deserialized object within the given param element.
* @throws XMLRPCException Will be thrown when the structure of the document
* doesn't match the XML-RPC specification.
*/
private Object getReturnValueFromElement(Element element) throws XMLRPCException {
element = XMLUtil.getOnlyChildElement(element.getChildNodes());
return SerializerHandler.getDefault().deserialize(element);
}
} }

8
app/src/main/java/de/timroes/axmlrpc/XMLRPCClient.java

@ -9,6 +9,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HTTP;
@ -356,7 +357,7 @@ public class XMLRPCClient {
* Read the README file delivered with the source code of this library for more * Read the README file delivered with the source code of this library for more
* information. * information.
* *
* @param method A method name to call. * @param methodName A method name to call.
* @param params An array of parameters for the method. * @param params An array of parameters for the method.
* @return The result of the server. * @return The result of the server.
* @throws XMLRPCException Will be thrown if an error occurred during the call. * @throws XMLRPCException Will be thrown if an error occurred during the call.
@ -369,8 +370,9 @@ public class XMLRPCClient {
Call c = createCall(methodName, params); Call c = createCall(methodName, params);
// Prepare POST request // Prepare POST request
// FIXME: where creating a new HttpPost so calling #cancel isn't going to do anything
HttpPost post = new HttpPost(url); HttpPost post = new HttpPost(url);
post.getParams().setParameter("http.protocol.handle-redirects", false); post.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false);
post.setHeader(CONTENT_TYPE, TYPE_XML); post.setHeader(CONTENT_TYPE, TYPE_XML);
StringEntity entity = new StringEntity(c.getXML(), HTTP.UTF_8); StringEntity entity = new StringEntity(c.getXML(), HTTP.UTF_8);
entity.setContentType(TYPE_XML); entity.setContentType(TYPE_XML);
@ -444,7 +446,7 @@ public class XMLRPCClient {
} }
} }
return responseParser.parse(istream); return responseParser.parse(istream, entity);
} catch(SocketTimeoutException ex) { } catch(SocketTimeoutException ex) {
throw new XMLRPCTimeoutException("The XMLRPC call timed out."); throw new XMLRPCTimeoutException("The XMLRPC call timed out.");

99
app/src/main/java/de/timroes/axmlrpc/XMLUtil.java

@ -1,9 +1,6 @@
package de.timroes.axmlrpc; package de.timroes.axmlrpc;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/** /**
* This class provides some utility methods for the use with the Java DOM parser. * This class provides some utility methods for the use with the Java DOM parser.
@ -12,102 +9,6 @@ import org.w3c.dom.NodeList;
*/ */
public class XMLUtil { public class XMLUtil {
/**
* Returns the only child element in a given NodeList.
* Will throw an error if there is more then one child element or any other
* child that is not an element or an empty text string (whitespace are normal).
*
* @param list A NodeList of children nodes.
* @return The only child element in the given node list.
* @throws XMLRPCException Will be thrown if there is more then one child element
* except empty text nodes.
*/
public static Element getOnlyChildElement(NodeList list) throws XMLRPCException {
Element e = null;
Node n;
for(int i = 0; i < list.getLength(); i++) {
n = list.item(i);
// Strip only whitespace text elements and comments
if((n.getNodeType() == Node.TEXT_NODE
&& n.getNodeValue().trim().length() <= 0)
|| n.getNodeType() == Node.COMMENT_NODE)
continue;
// Check if there is anything else than an element node.
if(n.getNodeType() != Node.ELEMENT_NODE) {
throw new XMLRPCException("Only element nodes allowed.");
}
// If there was already an element, throw exception.
if(e != null) {
throw new XMLRPCException("Element has more than one children.");
}
e = (Element)n;
}
return e;
}
/**
* Returns the text node from a given NodeList. If the list contains
* more then just text nodes, an exception will be thrown.
*
* @param list The given list of nodes.
* @return The text of the given node list.
* @throws XMLRPCException Will be thrown if there is more than just one
* text node within the list.
*/
public static String getOnlyTextContent(NodeList list) throws XMLRPCException {
StringBuilder builder = new StringBuilder();
Node n;
for(int i = 0; i < list.getLength(); i++) {
n = list.item(i);
// Skip comments inside text tag.
if(n.getNodeType() == Node.COMMENT_NODE) {
continue;
}
if(n.getNodeType() != Node.TEXT_NODE) {
throw new XMLRPCException("Element must contain only text elements.");
}
builder.append(n.getNodeValue());
}
return builder.toString();
}
/**
* Checks if the given {@link NodeList} contains a child element.
*
* @param list The {@link NodeList} to check.
* @return Whether the {@link NodeList} contains children.
*/
public static boolean hasChildElement(NodeList list) {
Node n;
for(int i = 0; i < list.getLength(); i++) {
n = list.item(i);
if(n.getNodeType() == Node.ELEMENT_NODE) {
return true;
}
}
return false;
}
/** /**
* Creates an xml tag with a given type and content. * Creates an xml tag with a given type and content.
* *

38
app/src/main/java/de/timroes/axmlrpc/serializer/ArraySerializer.java

@ -2,12 +2,7 @@ package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLRPCRuntimeException; import de.timroes.axmlrpc.XMLRPCRuntimeException;
import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/** /**
* *
@ -18,39 +13,6 @@ public class ArraySerializer implements Serializer {
private static final String ARRAY_DATA = "data"; private static final String ARRAY_DATA = "data";
private static final String ARRAY_VALUE = "value"; private static final String ARRAY_VALUE = "value";
public Object deserialize(Element content) throws XMLRPCException {
List<Object> list = new ArrayList<Object>();
Element data = XMLUtil.getOnlyChildElement(content.getChildNodes());
if(!ARRAY_DATA.equals(data.getNodeName())) {
throw new XMLRPCException("The array must contain one data tag.");
}
// Deserialize every array element
Node value;
for(int i = 0; i < data.getChildNodes().getLength(); i++) {
value = data.getChildNodes().item(i);
// Strip only whitespace text elements and comments
if(value == null || (value.getNodeType() == Node.TEXT_NODE
&& value.getNodeValue().trim().length() <= 0)
|| value.getNodeType() == Node.COMMENT_NODE)
continue;
if(value.getNodeType() != Node.ELEMENT_NODE) {
throw new XMLRPCException("Wrong element inside of array.");
}
list.add(SerializerHandler.getDefault().deserialize((Element)value));
}
return list.toArray();
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
Iterable<?> iter = (Iterable<?>)object; Iterable<?> iter = (Iterable<?>)object;

6
app/src/main/java/de/timroes/axmlrpc/serializer/Base64Serializer.java

@ -1,10 +1,8 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import de.timroes.base64.Base64; import de.timroes.base64.Base64;
import org.w3c.dom.Element;
/** /**
* *
@ -12,10 +10,6 @@ import org.w3c.dom.Element;
*/ */
public class Base64Serializer implements Serializer { public class Base64Serializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return Base64.decode(XMLUtil.getOnlyTextContent(content.getChildNodes()));
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BASE64, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BASE64,
Base64.encode((Byte[])object)); Base64.encode((Byte[])object));

7
app/src/main/java/de/timroes/axmlrpc/serializer/BooleanSerializer.java

@ -1,9 +1,7 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -11,11 +9,6 @@ import org.w3c.dom.Element;
*/ */
public class BooleanSerializer implements Serializer { public class BooleanSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return (XMLUtil.getOnlyTextContent(content.getChildNodes()).equals("1"))
? Boolean.TRUE : Boolean.FALSE;
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BOOLEAN, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BOOLEAN,
((Boolean)object == true) ? "1" : "0"); ((Boolean)object == true) ? "1" : "0");

15
app/src/main/java/de/timroes/axmlrpc/serializer/DateTimeSerializer.java

@ -1,11 +1,8 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import org.w3c.dom.Element;
/** /**
* *
@ -13,16 +10,8 @@ import org.w3c.dom.Element;
*/ */
public class DateTimeSerializer implements Serializer { public class DateTimeSerializer implements Serializer {
private static final String DATETIME_FORMAT = "yyyyMMdd'T'HH:mm:ss"; public static final String DATETIME_FORMAT = "yyyyMMdd'T'HH:mm:ss";
private static final SimpleDateFormat DATE_FORMATER = new SimpleDateFormat(DATETIME_FORMAT); public static final SimpleDateFormat DATE_FORMATER = new SimpleDateFormat(DATETIME_FORMAT);
public Object deserialize(Element content) throws XMLRPCException {
try {
return DATE_FORMATER.parse(XMLUtil.getOnlyTextContent(content.getChildNodes()));
} catch (ParseException ex) {
throw new XMLRPCException("Unable to parse given date.", ex);
}
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_DATETIME, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_DATETIME,

6
app/src/main/java/de/timroes/axmlrpc/serializer/DoubleSerializer.java

@ -1,10 +1,8 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.w3c.dom.Element;
/** /**
* This serializer is responsible for floating point numbers. * This serializer is responsible for floating point numbers.
@ -13,10 +11,6 @@ import org.w3c.dom.Element;
*/ */
public class DoubleSerializer implements Serializer { public class DoubleSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return Double.valueOf(XMLUtil.getOnlyTextContent(content.getChildNodes()));
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
// Turn double value of object into a BigDecimal to get the // Turn double value of object into a BigDecimal to get the
// right decimal point format. // right decimal point format.

6
app/src/main/java/de/timroes/axmlrpc/serializer/IntSerializer.java

@ -1,9 +1,7 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -11,10 +9,6 @@ import org.w3c.dom.Element;
*/ */
public class IntSerializer implements Serializer { public class IntSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return Integer.parseInt(XMLUtil.getOnlyTextContent(content.getChildNodes()));
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_INT, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_INT,
object.toString()); object.toString());

6
app/src/main/java/de/timroes/axmlrpc/serializer/LongSerializer.java

@ -1,9 +1,7 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -11,10 +9,6 @@ import org.w3c.dom.Element;
*/ */
class LongSerializer implements Serializer { class LongSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return Long.parseLong(XMLUtil.getOnlyTextContent(content.getChildNodes()));
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return XMLUtil.makeXmlTag(SerializerHandler.TYPE_LONG, return XMLUtil.makeXmlTag(SerializerHandler.TYPE_LONG,
((Long)object).toString()); ((Long)object).toString());

6
app/src/main/java/de/timroes/axmlrpc/serializer/NullSerializer.java

@ -1,8 +1,6 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -10,10 +8,6 @@ import org.w3c.dom.Element;
*/ */
public class NullSerializer implements Serializer { public class NullSerializer implements Serializer {
public Object deserialize(Element content) throws XMLRPCException {
return null;
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
return new XmlElement(SerializerHandler.TYPE_NULL); return new XmlElement(SerializerHandler.TYPE_NULL);
} }

11
app/src/main/java/de/timroes/axmlrpc/serializer/Serializer.java

@ -1,8 +1,6 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* A Serializer is responsible to serialize a specific type of data to * A Serializer is responsible to serialize a specific type of data to
@ -12,15 +10,6 @@ import org.w3c.dom.Element;
*/ */
public interface Serializer { public interface Serializer {
/**
* This method takes an xml type element and deserialize it to an object.
*
* @param content Must be an xml element of a specific type.
* @return The deserialized content.
* @throws XMLRPCException Will be thrown whenervt the deserialization fails.
*/
public Object deserialize(Element content) throws XMLRPCException;
/** /**
* This method takes an object and returns a representation as a string * This method takes an object and returns a representation as a string
* containing the right xml type tag. The returning string must be useable * containing the right xml type tag. The returning string must be useable

189
app/src/main/java/de/timroes/axmlrpc/serializer/SerializerHandler.java

@ -3,13 +3,27 @@ package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCClient; import de.timroes.axmlrpc.XMLRPCClient;
import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLRPCRuntimeException; import de.timroes.axmlrpc.XMLRPCRuntimeException;
import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.w3c.dom.Element; import java.util.SimpleTimeZone;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.util.Base64;
import android.util.Log;
/** /**
* The serializer handler serializes and deserialized objects. * The serializer handler serializes and deserialized objects.
@ -21,6 +35,18 @@ import org.w3c.dom.Element;
* @author Tim Roes * @author Tim Roes
*/ */
public class SerializerHandler { public class SerializerHandler {
private static final String LOG_NAME = "SerializerHandler";
public static final String TAG_NAME = "name";
public static final String TAG_MEMBER = "member";
public static final String TAG_VALUE = "value";
public static final String TAG_DATA = "data";
public static final String TYPE_DATE_TIME_ISO8601 = "dateTime.iso8601";
static SimpleDateFormat dateFormat = DateTimeSerializer.DATE_FORMATER;
static Calendar cal = Calendar.getInstance(new SimpleTimeZone(0, "GMT"));
public static final String TYPE_STRING = "string"; public static final String TYPE_STRING = "string";
public static final String TYPE_BOOLEAN = "boolean"; public static final String TYPE_BOOLEAN = "boolean";
@ -81,83 +107,120 @@ public class SerializerHandler {
*/ */
private SerializerHandler(int flags) { private SerializerHandler(int flags) {
this.flags = flags; this.flags = flags;
string = new StringSerializer( string = new StringSerializer((flags & XMLRPCClient.FLAGS_NO_STRING_ENCODE) == 0);
(flags & XMLRPCClient.FLAGS_NO_STRING_ENCODE) == 0,
(flags & XMLRPCClient.FLAGS_NO_STRING_DECODE) == 0
);
} }
/** /**
* Deserializes an incoming xml element to an java object. * Deserialize an incoming xml to a java object.
* The xml element must be the value element around the type element.
* The type of the returning object depends on the type tag. * The type of the returning object depends on the type tag.
* *
* @param element An type element from within a value tag. * @param parser Initialized parser.
* @return The deserialized object. * @return The deserialized object.
* @throws XMLRPCException Will be thrown whenever an error occurs. * @throws XmlPullParserException
* @throws IOException
* @throws NumberFormatException
*/ */
public Object deserialize(Element element) throws XMLRPCException { public static Object deserialize(XmlPullParser parser) throws XmlPullParserException, IOException, NumberFormatException {
parser.require(XmlPullParser.START_TAG, null, TAG_VALUE);
if(!XMLRPCClient.VALUE.equals(element.getNodeName())) { parser.nextTag();
throw new XMLRPCException("Value tag is missing around value."); String typeNodeName = parser.getName();
}
if(!XMLUtil.hasChildElement(element.getChildNodes())) { Object obj;
// Value element doesn't contain a child element if (typeNodeName.equals(TYPE_INT) || typeNodeName.equals(TYPE_INT2)) {
if((flags & XMLRPCClient.FLAGS_DEFAULT_TYPE_STRING) != 0) { String value = parser.nextText();
return string.deserialize(element); try {
} else { obj = Integer.parseInt(value);
throw new XMLRPCException("Missing type element inside of value element."); } catch (NumberFormatException nfe) {
Log.w(LOG_NAME, "Server replied with an invalid 4 bytes int value, trying to parse it as 8 bytes long.");
obj = Long.parseLong(value);
} }
} } else
if (typeNodeName.equals(TYPE_LONG)) {
// Grep type element from inside value element String value = parser.nextText();
element = XMLUtil.getOnlyChildElement(element.getChildNodes()); obj = Long.parseLong(value);
} else
Serializer s = null; if (typeNodeName.equals(TYPE_DOUBLE)) {
String value = parser.nextText();
String type; obj = Double.parseDouble(value);
} else
// If FLAGS_IGNORE_NAMESPACE has been set, only use local name. if (typeNodeName.equals(TYPE_BOOLEAN)) {
if((flags & XMLRPCClient.FLAGS_IGNORE_NAMESPACES) != 0) { String value = parser.nextText();
type = element.getLocalName() == null ? element.getNodeName() : element.getLocalName(); obj = value.equals("1") ? Boolean.TRUE : Boolean.FALSE;
} else { } else
type = element.getNodeName(); if (typeNodeName.equals(TYPE_STRING)) {
} obj = parser.nextText();
} else
if (typeNodeName.equals(TYPE_DATE_TIME_ISO8601)) {
dateFormat.setCalendar(cal);
String value = parser.nextText();
try {
obj = dateFormat.parseObject(value);
} catch (ParseException e) {
Log.e(LOG_NAME, "Error parsing date, using non-parsed string.");
obj = value;
}
} else
if (typeNodeName.equals(TYPE_BASE64)) {
String value = parser.nextText();
BufferedReader reader = new BufferedReader(new StringReader(value));
String line;
StringBuffer sb = new StringBuffer();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
obj = Base64.decode(sb.toString(), Base64.DEFAULT);
} else
if (typeNodeName.equals(TYPE_ARRAY)) {
parser.nextTag(); // TAG_DATA (<data>)
parser.require(XmlPullParser.START_TAG, null, TAG_DATA);
if((flags & XMLRPCClient.FLAGS_NIL) != 0 && TYPE_NULL.equals(type)) { parser.nextTag();
s = nil; List<Object> list = new ArrayList<Object>();
} else if(TYPE_STRING.equals(type)) { while (parser.getName().equals(TAG_VALUE)) {
s = string; list.add(deserialize(parser));
} else if(TYPE_BOOLEAN.equals(type)) { parser.nextTag();
s = bool;
} else if(TYPE_DOUBLE.equals(type)) {
s = floating;
} else if (TYPE_INT.equals(type) || TYPE_INT2.equals(type)) {
s = integer;
} else if(TYPE_DATETIME.equals(type)) {
s = datetime;
} else if (TYPE_LONG.equals(type)) {
if((flags & XMLRPCClient.FLAGS_8BYTE_INT) != 0) {
s = long8;
} else {
throw new XMLRPCException("8 byte integer is not in the specification. "
+ "You must use FLAGS_8BYTE_INT to enable the i8 tag.");
} }
} else if(TYPE_STRUCT.equals(type)) { parser.require(XmlPullParser.END_TAG, null, TAG_DATA);
s = struct; parser.nextTag(); // TAG_ARRAY (</array>)
} else if(TYPE_ARRAY.equals(type)) { parser.require(XmlPullParser.END_TAG, null, TYPE_ARRAY);
s = array; obj = list.toArray();
} else if(TYPE_BASE64.equals(type)) { } else
s = base64; if (typeNodeName.equals(TYPE_STRUCT)) {
parser.nextTag();
Map<String, Object> map = new HashMap<String, Object>();
while (parser.getName().equals(TAG_MEMBER)) {
String memberName = null;
Object memberValue = null;
while (true) {
parser.nextTag();
String name = parser.getName();
if (name.equals(TAG_NAME)) {
memberName = parser.nextText();
} else
if (name.equals(TAG_VALUE)) {
memberValue = deserialize(parser);
} else {
break;
}
}
if (memberName != null && memberValue != null) {
map.put(memberName, memberValue);
}
parser.require(XmlPullParser.END_TAG, null, TAG_MEMBER);
parser.nextTag();
}
parser.require(XmlPullParser.END_TAG, null, TYPE_STRUCT);
obj = map;
} else { } else {
throw new XMLRPCException("No deserializer found for type '" + type + "'."); throw new IOException("Cannot deserialize " + parser.getName());
} }
parser.nextTag(); // TAG_VALUE (</value>)
return s.deserialize(element); parser.require(XmlPullParser.END_TAG, null, TAG_VALUE);
return obj;
} }
/** /**
* Serialize an object to its representation as an xml element. * Serialize an object to its representation as an xml element.
* The xml element will be the type element for the use within a value tag. * The xml element will be the type element for the use within a value tag.

13
app/src/main/java/de/timroes/axmlrpc/serializer/StringSerializer.java

@ -1,9 +1,7 @@
package de.timroes.axmlrpc.serializer; package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import org.w3c.dom.Element;
/** /**
* *
@ -11,21 +9,12 @@ import org.w3c.dom.Element;
*/ */
public class StringSerializer implements Serializer { public class StringSerializer implements Serializer {
private boolean decodeStrings;
private boolean encodeStrings; private boolean encodeStrings;
public StringSerializer(boolean encodeStrings, boolean decodeStrings) { public StringSerializer(boolean encodeStrings) {
this.decodeStrings = decodeStrings;
this.encodeStrings = encodeStrings; this.encodeStrings = encodeStrings;
} }
public Object deserialize(Element content) throws XMLRPCException {
String text = XMLUtil.getOnlyTextContent(content.getChildNodes());
if(decodeStrings) {
text = text.replaceAll("&lt;", "<").replaceAll("&amp;", "&");
}
return text;
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
String content = object.toString(); String content = object.toString();

63
app/src/main/java/de/timroes/axmlrpc/serializer/StructSerializer.java

@ -2,12 +2,8 @@ package de.timroes.axmlrpc.serializer;
import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLRPCRuntimeException; import de.timroes.axmlrpc.XMLRPCRuntimeException;
import de.timroes.axmlrpc.XMLUtil;
import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.axmlrpc.xmlcreator.XmlElement;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/** /**
* *
@ -19,65 +15,6 @@ public class StructSerializer implements Serializer {
private static final String STRUCT_NAME = "name"; private static final String STRUCT_NAME = "name";
private static final String STRUCT_VALUE = "value"; private static final String STRUCT_VALUE = "value";
public Object deserialize(Element content) throws XMLRPCException {
Map<String, Object> map = new HashMap<String, Object>();
Node n, m;
String s;
Object o;
for(int i = 0; i < content.getChildNodes().getLength(); i++) {
n = content.getChildNodes().item(i);
// Strip only whitespace text elements and comments
if((n.getNodeType() == Node.TEXT_NODE
&& n.getNodeValue().trim().length() <= 0)
|| n.getNodeType() == Node.COMMENT_NODE)
continue;
if(n.getNodeType() != Node.ELEMENT_NODE
|| !STRUCT_MEMBER.equals(n.getNodeName())) {
throw new XMLRPCException("Only struct members allowed within a struct.");
}
// Grep name and value from member
s = null; o = null;
for(int j = 0; j < n.getChildNodes().getLength(); j++) {
m = n.getChildNodes().item(j);
// Strip only whitespace text elements and comments
if((m.getNodeType() == Node.TEXT_NODE
&& m.getNodeValue().trim().length() <= 0)
|| m.getNodeType() == Node.COMMENT_NODE)
continue;
if(STRUCT_NAME.equals(m.getNodeName())) {
if(s != null) {
throw new XMLRPCException("Name of a struct member cannot be set twice.");
} else {
s = XMLUtil.getOnlyTextContent(m.getChildNodes());
}
} else if(m.getNodeType() == Node.ELEMENT_NODE && STRUCT_VALUE.equals(m.getNodeName())) {
if(o != null) {
throw new XMLRPCException("Value of a struct member cannot be set twice.");
} else {
o = SerializerHandler.getDefault().deserialize((Element)m);
}
} else {
throw new XMLRPCException("A struct member must only contain one name and one value.");
}
}
map.put(s, o);
}
return map;
}
public XmlElement serialize(Object object) { public XmlElement serialize(Object object) {
XmlElement struct = new XmlElement(SerializerHandler.TYPE_STRUCT); XmlElement struct = new XmlElement(SerializerHandler.TYPE_STRUCT);

2
app/src/main/java/org/transdroid/core/app/settings/ApplicationSettings.java

@ -502,7 +502,7 @@ public class ApplicationSettings {
edit.putString("rssfeed_exclude_" + i, prefs.getString("rssfeed_exclude_" + (i + 1), null)); edit.putString("rssfeed_exclude_" + i, prefs.getString("rssfeed_exclude_" + (i + 1), null));
edit.putString("rssfeed_include_" + i, prefs.getString("rssfeed_include_" + (i + 1), null)); edit.putString("rssfeed_include_" + i, prefs.getString("rssfeed_include_" + (i + 1), null));
edit.putLong("rssfeed_lastviewed_" + i, prefs.getLong("rssfeed_lastviewed_" + (i + 1), -1)); edit.putLong("rssfeed_lastviewed_" + i, prefs.getLong("rssfeed_lastviewed_" + (i + 1), -1));
edit.putLong("rssfeed_lastvieweditemurl_" + i, prefs.getLong("rssfeed_lastvieweditemurl_" + (i + 1), -1)); edit.putString("rssfeed_lastvieweditemurl_" + i, prefs.getString("rssfeed_lastvieweditemurl_" + (i + 1), null));
} }
// Remove the last settings, of which we are now sure are no longer required // Remove the last settings, of which we are now sure are no longer required

48
app/src/main/java/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java

@ -135,7 +135,8 @@ public class RtorrentAdapter implements IDaemonAdapter {
"d.custom=seedingtime", "d.custom=seedingtime",
"d.custom1=", "d.custom1=",
"d.peers_complete=", "d.peers_complete=",
"d.peers_accounted=" }); "d.peers_accounted=",
"d.is_open=" });
// @formatter:on // @formatter:on
return new RetrieveTaskSuccessResult((RetrieveTask) task, onTorrentsRetrieved(result), return new RetrieveTaskSuccessResult((RetrieveTask) task, onTorrentsRetrieved(result),
lastKnownLabels); lastKnownLabels);
@ -226,49 +227,51 @@ public class RtorrentAdapter implements IDaemonAdapter {
case Pause: case Pause:
// Pause a torrent // Pause a torrent
makeRtorrentCall(log, "d.pause", new String[]{task.getTargetTorrent().getUniqueID()}); makeRtorrentCall(log, "d.stop", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
case PauseAll: case PauseAll:
// Resume all torrents // Resume all torrents
makeRtorrentCall(log, "d.multicall2", new String[]{"","main", "d.pause="}); makeRtorrentCall(log, "d.multicall2", new String[]{"","main", "d.stop="});
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
case Resume: case Resume:
// Resume a torrent // Resume a torrent
makeRtorrentCall(log, "d.resume", new String[]{task.getTargetTorrent().getUniqueID()}); makeRtorrentCall(log, "d.start", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
case ResumeAll: case ResumeAll:
// Resume all torrents // Resume all torrents
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.resume="}); makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.start="});
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
case Stop: case Stop:
// Stop a torrent // Stop a torrent
makeRtorrentCall(log, "d.stop", new String[]{task.getTargetTorrent().getUniqueID()}); makeRtorrentCall(log, "d.stop", new String[]{task.getTargetTorrent().getUniqueID()});
makeRtorrentCall(log, "d.close", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
case StopAll: case StopAll:
// Stop all torrents // Stop all torrents
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.stop="}); makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.stop=", "d.close="});
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
case Start: case Start:
// Start a torrent // Start a torrent
makeRtorrentCall(log, "d.open", new String[]{task.getTargetTorrent().getUniqueID()});
makeRtorrentCall(log, "d.start", new String[]{task.getTargetTorrent().getUniqueID()}); makeRtorrentCall(log, "d.start", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
case StartAll: case StartAll:
// Start all torrents // Start all torrents
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.start="}); makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.open=", "d.start="});
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
case SetFilePriorities: case SetFilePriorities:
@ -455,7 +458,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
i, i,
(String)info[0], // hash (String)info[0], // hash
(String)info[1], // name (String)info[1], // name
convertTorrentStatus((Long)info[2], (Long)info[13], (Long)info[14], (Long)info[15]), // status convertTorrentStatus((Long)info[2], (Long)info[24], (Long)info[13], (Long)info[14], (Long)info[15]), // status
(((Long)info[16]) == 1)? baseFilename: "" , // multi file? base_filename else "" (((Long)info[16]) == 1)? baseFilename: "" , // multi file? base_filename else ""
((Long)info[3]).intValue(), // rateDownload ((Long)info[3]).intValue(), // rateDownload
((Long)info[4]).intValue(), // rateUpload ((Long)info[4]).intValue(), // rateUpload
@ -486,7 +489,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
i, i,
(String)info[0], // hash (String)info[0], // hash
(String)info[1], // name (String)info[1], // name
convertTorrentStatus(((Integer)info[2]).longValue(), ((Integer)info[13]).longValue(), ((Integer)info[14]).longValue(), ((Integer)info[15]).longValue()), // status convertTorrentStatus(((Integer)info[2]).longValue(), ((Integer)info[24]).longValue(), ((Integer)info[13]).longValue(), ((Integer)info[14]).longValue(), ((Integer)info[15]).longValue()), // status
(((Integer)info[16]) == 1)? baseFilename: "" , // multi file? base_filename else "" (((Integer)info[16]) == 1)? baseFilename: "" , // multi file? base_filename else ""
rateDownload, // rateDownload rateDownload, // rateDownload
(Integer)info[4], // rateUpload (Integer)info[4], // rateUpload
@ -607,19 +610,24 @@ public class RtorrentAdapter implements IDaemonAdapter {
} }
} }
private TorrentStatus convertTorrentStatus(Long state, Long complete, Long active, Long checking) { private TorrentStatus convertTorrentStatus(Long state, Long open, Long complete, Long active, Long checking) {
if (state == 0) { if (checking == 1) {
return TorrentStatus.Queued;
} else if (active == 1) {
if (complete == 1) {
return TorrentStatus.Seeding;
} else {
return TorrentStatus.Downloading;
}
} else if (checking == 1) {
return TorrentStatus.Checking; return TorrentStatus.Checking;
} else { }
if (open == 1) {
// links with tracker/peers are open: not stopped
if (state == 1 && active == 1) {
if (complete == 1) {
return TorrentStatus.Seeding;
} else {
return TorrentStatus.Downloading;
}
}
return TorrentStatus.Paused; return TorrentStatus.Paused;
} else {
// maybe could be Stopped or Waiting
return TorrentStatus.Queued;
} }
} }

48
app/src/main/res/values-da/strings.xml

@ -25,8 +25,8 @@
<string name="action_search">Søg</string> <string name="action_search">Søg</string>
<string name="action_refresh">Genindlæs</string> <string name="action_refresh">Genindlæs</string>
<string name="action_rss">RSS</string> <string name="action_rss">RSS</string>
<string name="action_enableturtle">Aktiver skildpadetilstand</string> <string name="action_enableturtle">Aktiver skildpaddetilstand</string>
<string name="action_disableturtle">Deaktiver skildpadetilstand</string> <string name="action_disableturtle">Deaktiver skildpaddetilstand</string>
<string name="action_sort">Sorter liste</string> <string name="action_sort">Sorter liste</string>
<string name="action_sort_alpha">Navn</string> <string name="action_sort_alpha">Navn</string>
<string name="action_sort_status">Status</string> <string name="action_sort_status">Status</string>
@ -35,8 +35,8 @@
<string name="action_sort_percent">Procent downloadet</string> <string name="action_sort_percent">Procent downloadet</string>
<string name="action_sort_downspeed">Downloadhastighed</string> <string name="action_sort_downspeed">Downloadhastighed</string>
<string name="action_sort_upspeed">Uploadhastighed</string> <string name="action_sort_upspeed">Uploadhastighed</string>
<string name="action_sort_ratio">Fordelingsforhold</string> <string name="action_sort_ratio">Ratio</string>
<string name="action_sort_size">Størelse</string> <string name="action_sort_size">Størrelse</string>
<string name="action_filter">Filtrer liste</string> <string name="action_filter">Filtrer liste</string>
<string name="action_settings">Indstillinger</string> <string name="action_settings">Indstillinger</string>
<string name="action_help">Hjælp</string> <string name="action_help">Hjælp</string>
@ -56,7 +56,7 @@
<string name="action_setlabel">Set etiket</string> <string name="action_setlabel">Set etiket</string>
<string name="action_updatetrackers">Opdater trackers</string> <string name="action_updatetrackers">Opdater trackers</string>
<string name="action_changelocation">Ændr gem lokation</string> <string name="action_changelocation">Ændr gem lokation</string>
<string name="action_forcerecheck">Tving data gentjek</string> <string name="action_forcerecheck">Tving gentjek af data</string>
<string name="action_priority_off">Fra</string> <string name="action_priority_off">Fra</string>
<string name="action_priority_low">Lav</string> <string name="action_priority_low">Lav</string>
<string name="action_priority_normal">Normal</string> <string name="action_priority_normal">Normal</string>
@ -65,14 +65,14 @@
<string name="action_download">Download via FTP(S)</string> <string name="action_download">Download via FTP(S)</string>
<string name="action_copytoclipboard">Kopiér til upklipsholder</string> <string name="action_copytoclipboard">Kopiér til upklipsholder</string>
<string name="action_showdetails">Vis detaljer</string> <string name="action_showdetails">Vis detaljer</string>
<string name="action_openwebsite">Åben wedside</string> <string name="action_openwebsite">Åbn webside</string>
<string name="action_useassearch">Brug som ny søgning</string> <string name="action_useassearch">Brug som ny søgning</string>
<string name="action_removesettings">Fjern indstillinger</string> <string name="action_removesettings">Fjern indstillinger</string>
<string name="action_visitwebsite">Besøg transdroid.org</string> <string name="action_visitwebsite">Besøg transdroid.org</string>
<string name="action_close">Luk</string> <string name="action_close">Luk</string>
<string name="navigation_nosettings">%1$s giver dig mulighed for at overvåge og styre torrent klienten du kører derhjemme eller på din seedbox. Indstillingerne kan være en smule komplicerede men vi tilbyder trin-for-trin vejledninger og lover det vil være det værd!</string> <string name="navigation_nosettings">%1$s giver dig mulighed for at overvåge og styre torrent-klienten, som du kører derhjemme eller på din seedbox. Indstillingerne kan være en smule komplicerede, men vi tilbyder trin-for-trin vejledninger og lover at det vil være det værd!</string>
<string name="navigation_emptytorrents">Forbundet, men ingen torrents er aktive inden for det aktuelle filter</string> <string name="navigation_emptytorrents">Forbundet, men ingen torrents er aktive inden for det aktuelle filter</string>
<string name="navigation_emptydetails">Vælg en torrent for at de dens detaljer</string> <string name="navigation_emptydetails">Vælg en torrent for at se dens detaljer</string>
<string name="navigation_servers">SERVERE</string> <string name="navigation_servers">SERVERE</string>
<string name="navigation_status">STATUS</string> <string name="navigation_status">STATUS</string>
<string name="navigation_labels">ETIKETTER</string> <string name="navigation_labels">ETIKETTER</string>
@ -84,7 +84,7 @@
<string name="navigation_selectall">Vælg alle</string> <string name="navigation_selectall">Vælg alle</string>
<string name="navigation_selectfinished">Vælg færdige</string> <string name="navigation_selectfinished">Vælg færdige</string>
<string name="navigation_invertselection">Ombyt valgte</string> <string name="navigation_invertselection">Ombyt valgte</string>
<string name="navigation_pickserver">Tilføj torrent til...</string> <string name="navigation_pickserver">Tilføj torrent til&#8230;</string>
<string name="status_status">STATUS: %1$s</string> <string name="status_status">STATUS: %1$s</string>
<string name="status_waiting">Venter på at tjekke&#8230;</string> <string name="status_waiting">Venter på at tjekke&#8230;</string>
<string name="status_checking">Verificerer lokalt data&#8230;</string> <string name="status_checking">Verificerer lokalt data&#8230;</string>
@ -97,7 +97,7 @@
<string name="status_etalong">ETA %1$s</string> <string name="status_etalong">ETA %1$s</string>
<string name="status_ofsize">AF %1$s</string> <string name="status_ofsize">AF %1$s</string>
<string name="status_unknowneta">UKENDT ETA</string> <string name="status_unknowneta">UKENDT ETA</string>
<string name="status_ratio">FORDELINGSFORHOLD %1$s</string> <string name="status_ratio">RATIO %1$s</string>
<string name="status_seeders">%1$s AF %2$s SEEDERS</string> <string name="status_seeders">%1$s AF %2$s SEEDERS</string>
<string name="status_leechers">%1$s OF %2$s LEECHERS</string> <string name="status_leechers">%1$s OF %2$s LEECHERS</string>
<string name="status_downloading">Downloader</string> <string name="status_downloading">Downloader</string>
@ -113,17 +113,29 @@
<string name="status_trackers">T</string> <string name="status_trackers">T</string>
<string name="status_errors">FEJL</string> <string name="status_errors">FEJL</string>
<string name="status_files">FILER</string> <string name="status_files">FILER</string>
<string name="status_maxspeed">Maksimum overførelseshastigheder</string> <string name="status_maxspeed">Maksimale overførselshastigheder</string>
<string name="status_maxspeed_down">MAKS DOWNLOAD</string> <string name="status_maxspeed_down">MAKS DOWNLOAD</string>
<string name="status_maxspeed_up">MAKS UPLOAD</string> <string name="status_maxspeed_up">MAKS UPLOAD</string>
<string name="status_maxspeed_unit">TRACKERE</string> <string name="status_maxspeed_unit">KB/S</string>
<string name="status_maxspeed_reset">Nulstil</string> <string name="status_maxspeed_reset">Nulstil</string>
<string name="status_update">Opdater</string> <string name="status_update">Opdater</string>
<plurals name="status_service_added">
<item quantity="one">Ny torrent tilføjet</item>
<item quantity="other">%1$s nye torrents tilføjet</item>
</plurals>
<plurals name="status_service_finished">
<item quantity="one">Torrent er færdig</item>
<item quantity="other">%1$s torrents er færdige</item>
</plurals>
<plurals name="status_service_addedfinished">
<item quantity="one">%1$s tilføjet, %2$s færdig torrent</item>
<item quantity="other">%1$s tilføjede, %2$s færdige torrents</item>
</plurals>
<string name="status_service_andothers">%1$s og andre</string> <string name="status_service_andothers">%1$s og andre</string>
<string name="labels_showall">Alle etiketter</string> <string name="labels_showall">Alle etiketter</string>
<string name="labels_unlabeled">Ingen etiket</string> <string name="labels_unlabeled">Uden etiket</string>
<string name="labels_newlabel">Nu etiket</string> <string name="labels_newlabel">Ny etiket</string>
<string name="labels_no_support">Tilføj af etiket er ikke understøttet af din klient</string> <string name="labels_no_support">Redigering af etiket er ikke understøttet af din klient</string>
<string name="status_label_pick">VÆLG EN ETIKET</string> <string name="status_label_pick">VÆLG EN ETIKET</string>
<string name="status_label_new">NY ETIKET</string> <string name="status_label_new">NY ETIKET</string>
<string name="status_label_remove">Fjern etiket</string> <string name="status_label_remove">Fjern etiket</string>
@ -199,13 +211,13 @@
<string name="pref_feedurl">Feed URL</string> <string name="pref_feedurl">Feed URL</string>
<string name="pref_reqauth">Kræver autentificering</string> <string name="pref_reqauth">Kræver autentificering</string>
<string name="pref_reqauth_info">Åbner links i webbrowser for brugerlogin</string> <string name="pref_reqauth_info">Åbner links i webbrowser for brugerlogin</string>
<string name="pref_alarmrssnew">Notifikation for nyt objekt</string> <string name="pref_alarmrssnew">Notifikation for nyt indlæg</string>
<string name="pref_alarmrssnew_info">Besked når ny torrent bliver udgivet</string> <string name="pref_alarmrssnew_info">Besked når ny torrent bliver udgivet</string>
<string name="pref_servertype">Servertype</string> <string name="pref_servertype">Servertype</string>
<string name="pref_address">IP eller værtsnavn</string> <string name="pref_address">IP eller værtsnavn</string>
<string name="pref_port">Port nummer</string> <string name="pref_port">Port-nummer</string>
<string name="pref_user">Brugernavn</string> <string name="pref_user">Brugernavn</string>
<string name="pref_pass">Adgandskode</string> <string name="pref_pass">Adgangskode</string>
<string name="pref_extrapassword">Deluge web adgangskode</string> <string name="pref_extrapassword">Deluge web adgangskode</string>
<string name="pref_secret">Hemmelig token</string> <string name="pref_secret">Hemmelig token</string>
<string name="pref_advanced">Avancerede indstillinger</string> <string name="pref_advanced">Avancerede indstillinger</string>

Loading…
Cancel
Save