Fix CRLF for dev

pull/14/head
Arne Jans 16 years ago
parent d1ccce4e68
commit d7f17502fe

@ -1,39 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>astrid</name> <name>astrid</name>
<comment></comment> <comment></comment>
<projects> <projects>
</projects> </projects>
<buildSpec> <buildSpec>
<buildCommand> <buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name> <name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name> <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name> <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name> <name>org.eclipse.jdt.core.javabuilder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name> <name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
</buildSpec> </buildSpec>
<natures> <natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature> <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature> <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
</natures> </natures>
</projectDescription> </projectDescription>

@ -1,89 +1,89 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AND; import static com.todoroo.andlib.sql.SqlConstants.AND;
import static com.todoroo.andlib.sql.SqlConstants.EXISTS; import static com.todoroo.andlib.sql.SqlConstants.EXISTS;
import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS; import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.NOT; import static com.todoroo.andlib.sql.SqlConstants.NOT;
import static com.todoroo.andlib.sql.SqlConstants.OR; import static com.todoroo.andlib.sql.SqlConstants.OR;
import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS; import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public abstract class Criterion { public abstract class Criterion {
protected final Operator operator; protected final Operator operator;
Criterion(Operator operator) { Criterion(Operator operator) {
this.operator = operator; this.operator = operator;
} }
public static Criterion all = new Criterion(Operator.exists) { public static Criterion all = new Criterion(Operator.exists) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(1); sb.append(1);
} }
}; };
public static Criterion none = new Criterion(Operator.exists) { public static Criterion none = new Criterion(Operator.exists) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(0); sb.append(0);
} }
}; };
public static Criterion and(final Criterion criterion, final Criterion... criterions) { public static Criterion and(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.and) { return new Criterion(Operator.and) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(criterion); sb.append(criterion);
for (Criterion c : criterions) { for (Criterion c : criterions) {
sb.append(SPACE).append(AND).append(SPACE).append(c); sb.append(SPACE).append(AND).append(SPACE).append(c);
} }
} }
}; };
} }
public static Criterion or(final Criterion criterion, final Criterion... criterions) { public static Criterion or(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.or) { return new Criterion(Operator.or) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(criterion); sb.append(criterion);
for (Criterion c : criterions) { for (Criterion c : criterions) {
sb.append(SPACE).append(OR).append(SPACE).append(c.toString()); sb.append(SPACE).append(OR).append(SPACE).append(c.toString());
} }
} }
}; };
} }
public static Criterion exists(final Query query) { public static Criterion exists(final Query query) {
return new Criterion(Operator.exists) { return new Criterion(Operator.exists) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(EXISTS).append(SPACE).append(LEFT_PARENTHESIS).append(query).append(RIGHT_PARENTHESIS); sb.append(EXISTS).append(SPACE).append(LEFT_PARENTHESIS).append(query).append(RIGHT_PARENTHESIS);
} }
}; };
} }
public static Criterion not(final Criterion criterion) { public static Criterion not(final Criterion criterion) {
return new Criterion(Operator.not) { return new Criterion(Operator.not) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(NOT).append(SPACE); sb.append(NOT).append(SPACE);
criterion.populate(sb); criterion.populate(sb);
} }
}; };
} }
protected abstract void populate(StringBuilder sb); protected abstract void populate(StringBuilder sb);
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(LEFT_PARENTHESIS); StringBuilder builder = new StringBuilder(LEFT_PARENTHESIS);
populate(builder); populate(builder);
builder.append(RIGHT_PARENTHESIS); builder.append(RIGHT_PARENTHESIS);
return builder.toString(); return builder.toString();
} }
} }

@ -1,67 +1,67 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AS; import static com.todoroo.andlib.sql.SqlConstants.AS;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public abstract class DBObject<T extends DBObject<?>> implements Cloneable { public abstract class DBObject<T extends DBObject<?>> implements Cloneable {
protected String alias; protected String alias;
protected final String expression; protected final String expression;
protected DBObject(String expression){ protected DBObject(String expression){
this.expression = expression; this.expression = expression;
} }
public T as(String newAlias) { public T as(String newAlias) {
try { try {
T clone = (T) clone(); T clone = (T) clone();
clone.alias = newAlias; clone.alias = newAlias;
return clone; return clone;
} catch (CloneNotSupportedException e) { } catch (CloneNotSupportedException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public boolean hasAlias() { public boolean hasAlias() {
return alias != null; return alias != null;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
DBObject<?> dbObject = (DBObject<?>) o; DBObject<?> dbObject = (DBObject<?>) o;
if (alias != null ? !alias.equals(dbObject.alias) : dbObject.alias != null) return false; if (alias != null ? !alias.equals(dbObject.alias) : dbObject.alias != null) return false;
if (expression != null ? !expression.equals(dbObject.expression) : dbObject.expression != null) return false; if (expression != null ? !expression.equals(dbObject.expression) : dbObject.expression != null) return false;
return true; return true;
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = alias != null ? alias.hashCode() : 0; int result = alias != null ? alias.hashCode() : 0;
result = 31 * result + (expression != null ? expression.hashCode() : 0); result = 31 * result + (expression != null ? expression.hashCode() : 0);
return result; return result;
} }
@Override @Override
public final String toString() { public final String toString() {
if (hasAlias()) { if (hasAlias()) {
return alias; return alias;
} }
return expression; return expression;
} }
public final String toStringInSelect() { public final String toStringInSelect() {
StringBuilder sb = new StringBuilder(expression); StringBuilder sb = new StringBuilder(expression);
if (hasAlias()) { if (hasAlias()) {
sb.append(SPACE).append(AS).append(SPACE).append(alias); sb.append(SPACE).append(AS).append(SPACE).append(alias);
} else { } else {
int pos = expression.indexOf('.'); int pos = expression.indexOf('.');
if(pos > 0) if(pos > 0)
sb.append(SPACE).append(AS).append(SPACE).append(expression.substring(pos + 1)); sb.append(SPACE).append(AS).append(SPACE).append(expression.substring(pos + 1));
} }
return sb.toString(); return sb.toString();
} }
} }

@ -1,7 +1,7 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
public class EqCriterion extends UnaryCriterion { public class EqCriterion extends UnaryCriterion {
EqCriterion(Field field, Object value) { EqCriterion(Field field, Object value) {
super(field, Operator.eq, value); super(field, Operator.eq, value);
} }
} }

@ -1,90 +1,90 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AND; import static com.todoroo.andlib.sql.SqlConstants.AND;
import static com.todoroo.andlib.sql.SqlConstants.BETWEEN; import static com.todoroo.andlib.sql.SqlConstants.BETWEEN;
import static com.todoroo.andlib.sql.SqlConstants.COMMA; import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS; 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.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Field extends DBObject<Field> { public class Field extends DBObject<Field> {
protected Field(String expression) { protected Field(String expression) {
super(expression); super(expression);
} }
public static Field field(String expression) { public static Field field(String expression) {
return new Field(expression); return new Field(expression);
} }
public Criterion eq(Object value) { public Criterion eq(Object value) {
if(value == null) if(value == null)
return UnaryCriterion.isNull(this); return UnaryCriterion.isNull(this);
return UnaryCriterion.eq(this, value); return UnaryCriterion.eq(this, value);
} }
public Criterion neq(Object value) { public Criterion neq(Object value) {
if(value == null) if(value == null)
return UnaryCriterion.isNotNull(this); return UnaryCriterion.isNotNull(this);
return UnaryCriterion.neq(this, value); return UnaryCriterion.neq(this, value);
} }
public Criterion gt(Object value) { public Criterion gt(Object value) {
return UnaryCriterion.gt(this, value); return UnaryCriterion.gt(this, value);
} }
public Criterion lt(final Object value) { public Criterion lt(final Object value) {
return UnaryCriterion.lt(this, value); return UnaryCriterion.lt(this, value);
} }
public Criterion isNull() { public Criterion isNull() {
return UnaryCriterion.isNull(this); return UnaryCriterion.isNull(this);
} }
public Criterion isNotNull() { public Criterion isNotNull() {
return UnaryCriterion.isNotNull(this); return UnaryCriterion.isNotNull(this);
} }
public Criterion between(final Object lower, final Object upper) { public Criterion between(final Object lower, final Object upper) {
final Field field = this; final Field field = this;
return new Criterion(null) { return new Criterion(null) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(BETWEEN).append(SPACE).append(lower).append(SPACE).append(AND) sb.append(field).append(SPACE).append(BETWEEN).append(SPACE).append(lower).append(SPACE).append(AND)
.append(SPACE).append(upper); .append(SPACE).append(upper);
} }
}; };
} }
public Criterion like(final String value) { public Criterion like(final String value) {
return UnaryCriterion.like(this, value); return UnaryCriterion.like(this, value);
} }
public <T> Criterion in(final T... value) { public <T> Criterion in(final T... value) {
final Field field = this; final Field field = this;
return new Criterion(Operator.in) { return new Criterion(Operator.in) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS); sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS);
for (T t : value) { for (T t : value) {
sb.append(t.toString()).append(COMMA); sb.append(t.toString()).append(COMMA);
} }
sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS); sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS);
} }
}; };
} }
public Criterion in(final Query query) { public Criterion in(final Query query) {
final Field field = this; final Field field = this;
return new Criterion(Operator.in) { return new Criterion(Operator.in) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(query) sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(query)
.append(RIGHT_PARENTHESIS); .append(RIGHT_PARENTHESIS);
} }
}; };
} }
} }

@ -1,14 +1,14 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class GroupBy { public class GroupBy {
private List<Field> fields = new ArrayList<Field>(); private List<Field> fields = new ArrayList<Field>();
public static GroupBy groupBy(Field field) { public static GroupBy groupBy(Field field) {
GroupBy groupBy = new GroupBy(); GroupBy groupBy = new GroupBy();
groupBy.fields.add(field); groupBy.fields.add(field);
return groupBy; return groupBy;
} }
} }

@ -1,43 +1,43 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.JOIN; import static com.todoroo.andlib.sql.SqlConstants.JOIN;
import static com.todoroo.andlib.sql.SqlConstants.ON; import static com.todoroo.andlib.sql.SqlConstants.ON;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Join { public class Join {
private final SqlTable joinTable; private final SqlTable joinTable;
private final JoinType joinType; private final JoinType joinType;
private final Criterion[] criterions; private final Criterion[] criterions;
private Join(SqlTable table, JoinType joinType, Criterion... criterions) { private Join(SqlTable table, JoinType joinType, Criterion... criterions) {
joinTable = table; joinTable = table;
this.joinType = joinType; this.joinType = joinType;
this.criterions = criterions; this.criterions = criterions;
} }
public static Join inner(SqlTable expression, Criterion... criterions) { public static Join inner(SqlTable expression, Criterion... criterions) {
return new Join(expression, JoinType.INNER, criterions); return new Join(expression, JoinType.INNER, criterions);
} }
public static Join left(SqlTable table, Criterion... criterions) { public static Join left(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.LEFT, criterions); return new Join(table, JoinType.LEFT, criterions);
} }
public static Join right(SqlTable table, Criterion... criterions) { public static Join right(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.RIGHT, criterions); return new Join(table, JoinType.RIGHT, criterions);
} }
public static Join out(SqlTable table, Criterion... criterions) { public static Join out(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.OUT, criterions); return new Join(table, JoinType.OUT, criterions);
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON); sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON);
for (Criterion criterion : criterions) { for (Criterion criterion : criterions) {
sb.append(SPACE).append(criterion); sb.append(SPACE).append(criterion);
} }
return sb.toString(); return sb.toString();
} }
} }

@ -1,5 +1,5 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
public enum JoinType { public enum JoinType {
INNER, LEFT, RIGHT, OUT INNER, LEFT, RIGHT, OUT
} }

@ -1,57 +1,57 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@SuppressWarnings("nls") @SuppressWarnings("nls")
public final class Operator { public final class Operator {
private final String operator; private final String operator;
public static final Operator eq = new Operator("="); public static final Operator eq = new Operator("=");
public static final Operator neq = new Operator("<>"); public static final Operator neq = new Operator("<>");
public static final Operator isNull = new Operator("IS NULL"); public static final Operator isNull = new Operator("IS NULL");
public static final Operator isNotNull = new Operator("IS NOT NULL"); public static final Operator isNotNull = new Operator("IS NOT NULL");
public static final Operator gt = new Operator(">"); public static final Operator gt = new Operator(">");
public static final Operator lt = new Operator("<"); public static final Operator lt = new Operator("<");
public static final Operator gte = new Operator(">="); public static final Operator gte = new Operator(">=");
public static final Operator lte = new Operator("<="); public static final Operator lte = new Operator("<=");
public static final Operator and = new Operator("AND"); public static final Operator and = new Operator("AND");
public static final Operator or = new Operator("OR"); public static final Operator or = new Operator("OR");
public static final Operator not = new Operator("NOT"); public static final Operator not = new Operator("NOT");
public static final Operator exists = new Operator("EXISTS"); public static final Operator exists = new Operator("EXISTS");
public static final Operator like = new Operator("LIKE"); public static final Operator like = new Operator("LIKE");
public static final Operator in = new Operator("IN"); public static final Operator in = new Operator("IN");
private static final Map<Operator, Operator> contraryRegistry = new HashMap<Operator, Operator>(); private static final Map<Operator, Operator> contraryRegistry = new HashMap<Operator, Operator>();
static { static {
contraryRegistry.put(eq, neq); contraryRegistry.put(eq, neq);
contraryRegistry.put(neq, eq); contraryRegistry.put(neq, eq);
contraryRegistry.put(isNull, isNotNull); contraryRegistry.put(isNull, isNotNull);
contraryRegistry.put(isNotNull, isNull); contraryRegistry.put(isNotNull, isNull);
contraryRegistry.put(gt, lte); contraryRegistry.put(gt, lte);
contraryRegistry.put(lte, gt); contraryRegistry.put(lte, gt);
contraryRegistry.put(lt, gte); contraryRegistry.put(lt, gte);
contraryRegistry.put(gte, lt); contraryRegistry.put(gte, lt);
} }
private Operator(String operator) { private Operator(String operator) {
this.operator = operator; this.operator = operator;
} }
public Operator getContrary() { public Operator getContrary() {
if(!contraryRegistry.containsKey(this)){ if(!contraryRegistry.containsKey(this)){
Operator opposite = new Operator(not.toString() + SPACE + this.toString()); Operator opposite = new Operator(not.toString() + SPACE + this.toString());
contraryRegistry.put(this, opposite); contraryRegistry.put(this, opposite);
contraryRegistry.put(opposite, this); contraryRegistry.put(opposite, this);
} }
return contraryRegistry.get(this); return contraryRegistry.get(this);
} }
@Override @Override
public String toString() { public String toString() {
return this.operator.toString(); return this.operator.toString();
} }
} }

@ -1,30 +1,30 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Order { public class Order {
private final Object expression; private final Object expression;
private final OrderType orderType; private final OrderType orderType;
private Order(Object expression) { private Order(Object expression) {
this(expression, OrderType.ASC); this(expression, OrderType.ASC);
} }
private Order(Object expression, OrderType orderType) { private Order(Object expression, OrderType orderType) {
this.expression = expression; this.expression = expression;
this.orderType = orderType; this.orderType = orderType;
} }
public static Order asc(Object expression) { public static Order asc(Object expression) {
return new Order(expression); return new Order(expression);
} }
public static Order desc(Object expression) { public static Order desc(Object expression) {
return new Order(expression, OrderType.DESC); return new Order(expression, OrderType.DESC);
} }
@Override @Override
public String toString() { public String toString() {
return expression + SPACE + orderType; return expression + SPACE + orderType;
} }
} }

@ -1,5 +1,5 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
public enum OrderType { public enum OrderType {
DESC, ASC DESC, ASC
} }

@ -1,205 +1,205 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.ALL; import static com.todoroo.andlib.sql.SqlConstants.ALL;
import static com.todoroo.andlib.sql.SqlConstants.COMMA; import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.FROM; import static com.todoroo.andlib.sql.SqlConstants.FROM;
import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY; 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.LEFT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.LIMIT; import static com.todoroo.andlib.sql.SqlConstants.LIMIT;
import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY; 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.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.SELECT; import static com.todoroo.andlib.sql.SqlConstants.SELECT;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import static com.todoroo.andlib.sql.SqlConstants.WHERE; import static com.todoroo.andlib.sql.SqlConstants.WHERE;
import static com.todoroo.andlib.sql.SqlTable.table; import static com.todoroo.andlib.sql.SqlTable.table;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import java.util.ArrayList; import java.util.ArrayList;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
public final class Query { public final class Query {
private SqlTable table; private SqlTable table;
private String queryTemplate = null; private String queryTemplate = null;
private final ArrayList<Criterion> criterions = new ArrayList<Criterion>(); private final ArrayList<Criterion> criterions = new ArrayList<Criterion>();
private final ArrayList<Field> fields = new ArrayList<Field>(); private final ArrayList<Field> fields = new ArrayList<Field>();
private final ArrayList<Join> joins = new ArrayList<Join>(); private final ArrayList<Join> joins = new ArrayList<Join>();
private final ArrayList<Field> groupBies = new ArrayList<Field>(); private final ArrayList<Field> groupBies = new ArrayList<Field>();
private final ArrayList<Order> orders = new ArrayList<Order>(); private final ArrayList<Order> orders = new ArrayList<Order>();
private final ArrayList<Criterion> havings = new ArrayList<Criterion>(); private final ArrayList<Criterion> havings = new ArrayList<Criterion>();
private int limits = -1; private int limits = -1;
private Query(Field... fields) { private Query(Field... fields) {
this.fields.addAll(asList(fields)); this.fields.addAll(asList(fields));
} }
public static Query select(Field... fields) { public static Query select(Field... fields) {
return new Query(fields); return new Query(fields);
} }
public Query from(SqlTable fromTable) { public Query from(SqlTable fromTable) {
this.table = fromTable; this.table = fromTable;
return this; return this;
} }
public Query join(Join... join) { public Query join(Join... join) {
joins.addAll(asList(join)); joins.addAll(asList(join));
return this; return this;
} }
public Query where(Criterion criterion) { public Query where(Criterion criterion) {
criterions.add(criterion); criterions.add(criterion);
return this; return this;
} }
public Query groupBy(Field... groupBy) { public Query groupBy(Field... groupBy) {
groupBies.addAll(asList(groupBy)); groupBies.addAll(asList(groupBy));
return this; return this;
} }
public Query orderBy(Order... order) { public Query orderBy(Order... order) {
orders.addAll(asList(order)); orders.addAll(asList(order));
return this; return this;
} }
public Query limit(int limit) { public Query limit(int limit) {
limits = limit; limits = limit;
return this; return this;
} }
public Query appendSelectFields(Property<?>... selectFields) { public Query appendSelectFields(Property<?>... selectFields) {
this.fields.addAll(asList(selectFields)); this.fields.addAll(asList(selectFields));
return this; return this;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return this == o || !(o == null || getClass() != o.getClass()) && this.toString().equals(o.toString()); return this == o || !(o == null || getClass() != o.getClass()) && this.toString().equals(o.toString());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return toString().hashCode(); return toString().hashCode();
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sql = new StringBuilder(); StringBuilder sql = new StringBuilder();
visitSelectClause(sql); visitSelectClause(sql);
visitFromClause(sql); visitFromClause(sql);
if(queryTemplate == null) { if(queryTemplate == null) {
visitJoinClause(sql); visitJoinClause(sql);
visitWhereClause(sql); visitWhereClause(sql);
visitGroupByClause(sql); visitGroupByClause(sql);
visitOrderByClause(sql); visitOrderByClause(sql);
visitLimitClause(sql); visitLimitClause(sql);
} else { } else {
if(joins.size() > 0 || groupBies.size() > 0 || orders.size() > 0 || if(joins.size() > 0 || groupBies.size() > 0 || orders.size() > 0 ||
havings.size() > 0) havings.size() > 0)
throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$ throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$
sql.append(queryTemplate); sql.append(queryTemplate);
} }
return sql.toString(); return sql.toString();
} }
private void visitOrderByClause(StringBuilder sql) { private void visitOrderByClause(StringBuilder sql) {
if (orders.isEmpty()) { if (orders.isEmpty()) {
return; return;
} }
sql.append(ORDER_BY); sql.append(ORDER_BY);
for (Order order : orders) { for (Order order : orders) {
sql.append(SPACE).append(order).append(COMMA); sql.append(SPACE).append(order).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
private void visitGroupByClause(StringBuilder sql) { private void visitGroupByClause(StringBuilder sql) {
if (groupBies.isEmpty()) { if (groupBies.isEmpty()) {
return; return;
} }
sql.append(GROUP_BY); sql.append(GROUP_BY);
for (Field groupBy : groupBies) { for (Field groupBy : groupBies) {
sql.append(SPACE).append(groupBy).append(COMMA); sql.append(SPACE).append(groupBy).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
if (havings.isEmpty()) { if (havings.isEmpty()) {
return; return;
} }
sql.append("HAVING"); sql.append("HAVING");
for (Criterion havingCriterion : havings) { for (Criterion havingCriterion : havings) {
sql.append(SPACE).append(havingCriterion).append(COMMA); sql.append(SPACE).append(havingCriterion).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
private void visitWhereClause(StringBuilder sql) { private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) { if (criterions.isEmpty()) {
return; return;
} }
sql.append(WHERE); sql.append(WHERE);
for (Criterion criterion : criterions) { for (Criterion criterion : criterions) {
sql.append(SPACE).append(criterion).append(SPACE); sql.append(SPACE).append(criterion).append(SPACE);
} }
} }
private void visitJoinClause(StringBuilder sql) { private void visitJoinClause(StringBuilder sql) {
for (Join join : joins) { for (Join join : joins) {
sql.append(join).append(SPACE); sql.append(join).append(SPACE);
} }
} }
private void visitFromClause(StringBuilder sql) { private void visitFromClause(StringBuilder sql) {
if (table == null) { if (table == null) {
return; return;
} }
sql.append(FROM).append(SPACE).append(table).append(SPACE); sql.append(FROM).append(SPACE).append(table).append(SPACE);
} }
private void visitSelectClause(StringBuilder sql) { private void visitSelectClause(StringBuilder sql) {
sql.append(SELECT).append(SPACE); sql.append(SELECT).append(SPACE);
if (fields.isEmpty()) { if (fields.isEmpty()) {
sql.append(ALL).append(SPACE); sql.append(ALL).append(SPACE);
return; return;
} }
for (Field field : fields) { for (Field field : fields) {
sql.append(field.toStringInSelect()).append(COMMA); sql.append(field.toStringInSelect()).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
private void visitLimitClause(StringBuilder sql) { private void visitLimitClause(StringBuilder sql) {
if(limits > -1) if(limits > -1)
sql.append(LIMIT).append(SPACE).append(limits).append(SPACE); sql.append(LIMIT).append(SPACE).append(limits).append(SPACE);
} }
public SqlTable as(String alias) { public SqlTable as(String alias) {
return table(LEFT_PARENTHESIS + this.toString() + RIGHT_PARENTHESIS).as(alias); return table(LEFT_PARENTHESIS + this.toString() + RIGHT_PARENTHESIS).as(alias);
} }
public Query having(Criterion criterion) { public Query having(Criterion criterion) {
this.havings.add(criterion); this.havings.add(criterion);
return this; return this;
} }
/** /**
* Gets a list of fields returned by this query * Gets a list of fields returned by this query
* @return * @return
*/ */
public Property<?>[] getFields() { public Property<?>[] getFields() {
return fields.toArray(new Property<?>[fields.size()]); return fields.toArray(new Property<?>[fields.size()]);
} }
/** /**
* Add the SQL query template (comes after the "from") * Add the SQL query template (comes after the "from")
* @param sqlQuery * @param sqlQuery
* @return * @return
*/ */
public Query withQueryTemplate(String template) { public Query withQueryTemplate(String template) {
queryTemplate = template; queryTemplate = template;
return this; return this;
} }
} }

@ -1,117 +1,117 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.COMMA; import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY; import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY;
import static com.todoroo.andlib.sql.SqlConstants.LIMIT; import static com.todoroo.andlib.sql.SqlConstants.LIMIT;
import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY; import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import static com.todoroo.andlib.sql.SqlConstants.WHERE; import static com.todoroo.andlib.sql.SqlConstants.WHERE;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* Query Template returns a bunch of criteria that allows a query to be * Query Template returns a bunch of criteria that allows a query to be
* constructed * constructed
* *
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
public final class QueryTemplate { public final class QueryTemplate {
private final ArrayList<Criterion> criterions = new ArrayList<Criterion>(); private final ArrayList<Criterion> criterions = new ArrayList<Criterion>();
private final ArrayList<Join> joins = new ArrayList<Join>(); private final ArrayList<Join> joins = new ArrayList<Join>();
private final ArrayList<Field> groupBies = new ArrayList<Field>(); private final ArrayList<Field> groupBies = new ArrayList<Field>();
private final ArrayList<Order> orders = new ArrayList<Order>(); private final ArrayList<Order> orders = new ArrayList<Order>();
private final ArrayList<Criterion> havings = new ArrayList<Criterion>(); private final ArrayList<Criterion> havings = new ArrayList<Criterion>();
private Integer limit = null; private Integer limit = null;
public QueryTemplate join(Join... join) { public QueryTemplate join(Join... join) {
joins.addAll(asList(join)); joins.addAll(asList(join));
return this; return this;
} }
public QueryTemplate where(Criterion criterion) { public QueryTemplate where(Criterion criterion) {
criterions.add(criterion); criterions.add(criterion);
return this; return this;
} }
public QueryTemplate groupBy(Field... groupBy) { public QueryTemplate groupBy(Field... groupBy) {
groupBies.addAll(asList(groupBy)); groupBies.addAll(asList(groupBy));
return this; return this;
} }
public QueryTemplate orderBy(Order... order) { public QueryTemplate orderBy(Order... order) {
orders.addAll(asList(order)); orders.addAll(asList(order));
return this; return this;
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sql = new StringBuilder(); StringBuilder sql = new StringBuilder();
visitJoinClause(sql); visitJoinClause(sql);
visitWhereClause(sql); visitWhereClause(sql);
visitGroupByClause(sql); visitGroupByClause(sql);
visitOrderByClause(sql); visitOrderByClause(sql);
if(limit != null) if(limit != null)
sql.append(LIMIT).append(SPACE).append(limit); sql.append(LIMIT).append(SPACE).append(limit);
return sql.toString(); return sql.toString();
} }
private void visitOrderByClause(StringBuilder sql) { private void visitOrderByClause(StringBuilder sql) {
if (orders.isEmpty()) { if (orders.isEmpty()) {
return; return;
} }
sql.append(ORDER_BY); sql.append(ORDER_BY);
for (Order order : orders) { for (Order order : orders) {
sql.append(SPACE).append(order).append(COMMA); sql.append(SPACE).append(order).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
private void visitGroupByClause(StringBuilder sql) { private void visitGroupByClause(StringBuilder sql) {
if (groupBies.isEmpty()) { if (groupBies.isEmpty()) {
return; return;
} }
sql.append(GROUP_BY); sql.append(GROUP_BY);
for (Field groupBy : groupBies) { for (Field groupBy : groupBies) {
sql.append(SPACE).append(groupBy).append(COMMA); sql.append(SPACE).append(groupBy).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
if (havings.isEmpty()) { if (havings.isEmpty()) {
return; return;
} }
sql.append("HAVING"); sql.append("HAVING");
for (Criterion havingCriterion : havings) { for (Criterion havingCriterion : havings) {
sql.append(SPACE).append(havingCriterion).append(COMMA); sql.append(SPACE).append(havingCriterion).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
private void visitWhereClause(StringBuilder sql) { private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) { if (criterions.isEmpty()) {
return; return;
} }
sql.append(WHERE); sql.append(WHERE);
for (Criterion criterion : criterions) { for (Criterion criterion : criterions) {
sql.append(SPACE).append(criterion).append(SPACE); sql.append(SPACE).append(criterion).append(SPACE);
} }
} }
private void visitJoinClause(StringBuilder sql) { private void visitJoinClause(StringBuilder sql) {
for (Join join : joins) { for (Join join : joins) {
sql.append(join).append(SPACE); sql.append(join).append(SPACE);
} }
} }
public QueryTemplate having(Criterion criterion) { public QueryTemplate having(Criterion criterion) {
this.havings.add(criterion); this.havings.add(criterion);
return this; return this;
} }
public QueryTemplate limit(int limitValue) { public QueryTemplate limit(int limitValue) {
this.limit = limitValue; this.limit = limitValue;
return this; return this;
} }
} }

@ -1,25 +1,25 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
@SuppressWarnings("nls") @SuppressWarnings("nls")
public final class SqlConstants { public final class SqlConstants {
static final String SELECT = "SELECT"; static final String SELECT = "SELECT";
static final String SPACE = " "; static final String SPACE = " ";
static final String AS = "AS"; static final String AS = "AS";
static final String COMMA = ","; static final String COMMA = ",";
static final String FROM = "FROM"; static final String FROM = "FROM";
static final String ON = "ON"; static final String ON = "ON";
static final String JOIN = "JOIN"; static final String JOIN = "JOIN";
static final String ALL = "*"; static final String ALL = "*";
static final String LEFT_PARENTHESIS = "("; static final String LEFT_PARENTHESIS = "(";
static final String RIGHT_PARENTHESIS = ")"; static final String RIGHT_PARENTHESIS = ")";
static final String AND = "AND"; static final String AND = "AND";
static final String BETWEEN = "BETWEEN"; static final String BETWEEN = "BETWEEN";
static final String LIKE = "LIKE"; static final String LIKE = "LIKE";
static final String OR = "OR"; static final String OR = "OR";
static final String ORDER_BY = "ORDER BY"; static final String ORDER_BY = "ORDER BY";
static final String GROUP_BY = "GROUP BY"; static final String GROUP_BY = "GROUP BY";
static final String WHERE = "WHERE"; static final String WHERE = "WHERE";
public static final String EXISTS = "EXISTS"; public static final String EXISTS = "EXISTS";
public static final String NOT = "NOT"; public static final String NOT = "NOT";
public static final String LIMIT = "LIMIT"; public static final String LIMIT = "LIMIT";
} }

@ -1,20 +1,20 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
public class SqlTable extends DBObject<SqlTable> { public class SqlTable extends DBObject<SqlTable> {
protected SqlTable(String expression) { protected SqlTable(String expression) {
super(expression); super(expression);
} }
public static SqlTable table(String table) { public static SqlTable table(String table) {
return new SqlTable(table); return new SqlTable(table);
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
protected String fieldExpression(String fieldName) { protected String fieldExpression(String fieldName) {
if (hasAlias()) { if (hasAlias()) {
return alias + "." + fieldName; return alias + "." + fieldName;
} }
return expression+"."+fieldName; return expression+"."+fieldName;
} }
} }

@ -1,92 +1,92 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class UnaryCriterion extends Criterion { public class UnaryCriterion extends Criterion {
protected final Field expression; protected final Field expression;
protected final Object value; protected final Object value;
UnaryCriterion(Field expression, Operator operator, Object value) { UnaryCriterion(Field expression, Operator operator, Object value) {
super(operator); super(operator);
this.expression = expression; this.expression = expression;
this.value = value; this.value = value;
} }
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
beforePopulateOperator(sb); beforePopulateOperator(sb);
populateOperator(sb); populateOperator(sb);
afterPopulateOperator(sb); afterPopulateOperator(sb);
} }
public static Criterion eq(Field expression, Object value) { public static Criterion eq(Field expression, Object value) {
return new UnaryCriterion(expression, Operator.eq, value); return new UnaryCriterion(expression, Operator.eq, value);
} }
protected void beforePopulateOperator(StringBuilder sb) { protected void beforePopulateOperator(StringBuilder sb) {
sb.append(expression); sb.append(expression);
} }
protected void populateOperator(StringBuilder sb) { protected void populateOperator(StringBuilder sb) {
sb.append(operator); sb.append(operator);
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
protected void afterPopulateOperator(StringBuilder sb) { protected void afterPopulateOperator(StringBuilder sb) {
if(value == null) if(value == null)
return; return;
else if(value instanceof String) else if(value instanceof String)
sb.append("'").append(sanitize((String) value)).append("'"); sb.append("'").append(sanitize((String) value)).append("'");
else else
sb.append(value); sb.append(value);
} }
/** /**
* Sanitize the given input for SQL * Sanitize the given input for SQL
* @param input * @param input
* @return * @return
*/ */
@SuppressWarnings("nls") @SuppressWarnings("nls")
public static String sanitize(String input) { public static String sanitize(String input) {
return input.replace("'", "''"); return input.replace("'", "''");
} }
public static Criterion neq(Field field, Object value) { public static Criterion neq(Field field, Object value) {
return new UnaryCriterion(field, Operator.neq, value); return new UnaryCriterion(field, Operator.neq, value);
} }
public static Criterion gt(Field field, Object value) { public static Criterion gt(Field field, Object value) {
return new UnaryCriterion(field, Operator.gt, value); return new UnaryCriterion(field, Operator.gt, value);
} }
public static Criterion lt(Field field, Object value) { public static Criterion lt(Field field, Object value) {
return new UnaryCriterion(field, Operator.lt, value); return new UnaryCriterion(field, Operator.lt, value);
} }
public static Criterion isNull(Field field) { public static Criterion isNull(Field field) {
return new UnaryCriterion(field, Operator.isNull, null) { return new UnaryCriterion(field, Operator.isNull, null) {
@Override @Override
protected void populateOperator(StringBuilder sb) { protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator); sb.append(SPACE).append(operator);
} }
}; };
} }
public static Criterion isNotNull(Field field) { public static Criterion isNotNull(Field field) {
return new UnaryCriterion(field, Operator.isNotNull, null) { return new UnaryCriterion(field, Operator.isNotNull, null) {
@Override @Override
protected void populateOperator(StringBuilder sb) { protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator); sb.append(SPACE).append(operator);
} }
}; };
} }
public static Criterion like(Field field, String value) { public static Criterion like(Field field, String value) {
return new UnaryCriterion(field, Operator.like, value) { return new UnaryCriterion(field, Operator.like, value) {
@Override @Override
protected void populateOperator(StringBuilder sb) { protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator).append(SPACE); sb.append(SPACE).append(operator).append(SPACE);
} }
}; };
} }
} }

@ -1,146 +1,146 @@
package com.todoroo.astrid.gcal; package com.todoroo.astrid.gcal;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;
@SuppressWarnings("nls") @SuppressWarnings("nls")
public class Calendars { public class Calendars {
public static final String CALENDAR_CONTENT_CALENDARS = "calendars"; public static final String CALENDAR_CONTENT_CALENDARS = "calendars";
public static final String CALENDAR_CONTENT_EVENTS = "events"; public static final String CALENDAR_CONTENT_EVENTS = "events";
private static final String ID_COLUMN_NAME = "_id"; private static final String ID_COLUMN_NAME = "_id";
private static final String DISPLAY_COLUMN_NAME = "displayName"; private static final String DISPLAY_COLUMN_NAME = "displayName";
private static final String ACCES_LEVEL_COLUMN_NAME = "access_level"; private static final String ACCES_LEVEL_COLUMN_NAME = "access_level";
private static final String[] CALENDARS_PROJECTION = new String[] { private static final String[] CALENDARS_PROJECTION = new String[] {
ID_COLUMN_NAME, // Calendars._ID, ID_COLUMN_NAME, // Calendars._ID,
DISPLAY_COLUMN_NAME // Calendars.DISPLAY_NAME DISPLAY_COLUMN_NAME // Calendars.DISPLAY_NAME
}; };
// Only show calendars that the user can modify. Access level 500 // Only show calendars that the user can modify. Access level 500
// corresponds to Calendars.CONTRIBUTOR_ACCESS // corresponds to Calendars.CONTRIBUTOR_ACCESS
private static final String CALENDARS_WHERE = ACCES_LEVEL_COLUMN_NAME + ">= 500"; private static final String CALENDARS_WHERE = ACCES_LEVEL_COLUMN_NAME + ">= 500";
private static final String CALENDARS_SORT = "displayName ASC"; private static final String CALENDARS_SORT = "displayName ASC";
// --- api access // --- api access
/** Return content uri for calendars /** Return content uri for calendars
* @param table provider table, something like calendars, events * @param table provider table, something like calendars, events
*/ */
public static Uri getCalendarContentUri(String table) { public static Uri getCalendarContentUri(String table) {
if(AndroidUtilities.getSdkVersion() >= 8) if(AndroidUtilities.getSdkVersion() >= 8)
return Uri.parse("content://com.android.calendar/" + table); return Uri.parse("content://com.android.calendar/" + table);
else else
return Uri.parse("content://calendar/" + table); return Uri.parse("content://calendar/" + table);
} }
/** Return calendar package name */ /** Return calendar package name */
public static String getCalendarPackage() { public static String getCalendarPackage() {
if(AndroidUtilities.getSdkVersion() >= 8) if(AndroidUtilities.getSdkVersion() >= 8)
return "com.google.android.calendar"; return "com.google.android.calendar";
else else
return "com.android.calendar"; return "com.android.calendar";
} }
// --- helper data structure // --- helper data structure
/** /**
* Helper class for working with the results of getCalendars * Helper class for working with the results of getCalendars
*/ */
public static class CalendarResult { public static class CalendarResult {
/** calendar names */ /** calendar names */
public String[] calendars; public String[] calendars;
/** calendar ids. null entry -> use default */ /** calendar ids. null entry -> use default */
public String[] calendarIds; public String[] calendarIds;
/** default selection index */ /** default selection index */
public int defaultIndex = -1; public int defaultIndex = -1;
} }
/** /**
* Appends all user-modifiable calendars to listPreference. Always includes * Appends all user-modifiable calendars to listPreference. Always includes
* entry called "Astrid default" with calendar id of * entry called "Astrid default" with calendar id of
* prefs_defaultCalendar_default. * prefs_defaultCalendar_default.
* *
* @param context * @param context
* context * context
* @param listPreference * @param listPreference
* preference to init * preference to init
*/ */
public static CalendarResult getCalendars() { public static CalendarResult getCalendars() {
Context context = ContextManager.getContext(); Context context = ContextManager.getContext();
ContentResolver cr = context.getContentResolver(); ContentResolver cr = context.getContentResolver();
Resources r = context.getResources(); Resources r = context.getResources();
Cursor c = cr.query(getCalendarContentUri(CALENDAR_CONTENT_CALENDARS), CALENDARS_PROJECTION, Cursor c = cr.query(getCalendarContentUri(CALENDAR_CONTENT_CALENDARS), CALENDARS_PROJECTION,
CALENDARS_WHERE, null, CALENDARS_SORT); CALENDARS_WHERE, null, CALENDARS_SORT);
try { try {
// Fetch the current setting. Invalid calendar id will // Fetch the current setting. Invalid calendar id will
// be changed to default value. // be changed to default value.
String defaultSetting = Preferences.getStringValue(R.string.gcal_p_default); String defaultSetting = Preferences.getStringValue(R.string.gcal_p_default);
CalendarResult result = new CalendarResult(); CalendarResult result = new CalendarResult();
if (c == null || c.getCount() == 0) { if (c == null || c.getCount() == 0) {
// Something went wrong when querying calendars. Only offer them // Something went wrong when querying calendars. Only offer them
// the system default choice // the system default choice
result.calendars = new String[] { result.calendars = new String[] {
r.getString(R.string.gcal_GCP_default) }; r.getString(R.string.gcal_GCP_default) };
result.calendarIds = new String[] { null }; result.calendarIds = new String[] { null };
result.defaultIndex = 0; result.defaultIndex = 0;
return result; return result;
} }
int calendarCount = c.getCount(); int calendarCount = c.getCount();
result.calendars = new String[calendarCount]; result.calendars = new String[calendarCount];
result.calendarIds = new String[calendarCount]; result.calendarIds = new String[calendarCount];
// Iterate calendars one by one, and fill up the list preference // Iterate calendars one by one, and fill up the list preference
int row = 0; int row = 0;
int idColumn = c.getColumnIndex(ID_COLUMN_NAME); int idColumn = c.getColumnIndex(ID_COLUMN_NAME);
int nameColumn = c.getColumnIndex(DISPLAY_COLUMN_NAME); int nameColumn = c.getColumnIndex(DISPLAY_COLUMN_NAME);
while (c.moveToNext()) { while (c.moveToNext()) {
String id = c.getString(idColumn); String id = c.getString(idColumn);
String name = c.getString(nameColumn); String name = c.getString(nameColumn);
result.calendars[row] = name; result.calendars[row] = name;
result.calendarIds[row] = id; result.calendarIds[row] = id;
// We found currently selected calendar // We found currently selected calendar
if (defaultSetting != null && defaultSetting.equals(id)) { if (defaultSetting != null && defaultSetting.equals(id)) {
result.defaultIndex = row; result.defaultIndex = row;
} }
row++; row++;
} }
if (result.defaultIndex == -1 || result.defaultIndex >= calendarCount) { if (result.defaultIndex == -1 || result.defaultIndex >= calendarCount) {
result.defaultIndex = 0; result.defaultIndex = 0;
} }
return result; return result;
} finally { } finally {
if(c != null) if(c != null)
c.close(); c.close();
} }
} }
/** /**
* sets the default calendar for future use * sets the default calendar for future use
* @param defaultCalendar default calendar id * @param defaultCalendar default calendar id
*/ */
public static void setDefaultCalendar(String defaultCalendar) { public static void setDefaultCalendar(String defaultCalendar) {
Preferences.setString(R.string.gcal_p_default, defaultCalendar); Preferences.setString(R.string.gcal_p_default, defaultCalendar);
} }
} }

@ -1,95 +1,95 @@
/** /**
* See the file "LICENSE" for the full license governing this code. * See the file "LICENSE" for the full license governing this code.
*/ */
package com.todoroo.astrid.producteev; package com.todoroo.astrid.producteev;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.adapter.TaskAdapter; import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.DetailExposer; import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.producteev.sync.ProducteevDataService; import com.todoroo.astrid.producteev.sync.ProducteevDataService;
import com.todoroo.astrid.producteev.sync.ProducteevNote; import com.todoroo.astrid.producteev.sync.ProducteevNote;
import com.todoroo.astrid.producteev.sync.ProducteevTask; import com.todoroo.astrid.producteev.sync.ProducteevTask;
/** /**
* Exposes Task Details for Producteev: * Exposes Task Details for Producteev:
* - notes * - notes
* *
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
public class ProducteevDetailExposer extends BroadcastReceiver implements DetailExposer{ public class ProducteevDetailExposer extends BroadcastReceiver implements DetailExposer{
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
// if we aren't logged in, don't expose features // if we aren't logged in, don't expose features
if(!ProducteevUtilities.INSTANCE.isLoggedIn()) if(!ProducteevUtilities.INSTANCE.isLoggedIn())
return; return;
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1); long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1) if(taskId == -1)
return; return;
boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false); boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false);
String taskDetail = getTaskDetails(context, taskId, extended); String taskDetail = getTaskDetails(context, taskId, extended);
if(taskDetail == null) if(taskDetail == null)
return; return;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
} }
@Override @Override
public String getTaskDetails(Context context, long id, boolean extended) { public String getTaskDetails(Context context, long id, boolean extended) {
Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(id); Metadata metadata = ProducteevDataService.getInstance().getTaskMetadata(id);
if(metadata == null) if(metadata == null)
return null; return null;
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
if(!extended) { if(!extended) {
long dashboardId = metadata.getValue(ProducteevTask.DASHBOARD_ID); long dashboardId = metadata.getValue(ProducteevTask.DASHBOARD_ID);
String dashboardName = ProducteevDataService.getInstance().getDashboardName(dashboardId); String dashboardName = ProducteevDataService.getInstance().getDashboardName(dashboardId);
// Prod dashboard is out of date. don't display Producteev stuff // Prod dashboard is out of date. don't display Producteev stuff
if(dashboardName == null) if(dashboardName == null)
return null; return null;
if(dashboardId > 0) { if(dashboardId > 0) {
builder.append(context.getString(R.string.producteev_TLA_dashboard, builder.append(context.getString(R.string.producteev_TLA_dashboard,
dashboardId)).append(TaskAdapter.DETAIL_SEPARATOR); dashboardId)).append(TaskAdapter.DETAIL_SEPARATOR);
} }
} else { } else {
TodorooCursor<Metadata> notesCursor = ProducteevDataService.getInstance().getTaskNotesCursor(id); TodorooCursor<Metadata> notesCursor = ProducteevDataService.getInstance().getTaskNotesCursor(id);
try { try {
for(notesCursor.moveToFirst(); !notesCursor.isAfterLast(); notesCursor.moveToNext()) { for(notesCursor.moveToFirst(); !notesCursor.isAfterLast(); notesCursor.moveToNext()) {
metadata.readFromCursor(notesCursor); metadata.readFromCursor(notesCursor);
builder.append(metadata.getValue(ProducteevNote.MESSAGE)).append(TaskAdapter.DETAIL_SEPARATOR); builder.append(metadata.getValue(ProducteevNote.MESSAGE)).append(TaskAdapter.DETAIL_SEPARATOR);
} }
} finally { } finally {
notesCursor.close(); notesCursor.close();
} }
} }
if(builder.length() == 0) if(builder.length() == 0)
return null; return null;
String result = builder.toString(); String result = builder.toString();
return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length()); return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length());
} }
@Override @Override
public String getPluginIdentifier() { public String getPluginIdentifier() {
return ProducteevUtilities.IDENTIFIER; return ProducteevUtilities.IDENTIFIER;
} }
} }

@ -1,86 +1,86 @@
/** /**
* See the file "LICENSE" for the full license governing this code. * See the file "LICENSE" for the full license governing this code.
*/ */
package com.todoroo.astrid.producteev.sync; package com.todoroo.astrid.producteev.sync;
import android.content.ContentValues; import android.content.ContentValues;
import com.todoroo.andlib.data.AbstractModel; import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty; import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.model.Task;
/** /**
* Data Model which represents a dashboard in Producteev * Data Model which represents a dashboard in Producteev
* *
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
@SuppressWarnings("nls") @SuppressWarnings("nls")
public class ProducteevDashboard extends AbstractModel { public class ProducteevDashboard extends AbstractModel {
// --- table // --- table
public static final Table TABLE = new Table("dashboards", ProducteevDashboard.class); public static final Table TABLE = new Table("dashboards", ProducteevDashboard.class);
// --- properties // --- properties
/** ID (corresponds to RTM ID) */ /** ID (corresponds to RTM ID) */
public static final LongProperty ID = new LongProperty( public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME); TABLE, ID_PROPERTY_NAME);
/** Name */ /** Name */
public static final StringProperty NAME = new StringProperty( public static final StringProperty NAME = new StringProperty(
TABLE, "name"); TABLE, "name");
/** List of all properties for this model */ /** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(ProducteevDashboard.class); public static final Property<?>[] PROPERTIES = generateProperties(ProducteevDashboard.class);
// --- defaults // --- defaults
/** Default values container */ /** Default values container */
private static final ContentValues defaultValues = new ContentValues(); private static final ContentValues defaultValues = new ContentValues();
// static { // static {
// defaultValues.put(POSITION.name, 0); // defaultValues.put(POSITION.name, 0);
// defaultValues.put(ARCHIVED.name, 0); // defaultValues.put(ARCHIVED.name, 0);
// } // }
@Override @Override
public ContentValues getDefaultValues() { public ContentValues getDefaultValues() {
return defaultValues; return defaultValues;
} }
// --- data access boilerplate // --- data access boilerplate
public ProducteevDashboard() { public ProducteevDashboard() {
super(); super();
} }
public ProducteevDashboard(TodorooCursor<ProducteevDashboard> cursor) { public ProducteevDashboard(TodorooCursor<ProducteevDashboard> cursor) {
this(); this();
readPropertiesFromCursor(cursor); readPropertiesFromCursor(cursor);
} }
public void readFromCursor(TodorooCursor<ProducteevDashboard> cursor) { public void readFromCursor(TodorooCursor<ProducteevDashboard> cursor) {
super.readPropertiesFromCursor(cursor); super.readPropertiesFromCursor(cursor);
} }
@Override @Override
public long getId() { public long getId() {
return getIdHelper(ID); return getIdHelper(ID);
}; };
// --- parcelable helpers // --- parcelable helpers
private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class); private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class);
@Override @Override
protected Creator<? extends AbstractModel> getCreator() { protected Creator<? extends AbstractModel> getCreator() {
return CREATOR; return CREATOR;
} }
} }

@ -1,235 +1,235 @@
/** /**
* See the file "LICENSE" for the full license governing this code. * See the file "LICENSE" for the full license governing this code.
*/ */
package com.todoroo.astrid.producteev.sync; package com.todoroo.astrid.producteev.sync;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import android.content.Context; import android.content.Context;
import com.todoroo.andlib.data.GenericDao; import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.SoftHashMap; import com.todoroo.andlib.utility.SoftHashMap;
import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.producteev.ProducteevUtilities; import com.todoroo.astrid.producteev.ProducteevUtilities;
import com.todoroo.astrid.rmilk.data.MilkNote; import com.todoroo.astrid.rmilk.data.MilkNote;
import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagService;
public final class ProducteevDataService { public final class ProducteevDataService {
// --- constants // --- constants
/** Utility for joining tasks with metadata */ /** Utility for joining tasks with metadata */
public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK)); public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK));
// --- singleton // --- singleton
private static ProducteevDataService instance = null; private static ProducteevDataService instance = null;
public static synchronized ProducteevDataService getInstance() { public static synchronized ProducteevDataService getInstance() {
if(instance == null) if(instance == null)
instance = new ProducteevDataService(ContextManager.getContext()); instance = new ProducteevDataService(ContextManager.getContext());
return instance; return instance;
} }
// --- instance variables // --- instance variables
protected final Context context; protected final Context context;
private final ProducteevDatabase prodDatabase = new ProducteevDatabase(); private final ProducteevDatabase prodDatabase = new ProducteevDatabase();
private final GenericDao<ProducteevDashboard> prodDashboardDao; private final GenericDao<ProducteevDashboard> prodDashboardDao;
@Autowired @Autowired
private TaskDao taskDao; private TaskDao taskDao;
@Autowired @Autowired
private MetadataDao metadataDao; private MetadataDao metadataDao;
private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE; private final ProducteevUtilities preferences = ProducteevUtilities.INSTANCE;
static final Random random = new Random(); static final Random random = new Random();
private ProducteevDataService(Context context) { private ProducteevDataService(Context context) {
this.context = context; this.context = context;
DependencyInjectionService.getInstance().inject(this); DependencyInjectionService.getInstance().inject(this);
prodDashboardDao = new GenericDao<ProducteevDashboard>(ProducteevDashboard.class, prodDatabase); prodDashboardDao = new GenericDao<ProducteevDashboard>(ProducteevDashboard.class, prodDatabase);
} }
// --- task and metadata methods // --- task and metadata methods
/** /**
* Clears RTM metadata information. Used when user logs out of RTM * Clears RTM metadata information. Used when user logs out of RTM
*/ */
public void clearMetadata() { public void clearMetadata() {
metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY)); metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevTask.METADATA_KEY));
metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevNote.METADATA_KEY)); metadataDao.deleteWhere(Metadata.KEY.eq(ProducteevNote.METADATA_KEY));
} }
/** /**
* Gets tasks that were created since last sync * Gets tasks that were created since last sync
* @param properties * @param properties
* @return * @return
*/ */
public TodorooCursor<Task> getLocallyCreated(Property<?>[] properties) { public TodorooCursor<Task> getLocallyCreated(Property<?>[] properties) {
return return
taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).where(Criterion.and( taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(
Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE). Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).
where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), ProducteevTask.ID.gt(0))))), where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), ProducteevTask.ID.gt(0))))),
TaskCriteria.isActive())).groupBy(Task.ID)); TaskCriteria.isActive())).groupBy(Task.ID));
} }
/** /**
* Gets tasks that were modified since last sync * Gets tasks that were modified since last sync
* @param properties * @param properties
* @return null if never sync'd * @return null if never sync'd
*/ */
public TodorooCursor<Task> getLocallyUpdated(Property<?>[] properties) { public TodorooCursor<Task> getLocallyUpdated(Property<?>[] properties) {
long lastSyncDate = preferences.getLastSyncDate(); long lastSyncDate = preferences.getLastSyncDate();
if(lastSyncDate == 0) if(lastSyncDate == 0)
return taskDao.query(Query.select(Task.ID).where(Criterion.none)); return taskDao.query(Query.select(Task.ID).where(Criterion.none));
return return
taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN). taskDao.query(Query.select(properties).join(ProducteevDataService.METADATA_JOIN).
where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
Task.MODIFICATION_DATE.gt(lastSyncDate))).groupBy(Task.ID)); Task.MODIFICATION_DATE.gt(lastSyncDate))).groupBy(Task.ID));
} }
/** /**
* Searches for a local task with same remote id, updates this task's id * Searches for a local task with same remote id, updates this task's id
* @param remoteTask * @param remoteTask
*/ */
public void findLocalMatch(ProducteevTaskContainer remoteTask) { public void findLocalMatch(ProducteevTaskContainer remoteTask) {
if(remoteTask.task.getId() != Task.NO_ID) if(remoteTask.task.getId() != Task.NO_ID)
return; return;
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.ID). TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.ID).
join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), join(ProducteevDataService.METADATA_JOIN).where(Criterion.and(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
ProducteevTask.ID.eq(remoteTask.pdvTask.getValue(ProducteevTask.ID))))); ProducteevTask.ID.eq(remoteTask.pdvTask.getValue(ProducteevTask.ID)))));
try { try {
if(cursor.getCount() == 0) if(cursor.getCount() == 0)
return; return;
cursor.moveToFirst(); cursor.moveToFirst();
remoteTask.task.setId(cursor.get(Task.ID)); remoteTask.task.setId(cursor.get(Task.ID));
} finally { } finally {
cursor.close(); cursor.close();
} }
} }
/** /**
* Saves a task and its metadata * Saves a task and its metadata
* @param task * @param task
*/ */
public void saveTaskAndMetadata(ProducteevTaskContainer task) { public void saveTaskAndMetadata(ProducteevTaskContainer task) {
taskDao.save(task.task, true); taskDao.save(task.task, true);
metadataDao.deleteWhere(Criterion.and(MetadataCriteria.byTask(task.task.getId()), metadataDao.deleteWhere(Criterion.and(MetadataCriteria.byTask(task.task.getId()),
Criterion.or(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), Criterion.or(MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
MetadataCriteria.withKey(ProducteevNote.METADATA_KEY), MetadataCriteria.withKey(ProducteevNote.METADATA_KEY),
MetadataCriteria.withKey(TagService.KEY)))); MetadataCriteria.withKey(TagService.KEY))));
task.metadata.add(task.pdvTask); task.metadata.add(task.pdvTask);
task.pdvTask.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY); task.pdvTask.setValue(Metadata.KEY, ProducteevTask.METADATA_KEY);
for(Metadata metadata : task.metadata) { for(Metadata metadata : task.metadata) {
metadata.setValue(Metadata.TASK, task.task.getId()); metadata.setValue(Metadata.TASK, task.task.getId());
metadataDao.createNew(metadata); metadataDao.createNew(metadata);
} }
} }
/** /**
* Reads a task and its metadata * Reads a task and its metadata
* @param task * @param task
* @return * @return
*/ */
public ProducteevTaskContainer readTaskAndMetadata(TodorooCursor<Task> taskCursor) { public ProducteevTaskContainer readTaskAndMetadata(TodorooCursor<Task> taskCursor) {
Task task = new Task(taskCursor); Task task = new Task(taskCursor);
// read tags, notes, etc // read tags, notes, etc
ArrayList<Metadata> metadata = new ArrayList<Metadata>(); ArrayList<Metadata> metadata = new ArrayList<Metadata>();
TodorooCursor<Metadata> metadataCursor = metadataDao.query(Query.select(Metadata.PROPERTIES). TodorooCursor<Metadata> metadataCursor = metadataDao.query(Query.select(Metadata.PROPERTIES).
where(Criterion.and(MetadataCriteria.byTask(task.getId()), where(Criterion.and(MetadataCriteria.byTask(task.getId()),
Criterion.or(MetadataCriteria.withKey(TagService.KEY), Criterion.or(MetadataCriteria.withKey(TagService.KEY),
MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), MetadataCriteria.withKey(ProducteevTask.METADATA_KEY),
// FIXME: Constant from other plugin shouldnt be used // FIXME: Constant from other plugin shouldnt be used
MetadataCriteria.withKey(MilkNote.METADATA_KEY), MetadataCriteria.withKey(MilkNote.METADATA_KEY),
MetadataCriteria.withKey(ProducteevNote.METADATA_KEY))))); MetadataCriteria.withKey(ProducteevNote.METADATA_KEY)))));
try { try {
for(metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor.moveToNext()) { for(metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor.moveToNext()) {
metadata.add(new Metadata(metadataCursor)); metadata.add(new Metadata(metadataCursor));
} }
} finally { } finally {
metadataCursor.close(); metadataCursor.close();
} }
return new ProducteevTaskContainer(task, metadata); return new ProducteevTaskContainer(task, metadata);
} }
/** /**
* Reads metadata out of a task * Reads metadata out of a task
* @return null if no metadata found * @return null if no metadata found
*/ */
public Metadata getTaskMetadata(long taskId) { public Metadata getTaskMetadata(long taskId) {
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select( TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(
ProducteevTask.ID, ProducteevTask.DASHBOARD_ID).where( ProducteevTask.ID, ProducteevTask.DASHBOARD_ID).where(
MetadataCriteria.byTaskAndwithKey(taskId, ProducteevTask.METADATA_KEY))); MetadataCriteria.byTaskAndwithKey(taskId, ProducteevTask.METADATA_KEY)));
try { try {
if(cursor.getCount() == 0) if(cursor.getCount() == 0)
return null; return null;
cursor.moveToFirst(); cursor.moveToFirst();
return new Metadata(cursor); return new Metadata(cursor);
} finally { } finally {
cursor.close(); cursor.close();
} }
} }
/** /**
* Reads task notes out of a task * Reads task notes out of a task
*/ */
public TodorooCursor<Metadata> getTaskNotesCursor(long taskId) { public TodorooCursor<Metadata> getTaskNotesCursor(long taskId) {
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES). TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).
where(MetadataCriteria.byTaskAndwithKey(taskId, ProducteevNote.METADATA_KEY))); where(MetadataCriteria.byTaskAndwithKey(taskId, ProducteevNote.METADATA_KEY)));
return cursor; return cursor;
} }
// --- list methods // --- list methods
private final Map<Long, String> dashboardCache = private final Map<Long, String> dashboardCache =
Collections.synchronizedMap(new SoftHashMap<Long, String>()); Collections.synchronizedMap(new SoftHashMap<Long, String>());
/** /**
* Get dashboard name by dashboard id * Get dashboard name by dashboard id
* @param dashboardId * @param dashboardId
* @return null if no dashboard by this id exists, otherwise dashboard name * @return null if no dashboard by this id exists, otherwise dashboard name
*/ */
public String getDashboardName(long dashboardId) { public String getDashboardName(long dashboardId) {
if(dashboardCache.containsKey(dashboardId)) if(dashboardCache.containsKey(dashboardId))
return dashboardCache.get(dashboardId); return dashboardCache.get(dashboardId);
TodorooCursor<ProducteevDashboard> cursor = prodDashboardDao.query(Query.select( TodorooCursor<ProducteevDashboard> cursor = prodDashboardDao.query(Query.select(
ProducteevDashboard.NAME).where(ProducteevDashboard.ID.eq(dashboardId))); ProducteevDashboard.NAME).where(ProducteevDashboard.ID.eq(dashboardId)));
try { try {
if(cursor.getCount() == 0) { if(cursor.getCount() == 0) {
dashboardCache.put(dashboardId, null); dashboardCache.put(dashboardId, null);
return null; return null;
} }
cursor.moveToFirst(); cursor.moveToFirst();
String name = cursor.get(ProducteevDashboard.NAME); String name = cursor.get(ProducteevDashboard.NAME);
dashboardCache.put(dashboardId, name); dashboardCache.put(dashboardId, name);
return name; return name;
} finally { } finally {
cursor.close(); cursor.close();
} }
} }
} }

@ -1,76 +1,76 @@
/* /*
* Copyright (c) 2009, Todoroo Inc * Copyright (c) 2009, Todoroo Inc
* All Rights Reserved * All Rights Reserved
* http://www.todoroo.com * http://www.todoroo.com
*/ */
package com.todoroo.astrid.producteev.sync; package com.todoroo.astrid.producteev.sync;
import com.todoroo.andlib.data.AbstractDatabase; import com.todoroo.andlib.data.AbstractDatabase;
import com.todoroo.andlib.data.GenericDao; import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
/** /**
* Database wrapper * Database wrapper
* *
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
@SuppressWarnings("nls") @SuppressWarnings("nls")
public class ProducteevDatabase extends AbstractDatabase { public class ProducteevDatabase extends AbstractDatabase {
// --- constants // --- constants
/** /**
* Database version number. This variable must be updated when database * Database version number. This variable must be updated when database
* tables are updated, as it determines whether a database needs updating. * tables are updated, as it determines whether a database needs updating.
*/ */
public static final int VERSION = 1; public static final int VERSION = 1;
/** /**
* Database name (must be unique) * Database name (must be unique)
*/ */
private static final String NAME = "producteev"; private static final String NAME = "producteev";
/** /**
* List of table/ If you're adding a new table, add it to this list and * 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. * also make sure that our SQLite helper does the right thing.
*/ */
public static final Table[] TABLES = new Table[] { public static final Table[] TABLES = new Table[] {
ProducteevDashboard.TABLE, ProducteevDashboard.TABLE,
}; };
// --- implementation // --- implementation
private final GenericDao<ProducteevDashboard> dao = new GenericDao<ProducteevDashboard>(ProducteevDashboard.class, this); private final GenericDao<ProducteevDashboard> dao = new GenericDao<ProducteevDashboard>(ProducteevDashboard.class, this);
@Override @Override
protected String getName() { protected String getName() {
return NAME; return NAME;
} }
@Override @Override
protected int getVersion() { protected int getVersion() {
return VERSION; return VERSION;
} }
@Override @Override
public Table[] getTables() { public Table[] getTables() {
return TABLES; return TABLES;
} }
public GenericDao<ProducteevDashboard> getDao() { public GenericDao<ProducteevDashboard> getDao() {
return dao; return dao;
} }
@Override @Override
protected void onCreateTables() { protected void onCreateTables() {
// do nothing // do nothing
} }
@Override @Override
protected boolean onUpgrade(int oldVersion, int newVersion) { protected boolean onUpgrade(int oldVersion, int newVersion) {
return false; return false;
} }
} }

@ -1,24 +1,24 @@
package com.todoroo.astrid.producteev.sync; package com.todoroo.astrid.producteev.sync;
import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.model.Metadata;
/** /**
* Metadata entries for a Producteev Task * Metadata entries for a Producteev Task
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
public class ProducteevTask { public class ProducteevTask {
/** metadata key */ /** metadata key */
public static final String METADATA_KEY = "producteev"; //$NON-NLS-1$ public static final String METADATA_KEY = "producteev"; //$NON-NLS-1$
/** task id in producteev */ /** task id in producteev */
public static final LongProperty ID = new LongProperty(Metadata.TABLE, public static final LongProperty ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE1.name); Metadata.VALUE1.name);
/** dashboard id */ /** dashboard id */
public static final LongProperty DASHBOARD_ID = new LongProperty(Metadata.TABLE, public static final LongProperty DASHBOARD_ID = new LongProperty(Metadata.TABLE,
Metadata.VALUE2.name); Metadata.VALUE2.name);
} }

@ -1,149 +1,149 @@
/* /*
* ASTRID: Android's Simple Task Recording Dashboard * ASTRID: Android's Simple Task Recording Dashboard
* *
* Copyright (c) 2009 Tim Su * Copyright (c) 2009 Tim Su
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, but * This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. * for more details.
* *
* You should have received a copy of the GNU General Public License along * 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., * with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
package com.todoroo.astrid.reminders; package com.todoroo.astrid.reminders;
import java.util.Date; import java.util.Date;
import android.app.TimePickerDialog; import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener; import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TimePicker; import android.widget.TimePicker;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskListActivity; import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;
/** /**
* This activity is launched when a user opens up a notification from the * 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. * tray. It launches the appropriate activity based on the passed in parameters.
* *
* @author timsu * @author timsu
* *
*/ */
public class NotificationActivity extends TaskListActivity implements OnTimeSetListener { public class NotificationActivity extends TaskListActivity implements OnTimeSetListener {
// --- constants // --- constants
/** task id from notification */ /** task id from notification */
public static final String TOKEN_ID = "id"; //$NON-NLS-1$ public static final String TOKEN_ID = "id"; //$NON-NLS-1$
// --- implementation // --- implementation
private long taskId; private long taskId;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
populateFilter(getIntent()); populateFilter(getIntent());
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
displayNotificationPopup(); displayNotificationPopup();
} }
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
populateFilter(intent); populateFilter(intent);
super.onNewIntent(intent); super.onNewIntent(intent);
} }
private void populateFilter(Intent intent) { private void populateFilter(Intent intent) {
taskId = intent.getLongExtra(TOKEN_ID, -1); taskId = intent.getLongExtra(TOKEN_ID, -1);
if(taskId == -1) if(taskId == -1)
return; return;
Filter itemFilter = new Filter(getString(R.string.rmd_NoA_filter), Filter itemFilter = new Filter(getString(R.string.rmd_NoA_filter),
getString(R.string.rmd_NoA_filter), getString(R.string.rmd_NoA_filter),
new QueryTemplate().where(TaskCriteria.byId(taskId)), new QueryTemplate().where(TaskCriteria.byId(taskId)),
null); null);
intent.putExtra(TaskListActivity.TOKEN_FILTER, itemFilter); intent.putExtra(TaskListActivity.TOKEN_FILTER, itemFilter);
} }
/** /**
* Set up the UI for this activity * Set up the UI for this activity
*/ */
private void displayNotificationPopup() { private void displayNotificationPopup() {
// hide quick add // hide quick add
findViewById(R.id.taskListFooter).setVisibility(View.GONE); findViewById(R.id.taskListFooter).setVisibility(View.GONE);
// instantiate reminder window // instantiate reminder window
ViewGroup parent = (ViewGroup) findViewById(R.id.taskListParent); ViewGroup parent = (ViewGroup) findViewById(R.id.taskListParent);
getLayoutInflater().inflate(R.layout.notification_control, parent, true); getLayoutInflater().inflate(R.layout.notification_control, parent, true);
String reminder = Notifications.getRandomReminder(getResources().getStringArray(R.array.reminder_responses)); String reminder = Notifications.getRandomReminder(getResources().getStringArray(R.array.reminder_responses));
if(Preferences.getBoolean(R.string.p_rmd_nagging, true)) if(Preferences.getBoolean(R.string.p_rmd_nagging, true))
((TextView)findViewById(R.id.reminderLabel)).setText(reminder); ((TextView)findViewById(R.id.reminderLabel)).setText(reminder);
else { else {
findViewById(R.id.reminderLabel).setVisibility(View.GONE); findViewById(R.id.reminderLabel).setVisibility(View.GONE);
findViewById(R.id.astridIcon).setVisibility(View.GONE); findViewById(R.id.astridIcon).setVisibility(View.GONE);
} }
// set up listeners // set up listeners
((Button)findViewById(R.id.goAway)).setOnClickListener(new OnClickListener() { ((Button)findViewById(R.id.goAway)).setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View arg0) { public void onClick(View arg0) {
finish(); finish();
} }
}); });
((Button)findViewById(R.id.snooze)).setOnClickListener(new OnClickListener() { ((Button)findViewById(R.id.snooze)).setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View arg0) { public void onClick(View arg0) {
snooze(); snooze();
} }
}); });
} }
/** /**
* Snooze and re-trigger this alarm * Snooze and re-trigger this alarm
*/ */
private void snooze() { private void snooze() {
Date now = new Date(); Date now = new Date();
now.setHours(now.getHours() + 1); now.setHours(now.getHours() + 1);
int hour = now.getHours(); int hour = now.getHours();
int minute = now.getMinutes(); int minute = now.getMinutes();
TimePickerDialog timePicker = new TimePickerDialog(this, this, TimePickerDialog timePicker = new TimePickerDialog(this, this,
hour, minute, DateUtilities.is24HourFormat(this)); hour, minute, DateUtilities.is24HourFormat(this));
timePicker.show(); timePicker.show();
} }
/** snooze timer set */ /** snooze timer set */
@Override @Override
public void onTimeSet(TimePicker picker, int hours, int minutes) { public void onTimeSet(TimePicker picker, int hours, int minutes) {
Date alarmTime = new Date(); Date alarmTime = new Date();
alarmTime.setHours(hours); alarmTime.setHours(hours);
alarmTime.setMinutes(minutes); alarmTime.setMinutes(minutes);
if(alarmTime.getTime() < DateUtilities.now()) if(alarmTime.getTime() < DateUtilities.now())
alarmTime.setDate(alarmTime.getDate() + 1); alarmTime.setDate(alarmTime.getDate() + 1);
ReminderService.getInstance().scheduleSnoozeAlarm(taskId, alarmTime.getTime()); ReminderService.getInstance().scheduleSnoozeAlarm(taskId, alarmTime.getTime());
finish(); finish();
} }
} }

@ -1,128 +1,128 @@
package com.todoroo.astrid.rmilk; package com.todoroo.astrid.rmilk;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.rmilk.sync.RTMSyncProvider; import com.todoroo.astrid.rmilk.sync.RTMSyncProvider;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;
/** /**
* SynchronizationService is the service that performs Astrid's background * SynchronizationService is the service that performs Astrid's background
* synchronization with online task managers. Starting this service * synchronization with online task managers. Starting this service
* schedules a repeating alarm which handles the synchronization * schedules a repeating alarm which handles the synchronization
* *
* @author Tim Su * @author Tim Su
* *
*/ */
public class MilkBackgroundService extends Service { public class MilkBackgroundService extends Service {
/** Minimum time before an auto-sync */ /** Minimum time before an auto-sync */
private static final long AUTO_SYNC_MIN_OFFSET = 5*60*1000L; private static final long AUTO_SYNC_MIN_OFFSET = 5*60*1000L;
/** alarm identifier */ /** alarm identifier */
private static final String SYNC_ACTION = "sync"; //$NON-NLS-1$ private static final String SYNC_ACTION = "sync"; //$NON-NLS-1$
// --- BroadcastReceiver abstract methods // --- BroadcastReceiver abstract methods
/** Receive the alarm - start the synchronize service! */ /** Receive the alarm - start the synchronize service! */
@Override @Override
public void onStart(Intent intent, int startId) { public void onStart(Intent intent, int startId) {
if(SYNC_ACTION.equals(intent.getAction())) if(SYNC_ACTION.equals(intent.getAction()))
startSynchronization(this); startSynchronization(this);
} }
/** Start the actual synchronization */ /** Start the actual synchronization */
private void startSynchronization(Context context) { private void startSynchronization(Context context) {
if(context == null || context.getResources() == null) if(context == null || context.getResources() == null)
return; return;
ContextManager.setContext(context); ContextManager.setContext(context);
if(MilkUtilities.isOngoing()) if(MilkUtilities.isOngoing())
return; return;
new RTMSyncProvider().synchronize(context); new RTMSyncProvider().synchronize(context);
} }
// --- alarm management // --- alarm management
/** /**
* Schedules repeating alarm for auto-synchronization * Schedules repeating alarm for auto-synchronization
*/ */
public static void scheduleService() { public static void scheduleService() {
int syncFrequencySeconds = Preferences.getIntegerFromString( int syncFrequencySeconds = Preferences.getIntegerFromString(
R.string.rmilk_MPr_interval_key, -1); R.string.rmilk_MPr_interval_key, -1);
Context context = ContextManager.getContext(); Context context = ContextManager.getContext();
if(syncFrequencySeconds <= 0) { if(syncFrequencySeconds <= 0) {
unscheduleService(context); unscheduleService(context);
return; return;
} }
// figure out synchronization frequency // figure out synchronization frequency
long interval = 1000L * syncFrequencySeconds; long interval = 1000L * syncFrequencySeconds;
long offset = computeNextSyncOffset(interval); long offset = computeNextSyncOffset(interval);
// give a little padding // give a little padding
offset = Math.max(offset, AUTO_SYNC_MIN_OFFSET); offset = Math.max(offset, AUTO_SYNC_MIN_OFFSET);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, PendingIntent pendingIntent = PendingIntent.getService(context, 0,
createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT); createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
Log.i("Astrid", "Autosync set for " + offset / 1000 //$NON-NLS-1$ //$NON-NLS-2$ Log.i("Astrid", "Autosync set for " + offset / 1000 //$NON-NLS-1$ //$NON-NLS-2$
+ " seconds repeating every " + syncFrequencySeconds); //$NON-NLS-1$ + " seconds repeating every " + syncFrequencySeconds); //$NON-NLS-1$
// cancel all existing // cancel all existing
am.cancel(pendingIntent); am.cancel(pendingIntent);
// schedule new // schedule new
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + offset, am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + offset,
interval, pendingIntent); interval, pendingIntent);
} }
/** /**
* Removes repeating alarm for auto-synchronization * Removes repeating alarm for auto-synchronization
*/ */
private static void unscheduleService(Context context) { private static void unscheduleService(Context context) {
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, PendingIntent pendingIntent = PendingIntent.getService(context, 0,
createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT); createAlarmIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pendingIntent); am.cancel(pendingIntent);
} }
/** Create the alarm intent */ /** Create the alarm intent */
private static Intent createAlarmIntent(Context context) { private static Intent createAlarmIntent(Context context) {
Intent intent = new Intent(context, MilkBackgroundService.class); Intent intent = new Intent(context, MilkBackgroundService.class);
intent.setAction(SYNC_ACTION); intent.setAction(SYNC_ACTION);
return intent; return intent;
} }
// --- utility methods // --- utility methods
private static long computeNextSyncOffset(long interval) { private static long computeNextSyncOffset(long interval) {
// figure out last synchronize time // figure out last synchronize time
long lastSyncDate = MilkUtilities.getLastSyncDate(); long lastSyncDate = MilkUtilities.getLastSyncDate();
// if user never synchronized, give them a full offset period before bg sync // if user never synchronized, give them a full offset period before bg sync
if(lastSyncDate != 0) if(lastSyncDate != 0)
return Math.max(0, lastSyncDate + interval - DateUtilities.now()); return Math.max(0, lastSyncDate + interval - DateUtilities.now());
else else
return interval; return interval;
} }
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return null; return null;
} }
} }

@ -1,106 +1,106 @@
/* /*
* Copyright 2007, MetaDimensional Technologies Inc. * Copyright 2007, MetaDimensional Technologies Inc.
* *
* *
* This file is part of the RememberTheMilk Java API. * This file is part of the RememberTheMilk Java API.
* *
* The RememberTheMilk Java API is free software; you can redistribute it * 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 * 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 * as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version. * License, or (at your option) any later version.
* *
* The RememberTheMilk Java API is distributed in the hope that it will be * The RememberTheMilk Java API is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package com.todoroo.astrid.rmilk.api.data; package com.todoroo.astrid.rmilk.api.data;
import java.util.Date; import java.util.Date;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.EntityReference; import org.w3c.dom.EntityReference;
import org.w3c.dom.Text; import org.w3c.dom.Text;
import android.util.Log; import android.util.Log;
/** /**
* Represents a single task note. * Represents a single task note.
* *
* @author Edouard Mercier * @author Edouard Mercier
* @since 2008.04.22 * @since 2008.04.22
*/ */
@SuppressWarnings("nls") @SuppressWarnings("nls")
public class RtmTaskNote public class RtmTaskNote
extends RtmData extends RtmData
{ {
private final String id; private final String id;
private final Date created; private final Date created;
private final Date modified; private final Date modified;
private final String title; private final String title;
private String text; private String text;
public RtmTaskNote(Element element) public RtmTaskNote(Element element)
{ {
id = element.getAttribute("id"); id = element.getAttribute("id");
created = parseDate(element.getAttribute("created")); created = parseDate(element.getAttribute("created"));
modified = parseDate(element.getAttribute("modified")); modified = parseDate(element.getAttribute("modified"));
title = element.getAttribute("title"); title = element.getAttribute("title");
// The note text itself might be split across multiple children of the // The note text itself might be split across multiple children of the
// note element, so get all of the children. // note element, so get all of the children.
for (int i=0; i < element.getChildNodes().getLength(); i++) { for (int i=0; i < element.getChildNodes().getLength(); i++) {
Object innerNote = element.getChildNodes().item(i); Object innerNote = element.getChildNodes().item(i);
if(innerNote instanceof EntityReference) // this node is empty if(innerNote instanceof EntityReference) // this node is empty
continue; continue;
if(!(innerNote instanceof Text)) { if(!(innerNote instanceof Text)) {
Log.w("rtm-note", "Expected text type, got " + innerNote.getClass()); Log.w("rtm-note", "Expected text type, got " + innerNote.getClass());
continue; continue;
} }
Text innerText = (Text) innerNote; Text innerText = (Text) innerNote;
if (text == null) if (text == null)
text = innerText.getData(); text = innerText.getData();
else else
text = text.concat(innerText.getData()); text = text.concat(innerText.getData());
} }
} }
public String getId() public String getId()
{ {
return id; return id;
} }
public Date getCreated() public Date getCreated()
{ {
return created; return created;
} }
public Date getModified() public Date getModified()
{ {
return modified; return modified;
} }
public String getTitle() public String getTitle()
{ {
return title; return title;
} }
public String getText() public String getText()
{ {
return text; return text;
} }
} }

@ -1,50 +1,50 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout <FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:background="@drawable/background_gradient"> android:background="@drawable/background_gradient">
<!-- =================================================== tab: installed == --> <!-- =================================================== tab: installed == -->
<FrameLayout android:id="@+id/tab_installed" <FrameLayout android:id="@+id/tab_installed"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<TextView android:id="@+id/empty_installed" <TextView android:id="@+id/empty_installed"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:gravity="center" android:gravity="center"
android:text="@string/TEA_no_addons" android:text="@string/TEA_no_addons"
style="@style/TextAppearance.TLA_NoItems" /> style="@style/TextAppearance.TLA_NoItems" />
<ListView android:id="@+id/installed" <ListView android:id="@+id/installed"
android:paddingRight="8dip" android:paddingRight="8dip"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" /> android:layout_height="fill_parent" />
</FrameLayout> </FrameLayout>
<!-- =================================================== tab: available == --> <!-- =================================================== tab: available == -->
<FrameLayout android:id="@+id/tab_available" <FrameLayout android:id="@+id/tab_available"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<TextView android:id="@+id/empty_available" <TextView android:id="@+id/empty_available"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:gravity="center" android:gravity="center"
android:text="@string/TEA_no_addons" android:text="@string/TEA_no_addons"
style="@style/TextAppearance.TLA_NoItems" /> style="@style/TextAppearance.TLA_NoItems" />
<ListView android:id="@+id/available" <ListView android:id="@+id/available"
android:paddingRight="8dip" android:paddingRight="8dip"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" /> android:layout_height="fill_parent" />
</FrameLayout> </FrameLayout>
</FrameLayout> </FrameLayout>

@ -1,88 +1,88 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. --> <!-- See the file "LICENSE" for the full license governing this code. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:background="@android:drawable/list_selector_background" android:background="@android:drawable/list_selector_background"
android:paddingTop="4dip" android:paddingTop="4dip"
android:paddingBottom="4dip" android:paddingBottom="4dip"
android:paddingLeft="4dip" android:paddingLeft="4dip"
android:paddingRight="4dip"> android:paddingRight="4dip">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="5dip" android:paddingLeft="5dip"
android:orientation="vertical"> android:orientation="vertical">
<!-- icon --> <!-- icon -->
<ImageView android:id="@+id/icon" <ImageView android:id="@+id/icon"
android:layout_width="48dip" android:layout_width="48dip"
android:layout_height="48dip" android:layout_height="48dip"
android:gravity="center" android:gravity="center"
android:scaleType="fitCenter"/> android:scaleType="fitCenter"/>
<!-- free --> <!-- free -->
<TextView android:id="@+id/free" <TextView android:id="@+id/free"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:textSize="10sp" android:textSize="10sp"
android:textColor="#00ff00" android:textColor="#00ff00"
android:text="@string/AOA_free" /> android:text="@string/AOA_free" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="100" android:layout_weight="100"
android:paddingLeft="8dip" android:paddingLeft="8dip"
android:paddingRight="3dip" android:paddingRight="3dip"
android:orientation="vertical"> android:orientation="vertical">
<!-- add-on name --> <!-- add-on name -->
<TextView android:id="@+id/title" <TextView android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_toRightOf="@id/icon" android:layout_toRightOf="@id/icon"
android:singleLine="true" android:singleLine="true"
style="@style/TextAppearance.TAd_ItemTitle"/> style="@style/TextAppearance.TAd_ItemTitle"/>
<!-- description --> <!-- description -->
<TextView android:id="@+id/description" <TextView android:id="@+id/description"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/title" android:layout_below="@id/title"
android:layout_toRightOf="@id/icon" android:layout_toRightOf="@id/icon"
android:singleLine="false" android:singleLine="false"
android:textSize="11sp" android:textSize="11sp"
style="@style/TextAppearance.TAd_ItemDetails"/> style="@style/TextAppearance.TAd_ItemDetails"/>
</LinearLayout> </LinearLayout>
<!-- buttons --> <!-- buttons -->
<ImageButton android:id="@+id/button_web" <ImageButton android:id="@+id/button_web"
android:layout_width="48dip" android:layout_width="48dip"
android:layout_height="48dip" android:layout_height="48dip"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:layout_gravity="center" /> android:layout_gravity="center" />
<ImageButton android:id="@+id/button_market" <ImageButton android:id="@+id/button_market"
android:layout_width="48dip" android:layout_width="48dip"
android:layout_height="48dip" android:layout_height="48dip"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:layout_gravity="center" /> android:layout_gravity="center" />
<ImageView android:id="@+id/check" <ImageView android:id="@+id/check"
android:layout_width="48dip" android:layout_width="48dip"
android:layout_height="48dip" android:layout_height="48dip"
android:scaleType="center" android:scaleType="center"
android:layout_gravity="center" android:layout_gravity="center"
android:src="@android:drawable/checkbox_on_background"/> android:src="@android:drawable/checkbox_on_background"/>
</LinearLayout> </LinearLayout>

@ -1,304 +1,304 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout <FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:background="@drawable/background_gradient"> android:background="@drawable/background_gradient">
<!-- ======================================================= tab: basic == --> <!-- ======================================================= tab: basic == -->
<ScrollView <ScrollView
android:id="@+id/tab_basic" android:id="@+id/tab_basic"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<LinearLayout <LinearLayout
android:id="@+id/event" android:id="@+id/event"
android:paddingRight="8dip" android:paddingRight="8dip"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<!-- title --> <!-- title -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_title_label" android:text="@string/TEA_title_label"
style="@style/TextAppearance.GEN_EditLabel" /> style="@style/TextAppearance.GEN_EditLabel" />
<EditText <EditText
android:id="@+id/title" android:id="@+id/title"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/TEA_title_hint" android:hint="@string/TEA_title_hint"
android:autoText="true" android:autoText="true"
android:capitalize="sentences" /> android:capitalize="sentences" />
<!-- importance --> <!-- importance -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_importance_label" android:text="@string/TEA_importance_label"
style="@style/TextAppearance.GEN_EditLabel" /> style="@style/TextAppearance.GEN_EditLabel" />
<LinearLayout <LinearLayout
android:id="@+id/importance_container" android:id="@+id/importance_container"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
</LinearLayout> </LinearLayout>
<!-- urgency --> <!-- urgency -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_urgency_label" android:text="@string/TEA_urgency_label"
style="@style/TextAppearance.GEN_EditLabel" /> style="@style/TextAppearance.GEN_EditLabel" />
<Spinner <Spinner
android:id="@+id/urgency" android:id="@+id/urgency"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<!-- tags --> <!-- tags -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_tags_label" android:text="@string/TEA_tags_label"
style="@style/TextAppearance.GEN_EditLabel" /> style="@style/TextAppearance.GEN_EditLabel" />
<LinearLayout <LinearLayout
android:id="@+id/tags_container" android:id="@+id/tags_container"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="5dip" android:paddingBottom="5dip"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<!-- separator --> <!-- separator -->
<View <View
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="1dip" android:layout_height="1dip"
android:padding="5dip" android:padding="5dip"
android:background="@android:drawable/divider_horizontal_dark" /> android:background="@android:drawable/divider_horizontal_dark" />
<!-- notes --> <!-- notes -->
<TextView <TextView
android:paddingTop="5dip" android:paddingTop="5dip"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_note_label" android:text="@string/TEA_note_label"
style="@style/TextAppearance.GEN_EditLabel" /> style="@style/TextAppearance.GEN_EditLabel" />
<EditText <EditText
android:id="@+id/notes" android:id="@+id/notes"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:scrollbars="vertical" android:scrollbars="vertical"
android:gravity="top" android:gravity="top"
android:hint="@string/TEA_notes_hint" android:hint="@string/TEA_notes_hint"
android:autoText="true" android:autoText="true"
android:capitalize="sentences" android:capitalize="sentences"
android:singleLine="false" /> android:singleLine="false" />
<!-- buttons --> <!-- buttons -->
<LinearLayout <LinearLayout
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dip" android:layout_marginTop="10dip"
android:padding="5dip" android:padding="5dip"
android:background="@drawable/edit_header" android:background="@drawable/edit_header"
android:baselineAligned="false"> android:baselineAligned="false">
<ImageButton <ImageButton
android:id="@+id/save_basic" android:id="@+id/save_basic"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:src="@drawable/tango_save" /> android:src="@drawable/tango_save" />
<ImageButton <ImageButton
android:id="@+id/discard_basic" android:id="@+id/discard_basic"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:src="@drawable/tango_stop" /> android:src="@drawable/tango_stop" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<!-- ======================================================= tab: extra == --> <!-- ======================================================= tab: extra == -->
<ScrollView <ScrollView
android:id="@+id/tab_extra" android:id="@+id/tab_extra"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<LinearLayout <LinearLayout
android:paddingRight="8dip" android:paddingRight="8dip"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<!-- reminders --> <!-- reminders -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_reminder_label" android:text="@string/TEA_reminder_label"
style="@style/TextAppearance.GEN_EditLabel" /> style="@style/TextAppearance.GEN_EditLabel" />
<CheckBox <CheckBox
android:id="@+id/reminder_due" android:id="@+id/reminder_due"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_reminder_due" /> android:text="@string/TEA_reminder_due" />
<CheckBox <CheckBox
android:id="@+id/reminder_overdue" android:id="@+id/reminder_overdue"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_reminder_overdue" /> android:text="@string/TEA_reminder_overdue" />
<LinearLayout <LinearLayout
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<CheckBox <CheckBox
android:id="@+id/reminder_random" android:id="@+id/reminder_random"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_reminder_random" /> android:text="@string/TEA_reminder_random" />
<Spinner <Spinner
android:id="@+id/reminder_random_interval" android:id="@+id/reminder_random_interval"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
<!-- reminder mode --> <!-- reminder mode -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_reminder_alarm_label" android:text="@string/TEA_reminder_alarm_label"
style="@style/TextAppearance.GEN_EditLabel" /> style="@style/TextAppearance.GEN_EditLabel" />
<Spinner <Spinner
android:id="@+id/reminder_alarm" android:id="@+id/reminder_alarm"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<!-- separator --> <!-- separator -->
<View <View
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="1dip" android:layout_height="1dip"
android:padding="5dip" android:padding="5dip"
android:background="@android:drawable/divider_horizontal_dark" /> android:background="@android:drawable/divider_horizontal_dark" />
<!-- hide until --> <!-- hide until -->
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_hideUntil_label" android:text="@string/TEA_hideUntil_label"
style="@style/TextAppearance.GEN_EditLabel" /> style="@style/TextAppearance.GEN_EditLabel" />
<Spinner <Spinner
android:id="@+id/hideUntil" android:id="@+id/hideUntil"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<!-- add-ons --> <!-- add-ons -->
<LinearLayout android:id="@+id/tab_extra_addons" <LinearLayout android:id="@+id/tab_extra_addons"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" /> android:orientation="vertical" />
<!-- buttons --> <!-- buttons -->
<LinearLayout <LinearLayout
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dip" android:layout_marginTop="10dip"
android:padding="5dip" android:padding="5dip"
android:background="@drawable/edit_header" android:background="@drawable/edit_header"
android:baselineAligned="false"> android:baselineAligned="false">
<ImageButton <ImageButton
android:id="@+id/save_extra" android:id="@+id/save_extra"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:src="@drawable/tango_save" /> android:src="@drawable/tango_save" />
<ImageButton <ImageButton
android:id="@+id/discard_extra" android:id="@+id/discard_extra"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:src="@drawable/tango_stop" /> android:src="@drawable/tango_stop" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<!-- ===================================================== tab: add-ons == --> <!-- ===================================================== tab: add-ons == -->
<LinearLayout <LinearLayout
android:id="@+id/tab_addons" android:id="@+id/tab_addons"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical"> android:orientation="vertical">
<ScrollView <ScrollView
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_weight="100"> android:layout_weight="100">
<!-- add-ons --> <!-- add-ons -->
<LinearLayout android:id="@+id/tab_addons_addons" <LinearLayout android:id="@+id/tab_addons_addons"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical" /> android:orientation="vertical" />
</ScrollView> </ScrollView>
<LinearLayout android:id="@+id/addons_empty" <LinearLayout android:id="@+id/addons_empty"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_weight="100" android:layout_weight="100"
android:gravity="center" android:gravity="center"
android:visibility="gone" android:visibility="gone"
android:orientation="vertical"> android:orientation="vertical">
<ImageView <ImageView
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="20dip" android:padding="20dip"
android:src="@drawable/icon_pp" /> android:src="@drawable/icon_pp" />
<TextView <TextView
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/TEA_no_addons" android:text="@string/TEA_no_addons"
android:padding="10dip" android:padding="10dip"
android:gravity="center" android:gravity="center"
style="@style/TextAppearance.TLA_NoItems" /> style="@style/TextAppearance.TLA_NoItems" />
<Button android:id="@+id/addons_button" <Button android:id="@+id/addons_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="10dip" android:padding="10dip"
android:text="@string/TEA_addons_button" /> android:text="@string/TEA_addons_button" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginTop="10dip" android:layout_marginTop="10dip"
android:padding="5dip" android:padding="5dip"
android:orientation="horizontal" android:orientation="horizontal"
android:background="@drawable/edit_header" android:background="@drawable/edit_header"
android:baselineAligned="false"> android:baselineAligned="false">
<ImageButton <ImageButton
android:id="@+id/save_addons" android:id="@+id/save_addons"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:src="@drawable/tango_save" /> android:src="@drawable/tango_save" />
<ImageButton <ImageButton
android:id="@+id/discard_addons" android:id="@+id/discard_addons"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:src="@drawable/tango_stop" /> android:src="@drawable/tango_stop" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>

@ -1,30 +1,30 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. --> <!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ====================== Plugin Boilerplate ========================= --> <!-- ====================== Plugin Boilerplate ========================= -->
<!-- task detail showing Producteev dashboard information --> <!-- task detail showing Producteev dashboard information -->
<string name="producteev_TLA_dashboard">Producteev dashboard: %s</string> <string name="producteev_TLA_dashboard">Producteev dashboard: %s</string>
<!-- ==================================================== Preferences == --> <!-- ==================================================== Preferences == -->
<!-- Preferences Title: Producteev --> <!-- Preferences Title: Producteev -->
<string name="producteev_PPr_header">Producteev</string> <string name="producteev_PPr_header">Producteev</string>
<!-- ================================================ Synchronization == --> <!-- ================================================ Synchronization == -->
<!-- title for notification tray when synchronizing --> <!-- title for notification tray when synchronizing -->
<string name="producteev_notification_title">Astrid: Producteev</string> <string name="producteev_notification_title">Astrid: Producteev</string>
<!-- Error msg when io exception --> <!-- Error msg when io exception -->
<string name="producteev_ioerror">Connection Error! Check your Internet connection.</string> <string name="producteev_ioerror">Connection Error! Check your Internet connection.</string>
<!-- Prod Login email not specified--> <!-- Prod Login email not specified-->
<string name="producteev_MLA_email_empty">E-Mail was not specified!</string> <string name="producteev_MLA_email_empty">E-Mail was not specified!</string>
<!-- Prod Login password not specified--> <!-- Prod Login password not specified-->
<string name="producteev_MLA_password_empty">Password was not specified!</string> <string name="producteev_MLA_password_empty">Password was not specified!</string>
</resources> </resources>

@ -1,63 +1,63 @@
/* /*
* ASTRID: Android's Simple Task Recording Dashboard * ASTRID: Android's Simple Task Recording Dashboard
* *
* Copyright (c) 2009 Tim Su * Copyright (c) 2009 Tim Su
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, but * This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. * for more details.
* *
* You should have received a copy of the GNU General Public License along * 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., * with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
package com.timsu.astrid.activities; package com.timsu.astrid.activities;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import com.todoroo.astrid.activity.TaskListActivity; import com.todoroo.astrid.activity.TaskListActivity;
/** /**
* Legacy task shortcut, takes users to the updated {@link TaskListActivity}. * Legacy task shortcut, takes users to the updated {@link TaskListActivity}.
* This activity is around so users with existing desktop icons will * This activity is around so users with existing desktop icons will
* be able to still launch Astrid. * be able to still launch Astrid.
* *
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
public class TaskList extends Activity { public class TaskList extends Activity {
// --- implementation // --- implementation
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
launchTaskList(getIntent()); launchTaskList(getIntent());
} }
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
launchTaskList(intent); launchTaskList(intent);
} }
/** /**
* intent: ignored for now * intent: ignored for now
* @param intent * @param intent
*/ */
private void launchTaskList(Intent intent) { private void launchTaskList(Intent intent) {
Intent taskListIntent = new Intent(this, TaskListActivity.class); Intent taskListIntent = new Intent(this, TaskListActivity.class);
startActivity(taskListIntent); startActivity(taskListIntent);
finish(); finish();
} }
} }

@ -1,107 +1,107 @@
/* /*
* ASTRID: Android's Simple Task Recording Dashboard * ASTRID: Android's Simple Task Recording Dashboard
* *
* Copyright (c) 2009 Tim Su * Copyright (c) 2009 Tim Su
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, but * This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. * for more details.
* *
* You should have received a copy of the GNU General Public License along * 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., * with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
package com.todoroo.astrid.activity; package com.todoroo.astrid.activity;
import android.app.Activity; import android.app.Activity;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
/** /**
* This activity is launched when a user opens up a notification from the * 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. * tray. It launches the appropriate activity based on the passed in parameters.
* *
* @author timsu * @author timsu
* *
*/ */
public class ShortcutActivity extends Activity { public class ShortcutActivity extends Activity {
// --- constants // --- constants
/** token for passing a {@link Filter}'s title through extras */ /** token for passing a {@link Filter}'s title through extras */
public static final String TOKEN_FILTER_TITLE = "title"; //$NON-NLS-1$ public static final String TOKEN_FILTER_TITLE = "title"; //$NON-NLS-1$
/** token for passing a {@link Filter}'s sql through extras */ /** token for passing a {@link Filter}'s sql through extras */
public static final String TOKEN_FILTER_SQL = "sql"; //$NON-NLS-1$ public static final String TOKEN_FILTER_SQL = "sql"; //$NON-NLS-1$
/** token for passing a {@link Filter}'s values for new tasks through extras */ /** token for passing a {@link Filter}'s values for new tasks through extras */
public static final String TOKEN_FILTER_VALUES = "v4nt"; //$NON-NLS-1$ public static final String TOKEN_FILTER_VALUES = "v4nt"; //$NON-NLS-1$
/** token for passing a {@link Filter}'s values for new tasks through extras (keys) */ /** token for passing a {@link Filter}'s values for new tasks through extras (keys) */
public static final String TOKEN_FILTER_VALUES_KEYS = "v4ntk"; //$NON-NLS-1$ public static final String TOKEN_FILTER_VALUES_KEYS = "v4ntk"; //$NON-NLS-1$
/** token for passing a {@link Filter}'s values for new tasks through extras (values) */ /** token for passing a {@link Filter}'s values for new tasks through extras (values) */
public static final String TOKEN_FILTER_VALUES_VALUES = "v4ntv"; //$NON-NLS-1$ public static final String TOKEN_FILTER_VALUES_VALUES = "v4ntv"; //$NON-NLS-1$
// --- implementation // --- implementation
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
launchTaskList(getIntent()); launchTaskList(getIntent());
} }
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
launchTaskList(intent); launchTaskList(intent);
} }
private void launchTaskList(Intent intent) { private void launchTaskList(Intent intent) {
Bundle extras = intent.getExtras(); Bundle extras = intent.getExtras();
if(extras != null && extras.containsKey(TOKEN_FILTER_SQL)) { if(extras != null && extras.containsKey(TOKEN_FILTER_SQL)) {
// launched from desktop shortcut, must create a fake filter // launched from desktop shortcut, must create a fake filter
String title = extras.getString(TOKEN_FILTER_TITLE); String title = extras.getString(TOKEN_FILTER_TITLE);
String sql = extras.getString(TOKEN_FILTER_SQL); String sql = extras.getString(TOKEN_FILTER_SQL);
ContentValues values = null; ContentValues values = null;
if(extras.containsKey(TOKEN_FILTER_VALUES)) if(extras.containsKey(TOKEN_FILTER_VALUES))
values = AndroidUtilities.contentValuesFromString(extras.getString(TOKEN_FILTER_VALUES)); values = AndroidUtilities.contentValuesFromString(extras.getString(TOKEN_FILTER_VALUES));
Filter filter = new Filter("", title, new QueryTemplate(), values); //$NON-NLS-1$ Filter filter = new Filter("", title, new QueryTemplate(), values); //$NON-NLS-1$
filter.sqlQuery = sql; filter.sqlQuery = sql;
Intent taskListIntent = new Intent(this, TaskListActivity.class); Intent taskListIntent = new Intent(this, TaskListActivity.class);
taskListIntent.putExtra(TaskListActivity.TOKEN_FILTER, filter); taskListIntent.putExtra(TaskListActivity.TOKEN_FILTER, filter);
startActivity(taskListIntent); startActivity(taskListIntent);
} }
finish(); finish();
} }
public static Intent createIntent(Filter filter) { public static Intent createIntent(Filter filter) {
Intent shortcutIntent = new Intent(ContextManager.getContext(), Intent shortcutIntent = new Intent(ContextManager.getContext(),
ShortcutActivity.class); ShortcutActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW); shortcutIntent.setAction(Intent.ACTION_VIEW);
shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_TITLE, filter.title); shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_TITLE, filter.title);
shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_SQL, filter.sqlQuery); shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_SQL, filter.sqlQuery);
if(filter.valuesForNewTasks != null) { if(filter.valuesForNewTasks != null) {
shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_VALUES, shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_VALUES,
filter.valuesForNewTasks.toString()); filter.valuesForNewTasks.toString());
} }
return shortcutIntent; return shortcutIntent;
} }
} }

@ -1,288 +1,288 @@
package com.todoroo.astrid.provider; package com.todoroo.astrid.provider;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.UriMatcher; import android.content.UriMatcher;
import android.database.Cursor; import android.database.Cursor;
import android.database.MatrixCursor; import android.database.MatrixCursor;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.AstridDependencyInjector; import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.tags.TagService.Tag; import com.todoroo.astrid.tags.TagService.Tag;
/** /**
* This is the legacy Astrid task provider. While it will continue to be * This is the legacy Astrid task provider. While it will continue to be
* supported, note that it does not expose all of the information in * supported, note that it does not expose all of the information in
* Astrid, nor does it support many editing operations. * Astrid, nor does it support many editing operations.
* *
* See the individual methods for a description of what is returned. * See the individual methods for a description of what is returned.
* *
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
@SuppressWarnings("nls") @SuppressWarnings("nls")
public class Astrid2TaskProvider extends ContentProvider { public class Astrid2TaskProvider extends ContentProvider {
static { static {
AstridDependencyInjector.initialize(); AstridDependencyInjector.initialize();
} }
private static final String TAG = "MessageProvider"; private static final String TAG = "MessageProvider";
private static final boolean LOGD = false; private static final boolean LOGD = false;
public static final String AUTHORITY = "com.timsu.astrid.tasksprovider"; public static final String AUTHORITY = "com.timsu.astrid.tasksprovider";
public static final Uri CONTENT_URI = Uri.parse("content://com.timsu.astrid.tasksprovider"); public static final Uri CONTENT_URI = Uri.parse("content://com.timsu.astrid.tasksprovider");
private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
private static final int MAX_NUMBER_OF_TASKS = 30; private static final int MAX_NUMBER_OF_TASKS = 30;
private final static String NAME = "name"; private final static String NAME = "name";
private final static String IMPORTANCE_COLOR = "importance_color"; private final static String IMPORTANCE_COLOR = "importance_color";
private final static String IDENTIFIER = "identifier"; private final static String IDENTIFIER = "identifier";
private final static String PREFERRED_DUE_DATE = "preferredDueDate"; private final static String PREFERRED_DUE_DATE = "preferredDueDate";
private final static String DEFINITE_DUE_DATE = "definiteDueDate"; private final static String DEFINITE_DUE_DATE = "definiteDueDate";
private final static String IMPORTANCE = "importance"; private final static String IMPORTANCE = "importance";
private final static String ID = "id"; private final static String ID = "id";
// fake property for updating that completes a task // fake property for updating that completes a task
private final static String COMPLETED = "completed"; private final static String COMPLETED = "completed";
private final static String TAGS_ID = "tags_id"; private final static String TAGS_ID = "tags_id";
static String[] TASK_FIELD_LIST = new String[] { NAME, IMPORTANCE_COLOR, PREFERRED_DUE_DATE, DEFINITE_DUE_DATE, static String[] TASK_FIELD_LIST = new String[] { NAME, IMPORTANCE_COLOR, PREFERRED_DUE_DATE, DEFINITE_DUE_DATE,
IMPORTANCE, IDENTIFIER, TAGS_ID }; IMPORTANCE, IDENTIFIER, TAGS_ID };
static String[] TAGS_FIELD_LIST = new String[] { ID, NAME }; static String[] TAGS_FIELD_LIST = new String[] { ID, NAME };
private static final int URI_TASKS = 0; private static final int URI_TASKS = 0;
private static final int URI_TAGS = 1; private static final int URI_TAGS = 1;
private static final String TAG_SEPARATOR = "|"; private static final String TAG_SEPARATOR = "|";
@Autowired @Autowired
private TaskService taskService; private TaskService taskService;
private static Context ctx = null; private static Context ctx = null;
static { static {
URI_MATCHER.addURI(AUTHORITY, "tasks", URI_TASKS); URI_MATCHER.addURI(AUTHORITY, "tasks", URI_TASKS);
URI_MATCHER.addURI(AUTHORITY, "tags", URI_TAGS); URI_MATCHER.addURI(AUTHORITY, "tags", URI_TAGS);
AstridDependencyInjector.initialize(); AstridDependencyInjector.initialize();
} }
public Astrid2TaskProvider() { public Astrid2TaskProvider() {
DependencyInjectionService.getInstance().inject(this); DependencyInjectionService.getInstance().inject(this);
} }
@Override @Override
public int delete(Uri uri, String selection, String[] selectionArgs) { public int delete(Uri uri, String selection, String[] selectionArgs) {
if (LOGD) if (LOGD)
Log.d(TAG, "delete"); Log.d(TAG, "delete");
return 0; return 0;
} }
@Override @Override
public String getType(Uri uri) { public String getType(Uri uri) {
return null; return null;
} }
@Override @Override
public Uri insert(Uri uri, ContentValues values) { public Uri insert(Uri uri, ContentValues values) {
return null; return null;
} }
@Override @Override
public boolean onCreate() { public boolean onCreate() {
ctx = getContext(); ctx = getContext();
return false; return false;
} }
/** /**
* Note: tag id is no longer a real column, so we pass in a UID * Note: tag id is no longer a real column, so we pass in a UID
* generated from the tag string. * generated from the tag string.
* *
* @return two-column cursor: tag id (string) and tag name * @return two-column cursor: tag id (string) and tag name
*/ */
public Cursor getTags() { public Cursor getTags() {
Tag[] tags = TagService.getInstance().getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE); Tag[] tags = TagService.getInstance().getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE);
MatrixCursor ret = new MatrixCursor(TAGS_FIELD_LIST); MatrixCursor ret = new MatrixCursor(TAGS_FIELD_LIST);
for (int i = 0; i < tags.length; i++) { for (int i = 0; i < tags.length; i++) {
Object[] values = new Object[2]; Object[] values = new Object[2];
values[0] = tagNameToLong(tags[i].tag); values[0] = tagNameToLong(tags[i].tag);
values[1] = tags[i].tag; values[1] = tags[i].tag;
ret.addRow(values); ret.addRow(values);
} }
return ret; return ret;
} }
private long tagNameToLong(String tag) { private long tagNameToLong(String tag) {
MessageDigest m; MessageDigest m;
try { try {
m = MessageDigest.getInstance("MD5"); m = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
return -1; return -1;
} }
m.update(tag.getBytes(), 0, tag.length()); m.update(tag.getBytes(), 0, tag.length());
return new BigInteger(1, m.digest()).longValue(); return new BigInteger(1, m.digest()).longValue();
} }
/** /**
* Cursor with the following columns * Cursor with the following columns
* <ol> * <ol>
* <li>task title, string * <li>task title, string
* <li>task importance color, int android RGB color * <li>task importance color, int android RGB color
* <li>task due date (was: preferred due date), long millis since epoch * <li>task due date (was: preferred due date), long millis since epoch
* <li>task due date (was: absolute due date), long millis since epoch * <li>task due date (was: absolute due date), long millis since epoch
* <li>task importance, integer from 0 to 3 (0 => most important) * <li>task importance, integer from 0 to 3 (0 => most important)
* <li>task id, long * <li>task id, long
* <li>task tags, string tags separated by | * <li>task tags, string tags separated by |
* </ol> * </ol>
* *
* @return cursor as described above * @return cursor as described above
*/ */
public Cursor getTasks() { public Cursor getTasks() {
MatrixCursor ret = new MatrixCursor(TASK_FIELD_LIST); MatrixCursor ret = new MatrixCursor(TASK_FIELD_LIST);
TodorooCursor<Task> cursor = taskService.query(Query.select(Task.ID, Task.TITLE, TodorooCursor<Task> cursor = taskService.query(Query.select(Task.ID, Task.TITLE,
Task.IMPORTANCE, Task.DUE_DATE).where(Criterion.and(TaskCriteria.isActive(), Task.IMPORTANCE, Task.DUE_DATE).where(Criterion.and(TaskCriteria.isActive(),
TaskCriteria.isVisible())). TaskCriteria.isVisible())).
orderBy(TaskService.defaultTaskOrder()).limit(MAX_NUMBER_OF_TASKS)); orderBy(TaskService.defaultTaskOrder()).limit(MAX_NUMBER_OF_TASKS));
try { try {
int[] importanceColors = Task.getImportanceColors(ctx.getResources()); int[] importanceColors = Task.getImportanceColors(ctx.getResources());
Task task = new Task(); Task task = new Task();
for (int i = 0; i < cursor.getCount(); i++) { for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToNext(); cursor.moveToNext();
task.readFromCursor(cursor); task.readFromCursor(cursor);
StringBuilder taskTags = new StringBuilder(); StringBuilder taskTags = new StringBuilder();
TodorooCursor<Metadata> tagCursor = TagService.getInstance().getTags(task.getId()); TodorooCursor<Metadata> tagCursor = TagService.getInstance().getTags(task.getId());
try { try {
for(tagCursor.moveToFirst(); !tagCursor.isAfterLast(); tagCursor.moveToNext()) for(tagCursor.moveToFirst(); !tagCursor.isAfterLast(); tagCursor.moveToNext())
taskTags.append(tagCursor.get(TagService.TAG)).append(TAG_SEPARATOR); taskTags.append(tagCursor.get(TagService.TAG)).append(TAG_SEPARATOR);
} finally { } finally {
tagCursor.close(); tagCursor.close();
} }
Object[] values = new Object[7]; Object[] values = new Object[7];
values[0] = task.getValue(Task.TITLE); values[0] = task.getValue(Task.TITLE);
values[1] = importanceColors[task.getValue(Task.IMPORTANCE)]; values[1] = importanceColors[task.getValue(Task.IMPORTANCE)];
values[2] = task.getValue(Task.DUE_DATE); values[2] = task.getValue(Task.DUE_DATE);
values[3] = task.getValue(Task.DUE_DATE); values[3] = task.getValue(Task.DUE_DATE);
values[4] = task.getValue(Task.IMPORTANCE); values[4] = task.getValue(Task.IMPORTANCE);
values[5] = task.getId(); values[5] = task.getId();
values[6] = taskTags.toString(); values[6] = taskTags.toString();
ret.addRow(values); ret.addRow(values);
} }
} finally { } finally {
cursor.close(); cursor.close();
} }
return ret; return ret;
} }
@Override @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
if (LOGD) if (LOGD)
Log.d(TAG, "query"); Log.d(TAG, "query");
Cursor cursor; Cursor cursor;
switch (URI_MATCHER.match(uri)) { switch (URI_MATCHER.match(uri)) {
case URI_TASKS: case URI_TASKS:
cursor = getTasks(); cursor = getTasks();
break; break;
case URI_TAGS: case URI_TAGS:
cursor = getTags(); cursor = getTags();
break; break;
default: default:
throw new IllegalStateException("Unrecognized URI:" + uri); throw new IllegalStateException("Unrecognized URI:" + uri);
} }
return cursor; return cursor;
} }
@Override @Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
if (LOGD) if (LOGD)
Log.d(TAG, "update"); Log.d(TAG, "update");
switch (URI_MATCHER.match(uri)) { switch (URI_MATCHER.match(uri)) {
case URI_TASKS: case URI_TASKS:
Task task = new Task(); Task task = new Task();
// map values // map values
if(values.containsKey(NAME)) if(values.containsKey(NAME))
task.setValue(Task.TITLE, values.getAsString(NAME)); task.setValue(Task.TITLE, values.getAsString(NAME));
if(values.containsKey(PREFERRED_DUE_DATE)) if(values.containsKey(PREFERRED_DUE_DATE))
task.setValue(Task.DUE_DATE, values.getAsLong(PREFERRED_DUE_DATE)); task.setValue(Task.DUE_DATE, values.getAsLong(PREFERRED_DUE_DATE));
if(values.containsKey(DEFINITE_DUE_DATE)) if(values.containsKey(DEFINITE_DUE_DATE))
task.setValue(Task.DUE_DATE, values.getAsLong(DEFINITE_DUE_DATE)); task.setValue(Task.DUE_DATE, values.getAsLong(DEFINITE_DUE_DATE));
if(values.containsKey(IMPORTANCE)) if(values.containsKey(IMPORTANCE))
task.setValue(Task.IMPORTANCE, values.getAsInteger(IMPORTANCE)); task.setValue(Task.IMPORTANCE, values.getAsInteger(IMPORTANCE));
if(values.containsKey(COMPLETED)) if(values.containsKey(COMPLETED))
task.setValue(Task.COMPLETION_DATE, task.setValue(Task.COMPLETION_DATE,
values.getAsBoolean(COMPLETED) ? DateUtilities.now() : 0); values.getAsBoolean(COMPLETED) ? DateUtilities.now() : 0);
// map selection criteria // map selection criteria
String criteria = selection.replace(NAME, Task.TITLE.name). String criteria = selection.replace(NAME, Task.TITLE.name).
replace(PREFERRED_DUE_DATE, Task.DUE_DATE.name). replace(PREFERRED_DUE_DATE, Task.DUE_DATE.name).
replace(DEFINITE_DUE_DATE, Task.DUE_DATE.name). replace(DEFINITE_DUE_DATE, Task.DUE_DATE.name).
replace(IDENTIFIER, Task.ID.name). replace(IDENTIFIER, Task.ID.name).
replace(ID, Task.ID.name). replace(ID, Task.ID.name).
replace(IMPORTANCE, Task.IMPORTANCE.name); replace(IMPORTANCE, Task.IMPORTANCE.name);
return taskService.updateBySelection(criteria, selectionArgs, task); return taskService.updateBySelection(criteria, selectionArgs, task);
case URI_TAGS: case URI_TAGS:
throw new UnsupportedOperationException("tags updating: not yet"); throw new UnsupportedOperationException("tags updating: not yet");
default: default:
throw new IllegalStateException("Unrecognized URI:" + uri); throw new IllegalStateException("Unrecognized URI:" + uri);
} }
} }
public static void notifyDatabaseModification() { public static void notifyDatabaseModification() {
if (LOGD) if (LOGD)
Log.d(TAG, "notifyDatabaseModification"); Log.d(TAG, "notifyDatabaseModification");
ctx.getContentResolver().notifyChange(CONTENT_URI, null); ctx.getContentResolver().notifyChange(CONTENT_URI, null);
} }
} }

@ -1,144 +1,144 @@
package com.todoroo.astrid.widget; package com.todoroo.astrid.widget;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider; import android.appwidget.AppWidgetProvider;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskEditActivity; import com.todoroo.astrid.activity.TaskEditActivity;
import com.todoroo.astrid.activity.TaskListActivity; import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.CoreFilterExposer; import com.todoroo.astrid.core.CoreFilterExposer;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.AstridDependencyInjector; import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.service.TaskService;
public class TasksWidget extends AppWidgetProvider { public class TasksWidget extends AppWidgetProvider {
static { static {
AstridDependencyInjector.initialize(); AstridDependencyInjector.initialize();
} }
public final static int[] TEXT_IDS = { R.id.task_1, R.id.task_2, public final static int[] TEXT_IDS = { R.id.task_1, R.id.task_2,
R.id.task_3, R.id.task_4, R.id.task_5 }; R.id.task_3, R.id.task_4, R.id.task_5 };
public final static int[] SEPARATOR_IDS = { R.id.separator_1, public final static int[] SEPARATOR_IDS = { R.id.separator_1,
R.id.separator_2, R.id.separator_3, R.id.separator_4 }; R.id.separator_2, R.id.separator_3, R.id.separator_4 };
@Override @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) { int[] appWidgetIds) {
try { try {
super.onUpdate(context, appWidgetManager, appWidgetIds); super.onUpdate(context, appWidgetManager, appWidgetIds);
// Start in service to prevent Application Not Responding timeout // Start in service to prevent Application Not Responding timeout
context.startService(new Intent(context, UpdateService.class)); context.startService(new Intent(context, UpdateService.class));
} catch (SecurityException e) { } catch (SecurityException e) {
// :( // :(
} }
} }
public static class UpdateService extends Service { public static class UpdateService extends Service {
@Autowired @Autowired
Database database; Database database;
@Autowired @Autowired
TaskService taskService; TaskService taskService;
@Override @Override
public void onStart(Intent intent, int startId) { public void onStart(Intent intent, int startId) {
ContextManager.setContext(this); ContextManager.setContext(this);
RemoteViews updateViews = buildUpdate(this); RemoteViews updateViews = buildUpdate(this);
ComponentName thisWidget = new ComponentName(this, ComponentName thisWidget = new ComponentName(this,
TasksWidget.class); TasksWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this); AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews); manager.updateAppWidget(thisWidget, updateViews);
} }
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return null; return null;
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
public RemoteViews buildUpdate(Context context) { public RemoteViews buildUpdate(Context context) {
DependencyInjectionService.getInstance().inject(this); DependencyInjectionService.getInstance().inject(this);
RemoteViews views = null; RemoteViews views = null;
views = new RemoteViews(context.getPackageName(), views = new RemoteViews(context.getPackageName(),
R.layout.widget_initialized); R.layout.widget_initialized);
int[] textIDs = TEXT_IDS; int[] textIDs = TEXT_IDS;
int[] separatorIDs = SEPARATOR_IDS; int[] separatorIDs = SEPARATOR_IDS;
int numberOfTasks = 5; int numberOfTasks = 5;
Intent listIntent = new Intent(context, TaskListActivity.class); Intent listIntent = new Intent(context, TaskListActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
listIntent, 0); listIntent, 0);
views.setOnClickPendingIntent(R.id.taskbody, pendingIntent); views.setOnClickPendingIntent(R.id.taskbody, pendingIntent);
TodorooCursor<Task> cursor = null; TodorooCursor<Task> cursor = null;
try { try {
Filter inboxFilter = CoreFilterExposer.buildInboxFilter(getResources()); Filter inboxFilter = CoreFilterExposer.buildInboxFilter(getResources());
inboxFilter.sqlQuery += "ORDER BY " + TaskService.defaultTaskOrder() + " LIMIT " + numberOfTasks; inboxFilter.sqlQuery += "ORDER BY " + TaskService.defaultTaskOrder() + " LIMIT " + numberOfTasks;
database.openForReading(); database.openForReading();
cursor = taskService.fetchFiltered(inboxFilter, null, Task.TITLE, Task.DUE_DATE); cursor = taskService.fetchFiltered(inboxFilter, null, Task.TITLE, Task.DUE_DATE);
Task task = new Task(); Task task = new Task();
for (int i = 0; i < cursor.getCount(); i++) { for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i); cursor.moveToPosition(i);
task.readFromCursor(cursor); task.readFromCursor(cursor);
String textContent = ""; String textContent = "";
int textColor = Color.WHITE; int textColor = Color.WHITE;
textContent = task.getValue(Task.TITLE); textContent = task.getValue(Task.TITLE);
if(task.hasDueDate() && task.getValue(Task.DUE_DATE) < DateUtilities.now()) if(task.hasDueDate() && task.getValue(Task.DUE_DATE) < DateUtilities.now())
textColor = context.getResources().getColor(R.color.task_list_overdue); textColor = context.getResources().getColor(R.color.task_list_overdue);
if(i > 0) if(i > 0)
views.setViewVisibility(separatorIDs[i-1], View.VISIBLE); views.setViewVisibility(separatorIDs[i-1], View.VISIBLE);
views.setTextViewText(textIDs[i], textContent); views.setTextViewText(textIDs[i], textContent);
views.setTextColor(textIDs[i], textColor); views.setTextColor(textIDs[i], textColor);
} }
for(int i = cursor.getCount() - 1; i < separatorIDs.length; i++) { for(int i = cursor.getCount() - 1; i < separatorIDs.length; i++) {
if(i >= 0) if(i >= 0)
views.setViewVisibility(separatorIDs[i], View.INVISIBLE); views.setViewVisibility(separatorIDs[i], View.INVISIBLE);
} }
} catch (Exception e) { } catch (Exception e) {
// can happen if database is not ready // can happen if database is not ready
Log.e("WIDGET-UPDATE", "Error updating widget", e); Log.e("WIDGET-UPDATE", "Error updating widget", e);
} finally { } finally {
if(cursor != null) if(cursor != null)
cursor.close(); cursor.close();
} }
Intent editIntent = new Intent(context, TaskEditActivity.class); Intent editIntent = new Intent(context, TaskEditActivity.class);
pendingIntent = PendingIntent.getActivity(context, 0, pendingIntent = PendingIntent.getActivity(context, 0,
editIntent, 0); editIntent, 0);
views.setOnClickPendingIntent(R.id.widget_button, pendingIntent); views.setOnClickPendingIntent(R.id.widget_button, pendingIntent);
return views; return views;
} }
} }
} }

@ -1,25 +1,25 @@
package com.todoroo.andlib; package com.todoroo.andlib;
@SuppressWarnings("nls") @SuppressWarnings("nls")
public final class Constants { public final class Constants {
static final String SELECT = "SELECT"; static final String SELECT = "SELECT";
static final String SPACE = " "; static final String SPACE = " ";
static final String AS = "AS"; static final String AS = "AS";
static final String COMMA = ","; static final String COMMA = ",";
static final String FROM = "FROM"; static final String FROM = "FROM";
static final String ON = "ON"; static final String ON = "ON";
static final String JOIN = "JOIN"; static final String JOIN = "JOIN";
static final String ALL = "*"; static final String ALL = "*";
static final String LEFT_PARENTHESIS = "("; static final String LEFT_PARENTHESIS = "(";
static final String RIGHT_PARENTHESIS = ")"; static final String RIGHT_PARENTHESIS = ")";
static final String AND = "AND"; static final String AND = "AND";
static final String BETWEEN = "BETWEEN"; static final String BETWEEN = "BETWEEN";
static final String LIKE = "LIKE"; static final String LIKE = "LIKE";
static final String OR = "OR"; static final String OR = "OR";
static final String ORDER_BY = "ORDER BY"; static final String ORDER_BY = "ORDER BY";
static final String GROUP_BY = "GROUP BY"; static final String GROUP_BY = "GROUP BY";
static final String WHERE = "WHERE"; static final String WHERE = "WHERE";
public static final String EXISTS = "EXISTS"; public static final String EXISTS = "EXISTS";
public static final String NOT = "NOT"; public static final String NOT = "NOT";
public static final String LIMIT = "LIMIT"; public static final String LIMIT = "LIMIT";
} }

@ -1,89 +1,89 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import static com.todoroo.andlib.Constants.AND; import static com.todoroo.andlib.Constants.AND;
import static com.todoroo.andlib.Constants.EXISTS; import static com.todoroo.andlib.Constants.EXISTS;
import static com.todoroo.andlib.Constants.LEFT_PARENTHESIS; import static com.todoroo.andlib.Constants.LEFT_PARENTHESIS;
import static com.todoroo.andlib.Constants.NOT; import static com.todoroo.andlib.Constants.NOT;
import static com.todoroo.andlib.Constants.OR; import static com.todoroo.andlib.Constants.OR;
import static com.todoroo.andlib.Constants.RIGHT_PARENTHESIS; import static com.todoroo.andlib.Constants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.Constants.SPACE; import static com.todoroo.andlib.Constants.SPACE;
public abstract class Criterion { public abstract class Criterion {
protected final Operator operator; protected final Operator operator;
Criterion(Operator operator) { Criterion(Operator operator) {
this.operator = operator; this.operator = operator;
} }
public static Criterion all = new Criterion(Operator.exists) { public static Criterion all = new Criterion(Operator.exists) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(1); sb.append(1);
} }
}; };
public static Criterion none = new Criterion(Operator.exists) { public static Criterion none = new Criterion(Operator.exists) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(0); sb.append(0);
} }
}; };
public static Criterion and(final Criterion criterion, final Criterion... criterions) { public static Criterion and(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.and) { return new Criterion(Operator.and) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(criterion); sb.append(criterion);
for (Criterion c : criterions) { for (Criterion c : criterions) {
sb.append(SPACE).append(AND).append(SPACE).append(c); sb.append(SPACE).append(AND).append(SPACE).append(c);
} }
} }
}; };
} }
public static Criterion or(final Criterion criterion, final Criterion... criterions) { public static Criterion or(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.or) { return new Criterion(Operator.or) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(criterion); sb.append(criterion);
for (Criterion c : criterions) { for (Criterion c : criterions) {
sb.append(SPACE).append(OR).append(SPACE).append(c.toString()); sb.append(SPACE).append(OR).append(SPACE).append(c.toString());
} }
} }
}; };
} }
public static Criterion exists(final Query query) { public static Criterion exists(final Query query) {
return new Criterion(Operator.exists) { return new Criterion(Operator.exists) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(EXISTS).append(SPACE).append(LEFT_PARENTHESIS).append(query).append(RIGHT_PARENTHESIS); sb.append(EXISTS).append(SPACE).append(LEFT_PARENTHESIS).append(query).append(RIGHT_PARENTHESIS);
} }
}; };
} }
public static Criterion not(final Criterion criterion) { public static Criterion not(final Criterion criterion) {
return new Criterion(Operator.not) { return new Criterion(Operator.not) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(NOT).append(SPACE); sb.append(NOT).append(SPACE);
criterion.populate(sb); criterion.populate(sb);
} }
}; };
} }
protected abstract void populate(StringBuilder sb); protected abstract void populate(StringBuilder sb);
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(LEFT_PARENTHESIS); StringBuilder builder = new StringBuilder(LEFT_PARENTHESIS);
populate(builder); populate(builder);
builder.append(RIGHT_PARENTHESIS); builder.append(RIGHT_PARENTHESIS);
return builder.toString(); return builder.toString();
} }
} }

@ -1,68 +1,68 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import static com.todoroo.andlib.Constants.AS; import static com.todoroo.andlib.Constants.AS;
import static com.todoroo.andlib.Constants.SPACE; import static com.todoroo.andlib.Constants.SPACE;
public abstract class DBObject<T extends DBObject<?>> implements Cloneable { public abstract class DBObject<T extends DBObject<?>> implements Cloneable {
protected String alias; protected String alias;
protected final String expression; protected final String expression;
protected DBObject(String expression){ protected DBObject(String expression){
this.expression = expression; this.expression = expression;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public T as(String newAlias) { public T as(String newAlias) {
try { try {
T clone = (T) clone(); T clone = (T) clone();
clone.alias = newAlias; clone.alias = newAlias;
return clone; return clone;
} catch (CloneNotSupportedException e) { } catch (CloneNotSupportedException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public boolean hasAlias() { public boolean hasAlias() {
return alias != null; return alias != null;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
DBObject<?> dbObject = (DBObject<?>) o; DBObject<?> dbObject = (DBObject<?>) o;
if (alias != null ? !alias.equals(dbObject.alias) : dbObject.alias != null) return false; if (alias != null ? !alias.equals(dbObject.alias) : dbObject.alias != null) return false;
if (expression != null ? !expression.equals(dbObject.expression) : dbObject.expression != null) return false; if (expression != null ? !expression.equals(dbObject.expression) : dbObject.expression != null) return false;
return true; return true;
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = alias != null ? alias.hashCode() : 0; int result = alias != null ? alias.hashCode() : 0;
result = 31 * result + (expression != null ? expression.hashCode() : 0); result = 31 * result + (expression != null ? expression.hashCode() : 0);
return result; return result;
} }
@Override @Override
public final String toString() { public final String toString() {
if (hasAlias()) { if (hasAlias()) {
return alias; return alias;
} }
return expression; return expression;
} }
public final String toStringInSelect() { public final String toStringInSelect() {
StringBuilder sb = new StringBuilder(expression); StringBuilder sb = new StringBuilder(expression);
if (hasAlias()) { if (hasAlias()) {
sb.append(SPACE).append(AS).append(SPACE).append(alias); sb.append(SPACE).append(AS).append(SPACE).append(alias);
} else { } else {
int pos = expression.indexOf('.'); int pos = expression.indexOf('.');
if(pos > 0) if(pos > 0)
sb.append(SPACE).append(AS).append(SPACE).append(expression.substring(pos + 1)); sb.append(SPACE).append(AS).append(SPACE).append(expression.substring(pos + 1));
} }
return sb.toString(); return sb.toString();
} }
} }

@ -1,7 +1,7 @@
package com.todoroo.andlib; package com.todoroo.andlib;
public class EqCriterion extends UnaryCriterion { public class EqCriterion extends UnaryCriterion {
EqCriterion(Field field, Object value) { EqCriterion(Field field, Object value) {
super(field, Operator.eq, value); super(field, Operator.eq, value);
} }
} }

@ -1,90 +1,90 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import static com.todoroo.andlib.Constants.AND; import static com.todoroo.andlib.Constants.AND;
import static com.todoroo.andlib.Constants.BETWEEN; import static com.todoroo.andlib.Constants.BETWEEN;
import static com.todoroo.andlib.Constants.COMMA; import static com.todoroo.andlib.Constants.COMMA;
import static com.todoroo.andlib.Constants.LEFT_PARENTHESIS; import static com.todoroo.andlib.Constants.LEFT_PARENTHESIS;
import static com.todoroo.andlib.Constants.RIGHT_PARENTHESIS; import static com.todoroo.andlib.Constants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.Constants.SPACE; import static com.todoroo.andlib.Constants.SPACE;
public class Field extends DBObject<Field> { public class Field extends DBObject<Field> {
protected Field(String expression) { protected Field(String expression) {
super(expression); super(expression);
} }
public static Field field(String expression) { public static Field field(String expression) {
return new Field(expression); return new Field(expression);
} }
public Criterion eq(Object value) { public Criterion eq(Object value) {
if(value == null) if(value == null)
return UnaryCriterion.isNull(this); return UnaryCriterion.isNull(this);
return UnaryCriterion.eq(this, value); return UnaryCriterion.eq(this, value);
} }
public Criterion neq(Object value) { public Criterion neq(Object value) {
if(value == null) if(value == null)
return UnaryCriterion.isNotNull(this); return UnaryCriterion.isNotNull(this);
return UnaryCriterion.neq(this, value); return UnaryCriterion.neq(this, value);
} }
public Criterion gt(Object value) { public Criterion gt(Object value) {
return UnaryCriterion.gt(this, value); return UnaryCriterion.gt(this, value);
} }
public Criterion lt(final Object value) { public Criterion lt(final Object value) {
return UnaryCriterion.lt(this, value); return UnaryCriterion.lt(this, value);
} }
public Criterion isNull() { public Criterion isNull() {
return UnaryCriterion.isNull(this); return UnaryCriterion.isNull(this);
} }
public Criterion isNotNull() { public Criterion isNotNull() {
return UnaryCriterion.isNotNull(this); return UnaryCriterion.isNotNull(this);
} }
public Criterion between(final Object lower, final Object upper) { public Criterion between(final Object lower, final Object upper) {
final Field field = this; final Field field = this;
return new Criterion(null) { return new Criterion(null) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(BETWEEN).append(SPACE).append(lower).append(SPACE).append(AND) sb.append(field).append(SPACE).append(BETWEEN).append(SPACE).append(lower).append(SPACE).append(AND)
.append(SPACE).append(upper); .append(SPACE).append(upper);
} }
}; };
} }
public Criterion like(final String value) { public Criterion like(final String value) {
return UnaryCriterion.like(this, value); return UnaryCriterion.like(this, value);
} }
public <T> Criterion in(final T... value) { public <T> Criterion in(final T... value) {
final Field field = this; final Field field = this;
return new Criterion(Operator.in) { return new Criterion(Operator.in) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS); sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS);
for (T t : value) { for (T t : value) {
sb.append(t.toString()).append(COMMA); sb.append(t.toString()).append(COMMA);
} }
sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS); sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS);
} }
}; };
} }
public Criterion in(final Query query) { public Criterion in(final Query query) {
final Field field = this; final Field field = this;
return new Criterion(Operator.in) { return new Criterion(Operator.in) {
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(query) sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(query)
.append(RIGHT_PARENTHESIS); .append(RIGHT_PARENTHESIS);
} }
}; };
} }
} }

@ -1,14 +1,14 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class GroupBy { public class GroupBy {
private List<Field> fields = new ArrayList<Field>(); private List<Field> fields = new ArrayList<Field>();
public static GroupBy groupBy(Field field) { public static GroupBy groupBy(Field field) {
GroupBy groupBy = new GroupBy(); GroupBy groupBy = new GroupBy();
groupBy.fields.add(field); groupBy.fields.add(field);
return groupBy; return groupBy;
} }
} }

@ -1,43 +1,43 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import static com.todoroo.andlib.Constants.JOIN; import static com.todoroo.andlib.Constants.JOIN;
import static com.todoroo.andlib.Constants.ON; import static com.todoroo.andlib.Constants.ON;
import static com.todoroo.andlib.Constants.SPACE; import static com.todoroo.andlib.Constants.SPACE;
public class Join { public class Join {
private final SqlTable joinTable; private final SqlTable joinTable;
private final JoinType joinType; private final JoinType joinType;
private final Criterion[] criterions; private final Criterion[] criterions;
private Join(SqlTable table, JoinType joinType, Criterion... criterions) { private Join(SqlTable table, JoinType joinType, Criterion... criterions) {
joinTable = table; joinTable = table;
this.joinType = joinType; this.joinType = joinType;
this.criterions = criterions; this.criterions = criterions;
} }
public static Join inner(SqlTable expression, Criterion... criterions) { public static Join inner(SqlTable expression, Criterion... criterions) {
return new Join(expression, JoinType.INNER, criterions); return new Join(expression, JoinType.INNER, criterions);
} }
public static Join left(SqlTable table, Criterion... criterions) { public static Join left(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.LEFT, criterions); return new Join(table, JoinType.LEFT, criterions);
} }
public static Join right(SqlTable table, Criterion... criterions) { public static Join right(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.RIGHT, criterions); return new Join(table, JoinType.RIGHT, criterions);
} }
public static Join out(SqlTable table, Criterion... criterions) { public static Join out(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.OUT, criterions); return new Join(table, JoinType.OUT, criterions);
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON); sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON);
for (Criterion criterion : criterions) { for (Criterion criterion : criterions) {
sb.append(SPACE).append(criterion); sb.append(SPACE).append(criterion);
} }
return sb.toString(); return sb.toString();
} }
} }

@ -1,5 +1,5 @@
package com.todoroo.andlib; package com.todoroo.andlib;
public enum JoinType { public enum JoinType {
INNER, LEFT, RIGHT, OUT INNER, LEFT, RIGHT, OUT
} }

@ -1,57 +1,57 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import static com.todoroo.andlib.Constants.SPACE; import static com.todoroo.andlib.Constants.SPACE;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@SuppressWarnings("nls") @SuppressWarnings("nls")
public final class Operator { public final class Operator {
private final String operator; private final String operator;
public static final Operator eq = new Operator("="); public static final Operator eq = new Operator("=");
public static final Operator neq = new Operator("<>"); public static final Operator neq = new Operator("<>");
public static final Operator isNull = new Operator("IS NULL"); public static final Operator isNull = new Operator("IS NULL");
public static final Operator isNotNull = new Operator("IS NOT NULL"); public static final Operator isNotNull = new Operator("IS NOT NULL");
public static final Operator gt = new Operator(">"); public static final Operator gt = new Operator(">");
public static final Operator lt = new Operator("<"); public static final Operator lt = new Operator("<");
public static final Operator gte = new Operator(">="); public static final Operator gte = new Operator(">=");
public static final Operator lte = new Operator("<="); public static final Operator lte = new Operator("<=");
public static final Operator and = new Operator("AND"); public static final Operator and = new Operator("AND");
public static final Operator or = new Operator("OR"); public static final Operator or = new Operator("OR");
public static final Operator not = new Operator("NOT"); public static final Operator not = new Operator("NOT");
public static final Operator exists = new Operator("EXISTS"); public static final Operator exists = new Operator("EXISTS");
public static final Operator like = new Operator("LIKE"); public static final Operator like = new Operator("LIKE");
public static final Operator in = new Operator("IN"); public static final Operator in = new Operator("IN");
private static final Map<Operator, Operator> contraryRegistry = new HashMap<Operator, Operator>(); private static final Map<Operator, Operator> contraryRegistry = new HashMap<Operator, Operator>();
static { static {
contraryRegistry.put(eq, neq); contraryRegistry.put(eq, neq);
contraryRegistry.put(neq, eq); contraryRegistry.put(neq, eq);
contraryRegistry.put(isNull, isNotNull); contraryRegistry.put(isNull, isNotNull);
contraryRegistry.put(isNotNull, isNull); contraryRegistry.put(isNotNull, isNull);
contraryRegistry.put(gt, lte); contraryRegistry.put(gt, lte);
contraryRegistry.put(lte, gt); contraryRegistry.put(lte, gt);
contraryRegistry.put(lt, gte); contraryRegistry.put(lt, gte);
contraryRegistry.put(gte, lt); contraryRegistry.put(gte, lt);
} }
private Operator(String operator) { private Operator(String operator) {
this.operator = operator; this.operator = operator;
} }
public Operator getContrary() { public Operator getContrary() {
if(!contraryRegistry.containsKey(this)){ if(!contraryRegistry.containsKey(this)){
Operator opposite = new Operator(not.toString() + SPACE + this.toString()); Operator opposite = new Operator(not.toString() + SPACE + this.toString());
contraryRegistry.put(this, opposite); contraryRegistry.put(this, opposite);
contraryRegistry.put(opposite, this); contraryRegistry.put(opposite, this);
} }
return contraryRegistry.get(this); return contraryRegistry.get(this);
} }
@Override @Override
public String toString() { public String toString() {
return this.operator.toString(); return this.operator.toString();
} }
} }

@ -1,30 +1,30 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import static com.todoroo.andlib.Constants.SPACE; import static com.todoroo.andlib.Constants.SPACE;
public class Order { public class Order {
private final Object expression; private final Object expression;
private final OrderType orderType; private final OrderType orderType;
private Order(Object expression) { private Order(Object expression) {
this(expression, OrderType.ASC); this(expression, OrderType.ASC);
} }
private Order(Object expression, OrderType orderType) { private Order(Object expression, OrderType orderType) {
this.expression = expression; this.expression = expression;
this.orderType = orderType; this.orderType = orderType;
} }
public static Order asc(Object expression) { public static Order asc(Object expression) {
return new Order(expression); return new Order(expression);
} }
public static Order desc(Object expression) { public static Order desc(Object expression) {
return new Order(expression, OrderType.DESC); return new Order(expression, OrderType.DESC);
} }
@Override @Override
public String toString() { public String toString() {
return expression + SPACE + orderType; return expression + SPACE + orderType;
} }
} }

@ -1,5 +1,5 @@
package com.todoroo.andlib; package com.todoroo.andlib;
public enum OrderType { public enum OrderType {
DESC, ASC DESC, ASC
} }

@ -1,192 +1,192 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import static com.todoroo.andlib.Constants.ALL; import static com.todoroo.andlib.Constants.ALL;
import static com.todoroo.andlib.Constants.COMMA; import static com.todoroo.andlib.Constants.COMMA;
import static com.todoroo.andlib.Constants.FROM; import static com.todoroo.andlib.Constants.FROM;
import static com.todoroo.andlib.Constants.GROUP_BY; import static com.todoroo.andlib.Constants.GROUP_BY;
import static com.todoroo.andlib.Constants.LEFT_PARENTHESIS; import static com.todoroo.andlib.Constants.LEFT_PARENTHESIS;
import static com.todoroo.andlib.Constants.ORDER_BY; import static com.todoroo.andlib.Constants.ORDER_BY;
import static com.todoroo.andlib.Constants.RIGHT_PARENTHESIS; import static com.todoroo.andlib.Constants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.Constants.SELECT; import static com.todoroo.andlib.Constants.SELECT;
import static com.todoroo.andlib.Constants.SPACE; import static com.todoroo.andlib.Constants.SPACE;
import static com.todoroo.andlib.Constants.WHERE; import static com.todoroo.andlib.Constants.WHERE;
import static com.todoroo.andlib.SqlTable.table; import static com.todoroo.andlib.SqlTable.table;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import java.util.ArrayList; import java.util.ArrayList;
import com.todoroo.astrid.data.Property; import com.todoroo.astrid.data.Property;
public final class Query { public final class Query {
private SqlTable table; private SqlTable table;
private String queryTemplate = null; private String queryTemplate = null;
private final ArrayList<Criterion> criterions = new ArrayList<Criterion>(); private final ArrayList<Criterion> criterions = new ArrayList<Criterion>();
private final ArrayList<Field> fields = new ArrayList<Field>(); private final ArrayList<Field> fields = new ArrayList<Field>();
private final ArrayList<Join> joins = new ArrayList<Join>(); private final ArrayList<Join> joins = new ArrayList<Join>();
private final ArrayList<Field> groupBies = new ArrayList<Field>(); private final ArrayList<Field> groupBies = new ArrayList<Field>();
private final ArrayList<Order> orders = new ArrayList<Order>(); private final ArrayList<Order> orders = new ArrayList<Order>();
private final ArrayList<Criterion> havings = new ArrayList<Criterion>(); private final ArrayList<Criterion> havings = new ArrayList<Criterion>();
private Query(Field... fields) { private Query(Field... fields) {
this.fields.addAll(asList(fields)); this.fields.addAll(asList(fields));
} }
public static Query select(Field... fields) { public static Query select(Field... fields) {
return new Query(fields); return new Query(fields);
} }
public Query from(SqlTable fromTable) { public Query from(SqlTable fromTable) {
this.table = fromTable; this.table = fromTable;
return this; return this;
} }
public Query join(Join... join) { public Query join(Join... join) {
joins.addAll(asList(join)); joins.addAll(asList(join));
return this; return this;
} }
public Query where(Criterion criterion) { public Query where(Criterion criterion) {
criterions.add(criterion); criterions.add(criterion);
return this; return this;
} }
public Query groupBy(Field... groupBy) { public Query groupBy(Field... groupBy) {
groupBies.addAll(asList(groupBy)); groupBies.addAll(asList(groupBy));
return this; return this;
} }
public Query orderBy(Order... order) { public Query orderBy(Order... order) {
orders.addAll(asList(order)); orders.addAll(asList(order));
return this; return this;
} }
public Query appendSelectFields(Property<?>... selectFields) { public Query appendSelectFields(Property<?>... selectFields) {
this.fields.addAll(asList(selectFields)); this.fields.addAll(asList(selectFields));
return this; return this;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return this == o || !(o == null || getClass() != o.getClass()) && this.toString().equals(o.toString()); return this == o || !(o == null || getClass() != o.getClass()) && this.toString().equals(o.toString());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return toString().hashCode(); return toString().hashCode();
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sql = new StringBuilder(); StringBuilder sql = new StringBuilder();
visitSelectClause(sql); visitSelectClause(sql);
visitFromClause(sql); visitFromClause(sql);
if(queryTemplate == null) { if(queryTemplate == null) {
visitJoinClause(sql); visitJoinClause(sql);
visitWhereClause(sql); visitWhereClause(sql);
visitGroupByClause(sql); visitGroupByClause(sql);
visitOrderByClause(sql); visitOrderByClause(sql);
} else { } else {
if(joins.size() > 0 || groupBies.size() > 0 || orders.size() > 0 || if(joins.size() > 0 || groupBies.size() > 0 || orders.size() > 0 ||
havings.size() > 0) havings.size() > 0)
throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$ throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$
sql.append(queryTemplate); sql.append(queryTemplate);
} }
return sql.toString(); return sql.toString();
} }
private void visitOrderByClause(StringBuilder sql) { private void visitOrderByClause(StringBuilder sql) {
if (orders.isEmpty()) { if (orders.isEmpty()) {
return; return;
} }
sql.append(ORDER_BY); sql.append(ORDER_BY);
for (Order order : orders) { for (Order order : orders) {
sql.append(SPACE).append(order).append(COMMA); sql.append(SPACE).append(order).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
private void visitGroupByClause(StringBuilder sql) { private void visitGroupByClause(StringBuilder sql) {
if (groupBies.isEmpty()) { if (groupBies.isEmpty()) {
return; return;
} }
sql.append(GROUP_BY); sql.append(GROUP_BY);
for (Field groupBy : groupBies) { for (Field groupBy : groupBies) {
sql.append(SPACE).append(groupBy).append(COMMA); sql.append(SPACE).append(groupBy).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
if (havings.isEmpty()) { if (havings.isEmpty()) {
return; return;
} }
sql.append("HAVING"); sql.append("HAVING");
for (Criterion havingCriterion : havings) { for (Criterion havingCriterion : havings) {
sql.append(SPACE).append(havingCriterion).append(COMMA); sql.append(SPACE).append(havingCriterion).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
private void visitWhereClause(StringBuilder sql) { private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) { if (criterions.isEmpty()) {
return; return;
} }
sql.append(WHERE); sql.append(WHERE);
for (Criterion criterion : criterions) { for (Criterion criterion : criterions) {
sql.append(SPACE).append(criterion).append(SPACE); sql.append(SPACE).append(criterion).append(SPACE);
} }
} }
private void visitJoinClause(StringBuilder sql) { private void visitJoinClause(StringBuilder sql) {
for (Join join : joins) { for (Join join : joins) {
sql.append(join).append(SPACE); sql.append(join).append(SPACE);
} }
} }
private void visitFromClause(StringBuilder sql) { private void visitFromClause(StringBuilder sql) {
if (table == null) { if (table == null) {
return; return;
} }
sql.append(FROM).append(SPACE).append(table).append(SPACE); sql.append(FROM).append(SPACE).append(table).append(SPACE);
} }
private void visitSelectClause(StringBuilder sql) { private void visitSelectClause(StringBuilder sql) {
sql.append(SELECT).append(SPACE); sql.append(SELECT).append(SPACE);
if (fields.isEmpty()) { if (fields.isEmpty()) {
sql.append(ALL).append(SPACE); sql.append(ALL).append(SPACE);
return; return;
} }
for (Field field : fields) { for (Field field : fields) {
sql.append(field.toStringInSelect()).append(COMMA); sql.append(field.toStringInSelect()).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
public SqlTable as(String alias) { public SqlTable as(String alias) {
return table(LEFT_PARENTHESIS + this.toString() + RIGHT_PARENTHESIS).as(alias); return table(LEFT_PARENTHESIS + this.toString() + RIGHT_PARENTHESIS).as(alias);
} }
public Query having(Criterion criterion) { public Query having(Criterion criterion) {
this.havings.add(criterion); this.havings.add(criterion);
return this; return this;
} }
/** /**
* Gets a list of fields returned by this query * Gets a list of fields returned by this query
* @return * @return
*/ */
public Property<?>[] getFields() { public Property<?>[] getFields() {
return fields.toArray(new Property<?>[fields.size()]); return fields.toArray(new Property<?>[fields.size()]);
} }
/** /**
* Add the SQL query template (comes after the "from") * Add the SQL query template (comes after the "from")
* @param sqlQuery * @param sqlQuery
* @return * @return
*/ */
public Query withQueryTemplate(String template) { public Query withQueryTemplate(String template) {
queryTemplate = template; queryTemplate = template;
return this; return this;
} }
} }

@ -1,117 +1,117 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import static com.todoroo.andlib.Constants.COMMA; import static com.todoroo.andlib.Constants.COMMA;
import static com.todoroo.andlib.Constants.GROUP_BY; import static com.todoroo.andlib.Constants.GROUP_BY;
import static com.todoroo.andlib.Constants.LIMIT; import static com.todoroo.andlib.Constants.LIMIT;
import static com.todoroo.andlib.Constants.ORDER_BY; import static com.todoroo.andlib.Constants.ORDER_BY;
import static com.todoroo.andlib.Constants.SPACE; import static com.todoroo.andlib.Constants.SPACE;
import static com.todoroo.andlib.Constants.WHERE; import static com.todoroo.andlib.Constants.WHERE;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* Query Template returns a bunch of criteria that allows a query to be * Query Template returns a bunch of criteria that allows a query to be
* constructed * constructed
* *
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
public final class QueryTemplate { public final class QueryTemplate {
private final ArrayList<Criterion> criterions = new ArrayList<Criterion>(); private final ArrayList<Criterion> criterions = new ArrayList<Criterion>();
private final ArrayList<Join> joins = new ArrayList<Join>(); private final ArrayList<Join> joins = new ArrayList<Join>();
private final ArrayList<Field> groupBies = new ArrayList<Field>(); private final ArrayList<Field> groupBies = new ArrayList<Field>();
private final ArrayList<Order> orders = new ArrayList<Order>(); private final ArrayList<Order> orders = new ArrayList<Order>();
private final ArrayList<Criterion> havings = new ArrayList<Criterion>(); private final ArrayList<Criterion> havings = new ArrayList<Criterion>();
private Integer limit = null; private Integer limit = null;
public QueryTemplate join(Join... join) { public QueryTemplate join(Join... join) {
joins.addAll(asList(join)); joins.addAll(asList(join));
return this; return this;
} }
public QueryTemplate where(Criterion criterion) { public QueryTemplate where(Criterion criterion) {
criterions.add(criterion); criterions.add(criterion);
return this; return this;
} }
public QueryTemplate groupBy(Field... groupBy) { public QueryTemplate groupBy(Field... groupBy) {
groupBies.addAll(asList(groupBy)); groupBies.addAll(asList(groupBy));
return this; return this;
} }
public QueryTemplate orderBy(Order... order) { public QueryTemplate orderBy(Order... order) {
orders.addAll(asList(order)); orders.addAll(asList(order));
return this; return this;
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sql = new StringBuilder(); StringBuilder sql = new StringBuilder();
visitJoinClause(sql); visitJoinClause(sql);
visitWhereClause(sql); visitWhereClause(sql);
visitGroupByClause(sql); visitGroupByClause(sql);
visitOrderByClause(sql); visitOrderByClause(sql);
if(limit != null) if(limit != null)
sql.append(LIMIT).append(SPACE).append(limit); sql.append(LIMIT).append(SPACE).append(limit);
return sql.toString(); return sql.toString();
} }
private void visitOrderByClause(StringBuilder sql) { private void visitOrderByClause(StringBuilder sql) {
if (orders.isEmpty()) { if (orders.isEmpty()) {
return; return;
} }
sql.append(ORDER_BY); sql.append(ORDER_BY);
for (Order order : orders) { for (Order order : orders) {
sql.append(SPACE).append(order).append(COMMA); sql.append(SPACE).append(order).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
private void visitGroupByClause(StringBuilder sql) { private void visitGroupByClause(StringBuilder sql) {
if (groupBies.isEmpty()) { if (groupBies.isEmpty()) {
return; return;
} }
sql.append(GROUP_BY); sql.append(GROUP_BY);
for (Field groupBy : groupBies) { for (Field groupBy : groupBies) {
sql.append(SPACE).append(groupBy).append(COMMA); sql.append(SPACE).append(groupBy).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
if (havings.isEmpty()) { if (havings.isEmpty()) {
return; return;
} }
sql.append("HAVING"); sql.append("HAVING");
for (Criterion havingCriterion : havings) { for (Criterion havingCriterion : havings) {
sql.append(SPACE).append(havingCriterion).append(COMMA); sql.append(SPACE).append(havingCriterion).append(COMMA);
} }
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
private void visitWhereClause(StringBuilder sql) { private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) { if (criterions.isEmpty()) {
return; return;
} }
sql.append(WHERE); sql.append(WHERE);
for (Criterion criterion : criterions) { for (Criterion criterion : criterions) {
sql.append(SPACE).append(criterion).append(SPACE); sql.append(SPACE).append(criterion).append(SPACE);
} }
} }
private void visitJoinClause(StringBuilder sql) { private void visitJoinClause(StringBuilder sql) {
for (Join join : joins) { for (Join join : joins) {
sql.append(join).append(SPACE); sql.append(join).append(SPACE);
} }
} }
public QueryTemplate having(Criterion criterion) { public QueryTemplate having(Criterion criterion) {
this.havings.add(criterion); this.havings.add(criterion);
return this; return this;
} }
public QueryTemplate limit(int limitValue) { public QueryTemplate limit(int limitValue) {
this.limit = limitValue; this.limit = limitValue;
return this; return this;
} }
} }

@ -1,20 +1,20 @@
package com.todoroo.andlib; package com.todoroo.andlib;
public class SqlTable extends DBObject<SqlTable> { public class SqlTable extends DBObject<SqlTable> {
protected SqlTable(String expression) { protected SqlTable(String expression) {
super(expression); super(expression);
} }
public static SqlTable table(String table) { public static SqlTable table(String table) {
return new SqlTable(table); return new SqlTable(table);
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
protected String fieldExpression(String fieldName) { protected String fieldExpression(String fieldName) {
if (hasAlias()) { if (hasAlias()) {
return alias + "." + fieldName; return alias + "." + fieldName;
} }
return expression+"."+fieldName; return expression+"."+fieldName;
} }
} }

@ -1,92 +1,92 @@
package com.todoroo.andlib; package com.todoroo.andlib;
import static com.todoroo.andlib.Constants.SPACE; import static com.todoroo.andlib.Constants.SPACE;
public class UnaryCriterion extends Criterion { public class UnaryCriterion extends Criterion {
protected final Field expression; protected final Field expression;
protected final Object value; protected final Object value;
UnaryCriterion(Field expression, Operator operator, Object value) { UnaryCriterion(Field expression, Operator operator, Object value) {
super(operator); super(operator);
this.expression = expression; this.expression = expression;
this.value = value; this.value = value;
} }
@Override @Override
protected void populate(StringBuilder sb) { protected void populate(StringBuilder sb) {
beforePopulateOperator(sb); beforePopulateOperator(sb);
populateOperator(sb); populateOperator(sb);
afterPopulateOperator(sb); afterPopulateOperator(sb);
} }
public static Criterion eq(Field expression, Object value) { public static Criterion eq(Field expression, Object value) {
return new UnaryCriterion(expression, Operator.eq, value); return new UnaryCriterion(expression, Operator.eq, value);
} }
protected void beforePopulateOperator(StringBuilder sb) { protected void beforePopulateOperator(StringBuilder sb) {
sb.append(expression); sb.append(expression);
} }
protected void populateOperator(StringBuilder sb) { protected void populateOperator(StringBuilder sb) {
sb.append(operator); sb.append(operator);
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
protected void afterPopulateOperator(StringBuilder sb) { protected void afterPopulateOperator(StringBuilder sb) {
if(value == null) if(value == null)
return; return;
else if(value instanceof String) else if(value instanceof String)
sb.append("'").append(sanitize((String) value)).append("'"); sb.append("'").append(sanitize((String) value)).append("'");
else else
sb.append(value); sb.append(value);
} }
/** /**
* Sanitize the given input for SQL * Sanitize the given input for SQL
* @param input * @param input
* @return * @return
*/ */
@SuppressWarnings("nls") @SuppressWarnings("nls")
public static String sanitize(String input) { public static String sanitize(String input) {
return input.replace("\\", "\\\\").replace("'", "\\'"); return input.replace("\\", "\\\\").replace("'", "\\'");
} }
public static Criterion neq(Field field, Object value) { public static Criterion neq(Field field, Object value) {
return new UnaryCriterion(field, Operator.neq, value); return new UnaryCriterion(field, Operator.neq, value);
} }
public static Criterion gt(Field field, Object value) { public static Criterion gt(Field field, Object value) {
return new UnaryCriterion(field, Operator.gt, value); return new UnaryCriterion(field, Operator.gt, value);
} }
public static Criterion lt(Field field, Object value) { public static Criterion lt(Field field, Object value) {
return new UnaryCriterion(field, Operator.lt, value); return new UnaryCriterion(field, Operator.lt, value);
} }
public static Criterion isNull(Field field) { public static Criterion isNull(Field field) {
return new UnaryCriterion(field, Operator.isNull, null) { return new UnaryCriterion(field, Operator.isNull, null) {
@Override @Override
protected void populateOperator(StringBuilder sb) { protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator); sb.append(SPACE).append(operator);
} }
}; };
} }
public static Criterion isNotNull(Field field) { public static Criterion isNotNull(Field field) {
return new UnaryCriterion(field, Operator.isNotNull, null) { return new UnaryCriterion(field, Operator.isNotNull, null) {
@Override @Override
protected void populateOperator(StringBuilder sb) { protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator); sb.append(SPACE).append(operator);
} }
}; };
} }
public static Criterion like(Field field, String value) { public static Criterion like(Field field, String value) {
return new UnaryCriterion(field, Operator.like, value) { return new UnaryCriterion(field, Operator.like, value) {
@Override @Override
protected void populateOperator(StringBuilder sb) { protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator).append(SPACE); sb.append(SPACE).append(operator).append(SPACE);
} }
}; };
} }
} }

Loading…
Cancel
Save