Added a sample filter project

pull/14/head
Tim Su 14 years ago
parent 3180b6bb7e
commit 03399811f7

@ -54,6 +54,7 @@
<ListView android:id="@android:id/list"
android:scrollbars="vertical"
android:cacheColorHint="#00000000"
android:textFilterEnabled="true"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>

@ -25,6 +25,7 @@ import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CursorAdapter;
import android.widget.Filterable;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
@ -56,7 +57,7 @@ import com.todoroo.astrid.utility.Preferences;
* @author Tim Su <tim@todoroo.com>
*
*/
public class TaskAdapter extends CursorAdapter {
public class TaskAdapter extends CursorAdapter implements Filterable {
public interface OnCompletedTaskListener {
public void onCompletedTask(Task item, boolean newState);

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="output" path="bin"/>
</classpath>

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

@ -0,0 +1,332 @@
#Wed Jul 07 18:43:41 PDT 2010
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
org.eclipse.jdt.core.compiler.problem.deadCode=ignore
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
org.eclipse.jdt.core.compiler.problem.nullReference=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.6
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
org.eclipse.jdt.core.formatter.comment.line_length=80
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=2
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
org.eclipse.jdt.core.formatter.indentation.size=4
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.lineSplit=80
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=space
org.eclipse.jdt.core.formatter.tabulation.size=4
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true

@ -0,0 +1,109 @@
#Tue Jun 29 14:53:46 PDT 2010
cleanup.add_default_serial_version_id=true
cleanup.add_generated_serial_version_id=false
cleanup.add_missing_annotations=true
cleanup.add_missing_deprecated_annotations=true
cleanup.add_missing_methods=false
cleanup.add_missing_nls_tags=false
cleanup.add_missing_override_annotations=true
cleanup.add_serial_version_id=true
cleanup.always_use_blocks=true
cleanup.always_use_parentheses_in_expressions=false
cleanup.always_use_this_for_non_static_field_access=false
cleanup.always_use_this_for_non_static_method_access=false
cleanup.convert_to_enhanced_for_loop=false
cleanup.correct_indentation=false
cleanup.format_source_code=false
cleanup.format_source_code_changes_only=false
cleanup.make_local_variable_final=true
cleanup.make_parameters_final=false
cleanup.make_private_fields_final=true
cleanup.make_type_abstract_if_missing_method=false
cleanup.make_variable_declarations_final=false
cleanup.never_use_blocks=false
cleanup.never_use_parentheses_in_expressions=true
cleanup.organize_imports=true
cleanup.qualify_static_field_accesses_with_declaring_class=false
cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
cleanup.qualify_static_member_accesses_with_declaring_class=true
cleanup.qualify_static_method_accesses_with_declaring_class=false
cleanup.remove_private_constructors=true
cleanup.remove_trailing_whitespaces=true
cleanup.remove_trailing_whitespaces_all=true
cleanup.remove_trailing_whitespaces_ignore_empty=false
cleanup.remove_unnecessary_casts=true
cleanup.remove_unnecessary_nls_tags=true
cleanup.remove_unused_imports=true
cleanup.remove_unused_local_variables=false
cleanup.remove_unused_private_fields=true
cleanup.remove_unused_private_members=false
cleanup.remove_unused_private_methods=true
cleanup.remove_unused_private_types=true
cleanup.sort_members=false
cleanup.sort_members_all=false
cleanup.use_blocks=false
cleanup.use_blocks_only_for_return_and_throw=false
cleanup.use_parentheses_in_expressions=false
cleanup.use_this_for_non_static_field_access=false
cleanup.use_this_for_non_static_field_access_only_if_necessary=true
cleanup.use_this_for_non_static_method_access=false
cleanup.use_this_for_non_static_method_access_only_if_necessary=true
cleanup_profile=_Astrid
cleanup_settings_version=2
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_Astrid
formatter_settings_version=11
org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=true
sp_cleanup.add_missing_deprecated_annotations=true
sp_cleanup.add_missing_methods=false
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.correct_indentation=false
sp_cleanup.format_source_code=false
sp_cleanup.format_source_code_changes_only=false
sp_cleanup.make_local_variable_final=false
sp_cleanup.make_parameters_final=false
sp_cleanup.make_private_fields_final=true
sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=true
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=true
sp_cleanup.organize_imports=true
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=false
sp_cleanup.remove_unnecessary_nls_tags=true
sp_cleanup.remove_unused_imports=true
sp_cleanup.remove_unused_local_variables=false
sp_cleanup.remove_unused_private_fields=true
sp_cleanup.remove_unused_private_members=false
sp_cleanup.remove_unused_private_methods=true
sp_cleanup.remove_unused_private_types=true
sp_cleanup.sort_members=false
sp_cleanup.sort_members_all=false
sp_cleanup.use_blocks=false
sp_cleanup.use_blocks_only_for_return_and_throw=false
sp_cleanup.use_parentheses_in_expressions=false
sp_cleanup.use_this_for_non_static_field_access=false
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
sp_cleanup.use_this_for_non_static_method_access=false
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.astrid.filter"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="com.todoroo.astrid.READ" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<receiver android:name="com.example.astrid.filter.FilterExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_FILTERS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>

@ -0,0 +1,7 @@
Filter Samples
This project shows you how to create a filter plugin. It involves three files:
FilterAddon.java - returns your addon, which allows users to turn it on or off
FilterExposer.java - returns your filters
AndroidManifest.xml - receives Astrid's requests and sends appropriate stuff

Binary file not shown.

Binary file not shown.

@ -0,0 +1,11 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-4

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World!</string>
<string name="app_name">Astrid Filter Samples</string>
</resources>

@ -0,0 +1,26 @@
package com.example.astrid.filter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* This class identifies the add-on to Astrid so users can re-order their
* add-ons or toggle their visibility.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class FilterAddon extends BroadcastReceiver {
/**
* Allows your plugin intents to identify themselves
*/
static final String IDENTIFIER = "samplefilter"; //$NON-NLS-1$
@Override
public void onReceive(Context context, Intent intent) {
// TODO coming in v3.0.0
}
}

@ -0,0 +1,47 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.example.astrid.filter;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import com.todoroo.andlib.Order;
import com.todoroo.andlib.QueryTemplate;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterListHeader;
import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.data.Task;
/**
* Exposes Filters when requested
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class FilterExposer extends BroadcastReceiver {
@SuppressWarnings("nls")
@Override
public void onReceive(Context context, Intent intent) {
FilterListItem[] list = new FilterListItem[2];
list[0] = new FilterListHeader("Sample Filters");
ContentValues completedValues = new ContentValues();
completedValues.put(Task.COMPLETION_DATE.name, Filter.VALUE_NOW);
list[1] = new Filter("Completed by Alpha",
"Completed by Alpha",
new QueryTemplate().where(Task.COMPLETION_DATE.gt(0)).orderBy(Order.asc(Task.TITLE)),
completedValues);
// transmit filter list
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
}

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

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

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

@ -0,0 +1,141 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.andlib;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import android.content.Context;
public class DateUtilities {
/* ======================================================================
* ============================================================ long time
* ====================================================================== */
/** Convert unixtime into date */
public static final Date unixtimeToDate(long millis) {
if(millis == 0)
return null;
return new Date(millis);
}
/** Convert date into unixtime */
public static final long dateToUnixtime(Date date) {
if(date == null)
return 0;
return date.getTime();
}
/** Returns unixtime for current time */
public static final long now() {
return System.currentTimeMillis();
}
/** Returns unixtime one month from now */
public static final long oneMonthFromNow() {
Date date = new Date();
date.setMonth(date.getMonth() + 1);
return date.getTime();
}
/** Represents a single hour */
public static long ONE_HOUR = 3600000L;
/** Represents a single day */
public static long ONE_DAY = 24 * ONE_HOUR;
/** Represents a single week */
public static long ONE_WEEK = 7 * ONE_DAY;
/* ======================================================================
* =========================================================== formatters
* ====================================================================== */
@SuppressWarnings("nls")
public static boolean is24HourFormat(Context context) {
String value = android.provider.Settings.System.getString(context.getContentResolver(),
android.provider.Settings.System.TIME_12_24);
boolean b24 = !(value == null || value.equals("12"));
return b24;
}
/**
* @return time format (hours and minutes)
*/
public static SimpleDateFormat getTimeFormat(Context context) {
String value = getTimeFormatString(context);
return new SimpleDateFormat(value);
}
/**
* @return string used for time formatting
*/
@SuppressWarnings("nls")
private static String getTimeFormatString(Context context) {
String value;
if (is24HourFormat(context)) {
value = "H:mm";
} else {
value = "h:mm a";
}
return value;
}
/**
* @return string used for date formatting
*/
@SuppressWarnings("nls")
private static String getDateFormatString(Context context) {
String value = android.provider.Settings.System.getString(context.getContentResolver(),
android.provider.Settings.System.DATE_FORMAT);
if (value == null) {
// united states, you are special
if (Locale.US.equals(Locale.getDefault())
|| Locale.CANADA.equals(Locale.getDefault()))
value = "MMM d yyyy";
else
value = "d MMM yyyy";
}
return value;
}
/**
* @return date format (month, day, year)
*/
public static SimpleDateFormat getDateFormat(Context context) {
return new SimpleDateFormat(getDateFormatString(context));
}
/**
* @return date format as getDateFormat with weekday
*/
@SuppressWarnings("nls")
public static SimpleDateFormat getDateFormatWithWeekday(Context context) {
return new SimpleDateFormat("EEE, " + getDateFormatString(context));
}
/**
* @return date with time at the end
*/
@SuppressWarnings("nls")
public static SimpleDateFormat getDateWithTimeFormat(Context context) {
return new SimpleDateFormat(getDateFormatString(context) + " " +
getTimeFormatString(context));
}
/**
* @return formatted date (will contain month, day, year)
*/
public static String getFormattedDate(Context context, Date date) {
return getDateFormat(context).format(date);
}
}

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

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

@ -0,0 +1,28 @@
package com.todoroo.andlib;
@SuppressWarnings("nls")
public final class Functions {
public static String caseStatement(Criterion when, Object ifTrue, Object ifFalse) {
return new StringBuilder("(CASE WHEN ").
append(when.toString()).append(" THEN ").append(value(ifTrue)).
append(" ELSE ").append(value(ifFalse)).append(" END)").toString();
}
private static String value(Object value) {
return value.toString();
}
public static Field upper(Field title) {
return new Field("UPPER(" + title.toString() + ")");
}
/**
* @return SQL now (in milliseconds)
*/
public static Field now() {
return new Field("(strftime('%s','now')*1000)");
}
}

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

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

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

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

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

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

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

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

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

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

@ -0,0 +1,93 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents an add-onn for Astrid. Users can enable or disable add-ons,
* which affect all other extension points that share the same identifier.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class Addon implements Parcelable {
/**
* Add-on Identifier
*/
public String addon = null;
/**
* Plug-in Title
*/
public String title = null;
/**
* Plug-in Author
*/
public String author = null;
/**
* Plug-in Description
*/
public String description = null;
/**
* Convenience constructor to generate a plug-in object
*
* @param addon
* @param title
* @param author
* @param description
*/
public Addon(String addon, String title, String author, String description) {
this.addon = addon;
this.title = title;
this.author = author;
this.description = description;
}
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(addon);
dest.writeString(title);
dest.writeString(author);
dest.writeString(description);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<Addon> CREATOR = new Parcelable.Creator<Addon>() {
/**
* {@inheritDoc}
*/
public Addon createFromParcel(Parcel source) {
return new Addon(source.readString(), source.readString(),
source.readString(), source.readString());
}
/**
* {@inheritDoc}
*/
public Addon[] newArray(int size) {
return new Addon[size];
};
};
}

@ -0,0 +1,197 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.widget.RemoteViews;
/**
* Constants for interfacing with Astrid.
*
* @author Tim Su <tim@todoroo.com>
*/
@SuppressWarnings("nls")
public class AstridApiConstants {
// --- General Constants
/**
* Astrid application package name
*/
public static final String PACKAGE = "com.todoroo.astrid";
/**
* Permission for reading tasks and receiving to GET_FILTERS intent
*/
public static final String PERMISSION_READ = PACKAGE + ".READ";
/**
* Permission for writing and creating tasks
*/
public static final String PERMISSION_WRITE = PACKAGE + ".WRITE";
// --- Broadcast Extras
/**
* Extras name for task id
*/
public static final String EXTRAS_TASK_ID = "task";
/**
* Extras name for a response item broadcast to astrid
*/
public static final String EXTRAS_RESPONSE = "response";
/**
* Extras name for plug-in identifier
*/
public static final String EXTRAS_ADDON = "addon";
/**
* Extras name for whether task detail request is extended
*/
public static final String EXTRAS_EXTENDED = "extended";
// --- Add-ons API
/**
* Action name for broadcast intent requesting add-ons
*/
public static final String BROADCAST_REQUEST_ADDONS = PACKAGE + ".REQUEST_ADDONS";
/**
* Action name for broadcast intent sending add-ons back to Astrid
* @extra EXTRAS_RESPONSE an {@link Addon} object
*/
public static final String BROADCAST_SEND_ADDONS = PACKAGE + ".SEND_ADDONS";
// --- Filters API
/**
* Action name for broadcast intent requesting filters
*/
public static final String BROADCAST_REQUEST_FILTERS = PACKAGE + ".REQUEST_FILTERS";
/**
* Action name for broadcast intent sending filters back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE an array of {@link FilterListItem}s
*/
public static final String BROADCAST_SEND_FILTERS = PACKAGE + ".SEND_FILTERS";
// --- Edit Controls API
/**
* Action name for broadcast intent requesting task edit controls
* @extra EXTRAS_TASK_ID id of the task user is editing
*/
public static final String BROADCAST_REQUEST_EDIT_CONTROLS = PACKAGE + ".REQUEST_EDIT_CONTROLS";
/**
* Action name for broadcast intent sending task edit controls back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE a {@link RemoteViews} with your edit controls
*/
public static final String BROADCAST_SEND_EDIT_CONTROLS = PACKAGE + ".SEND_EDIT_CONTROLS";
// --- Task Details API
/**
* Action name for broadcast intent requesting details for a task.
* Extended details are displayed when a user presses on a task.
*
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_EXTENDED whether request is for standard or extended details
*/
public static final String BROADCAST_REQUEST_DETAILS = PACKAGE + ".REQUEST_DETAILS";
/**
* Action name for broadcast intent sending details back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_EXTENDED whether request is for standard or extended details
* @extra EXTRAS_RESPONSE a String
*/
public static final String BROADCAST_SEND_DETAILS = PACKAGE + ".SEND_DETAILS";
// --- Task Actions API
/**
* Action name for broadcast intent requesting actions for a task
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_REQUEST_ACTIONS = PACKAGE + ".REQUEST_ACTIONS";
/**
* Action name for broadcast intent sending actions back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_RESPONSE a String
*/
public static final String BROADCAST_SEND_ACTIONS = PACKAGE + ".SEND_ACTIONS";
// --- Task Decorations API
/**
* Action name for broadcast intent requesting task list decorations for a task
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_REQUEST_DECORATIONS = PACKAGE + ".REQUEST_DECORATIONS";
/**
* Action name for broadcast intent sending decorations back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_RESPONSE a {@link TaskDecoration}
*/
public static final String BROADCAST_SEND_DECORATIONS = PACKAGE + ".SEND_DECORATIONS";
// --- Actions API
/**
* Action name for intents to be displayed on task context menu
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String ACTION_TASK_CONTEXT_MENU = PACKAGE + ".CONTEXT_MENU";
/**
* Action name for intents to be displayed on Astrid's task list menu
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE an array of {@link Intent}s
*/
public static final String ACTION_TASK_LIST_MENU = PACKAGE + ".TASK_LIST_MENU";
// --- Settings API
/**
* Action name for intents to be displayed in Astrid's settings
*/
public static final String ACTION_SETTINGS = PACKAGE + ".SETTINGS";
// --- Events API
/**
* Action name for broadcast intent notifying that task was completed
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_EVENT_TASK_COMPLETED = PACKAGE + ".TASK_COMPLETED";
/**
* Action name for broadcast intent notifying that task was created
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_EVENT_TASK_CREATED = PACKAGE + ".TASK_CREATED";
// --- SQL Constants
/**
* Table name for tasks
*/
public static final String TASK_TABLE = "tasks";
/**
* Table name for metadata
*/
public static final String METADATA_TABLE = "metadata";
}

@ -0,0 +1,142 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.content.ContentValues;
import android.os.Parcel;
import android.os.Parcelable;
import com.todoroo.andlib.QueryTemplate;
/**
* A <code>FilterListFilter</code> allows users to display tasks that have
* something in common.
* <p>
* A plug-in can expose new <code>FilterListFilter</code>s to the system by
* responding to the <code>com.todoroo.astrid.GET_FILTERS</code> broadcast
* intent.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class Filter extends FilterListItem {
// --- constants
/** Constant for valuesForNewTasks to indicate the value should be replaced
* with the current time as long */
public static final long VALUE_NOW = Long.MIN_VALUE + 1;
// --- instance variables
/**
* Expanded title of this filter. This is displayed at the top
* of the screen when user is viewing this filter.
* <p>
* e.g "Tasks With Notes"
*/
public String title;
/**
* SQL query for this filter. The query will be appended to the select
* statement after "<code>SELECT fields FROM table %s</code>". It is
* recommended that you use a {@link QueryTemplate} to construct your
* query.
* <p>
* Examples:
* <ul>
* <li><code>"WHERE completionDate = 0"</code>
* <li><code>"INNER JOIN " +
* Constants.TABLE_METADATA + " ON metadata.task = tasks.id WHERE
* metadata.namespace = " + NAMESPACE + " AND metadata.key = 'a' AND
* metadata.value = 'b' GROUP BY tasks.id ORDER BY tasks.title"</code>
* </ul>
*/
public String sqlQuery;
/**
* Values to apply to a task when quick-adding a task from this filter.
* For example, when a user views tasks tagged 'ABC', the
* tasks they create should also be tagged 'ABC'. If set to null, no
* additional values will be stored for a task.
*/
public ContentValues valuesForNewTasks = null;
/**
* Utility constructor for creating a TaskList object
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
* @param title
* Expanded title of this filter when user is viewing this
* filter, e.g. Inbox (20 tasks)
* @param sqlQuery
* SQL query for this list (see {@link sqlQuery} for examples).
* @param valuesForNewTasks
* see {@link sqlForNewTasks}
*/
public Filter(String listingTitle, String title,
QueryTemplate sqlQuery, ContentValues valuesForNewTasks) {
this.listingTitle = listingTitle;
this.title = title;
if(sqlQuery != null)
this.sqlQuery = sqlQuery.toString();
this.valuesForNewTasks = valuesForNewTasks;
}
/**
* Utility constructor
*
* @param plugin
* {@link Addon} identifier that encompasses object
*/
protected Filter() {
// do nothing
}
// --- parcelable
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(title);
dest.writeString(sqlQuery);
dest.writeParcelable(valuesForNewTasks, 0);
}
/**
* Parcelable Creator Object
*/
public static final Parcelable.Creator<Filter> CREATOR = new Parcelable.Creator<Filter>() {
/**
* {@inheritDoc}
*/
public Filter createFromParcel(Parcel source) {
Filter item = new Filter();
item.readFromParcel(source);
item.title = source.readString();
item.sqlQuery = source.readString();
item.valuesForNewTasks = source.readParcelable(ContentValues.class.getClassLoader());
return item;
}
/**
* {@inheritDoc}
*/
public Filter[] newArray(int size) {
return new Filter[size];
}
};
}

@ -0,0 +1,96 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.os.Parcel;
import android.os.Parcelable;
/**
* A <code>FilterCategory</code> groups common {@link Filter}s and allows
* a user to show/hide all of its children.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class FilterCategory extends FilterListItem {
/**
* {@link Filter}s contained by this category
*/
public Filter[] children;
/**
* Constructor for creating a new FilterCategory
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
* @param children
* filters belonging to this category
*/
public FilterCategory(String listingTitle, Filter[] children) {
this.listingTitle = listingTitle;
this.children = children;
}
/**
* Constructor for creating a new FilterCategory
*
* @param plugin
* {@link Addon} identifier that encompasses object
*/
protected FilterCategory() {
//
}
// --- parcelable
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeParcelableArray(children, 0);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<FilterCategory> CREATOR = new Parcelable.Creator<FilterCategory>() {
/**
* {@inheritDoc}
*/
public FilterCategory createFromParcel(Parcel source) {
FilterCategory item = new FilterCategory();
item.readFromParcel(source);
Parcelable[] parcelableChildren = source.readParcelableArray(
FilterCategory.class.getClassLoader());
item.children = new Filter[parcelableChildren.length];
for(int i = 0; i < item.children.length; i++) {
if(parcelableChildren[i] instanceof FilterListItem)
item.children[i] = (Filter) parcelableChildren[i];
else
item.children[i] = null;
}
return item;
}
/**
* {@inheritDoc}
*/
public FilterCategory[] newArray(int size) {
return new FilterCategory[size];
}
};
}

@ -0,0 +1,61 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Section Header for Filter List
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class FilterListHeader extends FilterListItem {
/**
* Constructor for creating a new FilterListHeader
* @param listingTitle
* @param listingIconResource
* @param priority
*/
public FilterListHeader(String listingTitle) {
this.listingTitle = listingTitle;
}
/**
* Constructor for creating a new FilterListHeader
*
* @param plugin
* {@link Addon} identifier that encompasses object
*/
protected FilterListHeader() {
//
}
// --- parcelable
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
}
public static final Parcelable.Creator<FilterListHeader> CREATOR = new Parcelable.Creator<FilterListHeader>() {
public FilterListHeader createFromParcel(Parcel source) {
FilterListHeader item = new FilterListHeader();
item.readFromParcel(source);
return item;
}
public FilterListHeader[] newArray(int size) {
return new FilterListHeader[size];
}
};
}

@ -0,0 +1,74 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents an item displayed by Astrid's FilterListActivity
*
* @author Tim Su <tim@todoroo.com>
*
*/
abstract public class FilterListItem implements Parcelable {
/**
* Title of this item displayed on the Filters page
*/
public String listingTitle = null;
/**
* Bitmap for icon used on listing page. <code>null</code> => no icon
*/
public Bitmap listingIcon = null;
/**
* Context Menu labels. The context menu will be displayed when users
* long-press on this filter list item.
*/
public String contextMenuLabels[] = new String[0];
/**
* Context menu intents. This intent will be started when the corresponding
* content menu label is invoked. This array must be the same size as
* the contextMenuLabels array.
*/
public Intent contextMenuIntents[] = new Intent[0];
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(listingTitle);
dest.writeParcelable(listingIcon, 0);
// write array lengths before arrays
dest.writeInt(contextMenuLabels.length);
dest.writeStringArray(contextMenuLabels);
dest.writeInt(contextMenuIntents.length);
dest.writeTypedArray(contextMenuIntents, 0);
}
/**
* Utility method to read FilterListItem properties from a parcel.
*
* @param source
*/
public void readFromParcel(Parcel source) {
listingTitle = source.readString();
listingIcon = source.readParcelable(Bitmap.class.getClassLoader());
int length = source.readInt();
contextMenuLabels = new String[length];
source.readStringArray(contextMenuLabels);
length = source.readInt();
contextMenuIntents = new Intent[length];
source.readTypedArray(contextMenuIntents, Intent.CREATOR);
}
}

@ -0,0 +1,79 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents an intent that can be called on a task
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TaskAction implements Parcelable {
/**
* Label
*/
public String text = null;
/**
* Intent to call when invoking this operation
*/
public PendingIntent intent = null;
/**
* Create an EditOperation object
*
* @param text
* label to display
* @param intent
* intent to invoke. {@link EXTRAS_TASK_ID} will be passed
*/
public TaskAction(String text, PendingIntent intent) {
super();
this.text = text;
this.intent = intent;
}
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(text);
dest.writeParcelable(intent, 0);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<TaskAction> CREATOR = new Parcelable.Creator<TaskAction>() {
/**
* {@inheritDoc}
*/
public TaskAction createFromParcel(Parcel source) {
return new TaskAction(source.readString(), (PendingIntent)source.readParcelable(
PendingIntent.class.getClassLoader()));
}
/**
* {@inheritDoc}
*/
public TaskAction[] newArray(int size) {
return new TaskAction[size];
};
};
}

@ -0,0 +1,96 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.os.Parcel;
import android.os.Parcelable;
import android.widget.RemoteViews;
import android.widget.RemoteViews.RemoteView;
/**
* Represents a line of text displayed in the Task List
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class TaskDecoration implements Parcelable {
/**
* Place decoration between completion box and task title
*/
public static final int POSITION_LEFT = 0;
/**
* Place decoration between task title and importance bar
*/
public static final int POSITION_RIGHT = 1;
/**
* {@link RemoteView} decoration
*/
public RemoteViews decoration = null;
/**
* Decoration position
*/
public int position = POSITION_LEFT;
/**
* Decorated task background color. 0 is default
*/
public int color = 0;
/**
* Creates a TaskDetail object
* @param text
* text to display
* @param color
* color to use for text. Use <code>0</code> for default color
*/
public TaskDecoration(RemoteViews decoration, int position, int color) {
this.decoration = decoration;
this.position = position;
this.color = color;
}
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(decoration, 0);
dest.writeInt(position);
dest.writeInt(color);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<TaskDecoration> CREATOR = new Parcelable.Creator<TaskDecoration>() {
/**
* {@inheritDoc}
*/
public TaskDecoration createFromParcel(Parcel source) {
return new TaskDecoration((RemoteViews)source.readParcelable(
RemoteViews.class.getClassLoader()),
source.readInt(), source.readInt());
}
/**
* {@inheritDoc}
*/
public TaskDecoration[] newArray(int size) {
return new TaskDecoration[size];
};
};
}

@ -0,0 +1,425 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.astrid.data;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import android.content.ContentValues;
import android.os.Parcel;
import android.os.Parcelable;
import com.todoroo.astrid.data.Property.DoubleProperty;
import com.todoroo.astrid.data.Property.IntegerProperty;
import com.todoroo.astrid.data.Property.LongProperty;
import com.todoroo.astrid.data.Property.PropertyVisitor;
/**
* <code>AbstractModel</code> represents a row in a database.
* <p>
* A single database can be represented by multiple <code>AbstractModel</code>s
* corresponding to different queries that return a different set of columns.
* Each model exposes a set of properties that it contains.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public abstract class AbstractModel implements Parcelable {
// --- static variables
private static final ContentValuesSavingVisitor saver = new ContentValuesSavingVisitor();
// --- constants
/** id property common to all models */
protected static final String ID_PROPERTY_NAME = "_id"; //$NON-NLS-1$
/** id field common to all models */
public static final IntegerProperty ID_PROPERTY = new IntegerProperty(null, ID_PROPERTY_NAME);
/** sentinel for objects without an id */
public static final long NO_ID = 0;
// --- abstract methods
/** Get the default values for this object */
abstract public ContentValues getDefaultValues();
// --- data store variables and management
/* Data Source Ordering:
*
* In order to return the best data, we want to check first what the user
* has explicitly set (setValues), then the values we have read out of
* the database (values), then defaults (getDefaultValues)
*/
/** User set values */
protected ContentValues setValues = null;
/** Values from database */
protected ContentValues values = null;
/** Get database-read values for this object */
public ContentValues getDatabaseValues() {
return values;
}
/** Get the user-set values for this object */
public ContentValues getSetValues() {
return setValues;
}
/** Get a list of all field/value pairs merged across data sources */
public ContentValues getMergedValues() {
ContentValues mergedValues = new ContentValues();
ContentValues defaultValues = getDefaultValues();
if(defaultValues != null)
mergedValues.putAll(defaultValues);
if(values != null)
mergedValues.putAll(values);
if(setValues != null)
mergedValues.putAll(setValues);
return mergedValues;
}
/**
* Clear all data on this model
*/
public void clear() {
values = null;
setValues = null;
}
/**
* Transfers all set values into values. This occurs when a task is
* saved - future saves will not need to write all the data as before.
*/
public void markSaved() {
if(values == null)
values = setValues;
else if(setValues != null)
values.putAll(setValues);
setValues = null;
}
/**
* Use merged values to compare two models to each other. Must be of
* exactly the same class.
*/
@Override
public boolean equals(Object other) {
if(other == null || other.getClass() != getClass())
return false;
return getMergedValues().equals(((AbstractModel)other).getMergedValues());
}
@Override
public int hashCode() {
return getMergedValues().hashCode() ^ getClass().hashCode();
}
// --- data retrieval
/**
* Reads all properties from the supplied cursor and store
*/
protected synchronized void readPropertiesFromCursor(TodorooCursor<? extends AbstractModel> cursor) {
if (values == null)
values = new ContentValues();
// clears user-set values
setValues = null;
for (Property<?> property : cursor.getProperties()) {
saver.save(property, values, cursor.get(property));
}
}
/**
* Reads the given property. Make sure this model has this property!
*/
public synchronized <TYPE> TYPE getValue(Property<TYPE> property) {
Object value;
if(setValues != null && setValues.containsKey(property.name))
value = setValues.get(property.name);
else if(values != null && values.containsKey(property.name) && (values.get(property.name) != null))
value = values.get(property.name);
else if(getDefaultValues().containsKey(property.name))
value = getDefaultValues().get(property.name);
else
throw new UnsupportedOperationException(
"Model Error: Did not read property " + property.name); //$NON-NLS-1$
// resolve properties that were retrieved with a different type than accessed
if(value instanceof String && property instanceof LongProperty)
return (TYPE) Long.valueOf((String)value);
else if(value instanceof String && property instanceof IntegerProperty)
return (TYPE) Integer.valueOf((String)value);
if(value instanceof String && property instanceof DoubleProperty)
return (TYPE) Double.valueOf((String)value);
return (TYPE) value;
}
/**
* Utility method to get the identifier of the model, if it exists.
*
* @return {@value #NO_ID} if this model was not added to the database
*/
abstract public long getId();
protected long getIdHelper(LongProperty id) {
if(setValues != null && setValues.containsKey(id.name))
return setValues.getAsLong(id.name);
else if(values != null && values.containsKey(id.name))
return values.getAsLong(id.name);
else
return NO_ID;
}
public void setId(long id) {
if (setValues == null)
setValues = new ContentValues();
if(id == NO_ID)
setValues.remove(ID_PROPERTY_NAME);
else
setValues.put(ID_PROPERTY_NAME, id);
}
/**
* @return true if this model has found Jesus (i.e. the database)
*/
public boolean isSaved() {
return getId() != NO_ID;
}
/**
* @param property
* @return true if setValues or values contains this property
*/
public boolean containsValue(Property<?> property) {
if(setValues != null && setValues.containsKey(property.name))
return true;
if(values != null && values.containsKey(property.name))
return true;
return false;
}
/**
* @param property
* @return true if setValues or values contains this property, and the value
* stored is not null
*/
public boolean containsNonNullValue(Property<?> property) {
if(setValues != null && setValues.containsKey(property.name))
return setValues.get(property.name) != null;
if(values != null && values.containsKey(property.name))
return values.get(property.name) != null;
return false;
}
// --- data storage
/**
* Check whether the user has changed this property value and it should be
* stored for saving in the database
*/
protected synchronized <TYPE> boolean shouldSaveValue(
Property<TYPE> property, TYPE newValue) {
// we've already decided to save it, so overwrite old value
if (setValues.containsKey(property.name))
return true;
// values contains this key, we should check it out
if(values != null && values.containsKey(property.name)) {
TYPE value = getValue(property);
if (value == null) {
if (newValue == null)
return false;
} else if (value.equals(newValue))
return false;
}
// otherwise, good to save
return true;
}
/**
* Sets the given property. Make sure this model has this property!
*/
public synchronized <TYPE> void setValue(Property<TYPE> property,
TYPE value) {
if (setValues == null)
setValues = new ContentValues();
if (!shouldSaveValue(property, value))
return;
saver.save(property, setValues, value);
}
/**
* Merges content values with those coming from another source
*/
public synchronized <TYPE> void mergeWith(ContentValues other) {
if (setValues == null)
setValues = other;
else
setValues.putAll(other);
}
/**
* Clear the key for the given property
* @param property
*/
public synchronized void clearValue(Property<?> property) {
if(setValues != null && setValues.containsKey(property.name))
setValues.remove(property.name);
else if(values != null && values.containsKey(property.name))
values.remove(property.name);
else if(getDefaultValues().containsKey(property.name))
throw new IllegalArgumentException("Property has a default value"); //$NON-NLS-1$
}
// --- property management
/**
* Looks inside the given class and finds all declared properties
*/
protected static Property<?>[] generateProperties(Class<? extends AbstractModel> cls) {
ArrayList<Property<?>> properties = new ArrayList<Property<?>>();
if(cls.getSuperclass() != AbstractModel.class)
properties.addAll(Arrays.asList(generateProperties(
(Class<? extends AbstractModel>) cls.getSuperclass())));
// a property is public, static & extends Property
for(Field field : cls.getFields()) {
if((field.getModifiers() & Modifier.STATIC) == 0)
continue;
if(!Property.class.isAssignableFrom(field.getType()))
continue;
try {
properties.add((Property<?>) field.get(null));
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return properties.toArray(new Property<?>[properties.size()]);
}
/**
* Visitor that saves a value into a content values store
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class ContentValuesSavingVisitor implements PropertyVisitor<Void, Object> {
private ContentValues store;
public synchronized void save(Property<?> property, ContentValues newStore, Object value) {
this.store = newStore;
property.accept(this, value);
}
public Void visitDouble(Property<Double> property, Object value) {
store.put(property.name, (Double) value);
return null;
}
public Void visitInteger(Property<Integer> property, Object value) {
store.put(property.name, (Integer) value);
return null;
}
public Void visitLong(Property<Long> property, Object value) {
store.put(property.name, (Long) value);
return null;
}
public Void visitString(Property<String> property, Object value) {
store.put(property.name, (String) value);
return null;
}
}
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(setValues, 0);
dest.writeParcelable(values, 0);
}
/**
* In addition to overriding this class, model classes should create
* a static final variable named "CREATOR" in order to satisfy the
* requirements of the Parcelable interface.
*/
abstract protected Parcelable.Creator<? extends AbstractModel> getCreator();
/**
* Parcelable creator helper
*/
protected static final class ModelCreator<TYPE extends AbstractModel>
implements Parcelable.Creator<TYPE> {
private final Class<TYPE> cls;
public ModelCreator(Class<TYPE> cls) {
super();
this.cls = cls;
}
/**
* {@inheritDoc}
*/
public TYPE createFromParcel(Parcel source) {
TYPE model;
try {
model = cls.newInstance();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
model.setValues = source.readParcelable(ContentValues.class.getClassLoader());
model.values = source.readParcelable(ContentValues.class.getClassLoader());
return model;
}
/**
* {@inheritDoc}
*/
public TYPE[] newArray(int size) {
return (TYPE[]) Array.newInstance(cls, size);
};
};
}

@ -0,0 +1,101 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.data;
import android.content.ContentValues;
import com.todoroo.astrid.data.Property.LongProperty;
import com.todoroo.astrid.data.Property.StringProperty;
/**
* Data Model which represents a piece of metadata associated with a task
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public class Metadata extends AbstractModel {
// --- table
public static final Table TABLE = new Table("metadata", Metadata.class);
// --- properties
/** ID */
public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME);
/** Associated Task */
public static final LongProperty TASK = new LongProperty(
TABLE, "task");
/** Metadata Key */
public static final StringProperty KEY = new StringProperty(
TABLE, "key");
/** Metadata Text Value Column 1 */
public static final StringProperty VALUE1 = new StringProperty(
TABLE, "value");
/** Metadata Text Value Column 2 */
public static final StringProperty VALUE2 = new StringProperty(
TABLE, "value2");
/** Metadata Text Value Column 1 */
public static final StringProperty VALUE3 = new StringProperty(
TABLE, "value3");
/** Metadata Text Value Column 1 */
public static final StringProperty VALUE4 = new StringProperty(
TABLE, "value4");
/** Metadata Text Value Column 1 */
public static final StringProperty VALUE5 = new StringProperty(
TABLE, "value5");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(Metadata.class);
// --- defaults
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- data access boilerplate
public Metadata() {
super();
}
public Metadata(TodorooCursor<Metadata> cursor) {
this();
readPropertiesFromCursor(cursor);
}
public void readFromCursor(TodorooCursor<Metadata> cursor) {
super.readPropertiesFromCursor(cursor);
}
@Override
public long getId() {
return getIdHelper(ID);
};
// --- parcelable helpers
private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {
return CREATOR;
}
}

@ -0,0 +1,208 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.astrid.data;
import com.todoroo.andlib.Field;
/**
* Property represents a typed column in a database.
*
* Within a given database row, the parameter may not exist, in which case the
* value is null, it may be of an incorrect type, in which case an exception is
* thrown, or the correct type, in which case the value is returned.
*
* @author Tim Su <tim@todoroo.com>
*
* @param <TYPE>
* a database supported type, such as String or Integer
*/
@SuppressWarnings("nls")
public abstract class Property<TYPE> extends Field implements Cloneable {
// --- implementation
/** The database table name this property */
public final Table table;
/** The database column name for this property */
public final String name;
/**
* Create a property by table and column name. Uses the default property
* expression which is derived from default table name
*/
protected Property(Table table, String columnName) {
this(table, columnName, (table == null) ? (columnName) : (table.name + "." + columnName));
}
/**
* Create a property by table and column name, manually specifying an
* expression to use in SQL
*/
protected Property(Table table, String columnName, String expression) {
super(expression);
this.table = table;
this.name = columnName;
}
/**
* Accept a visitor
*/
abstract public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data);
/**
* Return a clone of this property
*/
@SuppressWarnings("unchecked")
@Override
public Property<TYPE> clone() {
try {
return (Property<TYPE>) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
// --- helper classes and interfaces
/**
* Visitor interface for property classes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface PropertyVisitor<RETURN, PARAMETER> {
public RETURN visitInteger(Property<Integer> property, PARAMETER data);
public RETURN visitLong(Property<Long> property, PARAMETER data);
public RETURN visitDouble(Property<Double> property, PARAMETER data);
public RETURN visitString(Property<String> property, PARAMETER data);
}
// --- children
/**
* Integer property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class IntegerProperty extends Property<Integer> {
public IntegerProperty(Table table, String name) {
super(table, name);
}
protected IntegerProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitInteger(this, data);
}
}
/**
* String property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class StringProperty extends Property<String> {
public StringProperty(Table table, String name) {
super(table, name);
}
protected StringProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitString(this, data);
}
}
/**
* Double property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class DoubleProperty extends Property<Double> {
public DoubleProperty(Table table, String name) {
super(table, name);
}
protected DoubleProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitDouble(this, data);
}
}
/**
* Long property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class LongProperty extends Property<Long> {
public LongProperty(Table table, String name) {
super(table, name);
}
protected LongProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitLong(this, data);
}
}
// --- pseudo-properties
/** Runs a SQL function and returns the result as a string */
public static class StringFunctionProperty extends StringProperty {
public StringFunctionProperty(String function, String columnName) {
super(null, columnName, function);
alias = columnName;
}
}
/** Runs a SQL function and returns the result as a string */
public static class IntegerFunctionProperty extends IntegerProperty {
public IntegerFunctionProperty(String function, String columnName) {
super(null, columnName, function);
alias = columnName;
}
}
/** Counting in aggregated tables. Returns the result of COUNT(1) */
public static final class CountProperty extends IntegerFunctionProperty {
public CountProperty() {
super("COUNT(1)", "count");
}
}
}

@ -0,0 +1,68 @@
package com.todoroo.astrid.data;
import com.todoroo.andlib.Field;
import com.todoroo.andlib.SqlTable;
/**
* Table class. Most fields are final, so methods such as <code>as</code> will
* clone the table when it returns.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class Table extends SqlTable {
public final String name;
public final Class<? extends AbstractModel> modelClass;
public Table(String name, Class<? extends AbstractModel> modelClass) {
this(name, modelClass, null);
}
public Table(String name, Class<? extends AbstractModel> modelClass, String alias) {
super(name);
this.name = name;
this.alias = alias;
this.modelClass = modelClass;
}
/**
* Reads a list of properties from model class by reflection
* @return property array
*/
@SuppressWarnings("nls")
public Property<?>[] getProperties() {
try {
return (Property<?>[])modelClass.getField("PROPERTIES").get(null);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
// --- for sql-dsl
/**
* Create a new join table based on this table, but with an alias
*/
@Override
public Table as(String newAlias) {
return new Table(name, modelClass, newAlias);
}
/**
* Create a field object based on the given property
* @param property
* @return
*/
@SuppressWarnings("nls")
public Field field(Property<?> property) {
if(alias != null)
return Field.field(alias + "." + property.name);
return Field.field(name + "." + property.name);
}
}

@ -0,0 +1,382 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.astrid.data;
import java.util.Date;
import android.content.ContentValues;
import com.todoroo.andlib.DateUtilities;
import com.todoroo.astrid.data.Property.IntegerProperty;
import com.todoroo.astrid.data.Property.LongProperty;
import com.todoroo.astrid.data.Property.StringProperty;
/**
* Data Model which represents a task users need to accomplish.
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public final class Task extends AbstractModel {
// --- table
public static final Table TABLE = new Table("tasks", Task.class);
// --- properties
/** ID */
public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME);
/** Name of Task */
public static final StringProperty TITLE = new StringProperty(
TABLE, "title");
/** Importance of Task (see importance flags) */
public static final IntegerProperty IMPORTANCE = new IntegerProperty(
TABLE, "importance");
/** Unixtime Task is due, 0 if not set */
public static final LongProperty DUE_DATE = new LongProperty(
TABLE, "dueDate");
/** Unixtime Task should be hidden until, 0 if not set */
public static final LongProperty HIDE_UNTIL = new LongProperty(
TABLE, "hideUntil");
/** Unixtime Task was created */
public static final LongProperty CREATION_DATE = new LongProperty(
TABLE, "created");
/** Unixtime Task was last touched */
public static final LongProperty MODIFICATION_DATE = new LongProperty(
TABLE, "modified");
/** Unixtime Task was completed. 0 means active */
public static final LongProperty COMPLETION_DATE = new LongProperty(
TABLE, "completed");
/** Unixtime Task was deleted. 0 means not deleted */
public static final LongProperty DELETION_DATE = new LongProperty(
TABLE, "deleted");
// --- for migration purposes from astrid 2 (eventually we will want to
// move these into the metadata table and treat them as plug-ins
public static final StringProperty NOTES = new StringProperty(
TABLE, "notes");
public static final IntegerProperty ESTIMATED_SECONDS = new IntegerProperty(
TABLE, "estimatedSeconds");
public static final IntegerProperty ELAPSED_SECONDS = new IntegerProperty(
TABLE, "elapsedSeconds");
public static final LongProperty TIMER_START = new LongProperty(
TABLE, "timerStart");
public static final IntegerProperty POSTPONE_COUNT = new IntegerProperty(
TABLE, "postponeCount");
/** Flags for when to send reminders */
public static final IntegerProperty REMINDER_FLAGS = new IntegerProperty(
TABLE, "notificationFlags");
/** Reminder period, in milliseconds. 0 means disabled */
public static final LongProperty REMINDER_PERIOD = new LongProperty(
TABLE, "notifications");
/** Unixtime the last reminder was triggered */
public static final LongProperty REMINDER_LAST = new LongProperty(
TABLE, "lastNotified");
public static final StringProperty RECURRENCE = new StringProperty(
TABLE, "recurrence");
public static final IntegerProperty FLAGS = new IntegerProperty(
TABLE, "flags");
public static final StringProperty CALENDAR_URI = new StringProperty(
TABLE, "calendarUri");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(Task.class);
// --- flags
/** whether repeat occurs relative to completion date instead of due date */
public static final int FLAG_REPEAT_AFTER_COMPLETION = 1 << 1;
// --- notification flags
/** whether to send a reminder at deadline */
public static final int NOTIFY_AT_DEADLINE = 1 << 1;
/** whether to send reminders while task is overdue */
public static final int NOTIFY_AFTER_DEADLINE = 1 << 2;
/** reminder mode non-stop */
public static final int NOTIFY_NONSTOP = 1 << 3;
// --- importance settings
public static final int IMPORTANCE_DO_OR_DIE = 0;
public static final int IMPORTANCE_MUST_DO = 1;
public static final int IMPORTANCE_SHOULD_DO = 2;
public static final int IMPORTANCE_NONE = 3;
public static final int IMPORTANCE_MOST = IMPORTANCE_DO_OR_DIE;
public static final int IMPORTANCE_LEAST = IMPORTANCE_NONE;
// --- defaults
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
static {
defaultValues.put(TITLE.name, "");
defaultValues.put(DUE_DATE.name, 0);
defaultValues.put(HIDE_UNTIL.name, 0);
defaultValues.put(COMPLETION_DATE.name, 0);
defaultValues.put(DELETION_DATE.name, 0);
defaultValues.put(IMPORTANCE.name, IMPORTANCE_NONE);
defaultValues.put(CALENDAR_URI.name, "");
defaultValues.put(RECURRENCE.name, "");
defaultValues.put(REMINDER_PERIOD.name, 0);
defaultValues.put(REMINDER_FLAGS.name, 0);
defaultValues.put(ESTIMATED_SECONDS.name, 0);
defaultValues.put(ELAPSED_SECONDS.name, 0);
defaultValues.put(POSTPONE_COUNT.name, 0);
defaultValues.put(NOTES.name, "");
defaultValues.put(FLAGS.name, 0);
defaultValues.put(TIMER_START.name, 0);
}
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- data access boilerplate
public Task() {
super();
}
public Task(TodorooCursor<Task> cursor) {
this();
readPropertiesFromCursor(cursor);
}
public void readFromCursor(TodorooCursor<Task> cursor) {
super.readPropertiesFromCursor(cursor);
}
@Override
public long getId() {
return getIdHelper(ID);
}
// --- parcelable helpers
private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {
return CREATOR;
}
// --- data access methods
/** Checks whether task is done. Requires COMPLETION_DATE */
public boolean isCompleted() {
return getValue(COMPLETION_DATE) > 0;
}
/** Checks whether task is deleted. Will return false if DELETION_DATE not read */
public boolean isDeleted() {
// assume false if we didn't load deletion date
if(!containsValue(DELETION_DATE))
return false;
else
return getValue(DELETION_DATE) > 0;
}
/** Checks whether task is hidden. Requires HIDDEN_UNTIL */
public boolean isHidden() {
return getValue(HIDE_UNTIL) > DateUtilities.now();
}
/** Checks whether task is done. Requires DUE_DATE */
public boolean hasDueDate() {
return getValue(DUE_DATE) > 0;
}
/**
* Returns the set state of the given flag on the given property
* @param property
* @param flag
* @return
*/
public boolean getFlag(IntegerProperty property, int flag) {
return (getValue(property) & flag) > 0;
}
/**
* Sets the state of the given flag on the given property
* @param property
* @param flag
* @param value
*/
public void setFlag(IntegerProperty property, int flag, boolean value) {
if(value)
setValue(property, getValue(property) | flag);
else
setValue(property, getValue(property) & ~flag);
}
// --- due and hide until date management
/** urgency array index -> significance */
public static final int URGENCY_NONE = 0;
public static final int URGENCY_TODAY = 1;
public static final int URGENCY_TOMORROW = 2;
public static final int URGENCY_DAY_AFTER = 3;
public static final int URGENCY_NEXT_WEEK = 4;
public static final int URGENCY_NEXT_MONTH = 5;
public static final int URGENCY_SPECIFIC_DAY = 6;
public static final int URGENCY_SPECIFIC_DAY_TIME = 7;
/** hide until array index -> significance */
public static final int HIDE_UNTIL_NONE = 0;
public static final int HIDE_UNTIL_DUE = 1;
public static final int HIDE_UNTIL_DAY_BEFORE = 2;
public static final int HIDE_UNTIL_WEEK_BEFORE = 3;
public static final int HIDE_UNTIL_SPECIFIC_DAY = 4;
/**
* Creates due date for this task. If this due date has no time associated,
* we move it to the last millisecond of the day.
*
* @param setting
* one of the URGENCY_* constants
* @param customDate
* if specific day or day & time is set, this value
*/
public long createDueDate(int setting, long customDate) {
long date;
switch(setting) {
case URGENCY_NONE:
date = 0;
break;
case URGENCY_TODAY:
date = DateUtilities.now();
break;
case URGENCY_TOMORROW:
date = DateUtilities.now() + DateUtilities.ONE_DAY;
break;
case URGENCY_DAY_AFTER:
date = DateUtilities.now() + 2 * DateUtilities.ONE_DAY;
break;
case URGENCY_NEXT_WEEK:
date = DateUtilities.now() + DateUtilities.ONE_WEEK;
break;
case URGENCY_NEXT_MONTH:
date = DateUtilities.oneMonthFromNow();
break;
case URGENCY_SPECIFIC_DAY:
case URGENCY_SPECIFIC_DAY_TIME:
date = customDate;
break;
default:
throw new IllegalArgumentException("Unknown setting " + setting);
}
if(date <= 0)
return date;
Date dueDate = new Date(date / 1000L * 1000L); // get rid of millis
if(setting != URGENCY_SPECIFIC_DAY_TIME) {
dueDate.setHours(23);
dueDate.setMinutes(59);
dueDate.setSeconds(59);
} else if(isEndOfDay(dueDate)) {
dueDate.setSeconds(58);
}
return dueDate.getTime();
}
/**
* Create hide until for this task.
*
* @param setting
* one of the HIDE_UNTIL_* constants
* @param customDate
* if specific day is set, this value
* @return
*/
public long createHideUntil(int setting, long customDate) {
long date;
switch(setting) {
case HIDE_UNTIL_NONE:
return 0;
case HIDE_UNTIL_DUE:
date = getValue(DUE_DATE);
break;
case HIDE_UNTIL_DAY_BEFORE:
date = getValue(DUE_DATE) - DateUtilities.ONE_DAY;
break;
case HIDE_UNTIL_WEEK_BEFORE:
date = getValue(DUE_DATE) - DateUtilities.ONE_WEEK;
break;
case HIDE_UNTIL_SPECIFIC_DAY:
date = customDate;
break;
default:
throw new IllegalArgumentException("Unknown setting " + setting);
}
if(date <= 0)
return date;
Date hideUntil = new Date(date / 1000L * 1000L); // get rid of millis
hideUntil.setHours(0);
hideUntil.setMinutes(0);
hideUntil.setSeconds(0);
return hideUntil.getTime();
}
/**
* @return true if hours, minutes, and seconds indicate end of day
*/
private static boolean isEndOfDay(Date date) {
int hours = date.getHours();
int minutes = date.getMinutes();
int seconds = date.getSeconds();
return hours == 23 && minutes == 59 && seconds == 59;
}
/**
* Checks whether this due date has a due time or only a date
*/
public boolean hasDueTime() {
return hasDueTime(getValue(Task.DUE_DATE));
}
/**
* Checks whether provided due date has a due time or only a date
*/
public static boolean hasDueTime(long dueDate) {
return !isEndOfDay(new Date(dueDate));
}
}

@ -0,0 +1,109 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.data;
import java.util.WeakHashMap;
import android.database.Cursor;
import android.database.CursorWrapper;
import com.todoroo.astrid.data.Property.PropertyVisitor;
/**
* AstridCursor wraps a cursor and allows users to query for individual
* {@link Property} types or read an entire {@link AbstractModel} from
* a database row.
*
* @author Tim Su <tim@todoroo.com>
*
* @param <TYPE> a model type that is returned by this cursor
*/
public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
/** Properties read by this cursor */
private final Property<?>[] properties;
/** Weakly cache field name to column id references for this cursor.
* Because it's a weak hash map, entire keys can be discarded by GC */
private final WeakHashMap<String, Integer> columnIndexCache;
/** Property reading visitor */
private static final CursorReadingVisitor reader = new CursorReadingVisitor();
/**
* Create an <code>AstridCursor</code> from the supplied {@link Cursor}
* object.
*
* @param cursor
* @param properties properties read from this cursor
*/
public TodorooCursor(Cursor cursor, Property<?>[] properties) {
super(cursor);
this.properties = properties;
columnIndexCache = new WeakHashMap<String, Integer>();
}
/**
* Get the value for the given property on the underlying {@link Cursor}
*
* @param <PROPERTY_TYPE> type to return
* @param property to retrieve
* @return
*/
public <PROPERTY_TYPE> PROPERTY_TYPE get(Property<PROPERTY_TYPE> property) {
return (PROPERTY_TYPE)property.accept(reader, this);
}
/**
* Gets entire property list
* @return
*/
public Property<?>[] getProperties() {
return properties;
}
/**
* Use cache to get the column index for the given field name
*/
public synchronized int getColumnIndexFromCache(String field) {
Integer index = columnIndexCache.get(field);
if(index == null) {
index = getColumnIndexOrThrow(field);
columnIndexCache.put(field, index);
}
return index;
}
/**
* Visitor that reads the given property from a cursor
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class CursorReadingVisitor implements PropertyVisitor<Object, TodorooCursor<?>> {
public Object visitDouble(Property<Double> property,
TodorooCursor<?> cursor) {
return cursor.getDouble(cursor.getColumnIndexFromCache(property.name));
}
public Object visitInteger(Property<Integer> property,
TodorooCursor<?> cursor) {
return cursor.getInt(cursor.getColumnIndexFromCache(property.name));
}
public Object visitLong(Property<Long> property, TodorooCursor<?> cursor) {
return cursor.getLong(cursor.getColumnIndexFromCache(property.name));
}
public Object visitString(Property<String> property,
TodorooCursor<?> cursor) {
return cursor.getString(cursor.getColumnIndexFromCache(property.name));
}
}
}
Loading…
Cancel
Save