diff --git a/astrid/.project b/astrid/.project index 836231044..a8cc9332f 100644 --- a/astrid/.project +++ b/astrid/.project @@ -1,39 +1,39 @@ - - - astrid - - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - org.eclipse.wst.common.project.facet.core.nature - - + + + astrid + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/astrid/common-src/com/todoroo/andlib/sql/Criterion.java b/astrid/common-src/com/todoroo/andlib/sql/Criterion.java index a208f6db4..5acdc6535 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Criterion.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Criterion.java @@ -1,89 +1,89 @@ -package com.todoroo.andlib.sql; - -import static com.todoroo.andlib.sql.SqlConstants.AND; -import static com.todoroo.andlib.sql.SqlConstants.EXISTS; -import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS; -import static com.todoroo.andlib.sql.SqlConstants.NOT; -import static com.todoroo.andlib.sql.SqlConstants.OR; -import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS; -import static com.todoroo.andlib.sql.SqlConstants.SPACE; - -public abstract class Criterion { - protected final Operator operator; - - Criterion(Operator operator) { - this.operator = operator; - } - - public static Criterion all = new Criterion(Operator.exists) { - @Override - protected void populate(StringBuilder sb) { - sb.append(1); - } - }; - - public static Criterion none = new Criterion(Operator.exists) { - @Override - protected void populate(StringBuilder sb) { - sb.append(0); - } - }; - - public static Criterion and(final Criterion criterion, final Criterion... criterions) { - return new Criterion(Operator.and) { - - @Override - protected void populate(StringBuilder sb) { - sb.append(criterion); - for (Criterion c : criterions) { - sb.append(SPACE).append(AND).append(SPACE).append(c); - } - } - }; - } - - public static Criterion or(final Criterion criterion, final Criterion... criterions) { - return new Criterion(Operator.or) { - - @Override - protected void populate(StringBuilder sb) { - sb.append(criterion); - for (Criterion c : criterions) { - sb.append(SPACE).append(OR).append(SPACE).append(c.toString()); - } - } - }; - } - - public static Criterion exists(final Query query) { - return new Criterion(Operator.exists) { - - @Override - protected void populate(StringBuilder sb) { - sb.append(EXISTS).append(SPACE).append(LEFT_PARENTHESIS).append(query).append(RIGHT_PARENTHESIS); - } - }; - } - - public static Criterion not(final Criterion criterion) { - return new Criterion(Operator.not) { - - @Override - protected void populate(StringBuilder sb) { - sb.append(NOT).append(SPACE); - criterion.populate(sb); - } - }; - } - - protected abstract void populate(StringBuilder sb); - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(LEFT_PARENTHESIS); - populate(builder); - builder.append(RIGHT_PARENTHESIS); - return builder.toString(); - } - -} +package com.todoroo.andlib.sql; + +import static com.todoroo.andlib.sql.SqlConstants.AND; +import static com.todoroo.andlib.sql.SqlConstants.EXISTS; +import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS; +import static com.todoroo.andlib.sql.SqlConstants.NOT; +import static com.todoroo.andlib.sql.SqlConstants.OR; +import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS; +import static com.todoroo.andlib.sql.SqlConstants.SPACE; + +public abstract class Criterion { + protected final Operator operator; + + Criterion(Operator operator) { + this.operator = operator; + } + + public static Criterion all = new Criterion(Operator.exists) { + @Override + protected void populate(StringBuilder sb) { + sb.append(1); + } + }; + + public static Criterion none = new Criterion(Operator.exists) { + @Override + protected void populate(StringBuilder sb) { + sb.append(0); + } + }; + + public static Criterion and(final Criterion criterion, final Criterion... criterions) { + return new Criterion(Operator.and) { + + @Override + protected void populate(StringBuilder sb) { + sb.append(criterion); + for (Criterion c : criterions) { + sb.append(SPACE).append(AND).append(SPACE).append(c); + } + } + }; + } + + public static Criterion or(final Criterion criterion, final Criterion... criterions) { + return new Criterion(Operator.or) { + + @Override + protected void populate(StringBuilder sb) { + sb.append(criterion); + for (Criterion c : criterions) { + sb.append(SPACE).append(OR).append(SPACE).append(c.toString()); + } + } + }; + } + + public static Criterion exists(final Query query) { + return new Criterion(Operator.exists) { + + @Override + protected void populate(StringBuilder sb) { + sb.append(EXISTS).append(SPACE).append(LEFT_PARENTHESIS).append(query).append(RIGHT_PARENTHESIS); + } + }; + } + + public static Criterion not(final Criterion criterion) { + return new Criterion(Operator.not) { + + @Override + protected void populate(StringBuilder sb) { + sb.append(NOT).append(SPACE); + criterion.populate(sb); + } + }; + } + + protected abstract void populate(StringBuilder sb); + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(LEFT_PARENTHESIS); + populate(builder); + builder.append(RIGHT_PARENTHESIS); + return builder.toString(); + } + +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/DBObject.java b/astrid/common-src/com/todoroo/andlib/sql/DBObject.java index 3e4c9a29f..b29c6550b 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/DBObject.java +++ b/astrid/common-src/com/todoroo/andlib/sql/DBObject.java @@ -1,67 +1,67 @@ -package com.todoroo.andlib.sql; - -import static com.todoroo.andlib.sql.SqlConstants.AS; -import static com.todoroo.andlib.sql.SqlConstants.SPACE; - -public abstract class DBObject> implements Cloneable { - protected String alias; - protected final String expression; - - protected DBObject(String expression){ - this.expression = expression; - } - - public T as(String newAlias) { - try { - T clone = (T) clone(); - clone.alias = newAlias; - return clone; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - public boolean hasAlias() { - return alias != null; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - DBObject dbObject = (DBObject) o; - - if (alias != null ? !alias.equals(dbObject.alias) : dbObject.alias != null) return false; - if (expression != null ? !expression.equals(dbObject.expression) : dbObject.expression != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = alias != null ? alias.hashCode() : 0; - result = 31 * result + (expression != null ? expression.hashCode() : 0); - return result; - } - - @Override - public final String toString() { - if (hasAlias()) { - return alias; - } - return expression; - } - - public final String toStringInSelect() { - StringBuilder sb = new StringBuilder(expression); - if (hasAlias()) { - sb.append(SPACE).append(AS).append(SPACE).append(alias); - } else { - int pos = expression.indexOf('.'); - if(pos > 0) - sb.append(SPACE).append(AS).append(SPACE).append(expression.substring(pos + 1)); - } - return sb.toString(); - } -} +package com.todoroo.andlib.sql; + +import static com.todoroo.andlib.sql.SqlConstants.AS; +import static com.todoroo.andlib.sql.SqlConstants.SPACE; + +public abstract class DBObject> implements Cloneable { + protected String alias; + protected final String expression; + + protected DBObject(String expression){ + this.expression = expression; + } + + public T as(String newAlias) { + try { + T clone = (T) clone(); + clone.alias = newAlias; + return clone; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + public boolean hasAlias() { + return alias != null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DBObject dbObject = (DBObject) o; + + if (alias != null ? !alias.equals(dbObject.alias) : dbObject.alias != null) return false; + if (expression != null ? !expression.equals(dbObject.expression) : dbObject.expression != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = alias != null ? alias.hashCode() : 0; + result = 31 * result + (expression != null ? expression.hashCode() : 0); + return result; + } + + @Override + public final String toString() { + if (hasAlias()) { + return alias; + } + return expression; + } + + public final String toStringInSelect() { + StringBuilder sb = new StringBuilder(expression); + if (hasAlias()) { + sb.append(SPACE).append(AS).append(SPACE).append(alias); + } else { + int pos = expression.indexOf('.'); + if(pos > 0) + sb.append(SPACE).append(AS).append(SPACE).append(expression.substring(pos + 1)); + } + return sb.toString(); + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/EqCriterion.java b/astrid/common-src/com/todoroo/andlib/sql/EqCriterion.java index 99fc74ad2..8873069cb 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/EqCriterion.java +++ b/astrid/common-src/com/todoroo/andlib/sql/EqCriterion.java @@ -1,7 +1,7 @@ -package com.todoroo.andlib.sql; - -public class EqCriterion extends UnaryCriterion { - EqCriterion(Field field, Object value) { - super(field, Operator.eq, value); - } -} +package com.todoroo.andlib.sql; + +public class EqCriterion extends UnaryCriterion { + EqCriterion(Field field, Object value) { + super(field, Operator.eq, value); + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/Field.java b/astrid/common-src/com/todoroo/andlib/sql/Field.java index 28bbb00b2..fbd0a7c19 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Field.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Field.java @@ -1,90 +1,90 @@ -package com.todoroo.andlib.sql; - -import static com.todoroo.andlib.sql.SqlConstants.AND; -import static com.todoroo.andlib.sql.SqlConstants.BETWEEN; -import static com.todoroo.andlib.sql.SqlConstants.COMMA; -import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS; -import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS; -import static com.todoroo.andlib.sql.SqlConstants.SPACE; - -public class Field extends DBObject { - - protected Field(String expression) { - super(expression); - } - - public static Field field(String expression) { - return new Field(expression); - } - - public Criterion eq(Object value) { - if(value == null) - return UnaryCriterion.isNull(this); - return UnaryCriterion.eq(this, value); - } - - public Criterion neq(Object value) { - if(value == null) - return UnaryCriterion.isNotNull(this); - return UnaryCriterion.neq(this, value); - } - - public Criterion gt(Object value) { - return UnaryCriterion.gt(this, value); - } - - public Criterion lt(final Object value) { - return UnaryCriterion.lt(this, value); - } - - public Criterion isNull() { - return UnaryCriterion.isNull(this); - } - - public Criterion isNotNull() { - return UnaryCriterion.isNotNull(this); - } - - public Criterion between(final Object lower, final Object upper) { - final Field field = this; - return new Criterion(null) { - - @Override - protected void populate(StringBuilder sb) { - sb.append(field).append(SPACE).append(BETWEEN).append(SPACE).append(lower).append(SPACE).append(AND) - .append(SPACE).append(upper); - } - }; - } - - public Criterion like(final String value) { - return UnaryCriterion.like(this, value); - } - - public Criterion in(final T... value) { - final Field field = this; - return new Criterion(Operator.in) { - - @Override - protected void populate(StringBuilder sb) { - sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS); - for (T t : value) { - sb.append(t.toString()).append(COMMA); - } - sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS); - } - }; - } - - public Criterion in(final Query query) { - final Field field = this; - return new Criterion(Operator.in) { - - @Override - protected void populate(StringBuilder sb) { - sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(query) - .append(RIGHT_PARENTHESIS); - } - }; - } -} +package com.todoroo.andlib.sql; + +import static com.todoroo.andlib.sql.SqlConstants.AND; +import static com.todoroo.andlib.sql.SqlConstants.BETWEEN; +import static com.todoroo.andlib.sql.SqlConstants.COMMA; +import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS; +import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS; +import static com.todoroo.andlib.sql.SqlConstants.SPACE; + +public class Field extends DBObject { + + protected Field(String expression) { + super(expression); + } + + public static Field field(String expression) { + return new Field(expression); + } + + public Criterion eq(Object value) { + if(value == null) + return UnaryCriterion.isNull(this); + return UnaryCriterion.eq(this, value); + } + + public Criterion neq(Object value) { + if(value == null) + return UnaryCriterion.isNotNull(this); + return UnaryCriterion.neq(this, value); + } + + public Criterion gt(Object value) { + return UnaryCriterion.gt(this, value); + } + + public Criterion lt(final Object value) { + return UnaryCriterion.lt(this, value); + } + + public Criterion isNull() { + return UnaryCriterion.isNull(this); + } + + public Criterion isNotNull() { + return UnaryCriterion.isNotNull(this); + } + + public Criterion between(final Object lower, final Object upper) { + final Field field = this; + return new Criterion(null) { + + @Override + protected void populate(StringBuilder sb) { + sb.append(field).append(SPACE).append(BETWEEN).append(SPACE).append(lower).append(SPACE).append(AND) + .append(SPACE).append(upper); + } + }; + } + + public Criterion like(final String value) { + return UnaryCriterion.like(this, value); + } + + public Criterion in(final T... value) { + final Field field = this; + return new Criterion(Operator.in) { + + @Override + protected void populate(StringBuilder sb) { + sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS); + for (T t : value) { + sb.append(t.toString()).append(COMMA); + } + sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS); + } + }; + } + + public Criterion in(final Query query) { + final Field field = this; + return new Criterion(Operator.in) { + + @Override + protected void populate(StringBuilder sb) { + sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(query) + .append(RIGHT_PARENTHESIS); + } + }; + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/GroupBy.java b/astrid/common-src/com/todoroo/andlib/sql/GroupBy.java index 65f765c27..df8c19df9 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/GroupBy.java +++ b/astrid/common-src/com/todoroo/andlib/sql/GroupBy.java @@ -1,14 +1,14 @@ -package com.todoroo.andlib.sql; - -import java.util.ArrayList; -import java.util.List; - -public class GroupBy { - private List fields = new ArrayList(); - - public static GroupBy groupBy(Field field) { - GroupBy groupBy = new GroupBy(); - groupBy.fields.add(field); - return groupBy; - } -} +package com.todoroo.andlib.sql; + +import java.util.ArrayList; +import java.util.List; + +public class GroupBy { + private List fields = new ArrayList(); + + public static GroupBy groupBy(Field field) { + GroupBy groupBy = new GroupBy(); + groupBy.fields.add(field); + return groupBy; + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/Join.java b/astrid/common-src/com/todoroo/andlib/sql/Join.java index 646ca8cc5..ac8a479f2 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Join.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Join.java @@ -1,43 +1,43 @@ -package com.todoroo.andlib.sql; - -import static com.todoroo.andlib.sql.SqlConstants.JOIN; -import static com.todoroo.andlib.sql.SqlConstants.ON; -import static com.todoroo.andlib.sql.SqlConstants.SPACE; - -public class Join { - private final SqlTable joinTable; - private final JoinType joinType; - private final Criterion[] criterions; - - private Join(SqlTable table, JoinType joinType, Criterion... criterions) { - joinTable = table; - this.joinType = joinType; - this.criterions = criterions; - } - - public static Join inner(SqlTable expression, Criterion... criterions) { - return new Join(expression, JoinType.INNER, criterions); - } - - public static Join left(SqlTable table, Criterion... criterions) { - return new Join(table, JoinType.LEFT, criterions); - } - - public static Join right(SqlTable table, Criterion... criterions) { - return new Join(table, JoinType.RIGHT, criterions); - } - - public static Join out(SqlTable table, Criterion... criterions) { - return new Join(table, JoinType.OUT, criterions); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON); - for (Criterion criterion : criterions) { - sb.append(SPACE).append(criterion); - } - return sb.toString(); - } -} +package com.todoroo.andlib.sql; + +import static com.todoroo.andlib.sql.SqlConstants.JOIN; +import static com.todoroo.andlib.sql.SqlConstants.ON; +import static com.todoroo.andlib.sql.SqlConstants.SPACE; + +public class Join { + private final SqlTable joinTable; + private final JoinType joinType; + private final Criterion[] criterions; + + private Join(SqlTable table, JoinType joinType, Criterion... criterions) { + joinTable = table; + this.joinType = joinType; + this.criterions = criterions; + } + + public static Join inner(SqlTable expression, Criterion... criterions) { + return new Join(expression, JoinType.INNER, criterions); + } + + public static Join left(SqlTable table, Criterion... criterions) { + return new Join(table, JoinType.LEFT, criterions); + } + + public static Join right(SqlTable table, Criterion... criterions) { + return new Join(table, JoinType.RIGHT, criterions); + } + + public static Join out(SqlTable table, Criterion... criterions) { + return new Join(table, JoinType.OUT, criterions); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON); + for (Criterion criterion : criterions) { + sb.append(SPACE).append(criterion); + } + return sb.toString(); + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/JoinType.java b/astrid/common-src/com/todoroo/andlib/sql/JoinType.java index c1cb7389b..84e632b53 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/JoinType.java +++ b/astrid/common-src/com/todoroo/andlib/sql/JoinType.java @@ -1,5 +1,5 @@ -package com.todoroo.andlib.sql; - -public enum JoinType { - INNER, LEFT, RIGHT, OUT -} +package com.todoroo.andlib.sql; + +public enum JoinType { + INNER, LEFT, RIGHT, OUT +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/Operator.java b/astrid/common-src/com/todoroo/andlib/sql/Operator.java index 414b5d271..5581987d4 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Operator.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Operator.java @@ -1,57 +1,57 @@ -package com.todoroo.andlib.sql; - -import static com.todoroo.andlib.sql.SqlConstants.SPACE; - -import java.util.HashMap; -import java.util.Map; - -@SuppressWarnings("nls") -public final class Operator { - - private final String operator; - public static final Operator eq = new Operator("="); - public static final Operator neq = new Operator("<>"); - public static final Operator isNull = new Operator("IS NULL"); - public static final Operator isNotNull = new Operator("IS NOT NULL"); - public static final Operator gt = new Operator(">"); - public static final Operator lt = new Operator("<"); - public static final Operator gte = new Operator(">="); - public static final Operator lte = new Operator("<="); - public static final Operator and = new Operator("AND"); - public static final Operator or = new Operator("OR"); - public static final Operator not = new Operator("NOT"); - public static final Operator exists = new Operator("EXISTS"); - public static final Operator like = new Operator("LIKE"); - public static final Operator in = new Operator("IN"); - - private static final Map contraryRegistry = new HashMap(); - - static { - contraryRegistry.put(eq, neq); - contraryRegistry.put(neq, eq); - contraryRegistry.put(isNull, isNotNull); - contraryRegistry.put(isNotNull, isNull); - contraryRegistry.put(gt, lte); - contraryRegistry.put(lte, gt); - contraryRegistry.put(lt, gte); - contraryRegistry.put(gte, lt); - } - - private Operator(String operator) { - this.operator = operator; - } - - public Operator getContrary() { - if(!contraryRegistry.containsKey(this)){ - Operator opposite = new Operator(not.toString() + SPACE + this.toString()); - contraryRegistry.put(this, opposite); - contraryRegistry.put(opposite, this); - } - return contraryRegistry.get(this); - } - - @Override - public String toString() { - return this.operator.toString(); - } -} +package com.todoroo.andlib.sql; + +import static com.todoroo.andlib.sql.SqlConstants.SPACE; + +import java.util.HashMap; +import java.util.Map; + +@SuppressWarnings("nls") +public final class Operator { + + private final String operator; + public static final Operator eq = new Operator("="); + public static final Operator neq = new Operator("<>"); + public static final Operator isNull = new Operator("IS NULL"); + public static final Operator isNotNull = new Operator("IS NOT NULL"); + public static final Operator gt = new Operator(">"); + public static final Operator lt = new Operator("<"); + public static final Operator gte = new Operator(">="); + public static final Operator lte = new Operator("<="); + public static final Operator and = new Operator("AND"); + public static final Operator or = new Operator("OR"); + public static final Operator not = new Operator("NOT"); + public static final Operator exists = new Operator("EXISTS"); + public static final Operator like = new Operator("LIKE"); + public static final Operator in = new Operator("IN"); + + private static final Map contraryRegistry = new HashMap(); + + static { + contraryRegistry.put(eq, neq); + contraryRegistry.put(neq, eq); + contraryRegistry.put(isNull, isNotNull); + contraryRegistry.put(isNotNull, isNull); + contraryRegistry.put(gt, lte); + contraryRegistry.put(lte, gt); + contraryRegistry.put(lt, gte); + contraryRegistry.put(gte, lt); + } + + private Operator(String operator) { + this.operator = operator; + } + + public Operator getContrary() { + if(!contraryRegistry.containsKey(this)){ + Operator opposite = new Operator(not.toString() + SPACE + this.toString()); + contraryRegistry.put(this, opposite); + contraryRegistry.put(opposite, this); + } + return contraryRegistry.get(this); + } + + @Override + public String toString() { + return this.operator.toString(); + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/Order.java b/astrid/common-src/com/todoroo/andlib/sql/Order.java index 2c411fa4d..9ed380456 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Order.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Order.java @@ -1,30 +1,30 @@ -package com.todoroo.andlib.sql; - -import static com.todoroo.andlib.sql.SqlConstants.SPACE; - -public class Order { - private final Object expression; - private final OrderType orderType; - - private Order(Object expression) { - this(expression, OrderType.ASC); - } - - private Order(Object expression, OrderType orderType) { - this.expression = expression; - this.orderType = orderType; - } - - public static Order asc(Object expression) { - return new Order(expression); - } - - public static Order desc(Object expression) { - return new Order(expression, OrderType.DESC); - } - - @Override - public String toString() { - return expression + SPACE + orderType; - } -} +package com.todoroo.andlib.sql; + +import static com.todoroo.andlib.sql.SqlConstants.SPACE; + +public class Order { + private final Object expression; + private final OrderType orderType; + + private Order(Object expression) { + this(expression, OrderType.ASC); + } + + private Order(Object expression, OrderType orderType) { + this.expression = expression; + this.orderType = orderType; + } + + public static Order asc(Object expression) { + return new Order(expression); + } + + public static Order desc(Object expression) { + return new Order(expression, OrderType.DESC); + } + + @Override + public String toString() { + return expression + SPACE + orderType; + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/OrderType.java b/astrid/common-src/com/todoroo/andlib/sql/OrderType.java index 299c670c0..d84742e28 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/OrderType.java +++ b/astrid/common-src/com/todoroo/andlib/sql/OrderType.java @@ -1,5 +1,5 @@ -package com.todoroo.andlib.sql; - -public enum OrderType { - DESC, ASC -} +package com.todoroo.andlib.sql; + +public enum OrderType { + DESC, ASC +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/Query.java b/astrid/common-src/com/todoroo/andlib/sql/Query.java index 620087ced..98d402947 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Query.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Query.java @@ -1,205 +1,205 @@ -package com.todoroo.andlib.sql; - -import static com.todoroo.andlib.sql.SqlConstants.ALL; -import static com.todoroo.andlib.sql.SqlConstants.COMMA; -import static com.todoroo.andlib.sql.SqlConstants.FROM; -import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY; -import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS; -import static com.todoroo.andlib.sql.SqlConstants.LIMIT; -import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY; -import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS; -import static com.todoroo.andlib.sql.SqlConstants.SELECT; -import static com.todoroo.andlib.sql.SqlConstants.SPACE; -import static com.todoroo.andlib.sql.SqlConstants.WHERE; -import static com.todoroo.andlib.sql.SqlTable.table; -import static java.util.Arrays.asList; - -import java.util.ArrayList; - -import com.todoroo.andlib.data.Property; - -public final class Query { - - private SqlTable table; - private String queryTemplate = null; - private final ArrayList criterions = new ArrayList(); - private final ArrayList fields = new ArrayList(); - private final ArrayList joins = new ArrayList(); - private final ArrayList groupBies = new ArrayList(); - private final ArrayList orders = new ArrayList(); - private final ArrayList havings = new ArrayList(); - private int limits = -1; - - private Query(Field... fields) { - this.fields.addAll(asList(fields)); - } - - public static Query select(Field... fields) { - return new Query(fields); - } - - public Query from(SqlTable fromTable) { - this.table = fromTable; - return this; - } - - public Query join(Join... join) { - joins.addAll(asList(join)); - return this; - } - - public Query where(Criterion criterion) { - criterions.add(criterion); - return this; - } - - public Query groupBy(Field... groupBy) { - groupBies.addAll(asList(groupBy)); - return this; - } - - public Query orderBy(Order... order) { - orders.addAll(asList(order)); - return this; - } - - public Query limit(int limit) { - limits = limit; - return this; - } - - public Query appendSelectFields(Property... selectFields) { - this.fields.addAll(asList(selectFields)); - return this; - } - - @Override - public boolean equals(Object o) { - return this == o || !(o == null || getClass() != o.getClass()) && this.toString().equals(o.toString()); - } - - @Override - public int hashCode() { - return toString().hashCode(); - } - - @Override - public String toString() { - StringBuilder sql = new StringBuilder(); - visitSelectClause(sql); - visitFromClause(sql); - - if(queryTemplate == null) { - visitJoinClause(sql); - visitWhereClause(sql); - visitGroupByClause(sql); - visitOrderByClause(sql); - visitLimitClause(sql); - } else { - if(joins.size() > 0 || groupBies.size() > 0 || orders.size() > 0 || - havings.size() > 0) - throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$ - sql.append(queryTemplate); - } - - return sql.toString(); - } - - private void visitOrderByClause(StringBuilder sql) { - if (orders.isEmpty()) { - return; - } - sql.append(ORDER_BY); - for (Order order : orders) { - sql.append(SPACE).append(order).append(COMMA); - } - sql.deleteCharAt(sql.length() - 1).append(SPACE); - } - - @SuppressWarnings("nls") - private void visitGroupByClause(StringBuilder sql) { - if (groupBies.isEmpty()) { - return; - } - sql.append(GROUP_BY); - for (Field groupBy : groupBies) { - sql.append(SPACE).append(groupBy).append(COMMA); - } - sql.deleteCharAt(sql.length() - 1).append(SPACE); - if (havings.isEmpty()) { - return; - } - sql.append("HAVING"); - for (Criterion havingCriterion : havings) { - sql.append(SPACE).append(havingCriterion).append(COMMA); - } - sql.deleteCharAt(sql.length() - 1).append(SPACE); - } - - private void visitWhereClause(StringBuilder sql) { - if (criterions.isEmpty()) { - return; - } - sql.append(WHERE); - for (Criterion criterion : criterions) { - sql.append(SPACE).append(criterion).append(SPACE); - } - } - - private void visitJoinClause(StringBuilder sql) { - for (Join join : joins) { - sql.append(join).append(SPACE); - } - } - - private void visitFromClause(StringBuilder sql) { - if (table == null) { - return; - } - sql.append(FROM).append(SPACE).append(table).append(SPACE); - } - - private void visitSelectClause(StringBuilder sql) { - sql.append(SELECT).append(SPACE); - if (fields.isEmpty()) { - sql.append(ALL).append(SPACE); - return; - } - for (Field field : fields) { - sql.append(field.toStringInSelect()).append(COMMA); - } - sql.deleteCharAt(sql.length() - 1).append(SPACE); - } - - private void visitLimitClause(StringBuilder sql) { - if(limits > -1) - sql.append(LIMIT).append(SPACE).append(limits).append(SPACE); - } - - public SqlTable as(String alias) { - return table(LEFT_PARENTHESIS + this.toString() + RIGHT_PARENTHESIS).as(alias); - } - - public Query having(Criterion criterion) { - this.havings.add(criterion); - return this; - } - - /** - * Gets a list of fields returned by this query - * @return - */ - public Property[] getFields() { - return fields.toArray(new Property[fields.size()]); - } - - /** - * Add the SQL query template (comes after the "from") - * @param sqlQuery - * @return - */ - public Query withQueryTemplate(String template) { - queryTemplate = template; - return this; - } -} +package com.todoroo.andlib.sql; + +import static com.todoroo.andlib.sql.SqlConstants.ALL; +import static com.todoroo.andlib.sql.SqlConstants.COMMA; +import static com.todoroo.andlib.sql.SqlConstants.FROM; +import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY; +import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS; +import static com.todoroo.andlib.sql.SqlConstants.LIMIT; +import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY; +import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS; +import static com.todoroo.andlib.sql.SqlConstants.SELECT; +import static com.todoroo.andlib.sql.SqlConstants.SPACE; +import static com.todoroo.andlib.sql.SqlConstants.WHERE; +import static com.todoroo.andlib.sql.SqlTable.table; +import static java.util.Arrays.asList; + +import java.util.ArrayList; + +import com.todoroo.andlib.data.Property; + +public final class Query { + + private SqlTable table; + private String queryTemplate = null; + private final ArrayList criterions = new ArrayList(); + private final ArrayList fields = new ArrayList(); + private final ArrayList joins = new ArrayList(); + private final ArrayList groupBies = new ArrayList(); + private final ArrayList orders = new ArrayList(); + private final ArrayList havings = new ArrayList(); + private int limits = -1; + + private Query(Field... fields) { + this.fields.addAll(asList(fields)); + } + + public static Query select(Field... fields) { + return new Query(fields); + } + + public Query from(SqlTable fromTable) { + this.table = fromTable; + return this; + } + + public Query join(Join... join) { + joins.addAll(asList(join)); + return this; + } + + public Query where(Criterion criterion) { + criterions.add(criterion); + return this; + } + + public Query groupBy(Field... groupBy) { + groupBies.addAll(asList(groupBy)); + return this; + } + + public Query orderBy(Order... order) { + orders.addAll(asList(order)); + return this; + } + + public Query limit(int limit) { + limits = limit; + return this; + } + + public Query appendSelectFields(Property... selectFields) { + this.fields.addAll(asList(selectFields)); + return this; + } + + @Override + public boolean equals(Object o) { + return this == o || !(o == null || getClass() != o.getClass()) && this.toString().equals(o.toString()); + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + @Override + public String toString() { + StringBuilder sql = new StringBuilder(); + visitSelectClause(sql); + visitFromClause(sql); + + if(queryTemplate == null) { + visitJoinClause(sql); + visitWhereClause(sql); + visitGroupByClause(sql); + visitOrderByClause(sql); + visitLimitClause(sql); + } else { + if(joins.size() > 0 || groupBies.size() > 0 || orders.size() > 0 || + havings.size() > 0) + throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$ + sql.append(queryTemplate); + } + + return sql.toString(); + } + + private void visitOrderByClause(StringBuilder sql) { + if (orders.isEmpty()) { + return; + } + sql.append(ORDER_BY); + for (Order order : orders) { + sql.append(SPACE).append(order).append(COMMA); + } + sql.deleteCharAt(sql.length() - 1).append(SPACE); + } + + @SuppressWarnings("nls") + private void visitGroupByClause(StringBuilder sql) { + if (groupBies.isEmpty()) { + return; + } + sql.append(GROUP_BY); + for (Field groupBy : groupBies) { + sql.append(SPACE).append(groupBy).append(COMMA); + } + sql.deleteCharAt(sql.length() - 1).append(SPACE); + if (havings.isEmpty()) { + return; + } + sql.append("HAVING"); + for (Criterion havingCriterion : havings) { + sql.append(SPACE).append(havingCriterion).append(COMMA); + } + sql.deleteCharAt(sql.length() - 1).append(SPACE); + } + + private void visitWhereClause(StringBuilder sql) { + if (criterions.isEmpty()) { + return; + } + sql.append(WHERE); + for (Criterion criterion : criterions) { + sql.append(SPACE).append(criterion).append(SPACE); + } + } + + private void visitJoinClause(StringBuilder sql) { + for (Join join : joins) { + sql.append(join).append(SPACE); + } + } + + private void visitFromClause(StringBuilder sql) { + if (table == null) { + return; + } + sql.append(FROM).append(SPACE).append(table).append(SPACE); + } + + private void visitSelectClause(StringBuilder sql) { + sql.append(SELECT).append(SPACE); + if (fields.isEmpty()) { + sql.append(ALL).append(SPACE); + return; + } + for (Field field : fields) { + sql.append(field.toStringInSelect()).append(COMMA); + } + sql.deleteCharAt(sql.length() - 1).append(SPACE); + } + + private void visitLimitClause(StringBuilder sql) { + if(limits > -1) + sql.append(LIMIT).append(SPACE).append(limits).append(SPACE); + } + + public SqlTable as(String alias) { + return table(LEFT_PARENTHESIS + this.toString() + RIGHT_PARENTHESIS).as(alias); + } + + public Query having(Criterion criterion) { + this.havings.add(criterion); + return this; + } + + /** + * Gets a list of fields returned by this query + * @return + */ + public Property[] getFields() { + return fields.toArray(new Property[fields.size()]); + } + + /** + * Add the SQL query template (comes after the "from") + * @param sqlQuery + * @return + */ + public Query withQueryTemplate(String template) { + queryTemplate = template; + return this; + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/QueryTemplate.java b/astrid/common-src/com/todoroo/andlib/sql/QueryTemplate.java index 220905a01..abd506f18 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/QueryTemplate.java +++ b/astrid/common-src/com/todoroo/andlib/sql/QueryTemplate.java @@ -1,117 +1,117 @@ -package com.todoroo.andlib.sql; - -import static com.todoroo.andlib.sql.SqlConstants.COMMA; -import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY; -import static com.todoroo.andlib.sql.SqlConstants.LIMIT; -import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY; -import static com.todoroo.andlib.sql.SqlConstants.SPACE; -import static com.todoroo.andlib.sql.SqlConstants.WHERE; -import static java.util.Arrays.asList; - -import java.util.ArrayList; - -/** - * Query Template returns a bunch of criteria that allows a query to be - * constructed - * - * @author Tim Su - * - */ -public final class QueryTemplate { - - private final ArrayList criterions = new ArrayList(); - private final ArrayList joins = new ArrayList(); - private final ArrayList groupBies = new ArrayList(); - private final ArrayList orders = new ArrayList(); - private final ArrayList havings = new ArrayList(); - private Integer limit = null; - - public QueryTemplate join(Join... join) { - joins.addAll(asList(join)); - return this; - } - - public QueryTemplate where(Criterion criterion) { - criterions.add(criterion); - return this; - } - - public QueryTemplate groupBy(Field... groupBy) { - groupBies.addAll(asList(groupBy)); - return this; - } - - public QueryTemplate orderBy(Order... order) { - orders.addAll(asList(order)); - return this; - } - - @Override - public String toString() { - StringBuilder sql = new StringBuilder(); - visitJoinClause(sql); - visitWhereClause(sql); - visitGroupByClause(sql); - visitOrderByClause(sql); - if(limit != null) - sql.append(LIMIT).append(SPACE).append(limit); - return sql.toString(); - } - - private void visitOrderByClause(StringBuilder sql) { - if (orders.isEmpty()) { - return; - } - sql.append(ORDER_BY); - for (Order order : orders) { - sql.append(SPACE).append(order).append(COMMA); - } - sql.deleteCharAt(sql.length() - 1).append(SPACE); - } - - @SuppressWarnings("nls") - private void visitGroupByClause(StringBuilder sql) { - if (groupBies.isEmpty()) { - return; - } - sql.append(GROUP_BY); - for (Field groupBy : groupBies) { - sql.append(SPACE).append(groupBy).append(COMMA); - } - sql.deleteCharAt(sql.length() - 1).append(SPACE); - if (havings.isEmpty()) { - return; - } - sql.append("HAVING"); - for (Criterion havingCriterion : havings) { - sql.append(SPACE).append(havingCriterion).append(COMMA); - } - sql.deleteCharAt(sql.length() - 1).append(SPACE); - } - - private void visitWhereClause(StringBuilder sql) { - if (criterions.isEmpty()) { - return; - } - sql.append(WHERE); - for (Criterion criterion : criterions) { - sql.append(SPACE).append(criterion).append(SPACE); - } - } - - private void visitJoinClause(StringBuilder sql) { - for (Join join : joins) { - sql.append(join).append(SPACE); - } - } - - public QueryTemplate having(Criterion criterion) { - this.havings.add(criterion); - return this; - } - - public QueryTemplate limit(int limitValue) { - this.limit = limitValue; - return this; - } -} +package com.todoroo.andlib.sql; + +import static com.todoroo.andlib.sql.SqlConstants.COMMA; +import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY; +import static com.todoroo.andlib.sql.SqlConstants.LIMIT; +import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY; +import static com.todoroo.andlib.sql.SqlConstants.SPACE; +import static com.todoroo.andlib.sql.SqlConstants.WHERE; +import static java.util.Arrays.asList; + +import java.util.ArrayList; + +/** + * Query Template returns a bunch of criteria that allows a query to be + * constructed + * + * @author Tim Su + * + */ +public final class QueryTemplate { + + private final ArrayList criterions = new ArrayList(); + private final ArrayList joins = new ArrayList(); + private final ArrayList groupBies = new ArrayList(); + private final ArrayList orders = new ArrayList(); + private final ArrayList havings = new ArrayList(); + private Integer limit = null; + + public QueryTemplate join(Join... join) { + joins.addAll(asList(join)); + return this; + } + + public QueryTemplate where(Criterion criterion) { + criterions.add(criterion); + return this; + } + + public QueryTemplate groupBy(Field... groupBy) { + groupBies.addAll(asList(groupBy)); + return this; + } + + public QueryTemplate orderBy(Order... order) { + orders.addAll(asList(order)); + return this; + } + + @Override + public String toString() { + StringBuilder sql = new StringBuilder(); + visitJoinClause(sql); + visitWhereClause(sql); + visitGroupByClause(sql); + visitOrderByClause(sql); + if(limit != null) + sql.append(LIMIT).append(SPACE).append(limit); + return sql.toString(); + } + + private void visitOrderByClause(StringBuilder sql) { + if (orders.isEmpty()) { + return; + } + sql.append(ORDER_BY); + for (Order order : orders) { + sql.append(SPACE).append(order).append(COMMA); + } + sql.deleteCharAt(sql.length() - 1).append(SPACE); + } + + @SuppressWarnings("nls") + private void visitGroupByClause(StringBuilder sql) { + if (groupBies.isEmpty()) { + return; + } + sql.append(GROUP_BY); + for (Field groupBy : groupBies) { + sql.append(SPACE).append(groupBy).append(COMMA); + } + sql.deleteCharAt(sql.length() - 1).append(SPACE); + if (havings.isEmpty()) { + return; + } + sql.append("HAVING"); + for (Criterion havingCriterion : havings) { + sql.append(SPACE).append(havingCriterion).append(COMMA); + } + sql.deleteCharAt(sql.length() - 1).append(SPACE); + } + + private void visitWhereClause(StringBuilder sql) { + if (criterions.isEmpty()) { + return; + } + sql.append(WHERE); + for (Criterion criterion : criterions) { + sql.append(SPACE).append(criterion).append(SPACE); + } + } + + private void visitJoinClause(StringBuilder sql) { + for (Join join : joins) { + sql.append(join).append(SPACE); + } + } + + public QueryTemplate having(Criterion criterion) { + this.havings.add(criterion); + return this; + } + + public QueryTemplate limit(int limitValue) { + this.limit = limitValue; + return this; + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/SqlConstants.java b/astrid/common-src/com/todoroo/andlib/sql/SqlConstants.java index 63fee3fd0..bb61d9580 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/SqlConstants.java +++ b/astrid/common-src/com/todoroo/andlib/sql/SqlConstants.java @@ -1,25 +1,25 @@ -package com.todoroo.andlib.sql; - -@SuppressWarnings("nls") -public final class SqlConstants { - static final String SELECT = "SELECT"; - static final String SPACE = " "; - static final String AS = "AS"; - static final String COMMA = ","; - static final String FROM = "FROM"; - static final String ON = "ON"; - static final String JOIN = "JOIN"; - static final String ALL = "*"; - static final String LEFT_PARENTHESIS = "("; - static final String RIGHT_PARENTHESIS = ")"; - static final String AND = "AND"; - static final String BETWEEN = "BETWEEN"; - static final String LIKE = "LIKE"; - static final String OR = "OR"; - static final String ORDER_BY = "ORDER BY"; - static final String GROUP_BY = "GROUP BY"; - static final String WHERE = "WHERE"; - public static final String EXISTS = "EXISTS"; - public static final String NOT = "NOT"; - public static final String LIMIT = "LIMIT"; -} +package com.todoroo.andlib.sql; + +@SuppressWarnings("nls") +public final class SqlConstants { + static final String SELECT = "SELECT"; + static final String SPACE = " "; + static final String AS = "AS"; + static final String COMMA = ","; + static final String FROM = "FROM"; + static final String ON = "ON"; + static final String JOIN = "JOIN"; + static final String ALL = "*"; + static final String LEFT_PARENTHESIS = "("; + static final String RIGHT_PARENTHESIS = ")"; + static final String AND = "AND"; + static final String BETWEEN = "BETWEEN"; + static final String LIKE = "LIKE"; + static final String OR = "OR"; + static final String ORDER_BY = "ORDER BY"; + static final String GROUP_BY = "GROUP BY"; + static final String WHERE = "WHERE"; + public static final String EXISTS = "EXISTS"; + public static final String NOT = "NOT"; + public static final String LIMIT = "LIMIT"; +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/SqlTable.java b/astrid/common-src/com/todoroo/andlib/sql/SqlTable.java index 07bc2e998..4205ce617 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/SqlTable.java +++ b/astrid/common-src/com/todoroo/andlib/sql/SqlTable.java @@ -1,20 +1,20 @@ -package com.todoroo.andlib.sql; - -public class SqlTable extends DBObject { - - protected SqlTable(String expression) { - super(expression); - } - - public static SqlTable table(String table) { - return new SqlTable(table); - } - - @SuppressWarnings("nls") - protected String fieldExpression(String fieldName) { - if (hasAlias()) { - return alias + "." + fieldName; - } - return expression+"."+fieldName; - } -} +package com.todoroo.andlib.sql; + +public class SqlTable extends DBObject { + + protected SqlTable(String expression) { + super(expression); + } + + public static SqlTable table(String table) { + return new SqlTable(table); + } + + @SuppressWarnings("nls") + protected String fieldExpression(String fieldName) { + if (hasAlias()) { + return alias + "." + fieldName; + } + return expression+"."+fieldName; + } +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java b/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java index d7e958440..def79a855 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java +++ b/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java @@ -1,92 +1,92 @@ -package com.todoroo.andlib.sql; - -import static com.todoroo.andlib.sql.SqlConstants.SPACE; - -public class UnaryCriterion extends Criterion { - protected final Field expression; - protected final Object value; - - UnaryCriterion(Field expression, Operator operator, Object value) { - super(operator); - this.expression = expression; - this.value = value; - } - - @Override - protected void populate(StringBuilder sb) { - beforePopulateOperator(sb); - populateOperator(sb); - afterPopulateOperator(sb); - } - - public static Criterion eq(Field expression, Object value) { - return new UnaryCriterion(expression, Operator.eq, value); - } - - protected void beforePopulateOperator(StringBuilder sb) { - sb.append(expression); - } - - protected void populateOperator(StringBuilder sb) { - sb.append(operator); - } - - @SuppressWarnings("nls") - protected void afterPopulateOperator(StringBuilder sb) { - if(value == null) - return; - else if(value instanceof String) - sb.append("'").append(sanitize((String) value)).append("'"); - else - sb.append(value); - } - - /** - * Sanitize the given input for SQL - * @param input - * @return - */ - @SuppressWarnings("nls") - public static String sanitize(String input) { - return input.replace("'", "''"); - } - - public static Criterion neq(Field field, Object value) { - return new UnaryCriterion(field, Operator.neq, value); - } - - public static Criterion gt(Field field, Object value) { - return new UnaryCriterion(field, Operator.gt, value); - } - - public static Criterion lt(Field field, Object value) { - return new UnaryCriterion(field, Operator.lt, value); - } - - public static Criterion isNull(Field field) { - return new UnaryCriterion(field, Operator.isNull, null) { - @Override - protected void populateOperator(StringBuilder sb) { - sb.append(SPACE).append(operator); - } - }; - } - - public static Criterion isNotNull(Field field) { - return new UnaryCriterion(field, Operator.isNotNull, null) { - @Override - protected void populateOperator(StringBuilder sb) { - sb.append(SPACE).append(operator); - } - }; - } - - public static Criterion like(Field field, String value) { - return new UnaryCriterion(field, Operator.like, value) { - @Override - protected void populateOperator(StringBuilder sb) { - sb.append(SPACE).append(operator).append(SPACE); - } - }; - } -} +package com.todoroo.andlib.sql; + +import static com.todoroo.andlib.sql.SqlConstants.SPACE; + +public class UnaryCriterion extends Criterion { + protected final Field expression; + protected final Object value; + + UnaryCriterion(Field expression, Operator operator, Object value) { + super(operator); + this.expression = expression; + this.value = value; + } + + @Override + protected void populate(StringBuilder sb) { + beforePopulateOperator(sb); + populateOperator(sb); + afterPopulateOperator(sb); + } + + public static Criterion eq(Field expression, Object value) { + return new UnaryCriterion(expression, Operator.eq, value); + } + + protected void beforePopulateOperator(StringBuilder sb) { + sb.append(expression); + } + + protected void populateOperator(StringBuilder sb) { + sb.append(operator); + } + + @SuppressWarnings("nls") + protected void afterPopulateOperator(StringBuilder sb) { + if(value == null) + return; + else if(value instanceof String) + sb.append("'").append(sanitize((String) value)).append("'"); + else + sb.append(value); + } + + /** + * Sanitize the given input for SQL + * @param input + * @return + */ + @SuppressWarnings("nls") + public static String sanitize(String input) { + return input.replace("'", "''"); + } + + public static Criterion neq(Field field, Object value) { + return new UnaryCriterion(field, Operator.neq, value); + } + + public static Criterion gt(Field field, Object value) { + return new UnaryCriterion(field, Operator.gt, value); + } + + public static Criterion lt(Field field, Object value) { + return new UnaryCriterion(field, Operator.lt, value); + } + + public static Criterion isNull(Field field) { + return new UnaryCriterion(field, Operator.isNull, null) { + @Override + protected void populateOperator(StringBuilder sb) { + sb.append(SPACE).append(operator); + } + }; + } + + public static Criterion isNotNull(Field field) { + return new UnaryCriterion(field, Operator.isNotNull, null) { + @Override + protected void populateOperator(StringBuilder sb) { + sb.append(SPACE).append(operator); + } + }; + } + + public static Criterion like(Field field, String value) { + return new UnaryCriterion(field, Operator.like, value) { + @Override + protected void populateOperator(StringBuilder sb) { + sb.append(SPACE).append(operator).append(SPACE); + } + }; + } +} diff --git a/astrid/gen/.gitignore b/astrid/gen/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/astrid/plugin-src/com/todoroo/astrid/gcal/Calendars.java b/astrid/plugin-src/com/todoroo/astrid/gcal/Calendars.java index 00a61ba19..8f4befc42 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gcal/Calendars.java +++ b/astrid/plugin-src/com/todoroo/astrid/gcal/Calendars.java @@ -1,146 +1,146 @@ -package com.todoroo.astrid.gcal; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; - -import com.timsu.astrid.R; -import com.todoroo.andlib.service.ContextManager; -import com.todoroo.andlib.utility.AndroidUtilities; -import com.todoroo.astrid.utility.Preferences; - -@SuppressWarnings("nls") -public class Calendars { - - public static final String CALENDAR_CONTENT_CALENDARS = "calendars"; - public static final String CALENDAR_CONTENT_EVENTS = "events"; - - private static final String ID_COLUMN_NAME = "_id"; - private static final String DISPLAY_COLUMN_NAME = "displayName"; - private static final String ACCES_LEVEL_COLUMN_NAME = "access_level"; - - private static final String[] CALENDARS_PROJECTION = new String[] { - ID_COLUMN_NAME, // Calendars._ID, - DISPLAY_COLUMN_NAME // Calendars.DISPLAY_NAME - }; - - // Only show calendars that the user can modify. Access level 500 - // corresponds to Calendars.CONTRIBUTOR_ACCESS - private static final String CALENDARS_WHERE = ACCES_LEVEL_COLUMN_NAME + ">= 500"; - - private static final String CALENDARS_SORT = "displayName ASC"; - - // --- api access - - /** Return content uri for calendars - * @param table provider table, something like calendars, events - */ - public static Uri getCalendarContentUri(String table) { - if(AndroidUtilities.getSdkVersion() >= 8) - return Uri.parse("content://com.android.calendar/" + table); - else - return Uri.parse("content://calendar/" + table); - } - - /** Return calendar package name */ - public static String getCalendarPackage() { - if(AndroidUtilities.getSdkVersion() >= 8) - return "com.google.android.calendar"; - else - return "com.android.calendar"; - } - - // --- helper data structure - - /** - * Helper class for working with the results of getCalendars - */ - public static class CalendarResult { - /** calendar names */ - public String[] calendars; - - /** calendar ids. null entry -> use default */ - public String[] calendarIds; - - /** default selection index */ - public int defaultIndex = -1; - } - - /** - * Appends all user-modifiable calendars to listPreference. Always includes - * entry called "Astrid default" with calendar id of - * prefs_defaultCalendar_default. - * - * @param context - * context - * @param listPreference - * preference to init - */ - public static CalendarResult getCalendars() { - Context context = ContextManager.getContext(); - ContentResolver cr = context.getContentResolver(); - Resources r = context.getResources(); - Cursor c = cr.query(getCalendarContentUri(CALENDAR_CONTENT_CALENDARS), CALENDARS_PROJECTION, - CALENDARS_WHERE, null, CALENDARS_SORT); - try { - // Fetch the current setting. Invalid calendar id will - // be changed to default value. - String defaultSetting = Preferences.getStringValue(R.string.gcal_p_default); - - CalendarResult result = new CalendarResult(); - - if (c == null || c.getCount() == 0) { - // Something went wrong when querying calendars. Only offer them - // the system default choice - result.calendars = new String[] { - r.getString(R.string.gcal_GCP_default) }; - result.calendarIds = new String[] { null }; - result.defaultIndex = 0; - return result; - } - - int calendarCount = c.getCount(); - - result.calendars = new String[calendarCount]; - result.calendarIds = new String[calendarCount]; - - // Iterate calendars one by one, and fill up the list preference - int row = 0; - int idColumn = c.getColumnIndex(ID_COLUMN_NAME); - int nameColumn = c.getColumnIndex(DISPLAY_COLUMN_NAME); - while (c.moveToNext()) { - String id = c.getString(idColumn); - String name = c.getString(nameColumn); - result.calendars[row] = name; - result.calendarIds[row] = id; - - // We found currently selected calendar - if (defaultSetting != null && defaultSetting.equals(id)) { - result.defaultIndex = row; - } - - row++; - } - - if (result.defaultIndex == -1 || result.defaultIndex >= calendarCount) { - result.defaultIndex = 0; - } - - return result; - } finally { - if(c != null) - c.close(); - } - } - - /** - * sets the default calendar for future use - * @param defaultCalendar default calendar id - */ - public static void setDefaultCalendar(String defaultCalendar) { - Preferences.setString(R.string.gcal_p_default, defaultCalendar); - } - +package com.todoroo.astrid.gcal; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.database.Cursor; +import android.net.Uri; + +import com.timsu.astrid.R; +import com.todoroo.andlib.service.ContextManager; +import com.todoroo.andlib.utility.AndroidUtilities; +import com.todoroo.astrid.utility.Preferences; + +@SuppressWarnings("nls") +public class Calendars { + + public static final String CALENDAR_CONTENT_CALENDARS = "calendars"; + public static final String CALENDAR_CONTENT_EVENTS = "events"; + + private static final String ID_COLUMN_NAME = "_id"; + private static final String DISPLAY_COLUMN_NAME = "displayName"; + private static final String ACCES_LEVEL_COLUMN_NAME = "access_level"; + + private static final String[] CALENDARS_PROJECTION = new String[] { + ID_COLUMN_NAME, // Calendars._ID, + DISPLAY_COLUMN_NAME // Calendars.DISPLAY_NAME + }; + + // Only show calendars that the user can modify. Access level 500 + // corresponds to Calendars.CONTRIBUTOR_ACCESS + private static final String CALENDARS_WHERE = ACCES_LEVEL_COLUMN_NAME + ">= 500"; + + private static final String CALENDARS_SORT = "displayName ASC"; + + // --- api access + + /** Return content uri for calendars + * @param table provider table, something like calendars, events + */ + public static Uri getCalendarContentUri(String table) { + if(AndroidUtilities.getSdkVersion() >= 8) + return Uri.parse("content://com.android.calendar/" + table); + else + return Uri.parse("content://calendar/" + table); + } + + /** Return calendar package name */ + public static String getCalendarPackage() { + if(AndroidUtilities.getSdkVersion() >= 8) + return "com.google.android.calendar"; + else + return "com.android.calendar"; + } + + // --- helper data structure + + /** + * Helper class for working with the results of getCalendars + */ + public static class CalendarResult { + /** calendar names */ + public String[] calendars; + + /** calendar ids. null entry -> use default */ + public String[] calendarIds; + + /** default selection index */ + public int defaultIndex = -1; + } + + /** + * Appends all user-modifiable calendars to listPreference. Always includes + * entry called "Astrid default" with calendar id of + * prefs_defaultCalendar_default. + * + * @param context + * context + * @param listPreference + * preference to init + */ + public static CalendarResult getCalendars() { + Context context = ContextManager.getContext(); + ContentResolver cr = context.getContentResolver(); + Resources r = context.getResources(); + Cursor c = cr.query(getCalendarContentUri(CALENDAR_CONTENT_CALENDARS), CALENDARS_PROJECTION, + CALENDARS_WHERE, null, CALENDARS_SORT); + try { + // Fetch the current setting. Invalid calendar id will + // be changed to default value. + String defaultSetting = Preferences.getStringValue(R.string.gcal_p_default); + + CalendarResult result = new CalendarResult(); + + if (c == null || c.getCount() == 0) { + // Something went wrong when querying calendars. Only offer them + // the system default choice + result.calendars = new String[] { + r.getString(R.string.gcal_GCP_default) }; + result.calendarIds = new String[] { null }; + result.defaultIndex = 0; + return result; + } + + int calendarCount = c.getCount(); + + result.calendars = new String[calendarCount]; + result.calendarIds = new String[calendarCount]; + + // Iterate calendars one by one, and fill up the list preference + int row = 0; + int idColumn = c.getColumnIndex(ID_COLUMN_NAME); + int nameColumn = c.getColumnIndex(DISPLAY_COLUMN_NAME); + while (c.moveToNext()) { + String id = c.getString(idColumn); + String name = c.getString(nameColumn); + result.calendars[row] = name; + result.calendarIds[row] = id; + + // We found currently selected calendar + if (defaultSetting != null && defaultSetting.equals(id)) { + result.defaultIndex = row; + } + + row++; + } + + if (result.defaultIndex == -1 || result.defaultIndex >= calendarCount) { + result.defaultIndex = 0; + } + + return result; + } finally { + if(c != null) + c.close(); + } + } + + /** + * sets the default calendar for future use + * @param defaultCalendar default calendar id + */ + public static void setDefaultCalendar(String defaultCalendar) { + Preferences.setString(R.string.gcal_p_default, defaultCalendar); + } + } \ No newline at end of file diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java index 222a9de67..dc271c104 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevDetailExposer.java @@ -1,95 +1,95 @@ -/** - * See the file "LICENSE" for the full license governing this code. - */ -package com.todoroo.astrid.producteev; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -import com.timsu.astrid.R; -import com.todoroo.andlib.data.TodorooCursor; -import com.todoroo.astrid.adapter.TaskAdapter; -import com.todoroo.astrid.api.AstridApiConstants; -import com.todoroo.astrid.api.DetailExposer; -import com.todoroo.astrid.model.Metadata; -import com.todoroo.astrid.producteev.sync.ProducteevDataService; -import com.todoroo.astrid.producteev.sync.ProducteevNote; -import com.todoroo.astrid.producteev.sync.ProducteevTask; - -/** - * Exposes Task Details for Producteev: - * - notes - * - * @author Tim Su - * - */ -public class ProducteevDetailExposer extends BroadcastReceiver implements DetailExposer{ - - @Override - public void onReceive(Context context, Intent intent) { - // if we aren't logged in, don't expose features - if(!ProducteevUtilities.INSTANCE.isLoggedIn()) - return; - - long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1); - if(taskId == -1) - return; - - boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false); - String taskDetail = getTaskDetails(context, taskId, extended); - if(taskDetail == null) - return; - - Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); - broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER); - broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); - broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended); - broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail); - context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); - } - - @Override - public String getTaskDetails(Context context, long id, boolean extended) { - Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(id); - if(metadata == null) - return null; - - StringBuilder builder = new StringBuilder(); - - if(!extended) { - long dashboardId = metadata.getValue(ProducteevTask.DASHBOARD_ID); - String dashboardName = ProducteevDataService.getInstance().getDashboardName(dashboardId); - // Prod dashboard is out of date. don't display Producteev stuff - if(dashboardName == null) - return null; - - if(dashboardId > 0) { - builder.append(context.getString(R.string.producteev_TLA_dashboard, - dashboardId)).append(TaskAdapter.DETAIL_SEPARATOR); - } - - } else { - TodorooCursor notesCursor = ProducteevDataService.getInstance().getTaskNotesCursor(id); - try { - for(notesCursor.moveToFirst(); !notesCursor.isAfterLast(); notesCursor.moveToNext()) { - metadata.readFromCursor(notesCursor); - builder.append(metadata.getValue(ProducteevNote.MESSAGE)).append(TaskAdapter.DETAIL_SEPARATOR); - } - } finally { - notesCursor.close(); - } - } - - if(builder.length() == 0) - return null; - String result = builder.toString(); - return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length()); - } - - @Override - public String getPluginIdentifier() { - return ProducteevUtilities.IDENTIFIER; - } - -} +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.producteev; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.timsu.astrid.R; +import com.todoroo.andlib.data.TodorooCursor; +import com.todoroo.astrid.adapter.TaskAdapter; +import com.todoroo.astrid.api.AstridApiConstants; +import com.todoroo.astrid.api.DetailExposer; +import com.todoroo.astrid.model.Metadata; +import com.todoroo.astrid.producteev.sync.ProducteevDataService; +import com.todoroo.astrid.producteev.sync.ProducteevNote; +import com.todoroo.astrid.producteev.sync.ProducteevTask; + +/** + * Exposes Task Details for Producteev: + * - notes + * + * @author Tim Su + * + */ +public class ProducteevDetailExposer extends BroadcastReceiver implements DetailExposer{ + + @Override + public void onReceive(Context context, Intent intent) { + // if we aren't logged in, don't expose features + if(!ProducteevUtilities.INSTANCE.isLoggedIn()) + return; + + long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1); + if(taskId == -1) + return; + + boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false); + String taskDetail = getTaskDetails(context, taskId, extended); + if(taskDetail == null) + return; + + Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail); + context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); + } + + @Override + public String getTaskDetails(Context context, long id, boolean extended) { + Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(id); + if(metadata == null) + return null; + + StringBuilder builder = new StringBuilder(); + + if(!extended) { + long dashboardId = metadata.getValue(ProducteevTask.DASHBOARD_ID); + String dashboardName = ProducteevDataService.getInstance().getDashboardName(dashboardId); + // Prod dashboard is out of date. don't display Producteev stuff + if(dashboardName == null) + return null; + + if(dashboardId > 0) { + builder.append(context.getString(R.string.producteev_TLA_dashboard, + dashboardId)).append(TaskAdapter.DETAIL_SEPARATOR); + } + + } else { + TodorooCursor notesCursor = ProducteevDataService.getInstance().getTaskNotesCursor(id); + try { + for(notesCursor.moveToFirst(); !notesCursor.isAfterLast(); notesCursor.moveToNext()) { + metadata.readFromCursor(notesCursor); + builder.append(metadata.getValue(ProducteevNote.MESSAGE)).append(TaskAdapter.DETAIL_SEPARATOR); + } + } finally { + notesCursor.close(); + } + } + + if(builder.length() == 0) + return null; + String result = builder.toString(); + return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length()); + } + + @Override + public String getPluginIdentifier() { + return ProducteevUtilities.IDENTIFIER; + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDashboard.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDashboard.java index 4fe57074c..4ae8c651f 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDashboard.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDashboard.java @@ -1,86 +1,86 @@ -/** - * See the file "LICENSE" for the full license governing this code. - */ -package com.todoroo.astrid.producteev.sync; - - -import android.content.ContentValues; - -import com.todoroo.andlib.data.AbstractModel; -import com.todoroo.andlib.data.Property; -import com.todoroo.andlib.data.Table; -import com.todoroo.andlib.data.TodorooCursor; -import com.todoroo.andlib.data.Property.LongProperty; -import com.todoroo.andlib.data.Property.StringProperty; -import com.todoroo.astrid.model.Task; - -/** - * Data Model which represents a dashboard in Producteev - * - * @author Tim Su - * - */ -@SuppressWarnings("nls") -public class ProducteevDashboard extends AbstractModel { - - // --- table - - public static final Table TABLE = new Table("dashboards", ProducteevDashboard.class); - - // --- properties - - /** ID (corresponds to RTM ID) */ - public static final LongProperty ID = new LongProperty( - TABLE, ID_PROPERTY_NAME); - - /** Name */ - public static final StringProperty NAME = new StringProperty( - TABLE, "name"); - - /** List of all properties for this model */ - public static final Property[] PROPERTIES = generateProperties(ProducteevDashboard.class); - - // --- defaults - - /** Default values container */ - private static final ContentValues defaultValues = new ContentValues(); - -// static { -// defaultValues.put(POSITION.name, 0); -// defaultValues.put(ARCHIVED.name, 0); -// } - - @Override - public ContentValues getDefaultValues() { - return defaultValues; - } - - // --- data access boilerplate - - public ProducteevDashboard() { - super(); - } - - public ProducteevDashboard(TodorooCursor cursor) { - this(); - readPropertiesFromCursor(cursor); - } - - public void readFromCursor(TodorooCursor cursor) { - super.readPropertiesFromCursor(cursor); - } - - @Override - public long getId() { - return getIdHelper(ID); - }; - - // --- parcelable helpers - - private static final Creator CREATOR = new ModelCreator(Task.class); - - @Override - protected Creator getCreator() { - return CREATOR; - } -} +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.producteev.sync; + + +import android.content.ContentValues; + +import com.todoroo.andlib.data.AbstractModel; +import com.todoroo.andlib.data.Property; +import com.todoroo.andlib.data.Table; +import com.todoroo.andlib.data.TodorooCursor; +import com.todoroo.andlib.data.Property.LongProperty; +import com.todoroo.andlib.data.Property.StringProperty; +import com.todoroo.astrid.model.Task; + +/** + * Data Model which represents a dashboard in Producteev + * + * @author Tim Su + * + */ +@SuppressWarnings("nls") +public class ProducteevDashboard extends AbstractModel { + + // --- table + + public static final Table TABLE = new Table("dashboards", ProducteevDashboard.class); + + // --- properties + + /** ID (corresponds to RTM ID) */ + public static final LongProperty ID = new LongProperty( + TABLE, ID_PROPERTY_NAME); + + /** Name */ + public static final StringProperty NAME = new StringProperty( + TABLE, "name"); + + /** List of all properties for this model */ + public static final Property[] PROPERTIES = generateProperties(ProducteevDashboard.class); + + // --- defaults + + /** Default values container */ + private static final ContentValues defaultValues = new ContentValues(); + +// static { +// defaultValues.put(POSITION.name, 0); +// defaultValues.put(ARCHIVED.name, 0); +// } + + @Override + public ContentValues getDefaultValues() { + return defaultValues; + } + + // --- data access boilerplate + + public ProducteevDashboard() { + super(); + } + + public ProducteevDashboard(TodorooCursor cursor) { + this(); + readPropertiesFromCursor(cursor); + } + + public void readFromCursor(TodorooCursor cursor) { + super.readPropertiesFromCursor(cursor); + } + + @Override + public long getId() { + return getIdHelper(ID); + }; + + // --- parcelable helpers + + private static final Creator CREATOR = new ModelCreator(Task.class); + + @Override + protected Creator getCreator() { + return CREATOR; + } +} diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java index a77a7273e..e0cfc8abf 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java @@ -1,235 +1,235 @@ -/** - * See the file "LICENSE" for the full license governing this code. - */ -package com.todoroo.astrid.producteev.sync; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Map; -import java.util.Random; - -import android.content.Context; - -import com.todoroo.andlib.data.GenericDao; -import com.todoroo.andlib.data.Property; -import com.todoroo.andlib.data.TodorooCursor; -import com.todoroo.andlib.service.Autowired; -import com.todoroo.andlib.service.ContextManager; -import com.todoroo.andlib.service.DependencyInjectionService; -import com.todoroo.andlib.sql.Criterion; -import com.todoroo.andlib.sql.Join; -import com.todoroo.andlib.sql.Query; -import com.todoroo.andlib.utility.SoftHashMap; -import com.todoroo.astrid.dao.MetadataDao; -import com.todoroo.astrid.dao.TaskDao; -import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; -import com.todoroo.astrid.dao.TaskDao.TaskCriteria; -import com.todoroo.astrid.model.Metadata; -import com.todoroo.astrid.model.Task; -import com.todoroo.astrid.producteev.ProducteevUtilities; -import com.todoroo.astrid.rmilk.data.MilkNote; -import com.todoroo.astrid.tags.TagService; - -public final class ProducteevDataService { - - // --- constants - - /** Utility for joining tasks with metadata */ - public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK)); - - // --- singleton - - private static ProducteevDataService instance = null; - - public static synchronized ProducteevDataService getInstance() { - if(instance == null) - instance = new ProducteevDataService(ContextManager.getContext()); - return instance; - } - - // --- instance variables - - protected final Context context; - - private final ProducteevDatabase prodDatabase = new ProducteevDatabase(); - - private final GenericDao prodDashboardDao; - - @Autowired - private TaskDao taskDao; - - @Autowired - private MetadataDao metadataDao; - - private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE; - - static final Random random = new Random(); - - private ProducteevDataService(Context context) { - this.context = context; - DependencyInjectionService.getInstance().inject(this); - prodDashboardDao = new GenericDao(ProducteevDashboard.class, prodDatabase); - } - - // --- task and metadata methods - - /** - * Clears RTM metadata information. Used when user logs out of RTM - */ - public void clearMetadata() { - metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY)); - metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevNote.METADATA_KEY)); - } - - /** - * Gets tasks that were created since last sync - * @param properties - * @return - */ - public TodorooCursor getLocallyCreated(Property[] properties) { - return - taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).where(Criterion.and( - Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE). - where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), ProducteevTask.ID.gt(0))))), - TaskCriteria.isActive())).groupBy(Task.ID)); - } - - /** - * Gets tasks that were modified since last sync - * @param properties - * @return null if never sync'd - */ - public TodorooCursor getLocallyUpdated(Property[] properties) { - long lastSyncDate = preferences.getLastSyncDate(); - if(lastSyncDate == 0) - return taskDao.query(Query.select(Task.ID).where(Criterion.none)); - return - taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN). - where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), - Task.MODIFICATION_DATE.gt(lastSyncDate))).groupBy(Task.ID)); - } - - /** - * Searches for a local task with same remote id, updates this task's id - * @param remoteTask - */ - public void findLocalMatch(ProducteevTaskContainer remoteTask) { - if(remoteTask.task.getId() != Task.NO_ID) - return; - TodorooCursor cursor = taskDao.query(Query.select(Task.ID). - join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), - ProducteevTask.ID.eq(remoteTask.pdvTask.getValue(ProducteevTask.ID))))); - try { - if(cursor.getCount() == 0) - return; - cursor.moveToFirst(); - remoteTask.task.setId(cursor.get(Task.ID)); - } finally { - cursor.close(); - } - } - - /** - * Saves a task and its metadata - * @param task - */ - public void saveTaskAndMetadata(ProducteevTaskContainer task) { - taskDao.save(task.task, true); - - metadataDao.deleteWhere(Criterion.and(MetadataCriteria.byTask(task.task.getId()), - Criterion.or(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), - MetadataCriteria.withKey(ProducteevNote.METADATA_KEY), - MetadataCriteria.withKey(TagService.KEY)))); - task.metadata.add(task.pdvTask); - task.pdvTask.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY); - for(Metadata metadata : task.metadata) { - metadata.setValue(Metadata.TASK, task.task.getId()); - metadataDao.createNew(metadata); - } - } - - /** - * Reads a task and its metadata - * @param task - * @return - */ - public ProducteevTaskContainer readTaskAndMetadata(TodorooCursor taskCursor) { - Task task = new Task(taskCursor); - - // read tags, notes, etc - ArrayList metadata = new ArrayList(); - TodorooCursor metadataCursor = metadataDao.query(Query.select(Metadata.PROPERTIES). - where(Criterion.and(MetadataCriteria.byTask(task.getId()), - Criterion.or(MetadataCriteria.withKey(TagService.KEY), - MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), - // FIXME: Constant from other plugin shouldnt be used - MetadataCriteria.withKey(MilkNote.METADATA_KEY), - MetadataCriteria.withKey(ProducteevNote.METADATA_KEY))))); - try { - for(metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor.moveToNext()) { - metadata.add(new Metadata(metadataCursor)); - } - } finally { - metadataCursor.close(); - } - - return new ProducteevTaskContainer(task, metadata); - } - - /** - * Reads metadata out of a task - * @return null if no metadata found - */ - public Metadata getTaskMetadata(long taskId) { - TodorooCursor cursor = metadataDao.query(Query.select( - ProducteevTask.ID, ProducteevTask.DASHBOARD_ID).where( - MetadataCriteria.byTaskAndwithKey(taskId, ProducteevTask.METADATA_KEY))); - try { - if(cursor.getCount() == 0) - return null; - cursor.moveToFirst(); - return new Metadata(cursor); - } finally { - cursor.close(); - } - } - - /** - * Reads task notes out of a task - */ - public TodorooCursor getTaskNotesCursor(long taskId) { - TodorooCursor cursor = metadataDao.query(Query.select(Metadata.PROPERTIES). - where(MetadataCriteria.byTaskAndwithKey(taskId, ProducteevNote.METADATA_KEY))); - return cursor; - } - - // --- list methods - - private final Map dashboardCache = - Collections.synchronizedMap(new SoftHashMap()); - - /** - * Get dashboard name by dashboard id - * @param dashboardId - * @return null if no dashboard by this id exists, otherwise dashboard name - */ - public String getDashboardName(long dashboardId) { - if(dashboardCache.containsKey(dashboardId)) - return dashboardCache.get(dashboardId); - - TodorooCursor cursor = prodDashboardDao.query(Query.select( - ProducteevDashboard.NAME).where(ProducteevDashboard.ID.eq(dashboardId))); - try { - if(cursor.getCount() == 0) { - dashboardCache.put(dashboardId, null); - return null; - } - cursor.moveToFirst(); - String name = cursor.get(ProducteevDashboard.NAME); - dashboardCache.put(dashboardId, name); - return name; - } finally { - cursor.close(); - } - } -} +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.producteev.sync; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; +import java.util.Random; + +import android.content.Context; + +import com.todoroo.andlib.data.GenericDao; +import com.todoroo.andlib.data.Property; +import com.todoroo.andlib.data.TodorooCursor; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.ContextManager; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.sql.Criterion; +import com.todoroo.andlib.sql.Join; +import com.todoroo.andlib.sql.Query; +import com.todoroo.andlib.utility.SoftHashMap; +import com.todoroo.astrid.dao.MetadataDao; +import com.todoroo.astrid.dao.TaskDao; +import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; +import com.todoroo.astrid.dao.TaskDao.TaskCriteria; +import com.todoroo.astrid.model.Metadata; +import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.producteev.ProducteevUtilities; +import com.todoroo.astrid.rmilk.data.MilkNote; +import com.todoroo.astrid.tags.TagService; + +public final class ProducteevDataService { + + // --- constants + + /** Utility for joining tasks with metadata */ + public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK)); + + // --- singleton + + private static ProducteevDataService instance = null; + + public static synchronized ProducteevDataService getInstance() { + if(instance == null) + instance = new ProducteevDataService(ContextManager.getContext()); + return instance; + } + + // --- instance variables + + protected final Context context; + + private final ProducteevDatabase prodDatabase = new ProducteevDatabase(); + + private final GenericDao prodDashboardDao; + + @Autowired + private TaskDao taskDao; + + @Autowired + private MetadataDao metadataDao; + + private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE; + + static final Random random = new Random(); + + private ProducteevDataService(Context context) { + this.context = context; + DependencyInjectionService.getInstance().inject(this); + prodDashboardDao = new GenericDao(ProducteevDashboard.class, prodDatabase); + } + + // --- task and metadata methods + + /** + * Clears RTM metadata information. Used when user logs out of RTM + */ + public void clearMetadata() { + metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY)); + metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevNote.METADATA_KEY)); + } + + /** + * Gets tasks that were created since last sync + * @param properties + * @return + */ + public TodorooCursor getLocallyCreated(Property[] properties) { + return + taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).where(Criterion.and( + Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE). + where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), ProducteevTask.ID.gt(0))))), + TaskCriteria.isActive())).groupBy(Task.ID)); + } + + /** + * Gets tasks that were modified since last sync + * @param properties + * @return null if never sync'd + */ + public TodorooCursor getLocallyUpdated(Property[] properties) { + long lastSyncDate = preferences.getLastSyncDate(); + if(lastSyncDate == 0) + return taskDao.query(Query.select(Task.ID).where(Criterion.none)); + return + taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN). + where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), + Task.MODIFICATION_DATE.gt(lastSyncDate))).groupBy(Task.ID)); + } + + /** + * Searches for a local task with same remote id, updates this task's id + * @param remoteTask + */ + public void findLocalMatch(ProducteevTaskContainer remoteTask) { + if(remoteTask.task.getId() != Task.NO_ID) + return; + TodorooCursor cursor = taskDao.query(Query.select(Task.ID). + join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), + ProducteevTask.ID.eq(remoteTask.pdvTask.getValue(ProducteevTask.ID))))); + try { + if(cursor.getCount() == 0) + return; + cursor.moveToFirst(); + remoteTask.task.setId(cursor.get(Task.ID)); + } finally { + cursor.close(); + } + } + + /** + * Saves a task and its metadata + * @param task + */ + public void saveTaskAndMetadata(ProducteevTaskContainer task) { + taskDao.save(task.task, true); + + metadataDao.deleteWhere(Criterion.and(MetadataCriteria.byTask(task.task.getId()), + Criterion.or(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), + MetadataCriteria.withKey(ProducteevNote.METADATA_KEY), + MetadataCriteria.withKey(TagService.KEY)))); + task.metadata.add(task.pdvTask); + task.pdvTask.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY); + for(Metadata metadata : task.metadata) { + metadata.setValue(Metadata.TASK, task.task.getId()); + metadataDao.createNew(metadata); + } + } + + /** + * Reads a task and its metadata + * @param task + * @return + */ + public ProducteevTaskContainer readTaskAndMetadata(TodorooCursor taskCursor) { + Task task = new Task(taskCursor); + + // read tags, notes, etc + ArrayList metadata = new ArrayList(); + TodorooCursor metadataCursor = metadataDao.query(Query.select(Metadata.PROPERTIES). + where(Criterion.and(MetadataCriteria.byTask(task.getId()), + Criterion.or(MetadataCriteria.withKey(TagService.KEY), + MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), + // FIXME: Constant from other plugin shouldnt be used + MetadataCriteria.withKey(MilkNote.METADATA_KEY), + MetadataCriteria.withKey(ProducteevNote.METADATA_KEY))))); + try { + for(metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor.moveToNext()) { + metadata.add(new Metadata(metadataCursor)); + } + } finally { + metadataCursor.close(); + } + + return new ProducteevTaskContainer(task, metadata); + } + + /** + * Reads metadata out of a task + * @return null if no metadata found + */ + public Metadata getTaskMetadata(long taskId) { + TodorooCursor cursor = metadataDao.query(Query.select( + ProducteevTask.ID, ProducteevTask.DASHBOARD_ID).where( + MetadataCriteria.byTaskAndwithKey(taskId, ProducteevTask.METADATA_KEY))); + try { + if(cursor.getCount() == 0) + return null; + cursor.moveToFirst(); + return new Metadata(cursor); + } finally { + cursor.close(); + } + } + + /** + * Reads task notes out of a task + */ + public TodorooCursor getTaskNotesCursor(long taskId) { + TodorooCursor cursor = metadataDao.query(Query.select(Metadata.PROPERTIES). + where(MetadataCriteria.byTaskAndwithKey(taskId, ProducteevNote.METADATA_KEY))); + return cursor; + } + + // --- list methods + + private final Map dashboardCache = + Collections.synchronizedMap(new SoftHashMap()); + + /** + * Get dashboard name by dashboard id + * @param dashboardId + * @return null if no dashboard by this id exists, otherwise dashboard name + */ + public String getDashboardName(long dashboardId) { + if(dashboardCache.containsKey(dashboardId)) + return dashboardCache.get(dashboardId); + + TodorooCursor cursor = prodDashboardDao.query(Query.select( + ProducteevDashboard.NAME).where(ProducteevDashboard.ID.eq(dashboardId))); + try { + if(cursor.getCount() == 0) { + dashboardCache.put(dashboardId, null); + return null; + } + cursor.moveToFirst(); + String name = cursor.get(ProducteevDashboard.NAME); + dashboardCache.put(dashboardId, name); + return name; + } finally { + cursor.close(); + } + } +} diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDatabase.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDatabase.java index bcddb43bd..10537a9c5 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDatabase.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDatabase.java @@ -1,76 +1,76 @@ -/* - * Copyright (c) 2009, Todoroo Inc - * All Rights Reserved - * http://www.todoroo.com - */ -package com.todoroo.astrid.producteev.sync; - -import com.todoroo.andlib.data.AbstractDatabase; -import com.todoroo.andlib.data.GenericDao; -import com.todoroo.andlib.data.Table; - -/** - * Database wrapper - * - * @author Tim Su - * - */ -@SuppressWarnings("nls") -public class ProducteevDatabase extends AbstractDatabase { - - // --- constants - - /** - * Database version number. This variable must be updated when database - * tables are updated, as it determines whether a database needs updating. - */ - public static final int VERSION = 1; - - /** - * Database name (must be unique) - */ - private static final String NAME = "producteev"; - - /** - * List of table/ If you're adding a new table, add it to this list and - * also make sure that our SQLite helper does the right thing. - */ - public static final Table[] TABLES = new Table[] { - ProducteevDashboard.TABLE, - }; - - // --- implementation - - private final GenericDao dao = new GenericDao(ProducteevDashboard.class, this); - - @Override - protected String getName() { - return NAME; - } - - @Override - protected int getVersion() { - return VERSION; - } - - @Override - public Table[] getTables() { - return TABLES; - } - - public GenericDao getDao() { - return dao; - } - - @Override - protected void onCreateTables() { - // do nothing - } - - @Override - protected boolean onUpgrade(int oldVersion, int newVersion) { - return false; - } - -} - +/* + * Copyright (c) 2009, Todoroo Inc + * All Rights Reserved + * http://www.todoroo.com + */ +package com.todoroo.astrid.producteev.sync; + +import com.todoroo.andlib.data.AbstractDatabase; +import com.todoroo.andlib.data.GenericDao; +import com.todoroo.andlib.data.Table; + +/** + * Database wrapper + * + * @author Tim Su + * + */ +@SuppressWarnings("nls") +public class ProducteevDatabase extends AbstractDatabase { + + // --- constants + + /** + * Database version number. This variable must be updated when database + * tables are updated, as it determines whether a database needs updating. + */ + public static final int VERSION = 1; + + /** + * Database name (must be unique) + */ + private static final String NAME = "producteev"; + + /** + * List of table/ If you're adding a new table, add it to this list and + * also make sure that our SQLite helper does the right thing. + */ + public static final Table[] TABLES = new Table[] { + ProducteevDashboard.TABLE, + }; + + // --- implementation + + private final GenericDao dao = new GenericDao(ProducteevDashboard.class, this); + + @Override + protected String getName() { + return NAME; + } + + @Override + protected int getVersion() { + return VERSION; + } + + @Override + public Table[] getTables() { + return TABLES; + } + + public GenericDao getDao() { + return dao; + } + + @Override + protected void onCreateTables() { + // do nothing + } + + @Override + protected boolean onUpgrade(int oldVersion, int newVersion) { + return false; + } + +} + diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java index 0a9b52ddd..07f591ff0 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevSyncProvider.java @@ -1,594 +1,594 @@ -/** - * See the file "LICENSE" for the full license governing this code. - */ -package com.todoroo.astrid.producteev.sync; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.app.Activity; -import android.app.Notification; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.Handler; -import android.text.TextUtils; - -import com.flurry.android.FlurryAgent; -import com.timsu.astrid.R; -import com.todoroo.andlib.data.Property; -import com.todoroo.andlib.data.TodorooCursor; -import com.todoroo.andlib.service.Autowired; -import com.todoroo.andlib.service.ContextManager; -import com.todoroo.andlib.service.DependencyInjectionService; -import com.todoroo.andlib.service.ExceptionService; -import com.todoroo.andlib.utility.AndroidUtilities; -import com.todoroo.andlib.utility.DateUtilities; -import com.todoroo.andlib.utility.DialogUtilities; -import com.todoroo.astrid.api.TaskContainer; -import com.todoroo.astrid.common.SyncProvider; -import com.todoroo.astrid.model.Metadata; -import com.todoroo.astrid.model.Task; -import com.todoroo.astrid.producteev.ProducteevLoginActivity; -import com.todoroo.astrid.producteev.ProducteevPreferences; -import com.todoroo.astrid.producteev.ProducteevUtilities; -import com.todoroo.astrid.producteev.ProducteevLoginActivity.SyncLoginCallback; -import com.todoroo.astrid.producteev.api.ApiResponseParseException; -import com.todoroo.astrid.producteev.api.ApiServiceException; -import com.todoroo.astrid.producteev.api.ApiUtilities; -import com.todoroo.astrid.producteev.api.ProducteevInvoker; -import com.todoroo.astrid.rmilk.api.ServiceInternalException; -import com.todoroo.astrid.rmilk.data.MilkNote; -import com.todoroo.astrid.service.AstridDependencyInjector; -import com.todoroo.astrid.tags.TagService; -import com.todoroo.astrid.utility.Preferences; - -@SuppressWarnings("nls") -public class ProducteevSyncProvider extends SyncProvider { - - private ProducteevDataService dataService = null; - private ProducteevInvoker invoker = null; - private long defaultDashboard; - private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE; - - /** map of producteev labels to id's */ - private final HashMap labelMap = new HashMap(); - - static { - AstridDependencyInjector.initialize(); - } - - @Autowired - protected ExceptionService exceptionService; - - @Autowired - protected DialogUtilities dialogUtilities; - - public ProducteevSyncProvider() { - super(); - DependencyInjectionService.getInstance().inject(this); - } - - // ---------------------------------------------------------------------- - // ------------------------------------------------------- public methods - // ---------------------------------------------------------------------- - - /** - * Sign out of service, deleting all synchronization metadata - */ - public void signOut() { - preferences.setToken(null); - preferences.clearLastSyncDate(); - - dataService = ProducteevDataService.getInstance(); - dataService.clearMetadata(); - } - - // ---------------------------------------------------------------------- - // ------------------------------------------------------- authentication - // ---------------------------------------------------------------------- - - /** - * Deal with a synchronization exception. If requested, will show an error - * to the user (unless synchronization is happening in background) - * - * @param context - * @param tag - * error tag - * @param e - * exception - * @param showError - * whether to display a dialog - */ - @Override - protected void handleException(String tag, Exception e, boolean showError) { - preferences.setLastError(e.toString()); - - // occurs when application was closed - if(e instanceof IllegalStateException) { - exceptionService.reportError(tag + "-caught", e); //$NON-NLS-1$ - - // occurs when network error - } else if(!(e instanceof ApiServiceException) && e instanceof IOException) { - exceptionService.reportError(tag + "-ioexception", e); //$NON-NLS-1$ - if(showError) { - Context context = ContextManager.getContext(); - showError(context, e, context.getString(R.string.producteev_ioerror)); - } - } else { - exceptionService.reportError(tag + "-unhandled", e); //$NON-NLS-1$ - if(showError) { - Context context = ContextManager.getContext(); - showError(context, e, null); - } - } - } - - @Override - protected void initiate(Context context) { - dataService = ProducteevDataService.getInstance(); - - // authenticate the user. this will automatically call the next step - authenticate(); - } - - /** - * Perform authentication with RTM. Will open the SyncBrowser if necessary - */ - private void authenticate() { - FlurryAgent.onEvent("producteev-started"); - - preferences.recordSyncStart(); - - try { - String authToken = preferences.getToken(); - - String z = stripslashes(0, "71o3346pr40o5o4nt4n7t6n287t4op28","2"); - String v = stripslashes(2, "9641n76n9s1736q1578q1o1337q19233","4ae"); - invoker = new ProducteevInvoker(z, v); - - String email = Preferences.getStringValue(R.string.producteev_PPr_email); - String password = Preferences.getStringValue(R.string.producteev_PPr_password); - - // check if we have a token & it works - if(authToken != null) { - invoker.setCredentials(authToken, email, password); - performSync(); - } else { - if (email == null && password == null) { - // display login-activity - final Context context = ContextManager.getContext(); - Intent intent = new Intent(context, ProducteevLoginActivity.class); - ProducteevLoginActivity.setCallback(new SyncLoginCallback() { - public String verifyLogin(final Handler syncLoginHandler, String em, String pass) { - try { - invoker.authenticate(em, pass); - preferences.setToken(invoker.getToken()); - Preferences.setString(R.string.producteev_PPr_email, em); - Preferences.setString(R.string.producteev_PPr_password, pass); - performSync(); - return null; - } catch (Exception e) { - // didn't work - exceptionService.reportError("producteev-verify-login", e); - if(e instanceof ServiceInternalException) - e = ((ServiceInternalException)e).getEnclosedException(); - return context.getString(R.string.producteev_ioerror, e.getMessage()); - } - } - }); - if(context instanceof Activity) - ((Activity)context).startActivityForResult(intent, 0); - else { - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - } else { - invoker.authenticate(email, password); - preferences.setToken(invoker.getToken()); - performSync(); - } - } - } catch (IllegalStateException e) { - // occurs when application was closed - } catch (Exception e) { - handleException("pdv-authenticate", e, true); - } finally { - preferences.stopOngoing(); - } - } - - // ---------------------------------------------------------------------- - // ----------------------------------------------------- synchronization! - // ---------------------------------------------------------------------- - - protected void performSync() { - try { - // load user information - JSONObject user = invoker.usersView(null); - defaultDashboard = user.getJSONObject("user").getLong("default_dashboard"); - - // get labels - JSONArray labels = invoker.labelsShowList(defaultDashboard, null); - readLabels(labels); - - // read all tasks - String lastServerSync = preferences.getLastServerSync(); - if(lastServerSync != null) - lastServerSync = lastServerSync.substring(0, lastServerSync.lastIndexOf(' ')); - JSONArray tasks = invoker.tasksShowList(defaultDashboard, lastServerSync); - - SyncData syncData = populateSyncData(tasks); - try { - synchronizeTasks(syncData); - } finally { - syncData.localCreated.close(); - syncData.localUpdated.close(); - } - - preferences.setLastServerSync(invoker.time()); - preferences.recordSuccessfulSync(); - - FlurryAgent.onEvent("pdv-sync-finished"); //$NON-NLS-1$ - } catch (IllegalStateException e) { - // occurs when application was closed - } catch (Exception e) { - handleException("pdv-sync", e, true); //$NON-NLS-1$ - } - } - - // ---------------------------------------------------------------------- - // ------------------------------------------------------------ sync data - // ---------------------------------------------------------------------- - - // all synchronized properties - private static final Property[] PROPERTIES = new Property[] { - Task.ID, - Task.TITLE, - Task.IMPORTANCE, - Task.DUE_DATE, - Task.CREATION_DATE, - Task.COMPLETION_DATE, - Task.DELETION_DATE, - Task.REMINDER_FLAGS, - Task.NOTES, - }; - - /** - * Populate SyncData data structure - * @throws JSONException - */ - private SyncData populateSyncData(JSONArray tasks) throws JSONException { - // fetch locally created tasks - TodorooCursor localCreated = dataService.getLocallyCreated(PROPERTIES); - - // fetch locally updated tasks - TodorooCursor localUpdated = dataService.getLocallyUpdated(PROPERTIES); - - // read json response - ArrayList remoteTasks = new ArrayList(tasks.length()); - for(int i = 0; i < tasks.length(); i++) { - ProducteevTaskContainer remote = parseRemoteTask(tasks.getJSONObject(i)); - dataService.findLocalMatch(remote); - remoteTasks.add(remote); - } - - return new SyncData(remoteTasks, localCreated, localUpdated); - } - - // ---------------------------------------------------------------------- - // ------------------------------------------------- create / push / pull - // ---------------------------------------------------------------------- - - @Override - protected void create(ProducteevTaskContainer local) throws IOException { - Task localTask = local.task; - long dashboard = defaultDashboard; - if(local.pdvTask.containsNonNullValue(ProducteevTask.DASHBOARD_ID)) - dashboard = local.pdvTask.getValue(ProducteevTask.DASHBOARD_ID); - JSONObject response = invoker.tasksCreate(localTask.getValue(Task.TITLE), - null, dashboard, createDeadline(localTask), createReminder(localTask), - localTask.isCompleted() ? 2 : 1, createStars(localTask)); - ProducteevTaskContainer newRemoteTask; - try { - newRemoteTask = parseRemoteTask(response); - } catch (JSONException e) { - throw new ApiResponseParseException(e); - } - transferIdentifiers(newRemoteTask, local); - push(local, newRemoteTask); - } - - /** Create a task container for the given RtmTaskSeries - * @throws JSONException */ - private ProducteevTaskContainer parseRemoteTask(JSONObject remoteTask) throws JSONException { - Task task = new Task(); - ArrayList metadata = new ArrayList(); - - if(remoteTask.has("task")) - remoteTask = remoteTask.getJSONObject("task"); - - task.setValue(Task.TITLE, ApiUtilities.decode(remoteTask.getString("title"))); - task.setValue(Task.CREATION_DATE, ApiUtilities.producteevToUnixTime(remoteTask.getString("time_created"), 0)); - task.setValue(Task.COMPLETION_DATE, remoteTask.getInt("status") == 2 ? DateUtilities.now() : 0); - task.setValue(Task.DELETION_DATE, remoteTask.getInt("deleted") == 1 ? DateUtilities.now() : 0); - - long dueDate = ApiUtilities.producteevToUnixTime(remoteTask.getString("deadline"), 0); - task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, dueDate)); - task.setValue(Task.IMPORTANCE, 5 - remoteTask.getInt("star")); - - JSONArray labels = remoteTask.getJSONArray("labels"); - for(int i = 0; i < labels.length(); i++) { - JSONObject label = labels.getJSONObject(i).getJSONObject("label"); - if(label.getInt("deleted") != 0) - continue; - - Metadata tagData = new Metadata(); - tagData.setValue(Metadata.KEY, TagService.KEY); - tagData.setValue(TagService.TAG, ApiUtilities.decode(label.getString("title"))); - metadata.add(tagData); - } - - JSONArray notes = remoteTask.getJSONArray("notes"); - for(int i = notes.length() - 1; i >= 0; i--) { - JSONObject note = notes.getJSONObject(i).getJSONObject("note"); - metadata.add(ProducteevNote.create(note)); - } - - ProducteevTaskContainer container = new ProducteevTaskContainer(task, metadata, remoteTask); - - return container; - } - - @Override - protected ProducteevTaskContainer pull(ProducteevTaskContainer task) throws IOException { - if(!task.pdvTask.containsNonNullValue(ProducteevTask.ID)) - throw new ApiServiceException("Tried to read an invalid task"); //$NON-NLS-1$ - - JSONObject remote = invoker.tasksView(task.pdvTask.getValue(ProducteevTask.ID)); - try { - return parseRemoteTask(remote); - } catch (JSONException e) { - throw new ApiResponseParseException(e); - } - } - - /** - * Send changes for the given Task across the wire. If a remoteTask is - * supplied, we attempt to intelligently only transmit the values that - * have changed. - */ - @Override - protected void push(ProducteevTaskContainer local, ProducteevTaskContainer remote) throws IOException { - boolean remerge = false; - - // fetch remote task for comparison - if(remote == null) - remote = pull(local); - - long idTask = local.pdvTask.getValue(ProducteevTask.ID); - - // either delete or re-create if necessary - if(shouldTransmit(local, Task.DELETION_DATE, remote)) { - if(local.task.getValue(Task.DELETION_DATE) > 0) - invoker.tasksDelete(idTask); - else - create(local); - } - - if(shouldTransmit(local, Task.TITLE, remote)) - invoker.tasksSetTitle(idTask, local.task.getValue(Task.TITLE)); - if(shouldTransmit(local, Task.IMPORTANCE, remote)) - invoker.tasksSetStar(idTask, createStars(local.task)); - if(shouldTransmit(local, Task.DUE_DATE, remote)) - invoker.tasksSetDeadline(idTask, createDeadline(local.task)); - if(shouldTransmit(local, Task.COMPLETION_DATE, remote)) - invoker.tasksSetStatus(idTask, local.task.isCompleted() ? 2 : 1); - - // tags - HashSet localTags = new HashSet(); - HashSet remoteTags = new HashSet(); - for(Metadata item : local.metadata) - if(TagService.KEY.equals(item.getValue(Metadata.KEY))) - localTags.add(item.getValue(TagService.TAG)); - if(remote != null && remote.metadata != null) { - for(Metadata item : remote.metadata) - if(TagService.KEY.equals(item.getValue(Metadata.KEY))) - remoteTags.add(item.getValue(TagService.TAG)); - } - - try { - if(!localTags.equals(remoteTags)) { - HashSet toAdd = new HashSet(localTags); - toAdd.removeAll(remoteTags); - HashSet toRemove = remoteTags; - toRemove.removeAll(localTags); - - if(toAdd.size() > 0) { - for(String label : toAdd) { - if(!labelMap.containsKey(label)) { - JSONObject result = invoker.labelsCreate(defaultDashboard, label).getJSONObject("label"); - labelMap.put(ApiUtilities.decode(result.getString("title")), result.getLong("id_label")); - } - invoker.tasksSetLabel(idTask, labelMap.get(label)); - } - } - - if(toRemove.size() > 0) { - for(String label : toRemove) { - if(!labelMap.containsKey(label)) - continue; - invoker.tasksUnsetLabel(idTask, labelMap.get(label)); - } - } - } - - // notes - if(!TextUtils.isEmpty(local.task.getValue(Task.NOTES))) { - String note = local.task.getValue(Task.NOTES); - JSONObject result = invoker.tasksNoteCreate(idTask, note); - local.metadata.add(ProducteevNote.create(result.getJSONObject("note"))); - local.task.setValue(Task.NOTES, ""); - } - - // milk note => producteev note - if(local.findMetadata(MilkNote.METADATA_KEY) != null && (remote == null || - (remote.findMetadata(ProducteevNote.METADATA_KEY) == null))) { - for(Metadata item : local.metadata) - if(MilkNote.METADATA_KEY.equals(item.getValue(Metadata.KEY))) { - String message = MilkNote.toTaskDetail(item); - JSONObject result = invoker.tasksNoteCreate(idTask, message); - local.metadata.add(ProducteevNote.create(result.getJSONObject("note"))); - } - } - } catch (JSONException e) { - throw new ApiResponseParseException(e); - } - - if(remerge) { - remote = pull(local); - remote.task.setId(local.task.getId()); - write(remote); - } - } - - - // ---------------------------------------------------------------------- - // --------------------------------------------------------- read / write - // ---------------------------------------------------------------------- - - @Override - protected ProducteevTaskContainer read(TodorooCursor cursor) throws IOException { - return dataService.readTaskAndMetadata(cursor); - } - - @Override - protected void write(ProducteevTaskContainer task) throws IOException { - dataService.saveTaskAndMetadata(task); - } - - // ---------------------------------------------------------------------- - // --------------------------------------------------------- misc helpers - // ---------------------------------------------------------------------- - - @Override - protected int matchTask(ArrayList tasks, ProducteevTaskContainer target) { - int length = tasks.size(); - for(int i = 0; i < length; i++) { - ProducteevTaskContainer task = tasks.get(i); - if(AndroidUtilities.equals(task.pdvTask, target.pdvTask)) - return i; - } - return -1; - } - - /** - * get stars in producteev format - * @param local - * @return - */ - private Integer createStars(Task local) { - return 5 - local.getValue(Task.IMPORTANCE); - } - - /** - * get reminder in producteev format - * @param local - * @return - */ - private Integer createReminder(Task local) { - if(local.getFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AT_DEADLINE)) - return 8; - return null; - } - - /** - * get deadline in producteev format - * @param task - * @return - */ - private String createDeadline(Task task) { - if(!task.hasDueDate()) - return null; - if(!task.hasDueTime()) - return ApiUtilities.unixDateToProducteev(task.getValue(Task.DUE_DATE)); - String time = ApiUtilities.unixTimeToProducteev(task.getValue(Task.DUE_DATE)); - return time.substring(0, time.lastIndexOf(' ')); - } - - /** - * Determine whether this task's property should be transmitted - * @param task task to consider - * @param property property to consider - * @param remoteTask remote task proxy - * @return - */ - private boolean shouldTransmit(TaskContainer task, Property property, TaskContainer remoteTask) { - if(!task.task.containsValue(property)) - return false; - - if(remoteTask == null) - return true; - if(!remoteTask.task.containsValue(property)) - return true; - - // special cases - match if they're zero or nonzero - if(property == Task.COMPLETION_DATE || - property == Task.DELETION_DATE) - return !AndroidUtilities.equals((Long)task.task.getValue(property) == 0, - (Long)remoteTask.task.getValue(property) == 0); - - return !AndroidUtilities.equals(task.task.getValue(property), - remoteTask.task.getValue(property)); - } - - @Override - protected void updateNotification(Context context, Notification notification) { - String notificationTitle = context.getString(R.string.producteev_notification_title); - Intent intent = new Intent(context, ProducteevPreferences.class); - PendingIntent notificationIntent = PendingIntent.getActivity(context, 0, - intent, 0); - notification.setLatestEventInfo(context, - notificationTitle, context.getString(R.string.SyP_progress), - notificationIntent); - return ; - } - - @Override - protected void transferIdentifiers(ProducteevTaskContainer source, - ProducteevTaskContainer destination) { - destination.pdvTask = source.pdvTask; - } - - - /** - * Read labels into label map - * @throws JSONException - * @throws ApiServiceException - * @throws IOException - */ - private void readLabels(JSONArray labels) throws JSONException, ApiServiceException, IOException { - for(int i = 0; i < labels.length(); i++) { - JSONObject label = labels.getJSONObject(i).getJSONObject("label"); - labelMap.put(ApiUtilities.decode(label.getString("title")), label.getLong("id_label")); - } - } - - // ---------------------------------------------------------------------- - // ------------------------------------------------------- helper methods - // ---------------------------------------------------------------------- - - private static final String stripslashes(int ____,String __,String ___) { - int _=__.charAt(____/92);_=_==116?_-1:_;_=((_>=97)&&(_<=123)? - ((_-83)%27+97):_);return TextUtils.htmlEncode(____==31?___: - stripslashes(____+1,__.substring(1),___+((char)_))); - } - - -} +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.producteev.sync; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.Activity; +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.text.TextUtils; + +import com.flurry.android.FlurryAgent; +import com.timsu.astrid.R; +import com.todoroo.andlib.data.Property; +import com.todoroo.andlib.data.TodorooCursor; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.ContextManager; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.service.ExceptionService; +import com.todoroo.andlib.utility.AndroidUtilities; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.andlib.utility.DialogUtilities; +import com.todoroo.astrid.api.TaskContainer; +import com.todoroo.astrid.common.SyncProvider; +import com.todoroo.astrid.model.Metadata; +import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.producteev.ProducteevLoginActivity; +import com.todoroo.astrid.producteev.ProducteevPreferences; +import com.todoroo.astrid.producteev.ProducteevUtilities; +import com.todoroo.astrid.producteev.ProducteevLoginActivity.SyncLoginCallback; +import com.todoroo.astrid.producteev.api.ApiResponseParseException; +import com.todoroo.astrid.producteev.api.ApiServiceException; +import com.todoroo.astrid.producteev.api.ApiUtilities; +import com.todoroo.astrid.producteev.api.ProducteevInvoker; +import com.todoroo.astrid.rmilk.api.ServiceInternalException; +import com.todoroo.astrid.rmilk.data.MilkNote; +import com.todoroo.astrid.service.AstridDependencyInjector; +import com.todoroo.astrid.tags.TagService; +import com.todoroo.astrid.utility.Preferences; + +@SuppressWarnings("nls") +public class ProducteevSyncProvider extends SyncProvider { + + private ProducteevDataService dataService = null; + private ProducteevInvoker invoker = null; + private long defaultDashboard; + private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE; + + /** map of producteev labels to id's */ + private final HashMap labelMap = new HashMap(); + + static { + AstridDependencyInjector.initialize(); + } + + @Autowired + protected ExceptionService exceptionService; + + @Autowired + protected DialogUtilities dialogUtilities; + + public ProducteevSyncProvider() { + super(); + DependencyInjectionService.getInstance().inject(this); + } + + // ---------------------------------------------------------------------- + // ------------------------------------------------------- public methods + // ---------------------------------------------------------------------- + + /** + * Sign out of service, deleting all synchronization metadata + */ + public void signOut() { + preferences.setToken(null); + preferences.clearLastSyncDate(); + + dataService = ProducteevDataService.getInstance(); + dataService.clearMetadata(); + } + + // ---------------------------------------------------------------------- + // ------------------------------------------------------- authentication + // ---------------------------------------------------------------------- + + /** + * Deal with a synchronization exception. If requested, will show an error + * to the user (unless synchronization is happening in background) + * + * @param context + * @param tag + * error tag + * @param e + * exception + * @param showError + * whether to display a dialog + */ + @Override + protected void handleException(String tag, Exception e, boolean showError) { + preferences.setLastError(e.toString()); + + // occurs when application was closed + if(e instanceof IllegalStateException) { + exceptionService.reportError(tag + "-caught", e); //$NON-NLS-1$ + + // occurs when network error + } else if(!(e instanceof ApiServiceException) && e instanceof IOException) { + exceptionService.reportError(tag + "-ioexception", e); //$NON-NLS-1$ + if(showError) { + Context context = ContextManager.getContext(); + showError(context, e, context.getString(R.string.producteev_ioerror)); + } + } else { + exceptionService.reportError(tag + "-unhandled", e); //$NON-NLS-1$ + if(showError) { + Context context = ContextManager.getContext(); + showError(context, e, null); + } + } + } + + @Override + protected void initiate(Context context) { + dataService = ProducteevDataService.getInstance(); + + // authenticate the user. this will automatically call the next step + authenticate(); + } + + /** + * Perform authentication with RTM. Will open the SyncBrowser if necessary + */ + private void authenticate() { + FlurryAgent.onEvent("producteev-started"); + + preferences.recordSyncStart(); + + try { + String authToken = preferences.getToken(); + + String z = stripslashes(0, "71o3346pr40o5o4nt4n7t6n287t4op28","2"); + String v = stripslashes(2, "9641n76n9s1736q1578q1o1337q19233","4ae"); + invoker = new ProducteevInvoker(z, v); + + String email = Preferences.getStringValue(R.string.producteev_PPr_email); + String password = Preferences.getStringValue(R.string.producteev_PPr_password); + + // check if we have a token & it works + if(authToken != null) { + invoker.setCredentials(authToken, email, password); + performSync(); + } else { + if (email == null && password == null) { + // display login-activity + final Context context = ContextManager.getContext(); + Intent intent = new Intent(context, ProducteevLoginActivity.class); + ProducteevLoginActivity.setCallback(new SyncLoginCallback() { + public String verifyLogin(final Handler syncLoginHandler, String em, String pass) { + try { + invoker.authenticate(em, pass); + preferences.setToken(invoker.getToken()); + Preferences.setString(R.string.producteev_PPr_email, em); + Preferences.setString(R.string.producteev_PPr_password, pass); + performSync(); + return null; + } catch (Exception e) { + // didn't work + exceptionService.reportError("producteev-verify-login", e); + if(e instanceof ServiceInternalException) + e = ((ServiceInternalException)e).getEnclosedException(); + return context.getString(R.string.producteev_ioerror, e.getMessage()); + } + } + }); + if(context instanceof Activity) + ((Activity)context).startActivityForResult(intent, 0); + else { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + } else { + invoker.authenticate(email, password); + preferences.setToken(invoker.getToken()); + performSync(); + } + } + } catch (IllegalStateException e) { + // occurs when application was closed + } catch (Exception e) { + handleException("pdv-authenticate", e, true); + } finally { + preferences.stopOngoing(); + } + } + + // ---------------------------------------------------------------------- + // ----------------------------------------------------- synchronization! + // ---------------------------------------------------------------------- + + protected void performSync() { + try { + // load user information + JSONObject user = invoker.usersView(null); + defaultDashboard = user.getJSONObject("user").getLong("default_dashboard"); + + // get labels + JSONArray labels = invoker.labelsShowList(defaultDashboard, null); + readLabels(labels); + + // read all tasks + String lastServerSync = preferences.getLastServerSync(); + if(lastServerSync != null) + lastServerSync = lastServerSync.substring(0, lastServerSync.lastIndexOf(' ')); + JSONArray tasks = invoker.tasksShowList(defaultDashboard, lastServerSync); + + SyncData syncData = populateSyncData(tasks); + try { + synchronizeTasks(syncData); + } finally { + syncData.localCreated.close(); + syncData.localUpdated.close(); + } + + preferences.setLastServerSync(invoker.time()); + preferences.recordSuccessfulSync(); + + FlurryAgent.onEvent("pdv-sync-finished"); //$NON-NLS-1$ + } catch (IllegalStateException e) { + // occurs when application was closed + } catch (Exception e) { + handleException("pdv-sync", e, true); //$NON-NLS-1$ + } + } + + // ---------------------------------------------------------------------- + // ------------------------------------------------------------ sync data + // ---------------------------------------------------------------------- + + // all synchronized properties + private static final Property[] PROPERTIES = new Property[] { + Task.ID, + Task.TITLE, + Task.IMPORTANCE, + Task.DUE_DATE, + Task.CREATION_DATE, + Task.COMPLETION_DATE, + Task.DELETION_DATE, + Task.REMINDER_FLAGS, + Task.NOTES, + }; + + /** + * Populate SyncData data structure + * @throws JSONException + */ + private SyncData populateSyncData(JSONArray tasks) throws JSONException { + // fetch locally created tasks + TodorooCursor localCreated = dataService.getLocallyCreated(PROPERTIES); + + // fetch locally updated tasks + TodorooCursor localUpdated = dataService.getLocallyUpdated(PROPERTIES); + + // read json response + ArrayList remoteTasks = new ArrayList(tasks.length()); + for(int i = 0; i < tasks.length(); i++) { + ProducteevTaskContainer remote = parseRemoteTask(tasks.getJSONObject(i)); + dataService.findLocalMatch(remote); + remoteTasks.add(remote); + } + + return new SyncData(remoteTasks, localCreated, localUpdated); + } + + // ---------------------------------------------------------------------- + // ------------------------------------------------- create / push / pull + // ---------------------------------------------------------------------- + + @Override + protected void create(ProducteevTaskContainer local) throws IOException { + Task localTask = local.task; + long dashboard = defaultDashboard; + if(local.pdvTask.containsNonNullValue(ProducteevTask.DASHBOARD_ID)) + dashboard = local.pdvTask.getValue(ProducteevTask.DASHBOARD_ID); + JSONObject response = invoker.tasksCreate(localTask.getValue(Task.TITLE), + null, dashboard, createDeadline(localTask), createReminder(localTask), + localTask.isCompleted() ? 2 : 1, createStars(localTask)); + ProducteevTaskContainer newRemoteTask; + try { + newRemoteTask = parseRemoteTask(response); + } catch (JSONException e) { + throw new ApiResponseParseException(e); + } + transferIdentifiers(newRemoteTask, local); + push(local, newRemoteTask); + } + + /** Create a task container for the given RtmTaskSeries + * @throws JSONException */ + private ProducteevTaskContainer parseRemoteTask(JSONObject remoteTask) throws JSONException { + Task task = new Task(); + ArrayList metadata = new ArrayList(); + + if(remoteTask.has("task")) + remoteTask = remoteTask.getJSONObject("task"); + + task.setValue(Task.TITLE, ApiUtilities.decode(remoteTask.getString("title"))); + task.setValue(Task.CREATION_DATE, ApiUtilities.producteevToUnixTime(remoteTask.getString("time_created"), 0)); + task.setValue(Task.COMPLETION_DATE, remoteTask.getInt("status") == 2 ? DateUtilities.now() : 0); + task.setValue(Task.DELETION_DATE, remoteTask.getInt("deleted") == 1 ? DateUtilities.now() : 0); + + long dueDate = ApiUtilities.producteevToUnixTime(remoteTask.getString("deadline"), 0); + task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, dueDate)); + task.setValue(Task.IMPORTANCE, 5 - remoteTask.getInt("star")); + + JSONArray labels = remoteTask.getJSONArray("labels"); + for(int i = 0; i < labels.length(); i++) { + JSONObject label = labels.getJSONObject(i).getJSONObject("label"); + if(label.getInt("deleted") != 0) + continue; + + Metadata tagData = new Metadata(); + tagData.setValue(Metadata.KEY, TagService.KEY); + tagData.setValue(TagService.TAG, ApiUtilities.decode(label.getString("title"))); + metadata.add(tagData); + } + + JSONArray notes = remoteTask.getJSONArray("notes"); + for(int i = notes.length() - 1; i >= 0; i--) { + JSONObject note = notes.getJSONObject(i).getJSONObject("note"); + metadata.add(ProducteevNote.create(note)); + } + + ProducteevTaskContainer container = new ProducteevTaskContainer(task, metadata, remoteTask); + + return container; + } + + @Override + protected ProducteevTaskContainer pull(ProducteevTaskContainer task) throws IOException { + if(!task.pdvTask.containsNonNullValue(ProducteevTask.ID)) + throw new ApiServiceException("Tried to read an invalid task"); //$NON-NLS-1$ + + JSONObject remote = invoker.tasksView(task.pdvTask.getValue(ProducteevTask.ID)); + try { + return parseRemoteTask(remote); + } catch (JSONException e) { + throw new ApiResponseParseException(e); + } + } + + /** + * Send changes for the given Task across the wire. If a remoteTask is + * supplied, we attempt to intelligently only transmit the values that + * have changed. + */ + @Override + protected void push(ProducteevTaskContainer local, ProducteevTaskContainer remote) throws IOException { + boolean remerge = false; + + // fetch remote task for comparison + if(remote == null) + remote = pull(local); + + long idTask = local.pdvTask.getValue(ProducteevTask.ID); + + // either delete or re-create if necessary + if(shouldTransmit(local, Task.DELETION_DATE, remote)) { + if(local.task.getValue(Task.DELETION_DATE) > 0) + invoker.tasksDelete(idTask); + else + create(local); + } + + if(shouldTransmit(local, Task.TITLE, remote)) + invoker.tasksSetTitle(idTask, local.task.getValue(Task.TITLE)); + if(shouldTransmit(local, Task.IMPORTANCE, remote)) + invoker.tasksSetStar(idTask, createStars(local.task)); + if(shouldTransmit(local, Task.DUE_DATE, remote)) + invoker.tasksSetDeadline(idTask, createDeadline(local.task)); + if(shouldTransmit(local, Task.COMPLETION_DATE, remote)) + invoker.tasksSetStatus(idTask, local.task.isCompleted() ? 2 : 1); + + // tags + HashSet localTags = new HashSet(); + HashSet remoteTags = new HashSet(); + for(Metadata item : local.metadata) + if(TagService.KEY.equals(item.getValue(Metadata.KEY))) + localTags.add(item.getValue(TagService.TAG)); + if(remote != null && remote.metadata != null) { + for(Metadata item : remote.metadata) + if(TagService.KEY.equals(item.getValue(Metadata.KEY))) + remoteTags.add(item.getValue(TagService.TAG)); + } + + try { + if(!localTags.equals(remoteTags)) { + HashSet toAdd = new HashSet(localTags); + toAdd.removeAll(remoteTags); + HashSet toRemove = remoteTags; + toRemove.removeAll(localTags); + + if(toAdd.size() > 0) { + for(String label : toAdd) { + if(!labelMap.containsKey(label)) { + JSONObject result = invoker.labelsCreate(defaultDashboard, label).getJSONObject("label"); + labelMap.put(ApiUtilities.decode(result.getString("title")), result.getLong("id_label")); + } + invoker.tasksSetLabel(idTask, labelMap.get(label)); + } + } + + if(toRemove.size() > 0) { + for(String label : toRemove) { + if(!labelMap.containsKey(label)) + continue; + invoker.tasksUnsetLabel(idTask, labelMap.get(label)); + } + } + } + + // notes + if(!TextUtils.isEmpty(local.task.getValue(Task.NOTES))) { + String note = local.task.getValue(Task.NOTES); + JSONObject result = invoker.tasksNoteCreate(idTask, note); + local.metadata.add(ProducteevNote.create(result.getJSONObject("note"))); + local.task.setValue(Task.NOTES, ""); + } + + // milk note => producteev note + if(local.findMetadata(MilkNote.METADATA_KEY) != null && (remote == null || + (remote.findMetadata(ProducteevNote.METADATA_KEY) == null))) { + for(Metadata item : local.metadata) + if(MilkNote.METADATA_KEY.equals(item.getValue(Metadata.KEY))) { + String message = MilkNote.toTaskDetail(item); + JSONObject result = invoker.tasksNoteCreate(idTask, message); + local.metadata.add(ProducteevNote.create(result.getJSONObject("note"))); + } + } + } catch (JSONException e) { + throw new ApiResponseParseException(e); + } + + if(remerge) { + remote = pull(local); + remote.task.setId(local.task.getId()); + write(remote); + } + } + + + // ---------------------------------------------------------------------- + // --------------------------------------------------------- read / write + // ---------------------------------------------------------------------- + + @Override + protected ProducteevTaskContainer read(TodorooCursor cursor) throws IOException { + return dataService.readTaskAndMetadata(cursor); + } + + @Override + protected void write(ProducteevTaskContainer task) throws IOException { + dataService.saveTaskAndMetadata(task); + } + + // ---------------------------------------------------------------------- + // --------------------------------------------------------- misc helpers + // ---------------------------------------------------------------------- + + @Override + protected int matchTask(ArrayList tasks, ProducteevTaskContainer target) { + int length = tasks.size(); + for(int i = 0; i < length; i++) { + ProducteevTaskContainer task = tasks.get(i); + if(AndroidUtilities.equals(task.pdvTask, target.pdvTask)) + return i; + } + return -1; + } + + /** + * get stars in producteev format + * @param local + * @return + */ + private Integer createStars(Task local) { + return 5 - local.getValue(Task.IMPORTANCE); + } + + /** + * get reminder in producteev format + * @param local + * @return + */ + private Integer createReminder(Task local) { + if(local.getFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AT_DEADLINE)) + return 8; + return null; + } + + /** + * get deadline in producteev format + * @param task + * @return + */ + private String createDeadline(Task task) { + if(!task.hasDueDate()) + return null; + if(!task.hasDueTime()) + return ApiUtilities.unixDateToProducteev(task.getValue(Task.DUE_DATE)); + String time = ApiUtilities.unixTimeToProducteev(task.getValue(Task.DUE_DATE)); + return time.substring(0, time.lastIndexOf(' ')); + } + + /** + * Determine whether this task's property should be transmitted + * @param task task to consider + * @param property property to consider + * @param remoteTask remote task proxy + * @return + */ + private boolean shouldTransmit(TaskContainer task, Property property, TaskContainer remoteTask) { + if(!task.task.containsValue(property)) + return false; + + if(remoteTask == null) + return true; + if(!remoteTask.task.containsValue(property)) + return true; + + // special cases - match if they're zero or nonzero + if(property == Task.COMPLETION_DATE || + property == Task.DELETION_DATE) + return !AndroidUtilities.equals((Long)task.task.getValue(property) == 0, + (Long)remoteTask.task.getValue(property) == 0); + + return !AndroidUtilities.equals(task.task.getValue(property), + remoteTask.task.getValue(property)); + } + + @Override + protected void updateNotification(Context context, Notification notification) { + String notificationTitle = context.getString(R.string.producteev_notification_title); + Intent intent = new Intent(context, ProducteevPreferences.class); + PendingIntent notificationIntent = PendingIntent.getActivity(context, 0, + intent, 0); + notification.setLatestEventInfo(context, + notificationTitle, context.getString(R.string.SyP_progress), + notificationIntent); + return ; + } + + @Override + protected void transferIdentifiers(ProducteevTaskContainer source, + ProducteevTaskContainer destination) { + destination.pdvTask = source.pdvTask; + } + + + /** + * Read labels into label map + * @throws JSONException + * @throws ApiServiceException + * @throws IOException + */ + private void readLabels(JSONArray labels) throws JSONException, ApiServiceException, IOException { + for(int i = 0; i < labels.length(); i++) { + JSONObject label = labels.getJSONObject(i).getJSONObject("label"); + labelMap.put(ApiUtilities.decode(label.getString("title")), label.getLong("id_label")); + } + } + + // ---------------------------------------------------------------------- + // ------------------------------------------------------- helper methods + // ---------------------------------------------------------------------- + + private static final String stripslashes(int ____,String __,String ___) { + int _=__.charAt(____/92);_=_==116?_-1:_;_=((_>=97)&&(_<=123)? + ((_-83)%27+97):_);return TextUtils.htmlEncode(____==31?___: + stripslashes(____+1,__.substring(1),___+((char)_))); + } + + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevTask.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevTask.java index 058770195..bb6b0d140 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevTask.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevTask.java @@ -1,24 +1,24 @@ -package com.todoroo.astrid.producteev.sync; - -import com.todoroo.andlib.data.Property.LongProperty; -import com.todoroo.astrid.model.Metadata; - -/** - * Metadata entries for a Producteev Task - * @author Tim Su - * - */ -public class ProducteevTask { - - /** metadata key */ - public static final String METADATA_KEY = "producteev"; //$NON-NLS-1$ - - /** task id in producteev */ - public static final LongProperty ID = new LongProperty(Metadata.TABLE, - Metadata.VALUE1.name); - - /** dashboard id */ - public static final LongProperty DASHBOARD_ID = new LongProperty(Metadata.TABLE, - Metadata.VALUE2.name); - -} +package com.todoroo.astrid.producteev.sync; + +import com.todoroo.andlib.data.Property.LongProperty; +import com.todoroo.astrid.model.Metadata; + +/** + * Metadata entries for a Producteev Task + * @author Tim Su + * + */ +public class ProducteevTask { + + /** metadata key */ + public static final String METADATA_KEY = "producteev"; //$NON-NLS-1$ + + /** task id in producteev */ + public static final LongProperty ID = new LongProperty(Metadata.TABLE, + Metadata.VALUE1.name); + + /** dashboard id */ + public static final LongProperty DASHBOARD_ID = new LongProperty(Metadata.TABLE, + Metadata.VALUE2.name); + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java b/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java index 6d38021dd..97255c878 100644 --- a/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java @@ -1,149 +1,149 @@ -/* - * ASTRID: Android's Simple Task Recording Dashboard - * - * Copyright (c) 2009 Tim Su - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package com.todoroo.astrid.reminders; - -import java.util.Date; - -import android.app.TimePickerDialog; -import android.app.TimePickerDialog.OnTimeSetListener; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; -import android.widget.TimePicker; - -import com.timsu.astrid.R; -import com.todoroo.andlib.sql.QueryTemplate; -import com.todoroo.andlib.utility.DateUtilities; -import com.todoroo.astrid.activity.TaskListActivity; -import com.todoroo.astrid.api.Filter; -import com.todoroo.astrid.dao.TaskDao.TaskCriteria; -import com.todoroo.astrid.utility.Preferences; - -/** - * This activity is launched when a user opens up a notification from the - * tray. It launches the appropriate activity based on the passed in parameters. - * - * @author timsu - * - */ -public class NotificationActivity extends TaskListActivity implements OnTimeSetListener { - - // --- constants - - /** task id from notification */ - public static final String TOKEN_ID = "id"; //$NON-NLS-1$ - - // --- implementation - - private long taskId; - - @Override - public void onCreate(Bundle savedInstanceState) { - populateFilter(getIntent()); - super.onCreate(savedInstanceState); - - displayNotificationPopup(); - } - - @Override - protected void onNewIntent(Intent intent) { - populateFilter(intent); - super.onNewIntent(intent); - } - - private void populateFilter(Intent intent) { - taskId = intent.getLongExtra(TOKEN_ID, -1); - if(taskId == -1) - return; - - Filter itemFilter = new Filter(getString(R.string.rmd_NoA_filter), - getString(R.string.rmd_NoA_filter), - new QueryTemplate().where(TaskCriteria.byId(taskId)), - null); - intent.putExtra(TaskListActivity.TOKEN_FILTER, itemFilter); - } - - /** - * Set up the UI for this activity - */ - private void displayNotificationPopup() { - // hide quick add - findViewById(R.id.taskListFooter).setVisibility(View.GONE); - - // instantiate reminder window - ViewGroup parent = (ViewGroup) findViewById(R.id.taskListParent); - getLayoutInflater().inflate(R.layout.notification_control, parent, true); - - String reminder = Notifications.getRandomReminder(getResources().getStringArray(R.array.reminder_responses)); - - if(Preferences.getBoolean(R.string.p_rmd_nagging, true)) - ((TextView)findViewById(R.id.reminderLabel)).setText(reminder); - else { - findViewById(R.id.reminderLabel).setVisibility(View.GONE); - findViewById(R.id.astridIcon).setVisibility(View.GONE); - } - - // set up listeners - ((Button)findViewById(R.id.goAway)).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View arg0) { - finish(); - } - }); - - ((Button)findViewById(R.id.snooze)).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View arg0) { - snooze(); - } - }); - } - - /** - * Snooze and re-trigger this alarm - */ - private void snooze() { - Date now = new Date(); - now.setHours(now.getHours() + 1); - int hour = now.getHours(); - int minute = now.getMinutes(); - TimePickerDialog timePicker = new TimePickerDialog(this, this, - hour, minute, DateUtilities.is24HourFormat(this)); - timePicker.show(); - } - - /** snooze timer set */ - @Override - public void onTimeSet(TimePicker picker, int hours, int minutes) { - Date alarmTime = new Date(); - alarmTime.setHours(hours); - alarmTime.setMinutes(minutes); - if(alarmTime.getTime() < DateUtilities.now()) - alarmTime.setDate(alarmTime.getDate() + 1); - ReminderService.getInstance().scheduleSnoozeAlarm(taskId, alarmTime.getTime()); - finish(); - } - - -} +/* + * ASTRID: Android's Simple Task Recording Dashboard + * + * Copyright (c) 2009 Tim Su + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package com.todoroo.astrid.reminders; + +import java.util.Date; + +import android.app.TimePickerDialog; +import android.app.TimePickerDialog.OnTimeSetListener; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; +import android.widget.TimePicker; + +import com.timsu.astrid.R; +import com.todoroo.andlib.sql.QueryTemplate; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.activity.TaskListActivity; +import com.todoroo.astrid.api.Filter; +import com.todoroo.astrid.dao.TaskDao.TaskCriteria; +import com.todoroo.astrid.utility.Preferences; + +/** + * This activity is launched when a user opens up a notification from the + * tray. It launches the appropriate activity based on the passed in parameters. + * + * @author timsu + * + */ +public class NotificationActivity extends TaskListActivity implements OnTimeSetListener { + + // --- constants + + /** task id from notification */ + public static final String TOKEN_ID = "id"; //$NON-NLS-1$ + + // --- implementation + + private long taskId; + + @Override + public void onCreate(Bundle savedInstanceState) { + populateFilter(getIntent()); + super.onCreate(savedInstanceState); + + displayNotificationPopup(); + } + + @Override + protected void onNewIntent(Intent intent) { + populateFilter(intent); + super.onNewIntent(intent); + } + + private void populateFilter(Intent intent) { + taskId = intent.getLongExtra(TOKEN_ID, -1); + if(taskId == -1) + return; + + Filter itemFilter = new Filter(getString(R.string.rmd_NoA_filter), + getString(R.string.rmd_NoA_filter), + new QueryTemplate().where(TaskCriteria.byId(taskId)), + null); + intent.putExtra(TaskListActivity.TOKEN_FILTER, itemFilter); + } + + /** + * Set up the UI for this activity + */ + private void displayNotificationPopup() { + // hide quick add + findViewById(R.id.taskListFooter).setVisibility(View.GONE); + + // instantiate reminder window + ViewGroup parent = (ViewGroup) findViewById(R.id.taskListParent); + getLayoutInflater().inflate(R.layout.notification_control, parent, true); + + String reminder = Notifications.getRandomReminder(getResources().getStringArray(R.array.reminder_responses)); + + if(Preferences.getBoolean(R.string.p_rmd_nagging, true)) + ((TextView)findViewById(R.id.reminderLabel)).setText(reminder); + else { + findViewById(R.id.reminderLabel).setVisibility(View.GONE); + findViewById(R.id.astridIcon).setVisibility(View.GONE); + } + + // set up listeners + ((Button)findViewById(R.id.goAway)).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + finish(); + } + }); + + ((Button)findViewById(R.id.snooze)).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + snooze(); + } + }); + } + + /** + * Snooze and re-trigger this alarm + */ + private void snooze() { + Date now = new Date(); + now.setHours(now.getHours() + 1); + int hour = now.getHours(); + int minute = now.getMinutes(); + TimePickerDialog timePicker = new TimePickerDialog(this, this, + hour, minute, DateUtilities.is24HourFormat(this)); + timePicker.show(); + } + + /** snooze timer set */ + @Override + public void onTimeSet(TimePicker picker, int hours, int minutes) { + Date alarmTime = new Date(); + alarmTime.setHours(hours); + alarmTime.setMinutes(minutes); + if(alarmTime.getTime() < DateUtilities.now()) + alarmTime.setDate(alarmTime.getDate() + 1); + ReminderService.getInstance().scheduleSnoozeAlarm(taskId, alarmTime.getTime()); + finish(); + } + + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkBackgroundService.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkBackgroundService.java index a3e14bc6a..50144084a 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkBackgroundService.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkBackgroundService.java @@ -1,128 +1,128 @@ -package com.todoroo.astrid.rmilk; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.IBinder; -import android.util.Log; - -import com.timsu.astrid.R; -import com.todoroo.andlib.service.ContextManager; -import com.todoroo.andlib.utility.DateUtilities; -import com.todoroo.astrid.rmilk.sync.RTMSyncProvider; -import com.todoroo.astrid.utility.Preferences; - -/** - * SynchronizationService is the service that performs Astrid's background - * synchronization with online task managers. Starting this service - * schedules a repeating alarm which handles the synchronization - * - * @author Tim Su - * - */ -public class MilkBackgroundService extends Service { - - /** Minimum time before an auto-sync */ - private static final long AUTO_SYNC_MIN_OFFSET = 5*60*1000L; - - /** alarm identifier */ - private static final String SYNC_ACTION = "sync"; //$NON-NLS-1$ - - // --- BroadcastReceiver abstract methods - - /** Receive the alarm - start the synchronize service! */ - @Override - public void onStart(Intent intent, int startId) { - if(SYNC_ACTION.equals(intent.getAction())) - startSynchronization(this); - } - - /** Start the actual synchronization */ - private void startSynchronization(Context context) { - if(context == null || context.getResources() == null) - return; - - ContextManager.setContext(context); - - if(MilkUtilities.isOngoing()) - return; - - new RTMSyncProvider().synchronize(context); - } - - // --- alarm management - - /** - * Schedules repeating alarm for auto-synchronization - */ - public static void scheduleService() { - int syncFrequencySeconds = Preferences.getIntegerFromString( - R.string.rmilk_MPr_interval_key, -1); - Context context = ContextManager.getContext(); - if(syncFrequencySeconds <= 0) { - unscheduleService(context); - return; - } - - // figure out synchronization frequency - long interval = 1000L * syncFrequencySeconds; - long offset = computeNextSyncOffset(interval); - - // give a little padding - offset = Math.max(offset, AUTO_SYNC_MIN_OFFSET); - - AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - PendingIntent pendingIntent = PendingIntent.getService(context, 0, - createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT); - - Log.i("Astrid", "Autosync set for " + offset / 1000 //$NON-NLS-1$ //$NON-NLS-2$ - + " seconds repeating every " + syncFrequencySeconds); //$NON-NLS-1$ - - // cancel all existing - am.cancel(pendingIntent); - - // schedule new - am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + offset, - interval, pendingIntent); - } - - - /** - * Removes repeating alarm for auto-synchronization - */ - private static void unscheduleService(Context context) { - AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); - PendingIntent pendingIntent = PendingIntent.getService(context, 0, - createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT); - am.cancel(pendingIntent); - } - - /** Create the alarm intent */ - private static Intent createAlarmIntent(Context context) { - Intent intent = new Intent(context, MilkBackgroundService.class); - intent.setAction(SYNC_ACTION); - return intent; - } - - // --- utility methods - - - private static long computeNextSyncOffset(long interval) { - // figure out last synchronize time - long lastSyncDate = MilkUtilities.getLastSyncDate(); - - // if user never synchronized, give them a full offset period before bg sync - if(lastSyncDate != 0) - return Math.max(0, lastSyncDate + interval - DateUtilities.now()); - else - return interval; - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - -} +package com.todoroo.astrid.rmilk; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; + +import com.timsu.astrid.R; +import com.todoroo.andlib.service.ContextManager; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.rmilk.sync.RTMSyncProvider; +import com.todoroo.astrid.utility.Preferences; + +/** + * SynchronizationService is the service that performs Astrid's background + * synchronization with online task managers. Starting this service + * schedules a repeating alarm which handles the synchronization + * + * @author Tim Su + * + */ +public class MilkBackgroundService extends Service { + + /** Minimum time before an auto-sync */ + private static final long AUTO_SYNC_MIN_OFFSET = 5*60*1000L; + + /** alarm identifier */ + private static final String SYNC_ACTION = "sync"; //$NON-NLS-1$ + + // --- BroadcastReceiver abstract methods + + /** Receive the alarm - start the synchronize service! */ + @Override + public void onStart(Intent intent, int startId) { + if(SYNC_ACTION.equals(intent.getAction())) + startSynchronization(this); + } + + /** Start the actual synchronization */ + private void startSynchronization(Context context) { + if(context == null || context.getResources() == null) + return; + + ContextManager.setContext(context); + + if(MilkUtilities.isOngoing()) + return; + + new RTMSyncProvider().synchronize(context); + } + + // --- alarm management + + /** + * Schedules repeating alarm for auto-synchronization + */ + public static void scheduleService() { + int syncFrequencySeconds = Preferences.getIntegerFromString( + R.string.rmilk_MPr_interval_key, -1); + Context context = ContextManager.getContext(); + if(syncFrequencySeconds <= 0) { + unscheduleService(context); + return; + } + + // figure out synchronization frequency + long interval = 1000L * syncFrequencySeconds; + long offset = computeNextSyncOffset(interval); + + // give a little padding + offset = Math.max(offset, AUTO_SYNC_MIN_OFFSET); + + AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + PendingIntent pendingIntent = PendingIntent.getService(context, 0, + createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT); + + Log.i("Astrid", "Autosync set for " + offset / 1000 //$NON-NLS-1$ //$NON-NLS-2$ + + " seconds repeating every " + syncFrequencySeconds); //$NON-NLS-1$ + + // cancel all existing + am.cancel(pendingIntent); + + // schedule new + am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + offset, + interval, pendingIntent); + } + + + /** + * Removes repeating alarm for auto-synchronization + */ + private static void unscheduleService(Context context) { + AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + PendingIntent pendingIntent = PendingIntent.getService(context, 0, + createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT); + am.cancel(pendingIntent); + } + + /** Create the alarm intent */ + private static Intent createAlarmIntent(Context context) { + Intent intent = new Intent(context, MilkBackgroundService.class); + intent.setAction(SYNC_ACTION); + return intent; + } + + // --- utility methods + + + private static long computeNextSyncOffset(long interval) { + // figure out last synchronize time + long lastSyncDate = MilkUtilities.getLastSyncDate(); + + // if user never synchronized, give them a full offset period before bg sync + if(lastSyncDate != 0) + return Math.max(0, lastSyncDate + interval - DateUtilities.now()); + else + return interval; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/api/data/RtmTaskNote.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/api/data/RtmTaskNote.java index 3e5875789..a12a42d40 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/api/data/RtmTaskNote.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/api/data/RtmTaskNote.java @@ -1,106 +1,106 @@ -/* - * Copyright 2007, MetaDimensional Technologies Inc. - * - * - * This file is part of the RememberTheMilk Java API. - * - * The RememberTheMilk Java API is free software; you can redistribute it - * and/or modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 3 of the - * License, or (at your option) any later version. - * - * The RememberTheMilk Java API 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 Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ -package com.todoroo.astrid.rmilk.api.data; - -import java.util.Date; - -import org.w3c.dom.Element; -import org.w3c.dom.EntityReference; -import org.w3c.dom.Text; - -import android.util.Log; - -/** - * Represents a single task note. - * - * @author Edouard Mercier - * @since 2008.04.22 - */ -@SuppressWarnings("nls") -public class RtmTaskNote - extends RtmData -{ - - private final String id; - - private final Date created; - - private final Date modified; - - private final String title; - - private String text; - - public RtmTaskNote(Element element) - { - id = element.getAttribute("id"); - created = parseDate(element.getAttribute("created")); - modified = parseDate(element.getAttribute("modified")); - title = element.getAttribute("title"); - - // The note text itself might be split across multiple children of the - // note element, so get all of the children. - for (int i=0; i < element.getChildNodes().getLength(); i++) { - Object innerNote = element.getChildNodes().item(i); - - if(innerNote instanceof EntityReference) // this node is empty - continue; - - if(!(innerNote instanceof Text)) { - Log.w("rtm-note", "Expected text type, got " + innerNote.getClass()); - continue; - } - - Text innerText = (Text) innerNote; - - if (text == null) - text = innerText.getData(); - else - text = text.concat(innerText.getData()); - - } - } - - public String getId() - { - return id; - } - - public Date getCreated() - { - return created; - } - - public Date getModified() - { - return modified; - } - - public String getTitle() - { - return title; - } - - public String getText() - { - return text; - } - -} +/* + * Copyright 2007, MetaDimensional Technologies Inc. + * + * + * This file is part of the RememberTheMilk Java API. + * + * The RememberTheMilk Java API is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * The RememberTheMilk Java API 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 Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +package com.todoroo.astrid.rmilk.api.data; + +import java.util.Date; + +import org.w3c.dom.Element; +import org.w3c.dom.EntityReference; +import org.w3c.dom.Text; + +import android.util.Log; + +/** + * Represents a single task note. + * + * @author Edouard Mercier + * @since 2008.04.22 + */ +@SuppressWarnings("nls") +public class RtmTaskNote + extends RtmData +{ + + private final String id; + + private final Date created; + + private final Date modified; + + private final String title; + + private String text; + + public RtmTaskNote(Element element) + { + id = element.getAttribute("id"); + created = parseDate(element.getAttribute("created")); + modified = parseDate(element.getAttribute("modified")); + title = element.getAttribute("title"); + + // The note text itself might be split across multiple children of the + // note element, so get all of the children. + for (int i=0; i < element.getChildNodes().getLength(); i++) { + Object innerNote = element.getChildNodes().item(i); + + if(innerNote instanceof EntityReference) // this node is empty + continue; + + if(!(innerNote instanceof Text)) { + Log.w("rtm-note", "Expected text type, got " + innerNote.getClass()); + continue; + } + + Text innerText = (Text) innerNote; + + if (text == null) + text = innerText.getData(); + else + text = text.concat(innerText.getData()); + + } + } + + public String getId() + { + return id; + } + + public Date getCreated() + { + return created; + } + + public Date getModified() + { + return modified; + } + + public String getTitle() + { + return title; + } + + public String getText() + { + return text; + } + +} diff --git a/astrid/res/layout/addon_activity.xml b/astrid/res/layout/addon_activity.xml index 4bd10cf80..c1999c104 100644 --- a/astrid/res/layout/addon_activity.xml +++ b/astrid/res/layout/addon_activity.xml @@ -1,50 +1,50 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/astrid/res/layout/addon_adapter_row.xml b/astrid/res/layout/addon_adapter_row.xml index b3f3cc6c0..c99ea3dc0 100644 --- a/astrid/res/layout/addon_adapter_row.xml +++ b/astrid/res/layout/addon_adapter_row.xml @@ -1,88 +1,88 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/astrid/res/layout/task_edit_activity.xml b/astrid/res/layout/task_edit_activity.xml index bf3218e7c..e6fdb48b2 100644 --- a/astrid/res/layout/task_edit_activity.xml +++ b/astrid/res/layout/task_edit_activity.xml @@ -1,304 +1,304 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -