New tests project, + legacy data structures for testing importing.

pull/14/head
Tim Su 14 years ago
parent 288a7a4bdd
commit 5ad2de1046

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="plugin-src"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry combineaccessrules="false" kind="src" path="/astrid-3.x"/>
<classpathentry kind="output" path="ecbuild"/>
</classpath>

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>astrid-3.x-tests</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,270 @@
#Sat Aug 22 23:48:16 PDT 2009
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.5
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.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.5
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,5 @@
#Sat Aug 22 23:48:16 PDT 2009
cleanup_settings_version=2
eclipse.preferences.version=1
formatter_profile=_Astrid
formatter_settings_version=11

@ -0,0 +1,23 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# We only want this apk build for tests.
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
# Notice that we don't have to include the src files of ApiDemos because, by
# running the tests using an instrumentation targeting ApiDemos, we
# automatically get all of its classes loaded into our environment.
LOCAL_PACKAGE_NAME := AstridTests
LOCAL_INSTRUMENTATION_FOR := Astrid
LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.todoroo.astrid.tests"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="3" />
<!-- We add an application tag here just so that we can indicate that
this package needs to link against the android.test library,
which is needed when building test cases. -->
<application>
<uses-library android:name="android.test.runner" />
</application>
<!--
This declares that this application uses the instrumentation test runner targeting
the package of the parent app. To run the tests use the command:
"adb shell am instrument -w com.xxx.xxx.tests/android.test.InstrumentationTestRunner"
-->
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.todoroo.astrid"
android:label="Tests for Astrid"/>
</manifest>

@ -0,0 +1,15 @@
# This file is used to override default values used by the Ant build system.
#
# This file must be checked in Version Control Systems, as it is
# integral to the build system of your project.
# The name of your application package as defined in the manifest.
# Used by the 'uninstall' rule.
#application-package=com.example.myproject
# The name of the source folder.
#source-folder=src
# The name of the output folder.
#out-folder=bin

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Main" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contain the path to the SDK. It should *NOT* be checked in in Version
Control Systems. -->
<property file="local.properties"/>
<!-- The build.properties file can be created by you and is never touched
by the 'android' tool. This is the place to change some of the default property values
used by the Ant rules.
Here are some properties you may want to change/update:
application-package
the name of your application package as defined in the manifest. Used by the
'uninstall' rule.
source-folder
the name of the source folder. Default is 'src'.
out-folder
the name of the output folder. Default is 'bin'.
Properties related to the SDK location or the project target should be updated
using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your application and
should be checked in in Version Control Systems.
-->
<property file="build.properties"/>
<!-- The default.properties file is created and updated by the 'android' tool, as well
as ADT.
This file is an integral part of the build system for your application and
should be checked in in Version Control Systems. -->
<property file="default.properties"/>
<!-- Custom Android task to deal with the project target, and import the proper rules.
This requires ant 1.6.0 or above. -->
<path id="android.antlibs">
<pathelement path="${sdk-location}/tools/lib/anttasks.jar" />
<pathelement path="${sdk-location}/tools/lib/sdklib.jar" />
<pathelement path="${sdk-location}/tools/lib/androidprefs.jar" />
<pathelement path="${sdk-location}/tools/lib/apkbuilder.jar" />
<pathelement path="${sdk-location}/tools/lib/jarutils.jar" />
</path>
<taskdef name="setup"
classname="com.android.ant.SetupTask"
classpathref="android.antlibs"/>
<!-- Execute the Android Setup task that will setup some properties specific to the target,
and import the rules files.
To customize the rules, copy/paste them below the task, and disable import by setting
the import attribute to false:
<setup import="false" />
This will ensure that the properties are setup correctly but that your customized
targets are used.
-->
<setup />
</project>

@ -0,0 +1,13 @@
# 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.
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-4

@ -0,0 +1,130 @@
package com.todoroo.astrid.reminders;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import com.todoroo.andlib.test.utility.DateUtilities;
import com.todoroo.astrid.model.Task;
public class AlarmReceiverTests extends PluginTestCase {
/** simple test of task at deadline */
public void testDeadlineReminder() {
Task task = new Task();
task.setValue(Task.TITLE, "poop");
task.setValue(Task.DUE_DATE, DateUtilities.now());
long id = createTask(task, null);
Intent alarmIntent = new Intent();
alarmIntent.putExtra(AlarmReceiver.TOKEN_TASK_ID, id);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_DEADLINE, true);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_ALARMCLOCK, false);
AlarmReceiver rx = new AlarmReceiver();
AssertiveNotificationManager nm = new AssertiveNotificationManager(getContext());
AlarmReceiver.notificationManager = nm;
rx.onReceive(getContext(), alarmIntent);
nm.assertNotified();
}
/** task at deadline, except hidden. no notification should sound */
public void testDeadlineReminderExceptHidden() {
Task task = new Task();
task.setValue(Task.TITLE, "poop");
task.setValue(Task.DUE_DATE, DateUtilities.now());
task.setValue(Task.HIDDEN_UNTIL, DateUtilities.now() + 100);
long id = createTask(task, null);
Intent alarmIntent = new Intent();
alarmIntent.putExtra(AlarmReceiver.TOKEN_TASK_ID, id);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_DEADLINE, true);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_ALARMCLOCK, false);
AlarmReceiver rx = new AlarmReceiver();
AssertiveNotificationManager nm = new AssertiveNotificationManager(getContext());
AlarmReceiver.notificationManager = nm;
rx.onReceive(getContext(), alarmIntent);
nm.assertNotNotified();
}
/** task upcoming */
public void testUpcomingReminder() {
Task task = new Task();
task.setValue(Task.TITLE, "poop");
task.setValue(Task.DUE_DATE, DateUtilities.now() + 100);
long id = createTask(task, null);
Intent alarmIntent = new Intent();
alarmIntent.putExtra(AlarmReceiver.TOKEN_TASK_ID, id);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_DEADLINE, false);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_ALARMCLOCK, false);
AlarmReceiver rx = new AlarmReceiver();
AssertiveNotificationManager nm = new AssertiveNotificationManager(getContext());
AlarmReceiver.notificationManager = nm;
rx.onReceive(getContext(), alarmIntent);
nm.assertNotified();
}
/** task overdue */
public void testOverdueReminder() {
Task task = new Task();
task.setValue(Task.TITLE, "poop");
task.setValue(Task.DUE_DATE, DateUtilities.now() - 100);
long id = createTask(task, null);
Intent alarmIntent = new Intent();
alarmIntent.putExtra(AlarmReceiver.TOKEN_TASK_ID, id);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_DEADLINE, false);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_ALARMCLOCK, false);
AlarmReceiver rx = new AlarmReceiver();
AssertiveNotificationManager nm = new AssertiveNotificationManager(getContext());
AlarmReceiver.notificationManager = nm;
rx.onReceive(getContext(), alarmIntent);
nm.assertNotified();
}
/** task alarm clock */
public void testAlarmClock() {
Task task = new Task();
task.setValue(Task.TITLE, "poop");
task.setValue(Task.DUE_DATE, DateUtilities.now());
long id = createTask(task, null);
Intent alarmIntent = new Intent();
alarmIntent.putExtra(AlarmReceiver.TOKEN_TASK_ID, id);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_DEADLINE, true);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_ALARMCLOCK, true);
AlarmReceiver rx = new AlarmReceiver();
AssertiveNotificationManager nm = new AssertiveNotificationManager(getContext());
AlarmReceiver.notificationManager = nm;
rx.onReceive(getContext(), alarmIntent);
nm.assertNotified();
assertTrue((nm.getNotification().flags & Notification.FLAG_INSISTENT) > 0);
}
/** test the intent that the alarm receiver creates */
public void testOpenIntent() throws Exception {
Task task = new Task();
task.setValue(Task.TITLE, "poop");
task.setValue(Task.DUE_DATE, DateUtilities.now());
long id = createTask(task, null);
Intent alarmIntent = new Intent();
alarmIntent.putExtra(AlarmReceiver.TOKEN_TASK_ID, id);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_DEADLINE, true);
alarmIntent.putExtra(AlarmReceiver.TOKEN_IS_ALARMCLOCK, false);
AlarmReceiver rx = new AlarmReceiver();
AssertiveNotificationManager nm = new AssertiveNotificationManager(getContext());
AlarmReceiver.notificationManager = nm;
rx.onReceive(getContext(), alarmIntent);
nm.assertNotified();
PendingIntent intent = nm.getNotification().contentIntent;
intent.send();
}
}

@ -0,0 +1,47 @@
package com.todoroo.astrid.reminders;
import android.app.Notification;
import android.content.Context;
import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager;
/**
* Notification manager that provides notifications and adds an
* extra method for verification.
*
* @author timsu
*
*/
public class AssertiveNotificationManager extends AndroidNotificationManager {
Notification notification = null;
public AssertiveNotificationManager(Context context) {
super(context);
}
@Override
public void notify(int id, Notification notification) {
super.notify(id, notification);
this.notification = notification;
}
public void assertNotified() {
if(notification == null)
throw new AssertionError("Notification was not triggered");
}
public void assertNotNotified() {
if(notification != null)
throw new AssertionError("Notification was triggered");
}
public Notification getNotification() {
return notification;
}
public void clear() {
notification = null;
}
}

@ -0,0 +1,110 @@
package com.todoroo.astrid.reminders;
import java.util.Date;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Resources;
import android.preference.PreferenceManager;
import android.test.AndroidTestCase;
import com.todoroo.astrid.R;
import com.todoroo.astrid.reminders.service.NotificationService;
public class NotificationServiceTests extends AndroidTestCase {
/**
* Test quiet hour determination logic
*/
public void testQuietHoursWrapped() {
Context context = getContext();
// test wrapped quiet hours
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
Editor editor = prefs.edit();
Resources r = getContext().getResources();
editor.putString(r.getString(R.string.rmd_EPr_quiet_hours_start_key),
Integer.toString(22));
editor.putString(r.getString(R.string.rmd_EPr_quiet_hours_end_key),
Integer.toString(8));
editor.commit();
Date date = new Date();
date.setHours(21);
date.setMinutes(59);
assertFalse(NotificationService.isInQuietHours(context, date));
date.setHours(22);
date.setMinutes(0);
assertTrue(NotificationService.isInQuietHours(context, date));
date.setHours(23);
assertTrue(NotificationService.isInQuietHours(context, date));
date.setHours(0);
assertTrue(NotificationService.isInQuietHours(context, date));
date.setHours(7);
date.setMinutes(59);
assertTrue(NotificationService.isInQuietHours(context, date));
date.setHours(8);
date.setMinutes(0);
assertFalse(NotificationService.isInQuietHours(context, date));
date.setHours(12);
assertFalse(NotificationService.isInQuietHours(context, date));
date.setHours(20);
assertFalse(NotificationService.isInQuietHours(context, date));
}
/**
* Test quiet hour determination logic
*/
public void testQuietHoursUnwrapped() {
Context context = getContext();
// test wrapped quiet hours
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
Editor editor = prefs.edit();
Resources r = getContext().getResources();
editor.putString(r.getString(R.string.rmd_EPr_quiet_hours_start_key),
Integer.toString(10));
editor.putString(r.getString(R.string.rmd_EPr_quiet_hours_end_key),
Integer.toString(16));
editor.commit();
Date date = new Date();
date.setHours(9);
date.setMinutes(59);
assertFalse(NotificationService.isInQuietHours(context, date));
date.setHours(10);
date.setMinutes(0);
assertTrue(NotificationService.isInQuietHours(context, date));
date.setHours(11);
assertTrue(NotificationService.isInQuietHours(context, date));
date.setHours(13);
assertTrue(NotificationService.isInQuietHours(context, date));
date.setHours(15);
date.setMinutes(59);
assertTrue(NotificationService.isInQuietHours(context, date));
date.setHours(16);
date.setMinutes(0);
assertFalse(NotificationService.isInQuietHours(context, date));
date.setHours(23);
assertFalse(NotificationService.isInQuietHours(context, date));
date.setHours(0);
assertFalse(NotificationService.isInQuietHours(context, date));
}
}

@ -0,0 +1,43 @@
package com.todoroo.astrid.reminders;
import java.util.HashMap;
import java.util.Map.Entry;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.test.DatabaseTestCase;
public class PluginTestCase extends DatabaseTestCase {
@Autowired
public TaskDao taskDao;
@Autowired
public MetadataDao metadataDao;
/**
* Helper method to create a task.
* @param task
* @param metadata
* @return task id
*/
public long createTask(Task task, HashMap<String, String> metadata) {
taskDao.save(database, task, false);
if(metadata != null) {
Metadata metadataItem = new Metadata();
metadataItem.setValue(Metadata.TASK, task.getId());
for(Entry<String, String> entry : metadata.entrySet()) {
metadataItem.setValue(Metadata.KEY, entry.getKey());
metadataItem.setValue(Metadata.VALUE, entry.getValue());
metadataDao.save(database, metadataItem);
}
}
return task.getId();
}
}

@ -0,0 +1,282 @@
package com.todoroo.astrid.reminders.service;
import java.util.HashMap;
import android.database.sqlite.SQLiteQueryBuilder;
import com.todoroo.andlib.test.data.TodorooCursor;
import com.todoroo.andlib.test.utility.DateUtilities;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.reminders.AlarmReceiver;
import com.todoroo.astrid.reminders.Constants;
import com.todoroo.astrid.reminders.PluginTestCase;
import com.todoroo.astrid.reminders.service.ReminderService.Alarm;
public class ReminderServiceTests extends PluginTestCase {
/**
* Test task fetching mechanism for reminders
*/
@SuppressWarnings("unused")
public void testTaskReminderFetch() {
ReminderService service = new ReminderService(getContext());
SQLiteQueryBuilder baseQuery = service.createBaseQuery();
TodorooCursor<Task> cursor = service.fetchTasksWithReminders(baseQuery);
assertEquals(0, cursor.getCount());
cursor.close();
// add new tasks
int now = DateUtilities.now();
Task task = new Task();
HashMap<String, String> metadata = new HashMap<String, String>();
task.setValue(Task.TITLE, "normal");
task.setValue(Task.DUE_DATE, now + 10);
metadata.put(Constants.KEY_REMINDER, Integer.toString(Constants.REMINDER_AT_DEADLINE));
long normalId = createTask(task, metadata);
task.clear();
task.setValue(Task.TITLE, "completed");
task.setValue(Task.DUE_DATE, now + 10);
task.setValue(Task.COMPLETION_DATE, now + 10);
metadata.put(Constants.KEY_REMINDER, Integer.toString(Constants.REMINDER_ALARM_CLOCK));
long completedId = createTask(task, metadata);
task.clear();
task.setValue(Task.TITLE, "hidden");
task.setValue(Task.DUE_DATE, now + 10);
task.setValue(Task.HIDDEN_UNTIL, now + 10);
metadata.put(Constants.KEY_REMINDER, Integer.toString(Constants.REMINDER_INCREASING));
long hiddenId = createTask(task, metadata);
task.clear();
task.setValue(Task.TITLE, "noalarm");
task.setValue(Task.DUE_DATE, now + 10);
metadata.put(Constants.KEY_REMINDER, Integer.toString(Constants.REMINDER_NONE));
long noAlarmId = createTask(task, metadata);
task.clear();
task.setValue(Task.TITLE, "overdue");
task.setValue(Task.DUE_DATE, now - 10);
metadata.put(Constants.KEY_REMINDER, Integer.toString(Constants.REMINDER_AT_DEADLINE));
long overdueId = createTask(task, metadata);
cursor = service.fetchTasksWithReminders(baseQuery);
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
task.readFromCursor(cursor, ReminderService.PROPERTIES);
assertEquals(task.getId(), normalId);
assertEquals(task.getValue(ReminderService.REMINDER_MODE),
Integer.toString(Constants.REMINDER_AT_DEADLINE));
cursor.moveToNext();
task.readFromCursor(cursor, ReminderService.PROPERTIES);
assertEquals(task.getId(), hiddenId);
assertEquals(task.getValue(ReminderService.REMINDER_MODE),
Integer.toString(Constants.REMINDER_INCREASING));
cursor.close();
}
/**
* Test task fetching mechanism for overdue interval
*/
@SuppressWarnings("unused")
public void testTaskOverdueFetch() {
ReminderService service = new ReminderService(getContext());
SQLiteQueryBuilder baseQuery = service.createBaseQuery();
TodorooCursor<Task> cursor = service.fetchTasksWithOverdueReminders(baseQuery);
assertEquals(0, cursor.getCount());
cursor.close();
// add new tasks
int now = DateUtilities.now();
Task task = new Task();
HashMap<String, String> metadata = new HashMap<String, String>();
task.setValue(Task.TITLE, "normal");
task.setValue(Task.DUE_DATE, now - 10);
metadata.put(Constants.KEY_OVERDUE, Integer.toString(1500));
long normalId = createTask(task, metadata);
task.clear();
task.setValue(Task.TITLE, "completed");
task.setValue(Task.DUE_DATE, now - 10);
task.setValue(Task.COMPLETION_DATE, now - 10);
metadata.put(Constants.KEY_OVERDUE, Integer.toString(1500));
long completedId = createTask(task, metadata);
task.clear();
task.setValue(Task.TITLE, "hidden");
task.setValue(Task.DUE_DATE, now - 10);
task.setValue(Task.HIDDEN_UNTIL, now + 10);
metadata.put(Constants.KEY_OVERDUE, Integer.toString(1500));
long hiddenId = createTask(task, metadata);
task.clear();
task.setValue(Task.TITLE, "noalarm");
task.setValue(Task.DUE_DATE, now - 10);
metadata.put(Constants.KEY_OVERDUE, Integer.toString(0));
long noAlarmId = createTask(task, metadata);
task.clear();
task.setValue(Task.TITLE, "not due");
task.setValue(Task.DUE_DATE, now + 10);
metadata.put(Constants.KEY_OVERDUE, Integer.toString(0));
long notDueId = createTask(task, metadata);
cursor = service.fetchTasksWithOverdueReminders(baseQuery);
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
task.readFromCursor(cursor, ReminderService.PROPERTIES);
assertEquals(task.getId(), normalId);
assertEquals(task.getValue(ReminderService.OVERDUE_INTERVAL),
Integer.valueOf(1500));
cursor.moveToNext();
task.readFromCursor(cursor, ReminderService.PROPERTIES);
assertEquals(task.getId(), hiddenId);
assertEquals(task.getValue(ReminderService.OVERDUE_INTERVAL),
Integer.valueOf(1500));
cursor.close();
}
/**
* Test task fetching mechanism for reminders
*/
public void testTaskFetchForReminders() {
ReminderService service = new ReminderService(getContext());
// try invalid
assertNull("could fetch invalid", service.fetchTask(-99));
// add new tasks
Task task = new Task();
HashMap<String, String> metadata = new HashMap<String, String>();
task.setValue(Task.TITLE, "salami");
task.setValue(Task.DUE_DATE, 100);
metadata.put(Constants.KEY_OVERDUE, "123");
metadata.put(Constants.KEY_LAST_NOTIFIED, "345");
metadata.put(Constants.KEY_REMINDER, "567");
long id = createTask(task, metadata);
task = service.fetchTask(id);
assertEquals(task.getValue(Task.TITLE), "salami");
assertEquals(task.getValue(ReminderService.OVERDUE_INTERVAL), Integer.valueOf(123));
assertEquals(task.getValue(ReminderService.LAST_NOTIFIED), Integer.valueOf(345));
assertEquals(task.getValue(ReminderService.REMINDER_MODE), Integer.valueOf(567));
task.setValue(ReminderService.OVERDUE_INTERVAL, 321);
task.setValue(ReminderService.LAST_NOTIFIED, 543);
task.setValue(ReminderService.REMINDER_MODE, 765);
service.writeReminderOptions(task);
task = service.fetchTask(id);
assertEquals(task.getValue(ReminderService.OVERDUE_INTERVAL), Integer.valueOf(321));
assertEquals(task.getValue(ReminderService.LAST_NOTIFIED), Integer.valueOf(543));
assertEquals(task.getValue(ReminderService.REMINDER_MODE), Integer.valueOf(765));
}
/**
* Test alarm generation
*/
public void testAlarmGeneration() {
ReminderService service = new ReminderService(getContext());
int now = DateUtilities.now();
// test simple task with deadline
Task task = new Task();
task.setValue(Task.TITLE, "blah");
task.setValue(Task.DUE_DATE, now + 10000);
task.setValue(ReminderService.REMINDER_MODE, Constants.REMINDER_AT_DEADLINE);
Alarm alarm = service.createReminderAlarm(task);
assertEquals(task.getValue(Task.DUE_DATE), Integer.valueOf(alarm.alarmTime));
// test simple task no reminders
task.setValue(ReminderService.REMINDER_MODE, Constants.REMINDER_NONE);
alarm = service.createReminderAlarm(task);
assertNull(alarm);
// test simple task with deadline passed and no overdue interval
task.setValue(Task.DUE_DATE, now - 10);
task.setValue(ReminderService.REMINDER_MODE, Constants.REMINDER_AT_DEADLINE);
alarm = service.createReminderAlarm(task);
assertNull(alarm);
// test alarm clock task
task.setValue(Task.DUE_DATE, now + 10000);
task.setValue(ReminderService.REMINDER_MODE, Constants.REMINDER_ALARM_CLOCK);
alarm = service.createReminderAlarm(task);
assertEquals(task.getValue(Task.DUE_DATE), Integer.valueOf(alarm.alarmTime));
// test overdue alarm clock task
task.setValue(Task.DUE_DATE, now - 10);
alarm = service.createReminderAlarm(task);
assertNull(alarm);
// test increasing 30 days from now
task.setValue(ReminderService.REMINDER_MODE, Constants.REMINDER_INCREASING);
task.setValue(ReminderService.LAST_NOTIFIED, now);
task.setValue(Task.DUE_DATE, now + 30 * 86400);
alarm = service.createReminderAlarm(task);
assertTrue(alarm.alarmTime > now + 5 * 86400 &&
alarm.alarmTime < now + 25 * 86400);
// test increasing 30 days from now, with last notified of 100 days ago
task.setValue(ReminderService.REMINDER_MODE, Constants.REMINDER_INCREASING);
task.setValue(Task.DUE_DATE, now + 30 * 86400);
task.setValue(ReminderService.LAST_NOTIFIED, now - 100 * 86400);
alarm = service.createReminderAlarm(task);
assertTrue(alarm.alarmTime > 0 &&
alarm.alarmTime < now + 1 * 86400);
// test increasing when we're within the day boundary and task is
// not defined with a specific time
task.setValue(Task.URGENCY, Task.URGENCY_THIS_WEEK);
task.setValue(Task.DUE_DATE, now + 80000);
task.setValue(ReminderService.LAST_NOTIFIED, now);
alarm = service.createReminderAlarm(task);
assertTrue(alarm.intent.getBooleanExtra(AlarmReceiver.TOKEN_IS_DEADLINE, false));
assertEquals(task.getValue(Task.DUE_DATE), Integer.valueOf(alarm.alarmTime));
// now same due date, but we've defined specific time
task.setValue(Task.URGENCY, Task.URGENCY_SPECIFIC_DAY_TIME);
alarm = service.createReminderAlarm(task);
assertTrue(DateUtilities.now() < alarm.alarmTime &&
alarm.alarmTime < task.getValue(Task.DUE_DATE));
// move the due date right up against actual
task.setValue(Task.DUE_DATE, now + 1000);
alarm = service.createReminderAlarm(task);
assertEquals(task.getValue(Task.DUE_DATE), Integer.valueOf(alarm.alarmTime));
assertTrue(alarm.intent.getBooleanExtra(AlarmReceiver.TOKEN_IS_DEADLINE, false));
// and after due date
task.setValue(Task.DUE_DATE, now - 80000);
alarm = service.createReminderAlarm(task);
assertNull(alarm);
}
/**
* Test alarm generation (overdue)
*/
public void testOverdueAlarmGeneration() {
ReminderService service = new ReminderService(getContext());
int now = DateUtilities.now();
// test simple overdue
Task task = new Task();
task.setValue(Task.TITLE, "blah");
task.setValue(Task.DUE_DATE, now - 100);
task.setValue(ReminderService.OVERDUE_INTERVAL, 1000);
Alarm alarm = service.createOverdueAlarm(task);
assertTrue(now + 500 < alarm.alarmTime && alarm.alarmTime < now + 1500);
// same case, but almost exact due as normal
task.setValue(Task.DUE_DATE, DateUtilities.now());
alarm = service.createOverdueAlarm(task);
assertTrue(now + 500 < alarm.alarmTime && alarm.alarmTime < now + 1500);
}
}

@ -0,0 +1,5 @@
package com.todoroo.astrid.rmilk;
public class RTMTestCase {
String token = "cb4281f7154114ed0e4ccc3a02d373c5a2429532";
}

@ -0,0 +1,240 @@
package com.todoroo.andlib.service;
import java.lang.reflect.Field;
import android.test.AndroidTestCase;
import com.todoroo.andlib.service.AbstractDependencyInjector;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
public class DependencyInjectionTests extends AndroidTestCase {
public void testNoAutowire() {
DependencyInjectionService service = new DependencyInjectionService();
service.setInjectors(new AbstractDependencyInjector[] {});
Object test = new Object();
service.inject(test);
}
public void testSimpleStringInjectionAutowire() {
DependencyInjectionService service = new DependencyInjectionService();
service.setInjectors(new AbstractDependencyInjector[] {
new AbstractDependencyInjector() {
public Object getInjection(Object object, Field field) {
if(field.getName().equals("foo"))
return "bar";
return null;
}
}
});
// test various permissions
Object test = new Object() {
@Autowired
public String foo;
@Override
public String toString() {
return foo;
}
};
service.inject(test);
assertEquals("bar", test.toString());
test = new Object() {
@Autowired
String foo;
@Override
public String toString() {
return foo;
}
};
service.inject(test);
assertEquals("bar", test.toString());
test = new Object() {
@Autowired
protected String foo;
@Override
public String toString() {
return foo;
}
};
service.inject(test);
assertEquals("bar", test.toString());
test = new Object() {
@Autowired
private String foo;
@Override
public String toString() {
return foo;
}
};
service.inject(test);
assertEquals("bar", test.toString());
// test no annotation
test = new Object() {
public String foo;
@Override
public String toString() {
return foo;
}
};
service.inject(test);
assertNull( test.toString());
}
public void testHierarchicalStringInjectionAutowire() {
DependencyInjectionService service = new DependencyInjectionService();
service.setInjectors(new AbstractDependencyInjector[] {
new AbstractDependencyInjector() {
public Object getInjection(Object object, Field field) {
if(field.getName().equals("foo"))
return "bar";
return null;
}
},
new AbstractDependencyInjector() {
public Object getInjection(Object object, Field field) {
return "malarkey";
}
}
});
Object test = new Object() {
@Autowired
public String foo;
@Override
public String toString() {
return foo;
}
};
service.inject(test);
assertEquals("bar", test.toString());
test = new Object() {
@Autowired
public String forks;
@Override
public String toString() {
return forks;
}
};
service.inject(test);
assertEquals("malarkey", test.toString());
}
public void testMissingInjection() {
DependencyInjectionService service = new DependencyInjectionService();
service.setInjectors(new AbstractDependencyInjector[] {
new AbstractDependencyInjector() {
public Object getInjection(Object object, Field field) {
if(field.getName().equals("wozzle"))
return "bar";
return null;
}
}
});
Object test = new Object() {
@Autowired
public String foo;
@Override
public String toString() {
return foo;
}
};
try {
service.inject(test);
fail("could inject with missing injector");
} catch (RuntimeException e) {
// expected
}
assertNull(test.toString());
}
public void testMultipleInjection() {
DependencyInjectionService service = new DependencyInjectionService();
service.setInjectors(new AbstractDependencyInjector[] {
new AbstractDependencyInjector() {
public Object getInjection(Object object, Field field) {
if(field.getName().equals("foo"))
return "bar";
return null;
}
}
});
Object test1 = new Object() {
@Autowired
public String foo;
@Override
public String toString() {
return foo;
}
};
Object test2 = new Object() {
@Autowired
public String foo;
@Override
public String toString() {
return foo;
}
};
service.inject(test1);
service.inject(test2);
assertEquals("bar", test1.toString());
assertEquals("bar", test2.toString());
}
public static class ParentInjectee {
@Autowired
protected String foo;
}
public static class ChildInjectee extends ParentInjectee {
@Autowired
protected String bar;
}
public void testInheritedInjection() {
DependencyInjectionService service = new DependencyInjectionService();
service.setInjectors(new AbstractDependencyInjector[] {
new AbstractDependencyInjector() {
public Object getInjection(Object object, Field field) {
if(field.getName().equals("foo"))
return "gotfoo";
else if(field.getName().equals("bar"))
return "hasbar";
return null;
}
}
});
ChildInjectee child = new ChildInjectee();
service.inject(child);
assertEquals("gotfoo", child.foo);
assertEquals("hasbar", child.bar);
}
}

@ -0,0 +1,59 @@
package com.todoroo.andlib.service;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
public class TestDependencyInjector implements AbstractDependencyInjector {
/**
* Dependencies this class knows how to handle
*/
private final HashMap<String, Object> injectables = new HashMap<String, Object>();
private String name;
public TestDependencyInjector(String name) {
this.name = name;
}
public void addInjectable(String field, Object injection) {
injectables.put(field, injection);
}
public Object getInjection(Object object, Field field) {
if(injectables.containsKey(field.getName())) {
return injectables.get(field.getName());
}
return null;
}
// --- static stuff
/**
* Install TestDependencyInjector above other injectors
*/
public synchronized static TestDependencyInjector initialize(String name) {
ArrayList<AbstractDependencyInjector> list =
new ArrayList<AbstractDependencyInjector>(Arrays.asList(DependencyInjectionService.getInstance().getInjectors()));
for(Iterator<AbstractDependencyInjector> i = list.iterator(); i.hasNext(); ) {
AbstractDependencyInjector injector = i.next();
// if another one of these injectors already exists in the
// stack, remove it
if(injector instanceof TestDependencyInjector) {
if(((TestDependencyInjector)injector).name.equals(name))
i.remove();
}
}
TestDependencyInjector instance = new TestDependencyInjector(name);
list.add(0, instance);
DependencyInjectionService.getInstance().setInjectors(list.toArray(new AbstractDependencyInjector[list.size()]));
return instance;
}
}

@ -0,0 +1,11 @@
package com.todoroo.andlib.test;
import junit.framework.Assert;
import android.test.AndroidTestCase;
public class SimpleAndroidTest extends AndroidTestCase {
public void testSimpleAssert() throws Throwable {
Assert.assertTrue(true);
}
}

@ -0,0 +1,25 @@
package com.todoroo.andlib.test;
/**
* Utility methods used in unit tests
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TestUtilities {
/**
* Sleep, suppressing exceptions
*
* @param millis
*/
public static void sleepDeep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
// do nothing
}
}
}

@ -0,0 +1,26 @@
package com.todoroo.andlib.test;
import android.test.AndroidTestCase;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
/**
* Base test case for Astrid tests
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TodorooTestCase extends AndroidTestCase {
public TodorooTestCase() {
DependencyInjectionService.getInstance().inject(this);
}
@Override
protected void setUp() throws Exception {
super.setUp();
ContextManager.setContext(this.getContext());
}
}

@ -0,0 +1,34 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.todoroo.astrid;
import junit.framework.Test;
import junit.framework.TestSuite;
import android.test.suitebuilder.TestSuiteBuilder;
/**
* A test suite containing activity-related tests
*/
public class ActivityTests extends TestSuite {
public static Test suite() {
return new TestSuiteBuilder(ActivityTests.class)
.includePackages("com.todoroo.astrid.activity")
.build();
}
}

@ -0,0 +1,53 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.todoroo.astrid;
import junit.framework.Test;
import junit.framework.TestSuite;
import android.test.suitebuilder.TestSuiteBuilder;
/**
* A test suite containing all tests for ApiDemos.
*
* To run all suites found in this apk:
* $ adb shell am instrument -w \
* com.example.android.apis.tests/android.test.InstrumentationTestRunner
*
* To run just this suite from the command line:
* $ adb shell am instrument -w \
* -e class com.example.android.apis.AllTests \
* com.example.android.apis.tests/android.test.InstrumentationTestRunner
*
* To run an individual test case, e.g. {@link com.example.android.apis.os.MorseCodeConverterTest}:
* $ adb shell am instrument -w \
* -e class com.example.android.apis.os.MorseCodeConverterTest \
* com.example.android.apis.tests/android.test.InstrumentationTestRunner
*
* To run an individual test, e.g. {@link com.example.android.apis.os.MorseCodeConverterTest#testCharacterS()}:
* $ adb shell am instrument -w \
* -e class com.example.android.apis.os.MorseCodeConverterTest#testCharacterS \
* com.example.android.apis.tests/android.test.InstrumentationTestRunner
*/
public class AllTests extends TestSuite {
public static Test suite() {
return new TestSuiteBuilder(AllTests.class)
.includeAllPackagesUnderHere()
.build();
}
}

@ -0,0 +1,30 @@
package com.todoroo.astrid.dao;
import com.todoroo.astrid.test.DatabaseTestCase;
import android.database.sqlite.SQLiteDatabase;
public class BasicDatabaseTests extends DatabaseTestCase {
/**
* Test that it's possible to open the database multiple times, to no effect
*/
public void testOpenMultipleTimes() {
SQLiteDatabase sqlDatabase = database.getDatabase();
database.openForReading(getContext());
assertEquals(sqlDatabase, database.getDatabase());
database.openForWriting(getContext());
assertEquals(sqlDatabase, database.getDatabase());
database.openForReading(getContext());
assertEquals(sqlDatabase, database.getDatabase());
}
public void testCloseAndReopen() {
SQLiteDatabase sqlDatabase = database.getDatabase();
database.close();
database.openForReading(getContext());
assertNotSame(sqlDatabase, database.getDatabase());
}
}

@ -0,0 +1,187 @@
package com.todoroo.astrid.dao;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.test.data.Property;
import com.todoroo.andlib.test.data.TodorooCursor;
import com.todoroo.astrid.dao.MetadataDao.MetadataSql;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.test.DatabaseTestCase;
public class MetadataDaoTests extends DatabaseTestCase {
@Autowired
MetadataDao metadataDao;
@Autowired
TaskDao taskDao;
public static Property<?>[] KEYS = new Property<?>[] { Metadata.ID,
Metadata.KEY };
/**
* Test basic creation, fetch, and save
*/
public void testCrud() throws Exception {
TodorooCursor<Metadata> cursor = metadataDao.fetch(database,
MetadataService.idProperty(), null, null);
assertEquals(0, cursor.getCount());
cursor.close();
// create "happy"
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, "happy");
assertTrue(metadataDao.save(database, metadata));
cursor = metadataDao.fetch(database, MetadataService.idProperty(), null, null);
assertEquals(1, cursor.getCount());
cursor.close();
long happyId = metadata.getId();
assertNotSame(Metadata.NO_ID, happyId);
metadata = metadataDao.fetch(database, KEYS, happyId);
assertEquals("happy", metadata.getValue(Metadata.KEY));
// create "sad"
metadata = new Metadata();
metadata.setValue(Metadata.KEY, "sad");
assertTrue(metadataDao.save(database, metadata));
cursor = metadataDao.fetch(database, MetadataService.idProperty(), null, null);
assertEquals(2, cursor.getCount());
cursor.close();
// rename sad to melancholy
long sadId = metadata.getId();
assertNotSame(Metadata.NO_ID, sadId);
metadata.setValue(Metadata.KEY, "melancholy");
assertTrue(metadataDao.save(database, metadata));
cursor = metadataDao.fetch(database, MetadataService.idProperty(), null, null);
assertEquals(2, cursor.getCount());
cursor.close();
// check state
metadata = metadataDao.fetch(database, KEYS, happyId);
assertEquals("happy", metadata.getValue(Metadata.KEY));
metadata = metadataDao.fetch(database, KEYS, sadId);
assertEquals("melancholy", metadata.getValue(Metadata.KEY));
// delete sad
assertTrue(metadataDao.delete(database, sadId));
cursor = metadataDao.fetch(database, KEYS, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
metadata.readFromCursor(cursor, KEYS);
assertEquals("happy", metadata.getValue(Metadata.KEY));
cursor.close();
}
/**
* Test metadata bound to task
*/
public void testMetadataConditions() throws Exception {
// create "happy"
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, "with1");
metadata.setValue(Metadata.TASK, 1L);
assertTrue(metadataDao.save(database, metadata));
metadata = new Metadata();
metadata.setValue(Metadata.KEY, "with2");
metadata.setValue(Metadata.TASK, 2L);
assertTrue(metadataDao.save(database, metadata));
metadata = new Metadata();
metadata.setValue(Metadata.KEY, "with1");
metadata.setValue(Metadata.TASK, 1L);
assertTrue(metadataDao.save(database, metadata));
TodorooCursor<Metadata> cursor = metadataDao.fetch(database,
KEYS, MetadataSql.byTask(1), null);
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
metadata.readFromCursor(cursor, KEYS);
assertEquals("with1", metadata.getValue(Metadata.KEY));
cursor.moveToNext();
metadata.readFromCursor(cursor, KEYS);
assertEquals("with1", metadata.getValue(Metadata.KEY));
cursor.close();
cursor = metadataDao.fetch(database,
MetadataService.idProperty(), MetadataSql.byTask(3), null);
assertEquals(0, cursor.getCount());
cursor.close();
int deleted = metadataDao.deleteWhere(database, MetadataSql.byTask(1));
assertEquals(2, deleted);
cursor = metadataDao.fetch(database,
MetadataService.idProperty(), null, null);
assertEquals(1, cursor.getCount());
cursor.close();
}
/**
* Test metadata deletion
*/
public void testFetchDangling() throws Exception {
// fetch with nothing in db
TodorooCursor<Metadata> cursor = metadataDao.fetchDangling(database, KEYS);
assertEquals(0, cursor.getCount());
cursor.close();
Task task1 = new Task();
taskDao.save(database, task1);
Task task2 = new Task();
taskDao.save(database, task2);
Task task3 = new Task();
taskDao.save(database, task3);
// fetch with only tasks
cursor = metadataDao.fetchDangling(database, KEYS);
assertEquals(0, cursor.getCount());
cursor.close();
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, "with1");
metadata.setValue(Metadata.TASK, task1.getId());
assertTrue(metadataDao.save(database, metadata));
metadata = new Metadata();
metadata.setValue(Metadata.KEY, "with2");
metadata.setValue(Metadata.TASK, task2.getId());
assertTrue(metadataDao.save(database, metadata));
metadata = new Metadata();
metadata.setValue(Metadata.KEY, "with3");
metadata.setValue(Metadata.TASK, task3.getId());
assertTrue(metadataDao.save(database, metadata));
// fetch with tasks and corresponding metadata
cursor = metadataDao.fetchDangling(database, KEYS);
assertEquals(0, cursor.getCount());
cursor.close();
long task2Id = task2.getId();
taskDao.delete(database, task2.getId());
// note: we should not have any dangling, since deleting a task
// will automatically delete metadata
cursor = metadataDao.fetchDangling(database, KEYS);
assertEquals(0, cursor.getCount());
cursor.close();
metadata = new Metadata();
metadata.setValue(Metadata.KEY, "with2");
metadata.setValue(Metadata.TASK, task2Id);
assertTrue(metadataDao.save(database, metadata));
// but if we simulate something bad happening by creating
// it manually.. well, what can i say, it should be broken
cursor = metadataDao.fetchDangling(database, KEYS);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
metadata.readFromCursor(cursor, KEYS);
assertEquals("with2", metadata.getValue(Metadata.KEY));
cursor.close();
}
}

@ -0,0 +1,216 @@
package com.todoroo.astrid.dao;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.test.data.Property;
import com.todoroo.andlib.test.data.TodorooCursor;
import com.todoroo.andlib.test.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao.TaskSql;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.test.DatabaseTestCase;
public class TaskDaoTests extends DatabaseTestCase {
public static Property<?>[] IDS = new Property<?>[] { Task.ID };
public static Property<?>[] TITLES = new Property<?>[] { Task.ID,
Task.TITLE };
@Autowired
TaskDao taskDao;
/**
* Test basic task creation, fetch, and save
*/
public void testTaskCreation() throws Exception {
TodorooCursor<Task> cursor = taskDao.fetch(database,
IDS, null, null, null);
assertEquals(0, cursor.getCount());
cursor.close();
// create task "happy"
Task task = new Task();
task.setValue(Task.TITLE, "happy");
assertTrue(taskDao.save(database, task, false));
cursor = taskDao.fetch(database, IDS, null, null, null);
assertEquals(1, cursor.getCount());
cursor.close();
long happyId = task.getId();
assertNotSame(Task.NO_ID, happyId);
task = taskDao.fetch(database, TITLES, happyId);
assertEquals("happy", task.getValue(Task.TITLE));
// create task "sad"
task = new Task();
task.setValue(Task.TITLE, "sad");
assertTrue(taskDao.save(database, task, false));
cursor = taskDao.fetch(database, IDS, null, null, null);
assertEquals(2, cursor.getCount());
cursor.close();
// rename sad to melancholy
long sadId = task.getId();
assertNotSame(Task.NO_ID, sadId);
task.setValue(Task.TITLE, "melancholy");
assertTrue(taskDao.save(database, task, false));
cursor = taskDao.fetch(database, IDS, null, null, null);
assertEquals(2, cursor.getCount());
cursor.close();
// check state
task = taskDao.fetch(database, TITLES, happyId);
assertEquals("happy", task.getValue(Task.TITLE));
task = taskDao.fetch(database, TITLES, sadId);
assertEquals("melancholy", task.getValue(Task.TITLE));
}
/**
* Test various task fetch conditions
*/
public void testTaskConditions() throws Exception {
// create normal task
Task task = new Task();
task.setValue(Task.TITLE, "normal");
assertTrue(taskDao.save(database, task, false));
// create blank task
task = new Task();
task.setValue(Task.TITLE, "");
assertTrue(taskDao.save(database, task, false));
// create hidden task
task = new Task();
task.setValue(Task.TITLE, "hidden");
task.setValue(Task.HIDDEN_UNTIL, DateUtilities.now() + 10000);
assertTrue(taskDao.save(database, task, false));
// create task with deadlines
task = new Task();
task.setValue(Task.TITLE, "deadlineInFuture");
task.setValue(Task.DUE_DATE, DateUtilities.now() + 10000);
assertTrue(taskDao.save(database, task, false));
task = new Task();
task.setValue(Task.TITLE, "deadlineInPast");
task.setValue(Task.DUE_DATE, DateUtilities.now() - 10000);
assertTrue(taskDao.save(database, task, false));
// create completed task
task = new Task();
task.setValue(Task.TITLE, "completed");
task.setValue(Task.COMPLETION_DATE, DateUtilities.now() - 10000);
assertTrue(taskDao.save(database, task, false));
// check has no name
TodorooCursor<Task> cursor = taskDao.fetch(database,
TITLES, TaskSql.hasNoName(), null, null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("", cursor.getString(1));
cursor.close();
// check has deadlines
cursor = taskDao.fetch(database, TITLES, TaskSql
.hasDeadlines(), Task.DUE_DATE + " ASC", null);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("deadlineInPast", cursor.getString(1));
cursor.moveToNext();
assertEquals("deadlineInFuture", cursor.getString(1));
cursor.close();
// check is active
cursor = taskDao.fetch(database, TITLES, TaskSql
.isActive(), null, null);
assertEquals(5, cursor.getCount());
cursor.close();
// check due before / after
cursor = taskDao.fetch(database, TITLES, TaskSql
.dueBefore(DateUtilities.now()), null, null);
cursor.moveToNext();
assertEquals(1, cursor.getCount());
cursor.close();
cursor = taskDao.fetch(database, TITLES, TaskSql
.dueAfter(DateUtilities.now()), null, null);
assertEquals(1, cursor.getCount());
cursor.close();
// check completed before
cursor = taskDao.fetch(database, TITLES, TaskSql
.completedBefore(DateUtilities.now()), null, null);
assertEquals(1, cursor.getCount());
cursor.close();
// check is visible
cursor = taskDao.fetch(database, TITLES, TaskSql
.isVisible(DateUtilities.now()), null, null);
assertEquals(5, cursor.getCount());
cursor.close();
}
/**
* Test task deletion
*/
public void testTDeletion() throws Exception {
TodorooCursor<Task> cursor = taskDao.fetch(database,
IDS, null, null, null);
assertEquals(0, cursor.getCount());
cursor.close();
// create task "happy"
Task task = new Task();
task.setValue(Task.TITLE, "happy");
assertTrue(taskDao.save(database, task, false));
cursor = taskDao.fetch(database, IDS, null, null, null);
assertEquals(1, cursor.getCount());
cursor.close();
// delete
long happyId = task.getId();
assertTrue(taskDao.delete(database, happyId));
cursor = taskDao.fetch(database, IDS, null, null, null);
assertEquals(0, cursor.getCount());
cursor.close();
}
/**
* Test save without prior create doesn't work
*/
public void testSaveWithoutCreate() throws Exception {
TodorooCursor<Task> cursor;
// try to save task "happy"
Task task = new Task();
task.setValue(Task.TITLE, "happy");
task.setValue(Task.ID, 1L);
assertFalse(taskDao.save(database, task, false));
cursor = taskDao.fetch(database, IDS, null, null, null);
assertEquals(0, cursor.getCount());
cursor.close();
}
/**
* Test passing invalid task indices to various things
*/
public void testInvalidIndex() throws Exception {
TodorooCursor<Task> cursor;
cursor = taskDao.fetch(database, IDS, null, null, null);
assertEquals(0, cursor.getCount());
cursor.close();
assertNull(taskDao.fetch(database, IDS, 1));
assertFalse(taskDao.delete(database, 1));
// make sure db still works
cursor = taskDao.fetch(database, IDS, null, null, null);
assertEquals(0, cursor.getCount());
cursor.close();
}
// TODO check eventing
}

@ -0,0 +1,94 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import com.timsu.astrid.data.AbstractModel;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
/** Abstract controller class. Mostly contains some static fields */
abstract public class AbstractController {
protected Context context;
// special columns
public static final String KEY_ROWID = "_id";
// database and table names
protected static final String TASK_TABLE_NAME = "tasks";
protected static final String TAG_TABLE_NAME = "tags";
protected static final String TAG_TASK_MAP_NAME = "tagTaskMap";
protected static final String ALERT_TABLE_NAME = "alerts";
protected static final String SYNC_TABLE_NAME = "sync";
abstract public void open();
abstract public void close();
// cursor iterator
public static class CursorIterator<TYPE extends AbstractModel> implements Iterator<TYPE> {
Cursor cursor;
Class<TYPE> cls;
public CursorIterator(Cursor cursor, Class<TYPE> cls) {
this.cursor = cursor;
this.cls = cls;
}
public boolean hasNext() {
return !cursor.isLast();
}
public TYPE next() {
try {
TYPE model = cls.getConstructor(Cursor.class).newInstance(cursor);
cursor.moveToNext();
return model;
// ugh...
} catch (IllegalArgumentException e) {
Log.e("CursorIterator", e.toString());
} catch (SecurityException e) {
Log.e("CursorIterator", e.toString());
} catch (InstantiationException e) {
Log.e("CursorIterator", e.toString());
} catch (IllegalAccessException e) {
Log.e("CursorIterator", e.toString());
} catch (InvocationTargetException e) {
Log.e("CursorIterator", e.toString());
} catch (NoSuchMethodException e) {
Log.e("CursorIterator", e.toString());
}
return null;
}
public void remove() {
throw new UnsupportedOperationException("Can't remove this way");
}
}
}

@ -0,0 +1,263 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data;
import java.util.Date;
import java.util.HashMap;
import android.content.ContentValues;
import android.database.Cursor;
/** A data object backed by a database */
public abstract class AbstractModel {
/* 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), the database itself (cursor), then defaults
* (getDefaultValues)
*/
/** User set values */
protected ContentValues setValues = new ContentValues();
/** Cached values from database */
private ContentValues values = new ContentValues();
/** Cursor into the database */
private Cursor cursor = null;
// --- constructors
/** Construct a model from scratch */
public AbstractModel() {
// ...
}
/** Construct a model from a database object */
public AbstractModel(Cursor cursor) {
this.cursor = cursor;
}
// --- data source getters
/** Get the user-set values for this object */
public ContentValues getSetValues() {
return setValues;
}
/** Get the default values for this object */
abstract public ContentValues getDefaultValues();
/** Get a list of all field/value pairs merged across data sources */
public ContentValues getMergedValues() {
ContentValues mergedValues = new ContentValues();
mergedValues.putAll(getDefaultValues());
mergedValues.putAll(values);
mergedValues.putAll(setValues);
return mergedValues;
}
/** Return the database cursor */
public Cursor getCursor() {
return cursor;
}
// --- checking against cached values
protected void putIfChangedFromDatabase(String field, String newValue) {
if(!setValues.containsKey(field) && values.containsKey(field)) {
String value = values.getAsString(field);
if(value == null) {
if(newValue == null)
return;
} else if(value.equals(newValue))
return;
}
setValues.put(field, newValue);
}
protected void putIfChangedFromDatabase(String field, Long newValue) {
if(!setValues.containsKey(field) && values.containsKey(field)) {
Long value = values.getAsLong(field);
if(value == null) {
if(newValue == null)
return;
} else if(value.equals(newValue))
return;
}
setValues.put(field, newValue);
}
protected void putIfChangedFromDatabase(String field, Integer newValue) {
if(!setValues.containsKey(field) && values.containsKey(field)) {
Integer value = values.getAsInteger(field);
if(value == null) {
if(newValue == null)
return;
} else if(value.equals(newValue))
return;
}
setValues.put(field, newValue);
}
protected void putIfChangedFromDatabase(String field, Double newValue) {
if(!setValues.containsKey(field) && values.containsKey(field)) {
Double value = values.getAsDouble(field);
if(value == null) {
if(newValue == null)
return;
} else if(value.equals(newValue))
return;
}
setValues.put(field, newValue);
}
protected static final HashMap<Class<?>, HashMap<String, Integer>>
columnIndexCache = new HashMap<Class<?>, HashMap<String, Integer>>();
private int getColumnIndex(String field) {
HashMap<String, Integer> classCache;
classCache = columnIndexCache.get(getClass());
if(classCache == null) {
classCache = new HashMap<String, Integer>();
columnIndexCache.put(getClass(), classCache);
}
Integer index = classCache.get(field);
if(index == null) {
index = cursor.getColumnIndexOrThrow(field);
classCache.put(field, index);
}
return index;
}
// --- data retrieval for the different object types
protected String retrieveString(String field) {
if(setValues.containsKey(field))
return setValues.getAsString(field);
if(values.containsKey(field))
return values.getAsString(field);
// if we have a database to hit, do that now
if(cursor != null) {
String value = cursor.getString(getColumnIndex(field));
values.put(field, value);
return value;
}
// do we have defaults?
ContentValues defaults = getDefaultValues();
if(defaults != null && defaults.containsKey(field))
return defaults.getAsString(field);
throw new UnsupportedOperationException("Could not read field " + field);
}
protected Integer retrieveInteger(String field) {
if(setValues.containsKey(field))
return setValues.getAsInteger(field);
if(values.containsKey(field))
return values.getAsInteger(field);
// if we have a database to hit, do that now
if(cursor != null) {
try {
Integer value = cursor.getInt(getColumnIndex(field));
values.put(field, value);
return value;
} catch (Exception e) {
// error reading from cursor, try to continue
}
}
// do we have defaults?
ContentValues defaults = getDefaultValues();
if(defaults != null && defaults.containsKey(field))
return defaults.getAsInteger(field);
throw new UnsupportedOperationException("Could not read field " + field);
}
protected Long retrieveLong(String field) {
if(setValues.containsKey(field))
return setValues.getAsLong(field);
if(values.containsKey(field))
return values.getAsLong(field);
// if we have a database to hit, do that now
if(cursor != null) {
Long value = cursor.getLong(getColumnIndex(field));
values.put(field, value);
return value;
}
// do we have defaults?
ContentValues defaults = getDefaultValues();
if(defaults != null && defaults.containsKey(field))
return defaults.getAsLong(field);
throw new UnsupportedOperationException("Could not read field " + field);
}
protected Double retrieveDouble(String field) {
if(setValues.containsKey(field))
return setValues.getAsDouble(field);
if(values.containsKey(field))
return values.getAsDouble(field);
// if we have a database to hit, do that now
if(cursor != null) {
Double value = cursor.getDouble(getColumnIndex(field));
values.put(field, value);
return value;
}
// do we have defaults?
ContentValues defaults = getDefaultValues();
if(defaults != null && defaults.containsKey(field))
return defaults.getAsDouble(field);
throw new UnsupportedOperationException("Could not read field " + field);
}
// --- retrieving composite objects
protected Date retrieveDate(String field) {
Long time;
try {
time = retrieveLong(field);
if(time == null || time == 0)
return null;
} catch (NullPointerException e) {
return null;
}
return new Date(time);
}
}

@ -0,0 +1,56 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data;
/** Identifier of a single object. Extend this class to create your own */
public abstract class Identifier {
private long id;
public Identifier(long id) {
this.id = id;
}
public long getId() {
return id;
}
public String idAsString() {
return Long.toString(id);
}
@Override
public int hashCode() {
return (int)id;
}
@Override
public boolean equals(Object o) {
if(o == null || o.getClass() != getClass())
return false;
return ((Identifier)o).getId() == getId();
}
@Override
public String toString() {
return getClass().getSimpleName() + ": " + id;
}
}

@ -0,0 +1,132 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.alerts;
import java.util.Date;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.AbstractModel;
import com.todoroo.astrid.legacy.data.task.TaskIdentifier;
/** A single alert on a task */
public class Alert extends AbstractModel {
/** Version number of this model */
static final int VERSION = 1;
// field names
static final String TASK = "task";
static final String DATE = "date";
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
TASK,
DATE,
};
// --- database helper
/** Database Helper manages creating new tables and updating old ones */
static class AlertDatabaseHelper extends SQLiteOpenHelper {
String tableName;
Context context;
AlertDatabaseHelper(Context context, String databaseName, String tableName) {
super(context, databaseName, null, VERSION);
this.tableName = tableName;
this.context = context;
}
@Override
public synchronized void onCreate(SQLiteDatabase db) {
String sql = new StringBuilder().
append("CREATE TABLE IF NOT EXISTS ").append(tableName).append(" (").
append(AbstractController.KEY_ROWID).append(" integer primary key autoincrement, ").
append(TASK).append(" integer not null,").
append(DATE).append(" integer not null,").
append("unique (").append(TASK).append(",").append(DATE).append(")").
append(");").toString();
db.execSQL(sql);
}
@Override
public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(getClass().getSimpleName(), "Upgrading database from version " +
oldVersion + " to " + newVersion + ".");
switch(oldVersion) {
default:
throw new RuntimeException("Alerts: Unsupported migration from " + oldVersion + " to " + newVersion);
}
}
}
// --- constructor pass-through
Alert(TaskIdentifier task, Date date) {
super();
setTask(task);
setDate(date);
}
public Alert(Cursor cursor) {
super(cursor);
}
// --- getters and setters: expose them as you see fit
public boolean isNew() {
return getCursor() == null;
}
public TaskIdentifier getTask() {
return new TaskIdentifier(retrieveLong(TASK));
}
public Date getDate() {
return new Date(retrieveLong(DATE));
}
private void setTask(TaskIdentifier task) {
setValues.put(TASK, task.getId());
}
private void setDate(Date date) {
setValues.put(DATE, date.getTime());
}
}

@ -0,0 +1,144 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.alerts;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.alerts.Alert.AlertDatabaseHelper;
import com.todoroo.astrid.legacy.data.task.TaskIdentifier;
/** Controller for Tag-related operations */
public class AlertController extends AbstractController {
private SQLiteDatabase alertDatabase;
/** Get a cursor to tag identifiers */
public Cursor getTaskAlertsCursor(TaskIdentifier taskId) throws SQLException {
Cursor cursor = alertDatabase.query(ALERT_TABLE_NAME,
Alert.FIELD_LIST, Alert.TASK + " = ?",
new String[] { taskId.idAsString() }, null, null, null);
return cursor;
}
/** Get a list of alerts for the given task */
public List<Date> getTaskAlerts(TaskIdentifier
taskId) throws SQLException {
List<Date> list = new LinkedList<Date>();
Cursor cursor = alertDatabase.query(ALERT_TABLE_NAME,
Alert.FIELD_LIST, Alert.TASK + " = ?",
new String[] { taskId.idAsString() }, null, null, null);
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new Alert(cursor).getDate());
} while(!cursor.isLast());
return list;
} finally {
cursor.close();
}
}
/** Get a list of alerts that are set for the future */
public Set<TaskIdentifier> getTasksWithActiveAlerts() throws SQLException {
Set<TaskIdentifier> list = new HashSet<TaskIdentifier>();
Cursor cursor = alertDatabase.query(ALERT_TABLE_NAME,
Alert.FIELD_LIST, Alert.DATE + " > ?",
new String[] { Long.toString(System.currentTimeMillis()) }, null, null, null);
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new Alert(cursor).getTask());
} while(!cursor.isLast());
return list;
} finally {
cursor.close();
}
}
/** Remove all alerts from the task */
public boolean removeAlerts(TaskIdentifier taskId)
throws SQLException{
return alertDatabase.delete(ALERT_TABLE_NAME,
String.format("%s = ?",
Alert.TASK),
new String[] { taskId.idAsString() }) > 0;
}
/** Add the given tag to the task */
public boolean addAlert(TaskIdentifier taskId, Date date)
throws SQLException {
ContentValues values = new ContentValues();
values.put(Alert.DATE, date.getTime());
values.put(Alert.TASK, taskId.getId());
return alertDatabase.insert(ALERT_TABLE_NAME, Alert.TASK,
values) >= 0;
}
// --- boilerplate
/**
* Constructor - takes the context to allow the database to be
* opened/created
*/
public AlertController(Context context) {
this.context = context;
}
/**
* Open the notes database. If it cannot be opened, try to create a new
* instance of the database. If it cannot be created, throw an exception to
* signal the failure
*
* @return this (self reference, allowing this to be chained in an
* initialization call)
* @throws SQLException if the database could be neither opened or created
*/
@Override
public void open() throws SQLException {
alertDatabase = new AlertDatabaseHelper(context,
ALERT_TABLE_NAME, ALERT_TABLE_NAME).getWritableDatabase();
}
/** Closes database resource */
@Override
public void close() {
alertDatabase.close();
}
}

@ -0,0 +1,66 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.enums;
import com.timsu.astrid.R;
public enum Importance {
// MOST IMPORTANT
LEVEL_1(R.string.importance_1,
R.color.importance_1,
R.color.task_list_importance_1),
LEVEL_2(R.string.importance_2,
R.color.importance_2,
R.color.task_list_importance_2),
LEVEL_3(R.string.importance_3,
R.color.importance_3,
R.color.task_list_importance_3),
LEVEL_4(R.string.importance_4,
R.color.importance_4,
R.color.task_list_importance_4),
// LEAST IMPORTANT
;
int label;
int color;
int taskListColor;
public static final Importance DEFAULT = LEVEL_3;
private Importance(int label, int color, int taskListColor) {
this.label = label;
this.color = color;
this.taskListColor = taskListColor;
}
public int getLabelResource() {
return label;
}
public int getColorResource() {
return color;
}
public int getTaskListColor() {
return taskListColor;
}
}

@ -0,0 +1,77 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.enums;
import java.util.Date;
import android.content.res.Resources;
import com.timsu.astrid.R;
public enum RepeatInterval {
DAYS(R.string.repeat_days) {
@Override
public void offsetDateBy(Date input, int number) {
input.setDate(input.getDate() + number);
}
},
WEEKS(R.string.repeat_weeks) {
@Override
public void offsetDateBy(Date input, int number) {
input.setDate(input.getDate() + 7 * number);
}
},
MONTHS(R.string.repeat_months) {
@Override
public void offsetDateBy(Date input, int number) {
input.setMonth(input.getMonth() + number);
}
},
HOURS(R.string.repeat_hours) {
@Override
public void offsetDateBy(Date input, int number) {
input.setHours(input.getHours() + number);
}
},
;
int label;
private RepeatInterval(int label) {
this.label = label;
}
public int getLabelResource() {
return label;
}
abstract public void offsetDateBy(Date input, int number);
public static String[] getLabels(Resources r) {
int intervalCount = RepeatInterval.values().length;
String[] result = new String[intervalCount];
for(int i = 0; i < intervalCount; i++)
result[i] = r.getString(RepeatInterval.values()[i].getLabelResource());
return result;
}
}

@ -0,0 +1,195 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.sync;
import java.util.HashSet;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.sync.SyncMapping.SyncMappingDatabaseHelper;
import com.todoroo.astrid.legacy.data.task.AbstractTaskModel;
import com.todoroo.astrid.legacy.data.task.TaskIdentifier;
import com.todoroo.astrid.legacy.data.task.TaskModelForSync;
/** Controller for Tag-related operations */
public class SyncDataController extends AbstractController {
private SQLiteDatabase syncDatabase;
// --- updated tasks list
/** Mark all updated tasks as finished synchronizing */
public boolean clearUpdatedTaskList(int syncServiceId) throws SQLException {
ContentValues values = new ContentValues();
values.put(SyncMapping.UPDATED, 0);
return syncDatabase.update(SYNC_TABLE_NAME, values,
SyncMapping.SYNC_SERVICE + " = " + syncServiceId, null) > 0;
}
/** Indicate that this task's properties were updated */
public boolean addToUpdatedList(TaskIdentifier taskId) throws SQLException {
ContentValues values = new ContentValues();
values.put(SyncMapping.UPDATED, 1);
return syncDatabase.update(SYNC_TABLE_NAME, values,
SyncMapping.TASK + " = " + taskId.getId(), null) > 0;
}
public static void taskUpdated(Context context, AbstractTaskModel task) {
if(!(task instanceof TaskModelForSync)) {
SyncDataController syncController = new SyncDataController(context);
syncController.open();
syncController.addToUpdatedList(task.getTaskIdentifier());
syncController.close();
}
}
// --- sync mapping
/** Get all mappings for the given synchronization service */
public HashSet<SyncMapping> getSyncMappings(int syncServiceId) throws SQLException {
HashSet<SyncMapping> list = new HashSet<SyncMapping>();
Cursor cursor = syncDatabase.query(SYNC_TABLE_NAME,
SyncMapping.FIELD_LIST,
SyncMapping.SYNC_SERVICE + " = " + syncServiceId,
null, null, null, null);
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new SyncMapping(cursor));
} while(!cursor.isLast());
return list;
} finally {
cursor.close();
}
}
/** Get all mappings for specified task for all synchronization services */
public HashSet<SyncMapping> getSyncMappings(TaskIdentifier taskId)
throws SQLException {
HashSet<SyncMapping> list = new HashSet<SyncMapping>();
Cursor cursor = syncDatabase.query(SYNC_TABLE_NAME,
SyncMapping.FIELD_LIST,
SyncMapping.TASK + " = ?",
new String[] { "" + taskId.getId() },
null, null, null);
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new SyncMapping(cursor));
} while(!cursor.isLast());
return list;
} finally {
cursor.close();
}
}
/** Get mapping for given task */
public SyncMapping getSyncMapping(int syncServiceId, TaskIdentifier taskId)
throws SQLException {
Cursor cursor = syncDatabase.query(SYNC_TABLE_NAME,
SyncMapping.FIELD_LIST,
SyncMapping.SYNC_SERVICE + " = ? AND " +
SyncMapping.TASK + " = ?",
new String[] { "" + syncServiceId, "" + taskId.getId() },
null, null, null);
try {
if(cursor.getCount() == 0)
return null;
cursor.moveToNext();
return new SyncMapping(cursor);
} finally {
cursor.close();
}
}
/** Saves the given task to the database. Returns true on success. */
public boolean saveSyncMapping(SyncMapping mapping) {
long newRow = syncDatabase.insert(SYNC_TABLE_NAME, SyncMapping.TASK,
mapping.getMergedValues());
mapping.setId(newRow);
return newRow >= 0;
}
/** Deletes the given mapping. Returns true on success */
public boolean deleteSyncMapping(SyncMapping mapping) {
// was never saved
if(mapping.getId() == 0)
return false;
return syncDatabase.delete(SYNC_TABLE_NAME, KEY_ROWID + "=" +
mapping.getId(), null) > 0;
}
/** Deletes the given mapping. Returns true on success */
public boolean deleteAllMappings(int syncServiceId) {
return syncDatabase.delete(SYNC_TABLE_NAME, SyncMapping.SYNC_SERVICE +
"=" + syncServiceId, null) > 0;
}
// --- boilerplate
/**
* Constructor - takes the context to allow the database to be
* opened/created
*/
public SyncDataController(Context context) {
this.context = context;
}
/**
* Open the notes database. If it cannot be opened, try to create a new
* instance of the database. If it cannot be created, throw an exception to
* signal the failure
*
* @return this (self reference, allowing this to be chained in an
* initialization call)
* @throws SQLException if the database could be neither opened or created
*/
@Override
public synchronized void open() throws SQLException {
SQLiteOpenHelper helper = new SyncMappingDatabaseHelper(context,
SYNC_TABLE_NAME, SYNC_TABLE_NAME);
syncDatabase = helper.getWritableDatabase();
}
/** Closes database resource */
@Override
public void close() {
syncDatabase.close();
}
}

@ -0,0 +1,166 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.sync;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.AbstractModel;
import com.todoroo.astrid.legacy.data.task.TaskIdentifier;
/** A single tag on a task */
public class SyncMapping extends AbstractModel {
/** Version number of this model */
static final int VERSION = 1;
// field names
static final String TASK = "task";
static final String SYNC_SERVICE = "service";
static final String REMOTE_ID = "remoteId";
static final String UPDATED = "updated";
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
static {
defaultValues.put(UPDATED, 0);
}
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
TASK,
SYNC_SERVICE,
REMOTE_ID,
UPDATED,
};
// --- database helper
/** Database Helper manages creating new tables and updating old ones */
static class SyncMappingDatabaseHelper extends SQLiteOpenHelper {
String tableName;
Context context;
SyncMappingDatabaseHelper(Context context, String databaseName, String tableName) {
super(context, databaseName, null, VERSION);
this.tableName = tableName;
this.context = context;
}
@Override
public synchronized void onCreate(SQLiteDatabase db) {
String sql = new StringBuilder().
append("CREATE TABLE IF NOT EXISTS ").append(tableName).append(" (").
append(AbstractController.KEY_ROWID).append(" integer primary key autoincrement, ").
append(TASK).append(" integer not null,").
append(SYNC_SERVICE).append(" integer not null,").
append(REMOTE_ID).append(" text not null,").
append(UPDATED).append(" integer not null,").
append("unique (").append(TASK).append(",").append(SYNC_SERVICE).append(")").
append(");").toString();
db.execSQL(sql);
}
@Override
public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(getClass().getSimpleName(), "Upgrading database from version " +
oldVersion + " to " + newVersion + ".");
switch(oldVersion) {
default:
throw new RuntimeException("Sync: Unsupported migration from " + oldVersion + " to " + newVersion);
}
}
}
// --- constructor pass-through
public SyncMapping(TaskIdentifier task, int syncServiceId, String remoteId) {
super();
setTask(task);
setSyncServiceId(syncServiceId);
setRemoteId(remoteId);
}
SyncMapping(Cursor cursor) {
super(cursor);
getId();
getTask();
getSyncServiceId();
getRemoteId();
isUpdated();
}
// --- getters and setters
public void setId(long id) {
putIfChangedFromDatabase(AbstractController.KEY_ROWID, id);
}
public long getId() {
try {
return retrieveLong(AbstractController.KEY_ROWID);
} catch (UnsupportedOperationException e) {
return 0;
}
}
public TaskIdentifier getTask() {
return new TaskIdentifier(retrieveLong(TASK));
}
public int getSyncServiceId() {
return retrieveInteger(SYNC_SERVICE);
}
public String getRemoteId() {
return retrieveString(REMOTE_ID);
}
public boolean isUpdated() {
return retrieveInteger(UPDATED) == 1;
}
private void setTask(TaskIdentifier task) {
setValues.put(TASK, task.getId());
}
private void setSyncServiceId(int id) {
setValues.put(SYNC_SERVICE, id);
}
private void setRemoteId(String remoteId) {
setValues.put(REMOTE_ID, remoteId);
}
}

@ -0,0 +1,196 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.tag;
import java.util.Date;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.AbstractModel;
/** Abstract model of a task. Subclasses implement the getters and setters
* they are interested in.
*
* @author timsu
*
*/
public abstract class AbstractTagModel extends AbstractModel {
/** Version number of this model */
static final int VERSION = 1;
// field names
static final String NAME = "name";
static final String NOTES = "notes";
// reserved fields
static final String ICON = "icon";
static final String PARENT = "parent";
static final String FLAGS = "flags";
static final String LOCATION_LAT = "locationLat";
static final String LOCATION_LONG = "locationLong";
static final String NOTIFICATIONS = "notifications";
// end reserved fields
static final String CREATION_DATE = "creationDate";
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
static {
defaultValues.put(NAME, "");
defaultValues.put(NOTES, "");
defaultValues.put(ICON, 0);
defaultValues.put(PARENT, 0);
defaultValues.put(FLAGS, 0);
defaultValues.put(LOCATION_LAT, 0);
defaultValues.put(LOCATION_LONG, 0);
defaultValues.put(NOTIFICATIONS, 0);
}
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- database helper
/** Database Helper manages creating new tables and updating old ones */
static class TagModelDatabaseHelper extends SQLiteOpenHelper {
String tableName;
Context context;
TagModelDatabaseHelper(Context context, String databaseName, String tableName) {
super(context, databaseName, null, VERSION);
this.tableName = tableName;
this.context = context;
}
@Override
public synchronized void onCreate(SQLiteDatabase db) {
String sql = new StringBuilder().
append("CREATE TABLE IF NOT EXISTS ").append(tableName).append(" (").
append(AbstractController.KEY_ROWID).append(" integer primary key autoincrement, ").
append(NAME).append(" text unique,").
append(NOTES).append(" text,").
append(ICON).append(" integer,").
append(PARENT).append(" integer,").
append(FLAGS).append(" integer,").
append(LOCATION_LAT).append(" integer,").
append(LOCATION_LONG).append(" integer,").
append(NOTIFICATIONS).append(" integer,").
append(CREATION_DATE).append(" integer").
append(");").toString();
db.execSQL(sql);
}
@Override
public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(getClass().getSimpleName(), "Upgrading database from version " +
oldVersion + " to " + newVersion + ".");
switch(oldVersion) {
default:
// we don't know how to handle it... show an error
throw new RuntimeException("Tags: Unsupported migration from " + oldVersion + " to " + newVersion);
}
}
}
// --- utility methods
// --- identifier
private TagIdentifier identifier = null;
public TagIdentifier getTagIdentifier() {
return identifier;
}
void setTagIdentifier(TagIdentifier identifier) {
this.identifier = identifier;
}
// --- constructor pass-through
AbstractTagModel() {
super();
}
/** Read identifier from database */
AbstractTagModel(Cursor cursor) {
super(cursor);
Integer id = retrieveInteger(AbstractController.KEY_ROWID);
setTagIdentifier(new TagIdentifier(id));
}
/** Get identifier from argument */
AbstractTagModel(TagIdentifier identifier, Cursor cursor) {
super(cursor);
setTagIdentifier(identifier);
}
// --- getters and setters: expose them as you see fit
protected String getName() {
return retrieveString(NAME);
}
protected String getNotes() {
return retrieveString(NOTES);
}
protected Date getCreationDate() {
return retrieveDate(CREATION_DATE);
}
// --- setters
protected void setName(String name) {
setValues.put(NAME, name.trim());
}
protected void setNotes(String notes) {
setValues.put(NOTES, notes);
}
protected void setCreationDate(Date creationDate) {
putDate(setValues, CREATION_DATE, creationDate);
}
// --- utility methods
static void putDate(ContentValues cv, String fieldName, Date date) {
if(date == null)
cv.put(fieldName, (Long)null);
else
cv.put(fieldName, date.getTime());
}
}

@ -0,0 +1,320 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.tag;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import com.timsu.astrid.provider.TasksProvider;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.tag.AbstractTagModel.TagModelDatabaseHelper;
import com.todoroo.astrid.legacy.data.tag.TagToTaskMapping.TagToTaskMappingDatabaseHelper;
import com.todoroo.astrid.legacy.data.task.TaskIdentifier;
import com.todoroo.astrid.legacy.data.task.AbstractTaskModel.TaskModelDatabaseHelper;
/** Controller for Tag-related operations */
public class TagController extends AbstractController {
private SQLiteDatabase tagDatabase, tagToTaskMapDatabase;
// --- tag batch operations
/** Get a list of all tags */
public LinkedList<TagModelForView> getAllTags()
throws SQLException {
LinkedList<TagModelForView> list = new LinkedList<TagModelForView>();
Cursor cursor = tagDatabase.query(TAG_TABLE_NAME,
TagModelForView.FIELD_LIST, null, null, null, null, null, null);
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new TagModelForView(cursor));
} while(!cursor.isLast());
} finally {
cursor.close();
}
return list;
}
// --- tag to task map batch operations
/** Get a list of all tags as an id => tag map */
public HashMap<TagIdentifier, TagModelForView> getAllTagsAsMap() throws SQLException {
HashMap<TagIdentifier, TagModelForView> map = new HashMap<TagIdentifier, TagModelForView>();
for(TagModelForView tag : getAllTags())
map.put(tag.getTagIdentifier(), tag);
return map;
}
/** Get a list of tag identifiers for the given task */
public LinkedList<TagIdentifier> getTaskTags(TaskIdentifier
taskId) throws SQLException {
LinkedList<TagIdentifier> list = new LinkedList<TagIdentifier>();
Cursor cursor = tagToTaskMapDatabase.query(TAG_TASK_MAP_NAME,
TagToTaskMapping.FIELD_LIST, TagToTaskMapping.TASK + " = ?",
new String[] { taskId.idAsString() }, null, null, null);
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new TagToTaskMapping(cursor).getTag());
} while(!cursor.isLast());
} finally {
cursor.close();
}
return list;
}
/** Get a list of task identifiers for the given tag.
* This searches for TAGGED tasks only.
* Use getUntaggedTasks() to get a list of UNTAGGED tasks **/
public LinkedList<TaskIdentifier> getTaggedTasks(TagIdentifier tagId)
throws SQLException {
LinkedList<TaskIdentifier> list = new LinkedList<TaskIdentifier>();
Cursor cursor = tagToTaskMapDatabase.query(TAG_TASK_MAP_NAME,
TagToTaskMapping.FIELD_LIST, TagToTaskMapping.TAG + " = ?",
new String[] { tagId.idAsString() }, null, null, null);
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new TagToTaskMapping(cursor).getTask());
} while(!cursor.isLast());
} finally {
cursor.close();
}
return list;
}
/** Returns a list of task identifiers in the provided set that are UNtagged.
*
* The calling SubActivity must provide the set of tasks, since
* TagController cannot access the appropriate instance of TaskController.
*
* The current implementation is not very efficient, because queries
* the TagToTask map once for each active task.
**/
public LinkedList<TaskIdentifier> getUntaggedTasks() throws SQLException {
HashSet<Long> ids = new HashSet<Long>();
String[] tagMapColumns = new String[] { TagToTaskMapping.TASK };
Cursor tagMapCursor = tagToTaskMapDatabase.query(TAG_TASK_MAP_NAME,
tagMapColumns, null, null, TagToTaskMapping.TASK, null,
TagToTaskMapping.TASK + " ASC");
SQLiteDatabase taskDatabase = new TaskModelDatabaseHelper(context,
TASK_TABLE_NAME, TASK_TABLE_NAME).getReadableDatabase();
String[] taskColumns = new String[] { KEY_ROWID };
Cursor taskCursor = taskDatabase.query(TASK_TABLE_NAME, taskColumns,
null, null, null, null, KEY_ROWID + " ASC");
LinkedList<TaskIdentifier> list = new LinkedList<TaskIdentifier>();
try {
if(taskCursor.getCount() == 0)
return list;
do {
taskCursor.moveToNext();
ids.add(taskCursor.getLong(0));
} while(!taskCursor.isLast());
if(tagMapCursor.getCount() > 0) {
do {
tagMapCursor.moveToNext();
ids.remove(tagMapCursor.getLong(0));
} while(!tagMapCursor.isLast());
}
} finally {
taskCursor.close();
tagMapCursor.close();
taskDatabase.close();
}
for(Long id : ids)
list.add(new TaskIdentifier(id));
return list;
}
// --- single tag operations
public TagIdentifier createTag(String name) throws SQLException {
if(name == null)
throw new NullPointerException("Name can't be null");
TagModelForView newTag = new TagModelForView(name);
long row = tagDatabase.insertOrThrow(TAG_TABLE_NAME, AbstractTagModel.NAME,
newTag.getMergedValues());
return new TagIdentifier(row);
}
/** Creates or saves the given tag */
public boolean saveTag(AbstractTagModel tag) throws SQLException {
boolean saveSucessful;
if(tag.getTagIdentifier() == null) {
long newRow = tagDatabase.insert(TAG_TABLE_NAME, AbstractTagModel.NAME,
tag.getMergedValues());
tag.setTagIdentifier(new TagIdentifier(newRow));
saveSucessful = newRow >= 0;
} else {
long id = tag.getTagIdentifier().getId();
saveSucessful = tagDatabase.update(TAG_TABLE_NAME, tag.getSetValues(),
KEY_ROWID + "=" + id, null) > 0;
}
return saveSucessful;
}
/** Returns a TaskModelForView corresponding to the given Tag Name */
public TagModelForView fetchTagFromName(String name) throws SQLException {
Cursor cursor = tagDatabase.query(true, TAG_TABLE_NAME,
TagModelForView.FIELD_LIST,
AbstractTagModel.NAME + " = ?", new String[] {name}, null, null, null, null);
try {
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
TagModelForView model = new TagModelForView(cursor);
return model;
}
return null;
} finally {
if(cursor != null)
cursor.close();
}
}
/** Returns a TaskModelForView corresponding to the given TagIdentifier */
public TagModelForView fetchTagForView(TagIdentifier tagId) throws SQLException {
long id = tagId.getId();
Cursor cursor = tagDatabase.query(true, TAG_TABLE_NAME,
TagModelForView.FIELD_LIST,
KEY_ROWID + "=" + id, null, null, null, null, null);
try {
if (cursor != null) {
cursor.moveToFirst();
TagModelForView model = new TagModelForView(cursor);
return model;
}
throw new SQLException("Returned empty set!");
} finally {
if(cursor != null)
cursor.close();
}
}
/** Deletes the tag and removes tag/task mappings */
public boolean deleteTag( TagIdentifier tagId)
throws SQLException{
if(tagToTaskMapDatabase.delete(TAG_TASK_MAP_NAME,
TagToTaskMapping.TAG + " = " + tagId.idAsString(), null) < 0)
return false;
int res = tagDatabase.delete(TAG_TABLE_NAME,
KEY_ROWID + " = " + tagId.idAsString(), null);
return res > 0;
}
// --- single tag to task operations
/** Remove the given tag from the task */
public boolean removeTag(TaskIdentifier taskId, TagIdentifier tagId)
throws SQLException{
int res = tagToTaskMapDatabase.delete(TAG_TASK_MAP_NAME,
String.format("%s = ? AND %s = ?",
TagToTaskMapping.TAG, TagToTaskMapping.TASK),
new String[] { tagId.idAsString(), taskId.idAsString() });
// notify modification
TasksProvider.notifyDatabaseModification();
return res > 0;
}
/** Add the given tag to the task */
public boolean addTag(TaskIdentifier taskId, TagIdentifier tagId)
throws SQLException {
ContentValues values = new ContentValues();
values.put(TagToTaskMapping.TAG, tagId.getId());
values.put(TagToTaskMapping.TASK, taskId.getId());
long res = tagToTaskMapDatabase.insert(TAG_TASK_MAP_NAME, TagToTaskMapping.TAG,
values);
return res >= 0;
}
// --- boilerplate
/**
* Constructor - takes the context to allow the database to be
* opened/created
*/
public TagController(Context context) {
this.context = context;
}
/**
* Open the notes database. If it cannot be opened, try to create a new
* instance of the database. If it cannot be created, throw an exception to
* signal the failure
*
* @return this (self reference, allowing this to be chained in an
* initialization call)
* @throws SQLException if the database could be neither opened or created
*/
@Override
public synchronized void open() throws SQLException {
tagToTaskMapDatabase = new TagToTaskMappingDatabaseHelper(context,
TAG_TASK_MAP_NAME, TAG_TASK_MAP_NAME).getWritableDatabase();
tagDatabase = new TagModelDatabaseHelper(context,
TAG_TABLE_NAME, TAG_TABLE_NAME).getWritableDatabase();
}
/** Closes database resource */
@Override
public void close() {
tagDatabase.close();
tagToTaskMapDatabase.close();
}
}

@ -0,0 +1,31 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.tag;
import com.todoroo.astrid.legacy.data.Identifier;
public class TagIdentifier extends Identifier {
public TagIdentifier(long id) {
super(id);
}
}

@ -0,0 +1,102 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.tag;
import android.database.Cursor;
import com.todoroo.astrid.legacy.data.AbstractController;
/** Tag model for viewing purposes. Contains task name */
public class TagModelForView extends AbstractTagModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
NAME,
};
// negative number, should not conflict with database row #'s
public static final TagIdentifier UNTAGGED_IDENTIFIER = new TagIdentifier(Long.MIN_VALUE);
public static final String UNTAGGED_DEFAULT_NAME = "[untagged]";
private static TagModelForView UNTAGGED_TASKS = new TagModelForView(UNTAGGED_DEFAULT_NAME);
public static final String HIDDEN_FROM_MAIN_LIST_PREFIX = "_";
/**
* Returns a TagModelForView object to represent "Untagged" tasks,
* whose Identifier is defined by the static final UNTAGGED_IDENTIFIER.
*
* Pass in a string to show the "Untagged" name in the desired language.
* @param untaggedLabel
* @return
*/
public static TagModelForView getUntaggedModel(String untaggedLabel) {
UNTAGGED_TASKS = new TagModelForView(untaggedLabel);
UNTAGGED_TASKS.setTagIdentifier(UNTAGGED_IDENTIFIER);
return UNTAGGED_TASKS;
}
/**
* Returns the default/last-used TagModelForView representing "Untagged"
* tasks. Set the localized name using getUntaggedModel(String...)
*/
public static TagModelForView getUntaggedModel() {
UNTAGGED_TASKS.setTagIdentifier(UNTAGGED_IDENTIFIER);
return UNTAGGED_TASKS;
}
// --- constructors
/** Constructor for creating a new model */
TagModelForView(String name) {
super();
setName(name);
}
/** Constructor for getting an existing model */
TagModelForView(Cursor cursor) {
super(cursor);
getName();
}
// --- getters and setters
@Override
public String getName() {
return super.getName();
}
@Override
public String toString() {
return getName();
}
public boolean shouldHideFromMainList() {
return getName().startsWith(HIDDEN_FROM_MAIN_LIST_PREFIX);
}
public void toggleHideFromMainList() {
if(shouldHideFromMainList())
setName(getName().substring(HIDDEN_FROM_MAIN_LIST_PREFIX.length()));
else
setName(HIDDEN_FROM_MAIN_LIST_PREFIX + getName());
}
}

@ -0,0 +1,130 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.tag;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.AbstractModel;
import com.todoroo.astrid.legacy.data.task.TaskIdentifier;
/** A single tag on a task */
public class TagToTaskMapping extends AbstractModel {
/** Version number of this model */
static final int VERSION = 2;
// field names
static final String TASK = "task";
static final String TAG = "tag";
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
TASK,
TAG,
};
// --- database helper
/** Database Helper manages creating new tables and updating old ones */
static class TagToTaskMappingDatabaseHelper extends SQLiteOpenHelper {
String tableName;
Context context;
TagToTaskMappingDatabaseHelper(Context context, String databaseName, String tableName) {
super(context, databaseName, null, VERSION);
this.tableName = tableName;
this.context = context;
}
@Override
public synchronized void onCreate(SQLiteDatabase db) {
String sql = new StringBuilder().
append("CREATE TABLE IF NOT EXISTS ").append(tableName).append(" (").
append(AbstractController.KEY_ROWID).append(" integer primary key autoincrement, ").
append(TASK).append(" integer not null,").
append(TAG).append(" integer not null,").
append("unique (").append(TASK).append(",").append(TAG).append(")").
append(");").toString();
db.execSQL(sql);
}
@Override
public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(getClass().getSimpleName(), "Upgrading database from version " +
oldVersion + " to " + newVersion + ".");
switch(oldVersion) {
default:
throw new RuntimeException("Tag: Unsupported migration from " + oldVersion + " to " + newVersion);
}
}
}
// --- constructor pass-through
TagToTaskMapping(TaskIdentifier task, TagIdentifier tag) {
super();
setTask(task);
setTag(tag);
}
TagToTaskMapping(Cursor cursor) {
super(cursor);
}
// --- getters and setters: expose them as you see fit
public boolean isNew() {
return getCursor() == null;
}
public TaskIdentifier getTask() {
return new TaskIdentifier(retrieveInteger(TASK));
}
public TagIdentifier getTag() {
return new TagIdentifier(retrieveInteger(TAG));
}
private void setTask(TaskIdentifier task) {
setValues.put(TASK, task.getId());
}
private void setTag(TagIdentifier tag) {
setValues.put(TAG, tag.getId());
}
}

@ -0,0 +1,606 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.AbstractModel;
import com.todoroo.astrid.legacy.data.enums.Importance;
import com.todoroo.astrid.legacy.data.enums.RepeatInterval;
/** Abstract model of a task. Subclasses implement the getters and setters
* they are interested in.
*
* @author timsu
*
*/
public abstract class AbstractTaskModel extends AbstractModel {
/** Version number of this model */
static final int VERSION = 8;
public static final int COMPLETE_PERCENTAGE = 100;
// field names
public static final String NAME = "name";
public static final String NOTES = "notes";
public static final String PROGRESS_PERCENTAGE = "progressPercentage";
public static final String IMPORTANCE = "importance";
public static final String ESTIMATED_SECONDS = "estimatedSeconds";
public static final String ELAPSED_SECONDS = "elapsedSeconds";
public static final String TIMER_START = "timerStart";
public static final String DEFINITE_DUE_DATE = "definiteDueDate";
public static final String PREFERRED_DUE_DATE = "preferredDueDate";
public static final String HIDDEN_UNTIL = "hiddenUntil";
public static final String POSTPONE_COUNT = "postponeCount";
public static final String NOTIFICATIONS = "notifications";
public static final String NOTIFICATION_FLAGS = "notificationFlags";
public static final String LAST_NOTIFIED = "lastNotified";
public static final String REPEAT = "repeat";
public static final String CREATION_DATE = "creationDate";
public static final String COMPLETION_DATE = "completionDate";
public static final String CALENDAR_URI = "calendarUri";
public static final String FLAGS = "flags";
// reserved fields ---
public static final String BLOCKING_ON = "blockingOn";
// notification flags
public static final int NOTIFY_BEFORE_DEADLINE = 1 << 0;
public static final int NOTIFY_AT_DEADLINE = 1 << 1;
public static final int NOTIFY_AFTER_DEADLINE = 1 << 2;
public static final int NOTIFY_NONSTOP = 1 << 3;
// other flags
public static final int FLAG_SYNC_ON_COMPLETE = 1 << 0;
/** Number of bits to shift repeat value by */
public static final int REPEAT_VALUE_OFFSET = 3;
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
static {
defaultValues.put(NAME, "");
defaultValues.put(NOTES, "");
defaultValues.put(PROGRESS_PERCENTAGE, 0);
defaultValues.put(IMPORTANCE, Importance.DEFAULT.ordinal());
defaultValues.put(ESTIMATED_SECONDS, 0);
defaultValues.put(ELAPSED_SECONDS, 0);
defaultValues.put(TIMER_START, 0);
defaultValues.put(DEFINITE_DUE_DATE, 0);
defaultValues.put(PREFERRED_DUE_DATE, 0);
defaultValues.put(HIDDEN_UNTIL, 0);
defaultValues.put(BLOCKING_ON, 0);
defaultValues.put(POSTPONE_COUNT, 0);
defaultValues.put(NOTIFICATIONS, 0);
defaultValues.put(NOTIFICATION_FLAGS, NOTIFY_AT_DEADLINE);
defaultValues.put(LAST_NOTIFIED, 0);
defaultValues.put(REPEAT, 0);
defaultValues.put(COMPLETION_DATE, 0);
defaultValues.put(CALENDAR_URI, (String)null);
defaultValues.put(FLAGS, 0);
}
// --- database helper
/** Database Helper manages creating new tables and updating old ones */
public static class TaskModelDatabaseHelper extends SQLiteOpenHelper {
String tableName;
Context context;
public TaskModelDatabaseHelper(Context context, String databaseName, String tableName) {
super(context, databaseName, null, VERSION);
this.tableName = tableName;
this.context = context;
}
@Override
public synchronized void onCreate(SQLiteDatabase db) {
String sql = new StringBuilder().
append("CREATE TABLE IF NOT EXISTS ").append(tableName).append(" (").
append(AbstractController.KEY_ROWID).append(" integer primary key autoincrement, ").
append(NAME).append(" text not null,").
append(NOTES).append(" text not null,").
append(PROGRESS_PERCENTAGE).append(" integer not null,").
append(IMPORTANCE).append(" integer not null,").
append(ESTIMATED_SECONDS).append(" integer,").
append(ELAPSED_SECONDS).append(" integer,").
append(TIMER_START).append(" integer,").
append(DEFINITE_DUE_DATE).append(" integer,").
append(PREFERRED_DUE_DATE).append(" integer,").
append(HIDDEN_UNTIL).append(" integer,").
append(BLOCKING_ON).append(" integer,").
append(POSTPONE_COUNT).append(" integer,").
append(NOTIFICATIONS).append(" integer,").
append(NOTIFICATION_FLAGS).append(" integer,").
append(LAST_NOTIFIED).append(" integer,").
append(REPEAT).append(" integer,").
append(FLAGS).append(" integer,").
append(CREATION_DATE).append(" integer,").
append(COMPLETION_DATE).append(" integer,").
append(CALENDAR_URI).append(" text").
append(");").toString();
db.execSQL(sql);
}
@Override
@SuppressWarnings("fallthrough")
public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(getClass().getSimpleName(), "Upgrading database from version " +
oldVersion + " to " + newVersion + ".");
String sql;
// note: we execute sql statements in their own try block to be more
// graceful if an upgrade dies halfway or something
switch(oldVersion) {
case 1:
sql = new StringBuilder().append("ALTER TABLE ").
append(tableName).append(" ADD COLUMN ").
append(LAST_NOTIFIED).append(" integer").toString();
try {
db.execSQL(sql);
} catch (Exception e) {
Log.e("astrid", "Error updating table!", e);
}
sql = new StringBuilder().append("ALTER TABLE ").
append(tableName).append(" ADD COLUMN ").
append(NOTIFICATION_FLAGS).append(" integer").toString();
try {
db.execSQL(sql);
} catch (Exception e) {
Log.e("astrid", "Error updating table!", e);
}
case 2:
sql = new StringBuilder().append("ALTER TABLE ").
append(tableName).append(" ADD COLUMN ").
append(REPEAT).append(" integer").toString();
try {
db.execSQL(sql);
} catch (Exception e) {
Log.e("astrid", "Error updating table!", e);
}
case 3:
sql = new StringBuilder().append("ALTER TABLE ").
append(tableName).append(" ADD COLUMN ").
append(CALENDAR_URI).append(" text").toString();
try {
db.execSQL(sql);
} catch (Exception e) {
Log.e("astrid", "Error updating table!", e);
}
case 4:
sql = new StringBuilder().append("ALTER TABLE ").
append(tableName).append(" ADD COLUMN ").
append(POSTPONE_COUNT).append(" integer").toString();
try {
db.execSQL(sql);
} catch (Exception e) {
Log.e("astrid", "Error updating table!", e);
}
case 5:
case 6:
// apparently some people didn't get the flags column
// from version 5 to version 6, so we try again
sql = new StringBuilder().append("ALTER TABLE ").
append(tableName).append(" ADD COLUMN ").
append(FLAGS).append(" integer").toString();
try {
db.execSQL(sql);
} catch (Exception e) {
Log.e("astrid", "Error updating table!", e);
}
case 7:
// not a real change, but make sure that columns that are null
// are converted into zeros, which was my previous assumption
for(String column : new String[] {
ESTIMATED_SECONDS,
ELAPSED_SECONDS,
TIMER_START,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
HIDDEN_UNTIL,
POSTPONE_COUNT,
LAST_NOTIFIED,
REPEAT,
CREATION_DATE,
COMPLETION_DATE }) {
sql = String.format("UPDATE %s SET %s = 0 WHERE %s ISNULL",
tableName, column, column);
try {
db.execSQL(sql);
} catch (Exception e) {
Log.e("astrid", "Error updating table!", e);
}
}
// --- break point
break;
default:
// we don't know how to handle it... show an error
throw new RuntimeException("Tasks: Unsupported migration from " + oldVersion + " to " + newVersion);
}
}
}
// --- utility methods
/** Gets task color. Requires definiteDueDate and importance */
protected int getTaskColorResource(Context context) {
if(getDefiniteDueDate() != null && getDefiniteDueDate().getTime() <
System.currentTimeMillis()) {
return 0;
}
return 1;
}
/** Checks whether task is done. Requires progressPercentage */
protected boolean isTaskCompleted() {
return getProgressPercentage() >= COMPLETE_PERCENTAGE;
}
/** Stops the timer & increments elapsed time. Requires timerStart and
* elapsedSeconds */
protected void stopTimerAndUpdateElapsedTime() {
if(getTimerStart() == null)
return;
long start = getTimerStart().getTime();
setTimerStart(null);
long secondsElapsed = (System.currentTimeMillis() - start)/1000;
setElapsedSeconds((int) (getElapsedSeconds() + secondsElapsed));
}
protected void prefetchData(String[] fields) {
for(String field : fields) {
if(field.equals(NAME))
getName();
else if(field.equals(NOTES))
getNotes();
else if(field.equals(PROGRESS_PERCENTAGE))
getProgressPercentage();
else if(field.equals(IMPORTANCE))
getImportance();
else if(field.equals(ESTIMATED_SECONDS))
getEstimatedSeconds();
else if(field.equals(ELAPSED_SECONDS))
getElapsedSeconds();
else if(field.equals(TIMER_START))
getTimerStart();
else if(field.equals(DEFINITE_DUE_DATE))
getDefiniteDueDate();
else if(field.equals(PREFERRED_DUE_DATE))
getPreferredDueDate();
else if(field.equals(HIDDEN_UNTIL))
getHiddenUntil();
else if(field.equals(BLOCKING_ON))
getBlockingOn();
else if(field.equals(POSTPONE_COUNT))
getPostponeCount();
else if(field.equals(NOTIFICATIONS))
getNotificationIntervalSeconds();
else if(field.equals(CREATION_DATE))
getCreationDate();
else if(field.equals(COMPLETION_DATE))
getCompletionDate();
else if(field.equals(NOTIFICATION_FLAGS))
getNotificationFlags();
else if(field.equals(LAST_NOTIFIED))
getLastNotificationDate();
else if(field.equals(REPEAT))
getRepeat();
else if(field.equals(FLAGS))
getFlags();
}
}
// --- helper classes
public static class RepeatInfo {
private RepeatInterval interval;
private int value;
public RepeatInfo(RepeatInterval repeatInterval, int value) {
this.interval = repeatInterval;
this.value = value;
}
public Date shiftDate(Date input) {
Date newDate = (Date)input.clone();
interval.offsetDateBy(newDate, value);
return newDate;
}
public RepeatInterval getInterval() {
return interval;
}
public int getValue() {
return value;
}
}
// --- task identifier
private TaskIdentifier identifier = null;
public TaskIdentifier getTaskIdentifier() {
return identifier;
}
void setTaskIdentifier(TaskIdentifier identifier) {
this.identifier = identifier;
}
// --- constructors and abstract methods
AbstractTaskModel() {
super();
}
/** Read identifier from database */
AbstractTaskModel(Cursor cursor) {
super(cursor);
Integer id = retrieveInteger(AbstractController.KEY_ROWID);
setTaskIdentifier(new TaskIdentifier(id));
}
/** Get identifier from argument */
AbstractTaskModel(TaskIdentifier identifier, Cursor cursor) {
super(cursor);
setTaskIdentifier(identifier);
}
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- getters and setters: expose them as you see fit
protected String getName() {
return retrieveString(NAME);
}
protected String getNotes() {
return retrieveString(NOTES);
}
protected int getProgressPercentage() {
return retrieveInteger(PROGRESS_PERCENTAGE);
}
protected Importance getImportance() {
Integer value = retrieveInteger(IMPORTANCE);
if(value == null)
return null;
return Importance.values()[value];
}
protected Integer getEstimatedSeconds() {
return retrieveInteger(ESTIMATED_SECONDS);
}
protected Integer getElapsedSeconds() {
return retrieveInteger(ELAPSED_SECONDS);
}
protected Date getTimerStart() {
return retrieveDate(TIMER_START);
}
protected Date getDefiniteDueDate() {
return retrieveDate(DEFINITE_DUE_DATE);
}
protected Date getPreferredDueDate() {
return retrieveDate(PREFERRED_DUE_DATE);
}
protected Date getHiddenUntil() {
return retrieveDate(HIDDEN_UNTIL);
}
protected boolean isHidden() {
if(getHiddenUntil() == null)
return false;
return getHiddenUntil().getTime() > System.currentTimeMillis();
}
protected Date getCreationDate() {
return retrieveDate(CREATION_DATE);
}
protected Date getCompletionDate() {
return retrieveDate(COMPLETION_DATE);
}
protected TaskIdentifier getBlockingOn() {
Long value = retrieveLong(BLOCKING_ON);
if(value == null)
return null;
return new TaskIdentifier(value);
}
protected Integer getPostponeCount() {
return retrieveInteger(POSTPONE_COUNT);
}
protected Integer getNotificationIntervalSeconds() {
return retrieveInteger(NOTIFICATIONS);
}
protected int getNotificationFlags() {
return retrieveInteger(NOTIFICATION_FLAGS);
}
protected Date getLastNotificationDate() {
return retrieveDate(LAST_NOTIFIED);
}
protected RepeatInfo getRepeat() {
int repeat = retrieveInteger(REPEAT);
if(repeat == 0)
return null;
int value = repeat >> REPEAT_VALUE_OFFSET;
RepeatInterval interval = RepeatInterval.values()
[repeat - (value << REPEAT_VALUE_OFFSET)];
return new RepeatInfo(interval, value);
}
protected String getCalendarUri() {
String uri = retrieveString(CALENDAR_URI);
if(uri != null && uri.length() == 0)
return null;
else
return uri;
}
protected int getFlags() {
return retrieveInteger(FLAGS);
}
// --- setters
protected void setName(String name) {
putIfChangedFromDatabase(NAME, name);
}
protected void setNotes(String notes) {
putIfChangedFromDatabase(NOTES, notes);
}
protected void setProgressPercentage(int progressPercentage) {
putIfChangedFromDatabase(PROGRESS_PERCENTAGE, progressPercentage);
if(getProgressPercentage() != progressPercentage &&
progressPercentage == COMPLETE_PERCENTAGE)
setCompletionDate(new Date());
}
protected void setImportance(Importance importance) {
putIfChangedFromDatabase(IMPORTANCE, importance.ordinal());
}
protected void setEstimatedSeconds(Integer estimatedSeconds) {
putIfChangedFromDatabase(ESTIMATED_SECONDS, estimatedSeconds);
}
protected void setElapsedSeconds(int elapsedSeconds) {
putIfChangedFromDatabase(ELAPSED_SECONDS, elapsedSeconds);
}
protected void setTimerStart(Date timerStart) {
putDate(TIMER_START, timerStart);
}
protected void setDefiniteDueDate(Date definiteDueDate) {
putDate(DEFINITE_DUE_DATE, definiteDueDate);
}
protected void setPreferredDueDate(Date preferredDueDate) {
putDate(PREFERRED_DUE_DATE, preferredDueDate);
}
protected void setHiddenUntil(Date hiddenUntil) {
putDate(HIDDEN_UNTIL, hiddenUntil);
}
protected void setBlockingOn(TaskIdentifier blockingOn) {
if(blockingOn == null || blockingOn.equals(getTaskIdentifier()))
putIfChangedFromDatabase(BLOCKING_ON, (Integer)null);
else
putIfChangedFromDatabase(BLOCKING_ON, blockingOn.getId());
}
protected void setPostponeCount(int postponeCount) {
putIfChangedFromDatabase(POSTPONE_COUNT, postponeCount);
}
protected void setCreationDate(Date creationDate) {
putDate(CREATION_DATE, creationDate);
}
protected void setCompletionDate(Date completionDate) {
putDate(COMPLETION_DATE, completionDate);
}
protected void setNotificationIntervalSeconds(Integer intervalInSeconds) {
putIfChangedFromDatabase(NOTIFICATIONS, intervalInSeconds);
}
protected void setNotificationFlags(int flags) {
putIfChangedFromDatabase(NOTIFICATION_FLAGS, flags);
}
protected void setLastNotificationTime(Date date) {
putDate(LAST_NOTIFIED, date);
}
protected void setRepeat(RepeatInfo repeatInfo) {
int repeat;
if(repeatInfo == null)
repeat = 0;
else
repeat = (repeatInfo.value << REPEAT_VALUE_OFFSET) +
repeatInfo.interval.ordinal();
putIfChangedFromDatabase(REPEAT, repeat);
}
protected void setCalendarUri(String uri) {
putIfChangedFromDatabase(CALENDAR_URI, uri);
}
protected void setFlags(int flags) {
putIfChangedFromDatabase(FLAGS, flags);
}
// --- utility methods
protected void putDate(String fieldName, Date date) {
if(date == null)
putIfChangedFromDatabase(fieldName, 0);
else
putIfChangedFromDatabase(fieldName, date.getTime());
}
}

@ -0,0 +1,556 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.util.Log;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.sync.SyncDataController;
import com.todoroo.astrid.legacy.data.task.AbstractTaskModel.RepeatInfo;
import com.todoroo.astrid.legacy.data.task.AbstractTaskModel.TaskModelDatabaseHelper;
/**
* Controller for task-related operations
*
* @author timsu
*
*/
public class TaskController extends AbstractController {
private SQLiteDatabase database;
// --- task list operations
/** Return a list of all active tasks with notifications */
public HashSet<TaskModelForNotify> getTasksWithNotifications() {
HashSet<TaskModelForNotify> list = new HashSet<TaskModelForNotify>();
Cursor cursor = database.query(TASK_TABLE_NAME, TaskModelForNotify.FIELD_LIST,
String.format("%s < %d AND (%s != 0 OR %s != 0)",
AbstractTaskModel.PROGRESS_PERCENTAGE,
AbstractTaskModel.COMPLETE_PERCENTAGE,
AbstractTaskModel.NOTIFICATIONS,
AbstractTaskModel.NOTIFICATION_FLAGS), null, null, null, null, null);
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new TaskModelForNotify(cursor));
} while(!cursor.isLast());
return list;
} finally {
cursor.close();
}
}
/** Return a list of all active tasks with deadlines */
public ArrayList<TaskModelForNotify> getTasksWithDeadlines() {
ArrayList<TaskModelForNotify> list = new ArrayList<TaskModelForNotify>();
Cursor cursor = database.query(TASK_TABLE_NAME, TaskModelForNotify.FIELD_LIST,
String.format("%s < %d AND (%s != 0 OR %s != 0)",
AbstractTaskModel.PROGRESS_PERCENTAGE,
AbstractTaskModel.COMPLETE_PERCENTAGE,
AbstractTaskModel.DEFINITE_DUE_DATE,
AbstractTaskModel.PREFERRED_DUE_DATE), null, null, null, null, null);
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new TaskModelForNotify(cursor));
} while(!cursor.isLast());
return list;
} finally {
cursor.close();
}
}
/** Return a list of all of the tasks with progress < COMPLETE_PERCENTAGE */
public Cursor getActiveTaskListCursor() {
return database.query(TASK_TABLE_NAME, TaskModelForList.FIELD_LIST,
AbstractTaskModel.PROGRESS_PERCENTAGE + " < " +
AbstractTaskModel.COMPLETE_PERCENTAGE, null, null, null,
null, null);
}
/** Return a list of all tasks */
public Cursor getAllTaskListCursor() {
return database.query(TASK_TABLE_NAME, TaskModelForList.FIELD_LIST,
null, null, null, null, null, null);
}
/** Return a list of all tasks */
public Cursor getBackupTaskListCursor() {
return database.query(TASK_TABLE_NAME, TaskModelForXml.FIELD_LIST,
AbstractTaskModel.PROGRESS_PERCENTAGE + " < " +
AbstractTaskModel.COMPLETE_PERCENTAGE, null, null, null,
null, null);
}
/** Delete all completed tasks with date < older than date */
public int deleteCompletedTasksOlderThan(Date olderThanDate) {
return database.delete(TASK_TABLE_NAME, String.format("`%s` >= '%d' AND `%s` <= '%d'",
AbstractTaskModel.PROGRESS_PERCENTAGE, AbstractTaskModel.COMPLETE_PERCENTAGE,
AbstractTaskModel.COMPLETION_DATE, olderThanDate.getTime()), null);
}
/** Create a list of tasks from the db cursor given */
public ArrayList<TaskModelForList> createTaskListFromCursor(Cursor cursor) {
ArrayList<TaskModelForList> list = new ArrayList<TaskModelForList>();
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new TaskModelForList(cursor));
} while(!cursor.isLast());
return list;
}
/** Helper method to take a cursor pointing to a list of id's and generate
* a hashset */
private HashSet<TaskIdentifier> createTaskIdentifierSet(Cursor cursor) {
HashSet<TaskIdentifier> list = new HashSet<TaskIdentifier>();
try {
if(cursor.getCount() == 0)
return list;
do {
cursor.moveToNext();
list.add(new TaskIdentifier(cursor.getInt(
cursor.getColumnIndexOrThrow(KEY_ROWID))));
} while(!cursor.isLast());
return list;
} finally {
cursor.close();
}
}
/** Get identifiers for all tasks */
public HashSet<TaskIdentifier> getAllTaskIdentifiers() {
Cursor cursor = database.query(TASK_TABLE_NAME, new String[] { KEY_ROWID },
null, null, null, null, null, null);
return createTaskIdentifierSet(cursor);
}
/** Get identifiers for all non-completed tasks */
public HashSet<TaskIdentifier> getActiveTaskIdentifiers() {
Cursor cursor = database.query(TASK_TABLE_NAME, new String[] { KEY_ROWID },
AbstractTaskModel.PROGRESS_PERCENTAGE + " < " +
AbstractTaskModel.COMPLETE_PERCENTAGE, null, null, null, null, null);
return createTaskIdentifierSet(cursor);
}
/** Get identifiers for all non-completed, non-hidden tasks */
public HashSet<TaskIdentifier> getActiveVisibleTaskIdentifiers() {
Cursor cursor = database.query(TASK_TABLE_NAME, new String[] { KEY_ROWID },
AbstractTaskModel.PROGRESS_PERCENTAGE + " < " +
AbstractTaskModel.COMPLETE_PERCENTAGE + " AND (" +
AbstractTaskModel.HIDDEN_UNTIL + " ISNULL OR " + AbstractTaskModel.HIDDEN_UNTIL + " < " +
System.currentTimeMillis() + ")", null, null, null, null, null);
return createTaskIdentifierSet(cursor);
}
/** Create a weighted list of tasks from the db cursor given */
public Cursor getTaskListCursorById(List<TaskIdentifier> idList) {
StringBuilder where = new StringBuilder();
for(int i = 0; i < idList.size(); i++) {
where.append(KEY_ROWID);
where.append("=");
where.append(idList.get(i).idAsString());
if(i < idList.size()-1)
where.append(" OR ");
}
// hack for empty arrays
if(idList.size() == 0)
where.append("0");
return database.query(true, TASK_TABLE_NAME,
TaskModelForList.FIELD_LIST, where.toString(), null, null,
null, null, null);
}
// --- single task operations
/** Delete the given task */
public boolean deleteTask(TaskIdentifier taskId) {
if(taskId == null)
throw new UnsupportedOperationException("Cannot delete uncreated task!");
long id = taskId.getId();
cleanupTask(taskId, false);
return database.delete(TASK_TABLE_NAME, KEY_ROWID + "=" + id, null) > 0;
}
/** Saves the given task to the database. Returns true on success.
*
* @param duringSync set to true when save is part of a synchronize
*/
public boolean saveTask(AbstractTaskModel task, boolean duringSync) {
boolean saveSucessful;
if(task.getTaskIdentifier() == null) {
long newRow = database.insert(TASK_TABLE_NAME, AbstractTaskModel.NAME,
task.getMergedValues());
task.setTaskIdentifier(new TaskIdentifier(newRow));
saveSucessful = newRow >= 0;
} else {
long id = task.getTaskIdentifier().getId();
ContentValues values = task.getSetValues();
if(values.size() == 0) // nothing changed
return true;
onTaskSave(task, values, duringSync);
saveSucessful = database.update(TASK_TABLE_NAME, values,
KEY_ROWID + "=" + id, null) > 0;
// task was completed
if(values.containsKey(AbstractTaskModel.PROGRESS_PERCENTAGE) &&
values.getAsInteger(AbstractTaskModel.PROGRESS_PERCENTAGE)
== AbstractTaskModel.COMPLETE_PERCENTAGE) {
onTaskCompleted(task, values, duringSync);
}
SyncDataController.taskUpdated(context, task);
}
return saveSucessful;
}
/**
* Called when the task is saved. Perform some processing on the task.
*
* @param task
* @param values
*/
private void onTaskSave(AbstractTaskModel task, ContentValues values, boolean duringSync) {
// save task completed date
if(values.containsKey(AbstractTaskModel.PROGRESS_PERCENTAGE) &&
values.getAsInteger(AbstractTaskModel.PROGRESS_PERCENTAGE)
== AbstractTaskModel.COMPLETE_PERCENTAGE) {
values.put(AbstractTaskModel.COMPLETION_DATE, System.currentTimeMillis());
}
}
/**
* Called when this task is set to completed.
*
* @param task task to process
* @param values mutable map of values to save
*/
private void onTaskCompleted(AbstractTaskModel task, ContentValues values, boolean duringSync) {
Cursor cursor = fetchTaskCursor(task.getTaskIdentifier(),
TaskModelForHandlers.FIELD_LIST);
TaskModelForHandlers model = new TaskModelForHandlers(cursor, values);
// handle repeat
RepeatInfo repeatInfo = model.getRepeat();
if(repeatInfo != null) {
model.repeatTaskBy(context, this, repeatInfo);
database.update(TASK_TABLE_NAME, values, KEY_ROWID + "=" +
task.getTaskIdentifier().getId(), null);
}
cursor.close();
cleanupTask(task.getTaskIdentifier(), repeatInfo != null);
}
/** Clean up state from a task. Called when deleting or completing it */
private void cleanupTask(TaskIdentifier taskId, boolean isRepeating) {
// delete calendar event if not repeating
if(!isRepeating) {
try {
Cursor cursor = fetchTaskCursor(taskId, new String[] {
AbstractTaskModel.CALENDAR_URI });
cursor.moveToFirst();
String uri = cursor.getString(0);
cursor.close();
if(uri != null && uri.length() > 0) {
ContentResolver cr = context.getContentResolver();
cr.delete(Uri.parse(uri), null, null);
ContentValues values = new ContentValues();
values.put(AbstractTaskModel.CALENDAR_URI, (String)null);
database.update(TASK_TABLE_NAME, values, KEY_ROWID + "=" +
taskId.getId(), null);
}
} catch (Exception e) {
Log.e("astrid", "Error deleting calendar event", e);
}
}
}
/** Set last notification date */
public boolean setLastNotificationTime(TaskIdentifier taskId, Date date) {
ContentValues values = new ContentValues();
values.put(AbstractTaskModel.LAST_NOTIFIED, date.getTime());
return database.update(TASK_TABLE_NAME, values,
KEY_ROWID + "=" + taskId.getId(), null) > 0;
}
// --- fetching different models
/** Creates a new task and returns the task identifier */
public TaskModelForEdit createNewTaskForEdit() {
TaskModelForEdit task = new TaskModelForEdit();
task.setTaskIdentifier(null);
return task;
}
/** Returns a TaskModelForEdit corresponding to the given TaskIdentifier */
public TaskModelForEdit fetchTaskForEdit(Activity activity, TaskIdentifier
taskId) throws SQLException {
Cursor cursor = fetchTaskCursor(taskId, TaskModelForEdit.FIELD_LIST);
activity.startManagingCursor(cursor);
TaskModelForEdit model = new TaskModelForEdit(taskId, cursor);
return model;
}
/** Returns a TaskModelForList corresponding to the given TaskIdentifier */
public TaskModelForList fetchTaskForList(TaskIdentifier taskId) throws SQLException {
Cursor cursor = fetchTaskCursor(taskId, TaskModelForList.FIELD_LIST);
TaskModelForList model = new TaskModelForList(cursor);
cursor.close();
return model;
}
/** Returns a TaskModelForXml corresponding to the given TaskIdentifier */
public TaskModelForXml fetchTaskForXml(TaskIdentifier taskId) throws SQLException {
Cursor cursor = fetchTaskCursor(taskId, TaskModelForXml.FIELD_LIST);
TaskModelForXml model = new TaskModelForXml(cursor);
cursor.close();
return model;
}
/* Attempts to return a TaskModelForXml for the given name and creation date */
public TaskModelForXml fetchTaskForXml(String name, Date creationDate) {
Cursor cursor;
try {
cursor = fetchTaskCursor(name, creationDate.getTime(),
TaskModelForXml.FIELD_LIST);
} catch (SQLException e) {
return null;
}
if (cursor == null || cursor.getCount() == 0) {
return null;
}
TaskModelForXml model = new TaskModelForXml(cursor);
cursor.close();
return model;
}
/** Returns a TaskModelForReminder corresponding to the given TaskIdentifier */
public TaskModelForReminder fetchTaskForReminder(TaskIdentifier taskId) throws SQLException {
Cursor cursor = fetchTaskCursor(taskId, TaskModelForReminder.FIELD_LIST);
TaskModelForReminder model = new TaskModelForReminder(cursor);
cursor.close();
return model;
}
/** Returns a TaskModelForSync corresponding to the given TaskIdentifier */
public TaskModelForSync fetchTaskForSync(TaskIdentifier taskId) throws SQLException {
Cursor cursor = fetchTaskCursor(taskId, TaskModelForSync.FIELD_LIST);
TaskModelForSync model = new TaskModelForSync(cursor);
cursor.close();
return model;
}
/** Returns a TaskModelForView by name */
public TaskModelForSync searchForTaskForSync(String name) throws SQLException {
Cursor cursor = database.query(true, TASK_TABLE_NAME, TaskModelForSync.FIELD_LIST,
AbstractTaskModel.NAME + " = ? AND " +
AbstractTaskModel.PROGRESS_PERCENTAGE + " < "+
AbstractTaskModel.COMPLETE_PERCENTAGE,
new String[] { name }, null, null, null, null);
try {
if (cursor == null || cursor.getCount() == 0)
return null;
cursor.moveToFirst();
TaskModelForSync model = new TaskModelForSync(cursor);
return model;
} finally {
if(cursor != null)
cursor.close();
}
}
/** Returns a TaskModelForView corresponding to the given TaskIdentifier */
public TaskModelForNotify fetchTaskForNotify(TaskIdentifier taskId) throws SQLException {
Cursor cursor = fetchTaskCursor(taskId, TaskModelForNotify.FIELD_LIST);
TaskModelForNotify model = new TaskModelForNotify(cursor);
cursor.close();
return model;
}
/** Moves cursor to the task.
* Don't forget to close the cursor when you're done. */
private Cursor fetchTaskCursor(TaskIdentifier taskId, String[] fieldList) {
long id = taskId.getId();
Cursor cursor = database.query(true, TASK_TABLE_NAME, fieldList,
KEY_ROWID + "=" + id, null, null, null, null, null);
if (cursor == null)
throw new SQLException("Returned empty set!");
cursor.moveToFirst();
return cursor;
}
/** Returns null if unsuccessful, otherwise moves cursor to the task.
* Don't forget to close the cursor when you're done. */
private Cursor fetchTaskCursor(String name, long creationDate, String[] fieldList) {
// truncate millis
final String where = AbstractTaskModel.NAME + " = ? AND "
+ AbstractTaskModel.CREATION_DATE + " LIKE ?";
String approximateCreationDate = (creationDate / 1000) + "%";
Cursor cursor = database.query(true, TASK_TABLE_NAME, fieldList,
where, new String[] {name, approximateCreationDate}, null, null, null, null);
if (cursor == null)
throw new SQLException("Returned empty set!");
if (cursor.moveToFirst()) {
return cursor;
}
cursor.close();
return null;
}
// --- methods supporting individual features
/** Returns a TaskModelForView corresponding to the given TaskIdentifier */
public int fetchTaskPostponeCount(TaskIdentifier taskId) throws SQLException {
Cursor cursor = fetchTaskCursor(taskId, new String[] {AbstractTaskModel.POSTPONE_COUNT});
try {
if (cursor == null || cursor.getCount() == 0)
return 0;
cursor.moveToFirst();
return cursor.getInt(0);
} catch (Exception e) {
return 0;
} finally {
if(cursor != null)
cursor.close();
}
}
public ArrayList<TaskModelForWidget> getTasksForWidget(String limit) {
Cursor cursor = database.query(TASK_TABLE_NAME, TaskModelForWidget.FIELD_LIST,
AbstractTaskModel.PROGRESS_PERCENTAGE + " < " +
AbstractTaskModel.COMPLETE_PERCENTAGE + " AND (" +
AbstractTaskModel.HIDDEN_UNTIL + " ISNULL OR " + AbstractTaskModel.HIDDEN_UNTIL + " < " +
System.currentTimeMillis() + ")", null, null, null,
AbstractTaskModel.IMPORTANCE + " * " + (5 * 24 * 3600 * 1000L) +
" + CASE WHEN MAX(pdd, ddd) = 0 THEN " +
(System.currentTimeMillis() + (7 * 24 * 3600 * 1000L)) +
" ELSE (CASE WHEN pdd = 0 THEN ddd ELSE pdd END) END ASC", limit);
try {
ArrayList<TaskModelForWidget> list = new ArrayList<TaskModelForWidget>();
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
list.add(new TaskModelForWidget(cursor));
return list;
} finally {
cursor.close();
}
}
public ArrayList<TaskModelForProvider> getTasksForProvider(String limit) {
Cursor cursor = database.query(TASK_TABLE_NAME, TaskModelForWidget.FIELD_LIST,
AbstractTaskModel.PROGRESS_PERCENTAGE + " < " +
AbstractTaskModel.COMPLETE_PERCENTAGE + " AND (" +
AbstractTaskModel.HIDDEN_UNTIL + " ISNULL OR " + AbstractTaskModel.HIDDEN_UNTIL + " < " +
System.currentTimeMillis() + ")", null, null, null,
AbstractTaskModel.IMPORTANCE + " * " + (5 * 24 * 3600 * 1000L) +
" + CASE WHEN MAX(pdd, ddd) = 0 THEN " +
(System.currentTimeMillis() + (7 * 24 * 3600 * 1000L)) +
" ELSE (CASE WHEN pdd = 0 THEN ddd ELSE pdd END) END ASC", limit);
try {
ArrayList<TaskModelForProvider> list = new ArrayList<TaskModelForProvider>();
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
list.add(new TaskModelForProvider(cursor));
return list;
} finally {
cursor.close();
}
}
// --- boilerplate
/**
* Constructor - takes the context to allow the database to be
* opened/created
*/
public TaskController(Context activity) {
this.context = activity;
}
/**
* Open the notes database. If it cannot be opened, try to create a new
* instance of the database. If it cannot be created, throw an exception to
* signal the failure
*
* @return this (self reference, allowing this to be chained in an
* initialization call)
* @throws SQLException if the database could be neither opened or created
*/
@Override
public synchronized void open() throws SQLException {
SQLiteOpenHelper databaseHelper = new TaskModelDatabaseHelper(
context, TASK_TABLE_NAME, TASK_TABLE_NAME);
database = databaseHelper.getWritableDatabase();
}
/** Closes database resource */
@Override
public void close() {
database.close();
}
}

@ -0,0 +1,30 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import com.todoroo.astrid.legacy.data.Identifier;
public class TaskIdentifier extends Identifier {
public TaskIdentifier(long id) {
super(id);
}
}

@ -0,0 +1,208 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import android.database.Cursor;
import com.todoroo.astrid.legacy.data.enums.Importance;
/** Fields that you would want to edit in the TaskModel */
public class TaskModelForEdit extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
NAME,
IMPORTANCE,
ESTIMATED_SECONDS,
ELAPSED_SECONDS,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
HIDDEN_UNTIL,
BLOCKING_ON,
NOTIFICATIONS,
NOTIFICATION_FLAGS,
LAST_NOTIFIED,
PROGRESS_PERCENTAGE,
NOTES,
REPEAT,
CALENDAR_URI,
};
// --- constructors
public TaskModelForEdit() {
super();
setCreationDate(new Date());
}
public TaskModelForEdit(TaskIdentifier identifier, Cursor cursor) {
super(identifier, cursor);
}
// --- getters and setters
@Override
public boolean isTaskCompleted() {
return super.isTaskCompleted();
}
@Override
public Integer getNotificationIntervalSeconds() {
return super.getNotificationIntervalSeconds();
}
@Override
public void setNotificationIntervalSeconds(Integer intervalInSeconds) {
super.setNotificationIntervalSeconds(intervalInSeconds);
}
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
@Override
public Integer getEstimatedSeconds() {
return super.getEstimatedSeconds();
}
@Override
public Integer getElapsedSeconds() {
return super.getElapsedSeconds();
}
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
@Override
public Importance getImportance() {
return super.getImportance();
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getNotes() {
return super.getNotes();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public int getProgressPercentage() {
return super.getProgressPercentage();
}
@Override
public TaskIdentifier getBlockingOn() {
return super.getBlockingOn();
}
@Override
public int getNotificationFlags() {
return super.getNotificationFlags();
}
@Override
public Date getLastNotificationDate() {
return super.getLastNotificationDate();
}
@Override
public RepeatInfo getRepeat() {
return super.getRepeat();
}
@Override
public String getCalendarUri() {
return super.getCalendarUri();
}
@Override
public void setDefiniteDueDate(Date definiteDueDate) {
super.setDefiniteDueDate(definiteDueDate);
}
@Override
public void setEstimatedSeconds(Integer estimatedSeconds) {
super.setEstimatedSeconds(estimatedSeconds);
}
@Override
public void setElapsedSeconds(int elapsedSeconds) {
super.setElapsedSeconds(elapsedSeconds);
}
@Override
public void setHiddenUntil(Date hiddenUntil) {
super.setHiddenUntil(hiddenUntil);
}
@Override
public void setImportance(Importance importance) {
super.setImportance(importance);
}
@Override
public void setName(String name) {
super.setName(name);
}
@Override
public void setNotes(String notes) {
super.setNotes(notes);
}
@Override
public void setPreferredDueDate(Date preferredDueDate) {
super.setPreferredDueDate(preferredDueDate);
}
@Override
public void setBlockingOn(TaskIdentifier blockingOn) {
super.setBlockingOn(blockingOn);
}
@Override
public void setNotificationFlags(int flags) {
super.setNotificationFlags(flags);
}
@Override
public void setRepeat(RepeatInfo taskRepeat) {
super.setRepeat(taskRepeat);
}
@Override
public void setCalendarUri(String uri) {
super.setCalendarUri(uri);
}
}

@ -0,0 +1,168 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.alerts.AlertController;
/** Fields that you would want to read or edit in the onTaskSave and onTaskComplete
* event handlers */
public class TaskModelForHandlers extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
REPEAT,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
HIDDEN_UNTIL,
PROGRESS_PERCENTAGE,
ESTIMATED_SECONDS,
LAST_NOTIFIED,
NOTIFICATIONS,
NOTIFICATION_FLAGS,
FLAGS,
};
/**
* This method updates the task to reflect a new repeat iteration. It moves
* back the due dates and updates other task properties accordingly.
*
* @param context
* @param taskController
* @param repeatInfo
*/
public void repeatTaskBy(Context context, TaskController taskController,
RepeatInfo repeatInfo) {
// move dates back
if(getDefiniteDueDate() != null)
setDefiniteDueDate(repeatInfo.shiftDate(getDefiniteDueDate()));
if(getHiddenUntil() != null)
setHiddenUntil(repeatInfo.shiftDate(getHiddenUntil()));
if(getPreferredDueDate() != null)
setPreferredDueDate(repeatInfo.shiftDate(getPreferredDueDate()));
setProgressPercentage(0);
// set elapsed time to 0... yes, we're losing data
setElapsedSeconds(0);
// if no deadlines set, create one (so users don't get confused)
if(getDefiniteDueDate() == null && getPreferredDueDate() == null)
setPreferredDueDate(repeatInfo.shiftDate(new Date()));
// shift fixed alerts
AlertController alertController = new AlertController(context);
alertController.open();
List<Date> alerts = alertController.getTaskAlerts(getTaskIdentifier());
alertController.removeAlerts(getTaskIdentifier());
for(int i = 0; i < alerts.size(); i++) {
Date newAlert = repeatInfo.shiftDate(alerts.get(i));
alertController.addAlert(getTaskIdentifier(), newAlert);
alerts.set(i, newAlert);
}
// reset periodic alerts
setLastNotificationTime(null);
alertController.close();
}
// --- constructors
public TaskModelForHandlers(Cursor cursor, ContentValues setValues) {
super(cursor);
this.setValues = setValues;
}
// --- getters and setters
@Override
public RepeatInfo getRepeat() {
return super.getRepeat();
}
@Override
public Integer getNotificationIntervalSeconds() {
return super.getNotificationIntervalSeconds();
}
@Override
public boolean isTaskCompleted() {
return super.isTaskCompleted();
}
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
@Override
public Integer getEstimatedSeconds() {
return super.getEstimatedSeconds();
}
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public int getNotificationFlags() {
return super.getNotificationFlags();
}
@Override
public Date getLastNotificationDate() {
return super.getLastNotificationDate();
}
@Override
public int getFlags() {
return super.getFlags();
}
@Override
public void setDefiniteDueDate(Date definiteDueDate) {
super.setDefiniteDueDate(definiteDueDate);
}
@Override
public void setPreferredDueDate(Date preferredDueDate) {
super.setPreferredDueDate(preferredDueDate);
}
@Override
public void setHiddenUntil(Date hiddenUntil) {
super.setHiddenUntil(hiddenUntil);
}
}

@ -0,0 +1,267 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import java.util.HashMap;
import android.content.Context;
import android.database.Cursor;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.enums.Importance;
/** Fields that you would want to edit in the TaskModel */
public class TaskModelForList extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
NAME,
IMPORTANCE,
ELAPSED_SECONDS,
ESTIMATED_SECONDS,
TIMER_START,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
NOTIFICATIONS,
PROGRESS_PERCENTAGE,
COMPLETION_DATE,
CREATION_DATE,
HIDDEN_UNTIL,
NOTES,
REPEAT,
FLAGS,
};
// pre-load the cache for our column keys
static {
HashMap<String, Integer> indexCache = new HashMap<String, Integer>();
columnIndexCache.put(TaskModelForList.class, indexCache);
for(int i = 0; i < FIELD_LIST.length; i++)
indexCache.put(FIELD_LIST[i], i);
}
/** Get the weighted score for this task. Smaller is more important */
public int getTaskWeight() {
int weight = 0;
// bubble tasks with timers to the top
if(getTimerStart() != null)
weight -= 10000;
// importance
weight += getImportance().ordinal() * 80;
// looming absolute deadline
if(getDefiniteDueDate() != null) {
int hoursLeft = (int) ((getDefiniteDueDate().getTime() -
System.currentTimeMillis())/1000/3600);
if(hoursLeft < 5*24)
weight += (hoursLeft - 5*24);
weight -= 20;
}
// looming preferred deadline
if(getPreferredDueDate() != null) {
int hoursLeft = (int) ((getPreferredDueDate().getTime() -
System.currentTimeMillis())/1000/3600);
if(hoursLeft < 5*24)
weight += (hoursLeft - 5*24)/2;
weight -= 10;
}
// bubble completed tasks to the bottom
if(isTaskCompleted()) {
if(getCompletionDate() == null)
weight += 1e6;
else
weight = (int)Math.max(10000 + (System.currentTimeMillis() -
getCompletionDate().getTime()) / 1000, 10000);
return weight;
}
return weight;
}
@Override
public boolean isHidden() {
return super.isHidden();
}
/** map of cached display labels */
private HashMap<Integer, String> displayLabels = new HashMap<Integer, String>();
public String getCachedLabel(int key) {
return displayLabels.get(key);
}
public void putCachedLabel(int key, String value) {
displayLabels.put(key, value);
}
public void clearCache() {
displayLabels.clear();
}
// --- constructors
public TaskModelForList(Cursor cursor) {
super(cursor);
prefetchData(FIELD_LIST);
}
// --- exposed getters and setters
@Override
public boolean isTaskCompleted() {
return super.isTaskCompleted();
}
@Override
public int getTaskColorResource(Context context) {
return super.getTaskColorResource(context);
}
@Override
public Integer getElapsedSeconds() {
return super.getElapsedSeconds();
}
public static int getCompletedPercentage() {
return COMPLETE_PERCENTAGE;
}
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
@Override
public Integer getEstimatedSeconds() {
return super.getEstimatedSeconds();
}
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
@Override
public Importance getImportance() {
return super.getImportance();
}
@Override
public String getName() {
return super.getName();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public int getProgressPercentage() {
return super.getProgressPercentage();
}
@Override
public Date getTimerStart() {
return super.getTimerStart();
}
@Override
public Date getCompletionDate() {
return super.getCompletionDate();
}
@Override
public String getNotes() {
return super.getNotes();
}
@Override
public Integer getNotificationIntervalSeconds() {
return super.getNotificationIntervalSeconds();
}
@Override
public RepeatInfo getRepeat() {
return super.getRepeat();
}
@Override
public Date getCreationDate() {
return super.getCreationDate();
}
@Override
public int getFlags() {
return super.getFlags();
}
// --- setters
@Override
public void setProgressPercentage(int progressPercentage) {
super.setProgressPercentage(progressPercentage);
}
@Override
public void setTimerStart(Date timerStart) {
super.setTimerStart(timerStart);
}
@Override
public void stopTimerAndUpdateElapsedTime() {
super.stopTimerAndUpdateElapsedTime();
}
public static String getNameField() {
return NAME;
}
@Override
public void setPreferredDueDate(Date preferredDueDate) {
super.setPreferredDueDate(preferredDueDate);
}
@Override
public void setDefiniteDueDate(Date definiteDueDate) {
super.setDefiniteDueDate(definiteDueDate);
}
@Override
public void setImportance(Importance importance) {
super.setImportance(importance);
}
@Override
public void setHiddenUntil(Date hiddenUntil) {
super.setHiddenUntil(hiddenUntil);
}
@Override
public void setPostponeCount(int postponeCount) {
super.setPostponeCount(postponeCount);
}
}

@ -0,0 +1,101 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import android.database.Cursor;
import com.todoroo.astrid.legacy.data.AbstractController;
/** Fields that you would want to see in the TaskView activity */
public class TaskModelForNotify extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
ESTIMATED_SECONDS,
NOTIFICATIONS,
NOTIFICATION_FLAGS,
LAST_NOTIFIED,
HIDDEN_UNTIL,
PROGRESS_PERCENTAGE,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
};
// --- constructors
public TaskModelForNotify(Cursor cursor) {
super(cursor);
prefetchData(FIELD_LIST);
}
// --- getters
@Override
public Integer getEstimatedSeconds() {
return super.getEstimatedSeconds();
}
@Override
public boolean isTaskCompleted() {
return super.isTaskCompleted();
}
@Override
public Integer getNotificationIntervalSeconds() {
return super.getNotificationIntervalSeconds();
}
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public int getNotificationFlags() {
return super.getNotificationFlags();
}
@Override
public Date getLastNotificationDate() {
return super.getLastNotificationDate();
}
// --- setters
@Override
public void setLastNotificationTime(Date date) {
super.setLastNotificationTime(date);
}
}

@ -0,0 +1,73 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Francois DESLANDES
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import android.database.Cursor;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.enums.Importance;
/** Fields that you would want to see in the TaskView activity */
public class TaskModelForProvider extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
NAME,
IMPORTANCE,
PREFERRED_DUE_DATE,
DEFINITE_DUE_DATE,
"COALESCE(" + PREFERRED_DUE_DATE + ", 0) as pdd",
"COALESCE(" + DEFINITE_DUE_DATE + ", 0) as ddd"
};
// --- constructors
public TaskModelForProvider(Cursor cursor) {
super(cursor);
prefetchData(FIELD_LIST);
}
// --- getters
@Override
public String getName() {
return super.getName();
}
@Override
public Importance getImportance() {
return super.getImportance();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
}

@ -0,0 +1,83 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import android.database.Cursor;
import com.todoroo.astrid.legacy.data.AbstractController;
/** Fields that you would want to see in the TaskView activity */
public class TaskModelForReminder extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
NAME,
NOTIFICATION_FLAGS,
HIDDEN_UNTIL,
TIMER_START,
PROGRESS_PERCENTAGE,
};
// --- constructors
public TaskModelForReminder(Cursor cursor) {
super(cursor);
prefetchData(FIELD_LIST);
}
// --- getters
@Override
public String getName() {
return super.getName();
}
@Override
public Date getTimerStart() {
return super.getTimerStart();
}
@Override
public boolean isTaskCompleted() {
return super.isTaskCompleted();
}
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
@Override
public int getNotificationFlags() {
return super.getNotificationFlags();
}
// --- setters
@Override
public void setLastNotificationTime(Date date) {
super.setLastNotificationTime(date);
}
}

@ -0,0 +1,236 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import android.database.Cursor;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.enums.Importance;
/** Fields that you would want to synchronize in the TaskModel */
public class TaskModelForSync extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
NAME,
IMPORTANCE,
ESTIMATED_SECONDS,
ELAPSED_SECONDS,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
HIDDEN_UNTIL,
BLOCKING_ON,
PROGRESS_PERCENTAGE,
CREATION_DATE,
COMPLETION_DATE,
NOTES,
REPEAT,
LAST_NOTIFIED,
NOTIFICATIONS,
NOTIFICATION_FLAGS,
FLAGS,
};
// --- constructors
public TaskModelForSync() {
super();
setCreationDate(new Date());
}
public TaskModelForSync(Cursor cursor) {
super(cursor);
prefetchData(FIELD_LIST);
}
// --- getters and setters
@Override
public boolean isTaskCompleted() {
return super.isTaskCompleted();
}
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
@Override
public Integer getEstimatedSeconds() {
return super.getEstimatedSeconds();
}
@Override
public int getProgressPercentage() {
return super.getProgressPercentage();
}
@Override
public Date getCreationDate() {
return super.getCreationDate();
}
@Override
public Date getCompletionDate() {
return super.getCompletionDate();
}
@Override
public Integer getElapsedSeconds() {
return super.getElapsedSeconds();
}
@Override
public Date getHiddenUntil() {
return super.getHiddenUntil();
}
@Override
public Importance getImportance() {
return super.getImportance();
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getNotes() {
return super.getNotes();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public TaskIdentifier getBlockingOn() {
return super.getBlockingOn();
}
@Override
public RepeatInfo getRepeat() {
return super.getRepeat();
}
@Override
public Integer getNotificationIntervalSeconds() {
return super.getNotificationIntervalSeconds();
}
@Override
public int getNotificationFlags() {
return super.getNotificationFlags();
}
@Override
public Date getLastNotificationDate() {
return super.getLastNotificationDate();
}
@Override
public int getFlags() {
return super.getFlags();
}
// --- setters
@Override
public void setDefiniteDueDate(Date definiteDueDate) {
super.setDefiniteDueDate(definiteDueDate);
}
@Override
public void setEstimatedSeconds(Integer estimatedSeconds) {
super.setEstimatedSeconds(estimatedSeconds);
}
@Override
public void setElapsedSeconds(int elapsedSeconds) {
super.setElapsedSeconds(elapsedSeconds);
}
@Override
public void setHiddenUntil(Date hiddenUntil) {
super.setHiddenUntil(hiddenUntil);
}
@Override
public void setImportance(Importance importance) {
super.setImportance(importance);
}
@Override
public void setName(String name) {
super.setName(name);
}
@Override
public void setNotes(String notes) {
super.setNotes(notes);
}
@Override
public void setPreferredDueDate(Date preferredDueDate) {
super.setPreferredDueDate(preferredDueDate);
}
@Override
public void setBlockingOn(TaskIdentifier blockingOn) {
super.setBlockingOn(blockingOn);
}
@Override
public void setRepeat(RepeatInfo taskRepeat) {
super.setRepeat(taskRepeat);
}
@Override
public void setCompletionDate(Date completionDate) {
super.setCompletionDate(completionDate);
}
@Override
public void setCreationDate(Date creationDate) {
super.setCreationDate(creationDate);
}
@Override
public void setProgressPercentage(int progressPercentage) {
super.setProgressPercentage(progressPercentage);
}
@Override
public void setNotificationIntervalSeconds(Integer intervalInSeconds) {
super.setNotificationIntervalSeconds(intervalInSeconds);
}
@Override
public void setFlags(int flags) {
super.setFlags(flags);
}
}

@ -0,0 +1,73 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import android.database.Cursor;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.enums.Importance;
/** Fields that you would want to see in the TaskView activity */
public class TaskModelForWidget extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
NAME,
IMPORTANCE,
PREFERRED_DUE_DATE,
DEFINITE_DUE_DATE,
"COALESCE(" + PREFERRED_DUE_DATE + ", 0) as pdd",
"COALESCE(" + DEFINITE_DUE_DATE + ", 0) as ddd"
};
// --- constructors
public TaskModelForWidget(Cursor cursor) {
super(cursor);
prefetchData(FIELD_LIST);
}
// --- getters
@Override
public String getName() {
return super.getName();
}
@Override
public Importance getImportance() {
return super.getImportance();
}
@Override
public Date getPreferredDueDate() {
return super.getPreferredDueDate();
}
@Override
public Date getDefiniteDueDate() {
return super.getDefiniteDueDate();
}
}

@ -0,0 +1,218 @@
package com.todoroo.astrid.legacy.data.task;
import java.util.Date;
import java.util.HashMap;
import android.database.Cursor;
import android.util.Log;
import com.timsu.astrid.R;
import com.timsu.astrid.utilities.DateUtilities;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.enums.Importance;
import com.todoroo.astrid.legacy.data.enums.RepeatInterval;
public class TaskModelForXml extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
NAME,
IMPORTANCE,
ELAPSED_SECONDS,
ESTIMATED_SECONDS,
TIMER_START,
DEFINITE_DUE_DATE,
PREFERRED_DUE_DATE,
NOTIFICATIONS,
PROGRESS_PERCENTAGE,
COMPLETION_DATE,
CREATION_DATE,
HIDDEN_UNTIL,
NOTES,
REPEAT,
FLAGS,
POSTPONE_COUNT,
BLOCKING_ON,
LAST_NOTIFIED,
NOTIFICATION_FLAGS,
CALENDAR_URI,
};
private HashMap<String, String> taskAttributesMap;
public static final String REPEAT_VALUE = "repeat_value";
public static final String REPEAT_INTERVAL = "repeat_interval";
private RepeatInterval repeatInterval = null;
private Integer repeatValue = null;
// --- constructors
public TaskModelForXml() {
super();
setCreationDate(new Date());
taskAttributesMap = new HashMap<String, String>(FIELD_LIST.length);
}
public TaskModelForXml(Cursor cursor) {
super(cursor);
prefetchData(FIELD_LIST);
taskAttributesMap = new HashMap<String, String>(FIELD_LIST.length);
}
/* Safely add a value from a date field (in case of null values) to the
taskAttributesMap.
*/
private void safePutDate(String field, Date value) {
if (value != null) {
taskAttributesMap.put(field, DateUtilities.getIso8601String(value));
}
}
// --- getters and setters
@Override
public Date getCreationDate() {
return super.getCreationDate();
}
/* Build a HashMap of task fields and associated values.
*/
public HashMap<String, String> getTaskAttributes() {
taskAttributesMap.put(AbstractController.KEY_ROWID, getTaskIdentifier().idAsString());
taskAttributesMap.put(NAME, getName());
taskAttributesMap.put(IMPORTANCE, getImportance().toString());
taskAttributesMap.put(ELAPSED_SECONDS, getElapsedSeconds().toString());
taskAttributesMap.put(ESTIMATED_SECONDS, getEstimatedSeconds().toString());
safePutDate(TIMER_START, getTimerStart());
safePutDate(DEFINITE_DUE_DATE, getDefiniteDueDate());
safePutDate(PREFERRED_DUE_DATE, getPreferredDueDate());
taskAttributesMap.put(NOTIFICATIONS, getNotificationIntervalSeconds().toString());
taskAttributesMap.put(PROGRESS_PERCENTAGE, Integer.toString(getProgressPercentage()));
safePutDate(COMPLETION_DATE, getCompletionDate());
safePutDate(CREATION_DATE, getCreationDate());
safePutDate(HIDDEN_UNTIL, getHiddenUntil());
taskAttributesMap.put(NOTES, getNotes());
RepeatInfo repeat = getRepeat();
if (repeat != null) {
taskAttributesMap.put(REPEAT_VALUE, Integer.toString(repeat.getValue()));
taskAttributesMap.put(REPEAT_INTERVAL, repeat.getInterval().toString());
}
taskAttributesMap.put(FLAGS, Integer.toString(getFlags()));
taskAttributesMap.put(POSTPONE_COUNT, getPostponeCount().toString());
taskAttributesMap.put(BLOCKING_ON, Long.toString(getBlockingOn().getId()));
safePutDate(LAST_NOTIFIED, getLastNotificationDate());
taskAttributesMap.put(NOTIFICATION_FLAGS, Integer.toString(getNotificationFlags()));
String calendarUri = getCalendarUri();
if (calendarUri != null) {
taskAttributesMap.put(CALENDAR_URI, calendarUri);
}
return taskAttributesMap;
}
// --- setters
public boolean setField(String field, String value) {
boolean success = true;
if(field.equals(NAME)) {
setName(value);
}
else if(field.equals(NOTES)) {
setNotes(value);
}
else if(field.equals(PROGRESS_PERCENTAGE)) {
setProgressPercentage(Integer.parseInt(value));
}
else if(field.equals(IMPORTANCE)) {
setImportance(Importance.valueOf(value));
}
else if(field.equals(ESTIMATED_SECONDS)) {
setEstimatedSeconds(Integer.parseInt(value));
}
else if(field.equals(ELAPSED_SECONDS)) {
setElapsedSeconds(Integer.parseInt(value));
}
else if(field.equals(TIMER_START)) {
setTimerStart(DateUtilities.getDateFromIso8601String(value));
}
else if(field.equals(DEFINITE_DUE_DATE)) {
setDefiniteDueDate(DateUtilities.getDateFromIso8601String(value));
}
else if(field.equals(PREFERRED_DUE_DATE)) {
setPreferredDueDate(DateUtilities.getDateFromIso8601String(value));
}
else if(field.equals(HIDDEN_UNTIL)) {
setHiddenUntil(DateUtilities.getDateFromIso8601String(value));
}
else if(field.equals(BLOCKING_ON)) {
setBlockingOn(new TaskIdentifier(Long.parseLong(value)));
}
else if(field.equals(POSTPONE_COUNT)) {
setPostponeCount(Integer.parseInt(value));
}
else if(field.equals(NOTIFICATIONS)) {
setNotificationIntervalSeconds(Integer.parseInt(value));
}
else if(field.equals(CREATION_DATE)) {
setCreationDate(DateUtilities.getDateFromIso8601String(value));
}
else if(field.equals(COMPLETION_DATE)) {
setCompletionDate(DateUtilities.getDateFromIso8601String(value));
}
else if(field.equals(NOTIFICATION_FLAGS)) {
setNotificationFlags(Integer.parseInt(value));
}
else if(field.equals(LAST_NOTIFIED)) {
setLastNotificationTime(DateUtilities.getDateFromIso8601String(value));
}
else if(field.equals(REPEAT_INTERVAL)) {
try {
setRepeatInterval(RepeatInterval.valueOf(value));
} catch (Exception e) {
RepeatInterval repeatInterval;
switch (Integer.parseInt(value)) {
case R.string.repeat_days:
repeatInterval = RepeatInterval.DAYS;
break;
case R.string.repeat_weeks:
repeatInterval = RepeatInterval.WEEKS;
break;
case R.string.repeat_months:
repeatInterval = RepeatInterval.MONTHS;
break;
case R.string.repeat_hours:
repeatInterval = RepeatInterval.HOURS;
break;
default:
Log.e("XmlImport", "Unable to set repeat interval");
repeatInterval = RepeatInterval.DAYS;
break;
}
setRepeatInterval(repeatInterval);
}
}
else if(field.equals(REPEAT_VALUE)) {
setRepeatValue(Integer.parseInt(value));
}
else if(field.equals(FLAGS)) {
setFlags(Integer.parseInt(value));
}
else {
success = false;
}
return success;
}
public void setRepeatInterval(RepeatInterval repeatInterval) {
this.repeatInterval = repeatInterval;
if (repeatValue != null) {
setRepeat(new RepeatInfo(repeatInterval, repeatValue));
}
}
public void setRepeatValue(Integer repeatValue) {
this.repeatValue = repeatValue;
if (repeatInterval != null) {
setRepeat(new RepeatInfo(repeatInterval, repeatValue));
}
}
}

@ -0,0 +1,117 @@
package com.todoroo.astrid.model;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.TreeSet;
import android.content.ContentValues;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.test.utility.DateUtilities;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.test.DatabaseTestCase;
import com.todoroo.astrid.utility.Preferences;
public class TaskTests extends DatabaseTestCase {
@Autowired
TaskService taskService;
/** Sanity-check the constants */
public void testSanity() {
assertTrue(Task.IMPORTANCE_DO_OR_DIE < Task.IMPORTANCE_MUST_DO);
assertTrue(Task.IMPORTANCE_MUST_DO < Task.IMPORTANCE_SHOULD_DO);
assertTrue(Task.IMPORTANCE_SHOULD_DO < Task.IMPORTANCE_NONE);
ArrayList<Integer> urgencies = new ArrayList<Integer>();
urgencies.add(Task.URGENCY_NONE);
urgencies.add(Task.URGENCY_SPECIFIC_DAY);
urgencies.add(Task.URGENCY_SPECIFIC_DAY_TIME);
urgencies.add(Task.URGENCY_THIS_MONTH);
urgencies.add(Task.URGENCY_THIS_WEEK);
urgencies.add(Task.URGENCY_TODAY);
urgencies.add(Task.URGENCY_WITHIN_A_YEAR);
urgencies.add(Task.URGENCY_WITHIN_SIX_MONTHS);
urgencies.add(Task.URGENCY_WITHIN_THREE_MONTHS);
// assert no duplicates
assertEquals(new TreeSet<Integer>(urgencies).size(),
urgencies.size());
}
/** Check defaults */
public void checkDefaults() {
Preferences.setPreferenceDefaults();
ContentValues defaults = new Task().getDefaultValues();
assertTrue(defaults.containsKey(Task.TITLE.name));
assertTrue(defaults.containsKey(Task.DUE_DATE.name));
assertTrue(defaults.containsKey(Task.HIDDEN_UNTIL.name));
assertTrue(defaults.containsKey(Task.COMPLETION_DATE.name));
assertTrue(defaults.containsKey(Task.URGENCY.name));
assertTrue(defaults.containsKey(Task.IMPORTANCE.name));
}
/** Check task gets a creation date at some point */
public void checkCreationDate() {
Task task = new Task();
taskService.save(task, false);
assertTrue(task.getValue(Task.CREATION_DATE) > 0);
}
/**
* Check various getters
*/
public void checkGetters() {
Task task = new Task();
assertFalse(task.isCompleted());
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
assertTrue(task.isCompleted());
task = new Task();
assertFalse(task.isHidden());
task.setValue(Task.HIDDEN_UNTIL, DateUtilities.now() + 1000);
assertTrue(task.isHidden());
task = new Task();
assertFalse(task.hasDueDate());
task.setValue(Task.DUE_DATE, DateUtilities.now() + 1000);
assertTrue(task.hasDueDate());
int[] colors = Task.getImportanceColors(getContext());
assertEquals(Math.abs(Task.IMPORTANCE_NONE - Task.IMPORTANCE_DO_OR_DIE + 1),
colors.length);
HashSet<Integer> set = new HashSet<Integer>();
for(int i = 0; i < colors.length; i++) {
assertFalse(set.contains(colors[i]));
set.add(colors[i]);
}
}
public void checkDueDateInitialization() {
assertEquals(0, Task.initializeDueDate(Task.URGENCY_NONE));
int date = Task.initializeDueDate(Task.URGENCY_THIS_MONTH);
assertTrue(date > DateUtilities.now() + 27 * 24 * 3600);
assertTrue(date < DateUtilities.now() + 32 * 24 * 3600);
date = Task.initializeDueDate(Task.URGENCY_THIS_WEEK);
assertTrue(date > DateUtilities.now() + 6 * 24 * 3600);
assertTrue(date < DateUtilities.now() + 8 * 24 * 3600);
date = Task.initializeDueDate(Task.URGENCY_TODAY);
assertTrue(date > DateUtilities.now() - 60);
assertTrue(date < DateUtilities.now() + 24 * 3600);
date = Task.initializeDueDate(Task.URGENCY_WITHIN_THREE_MONTHS);
assertTrue(date > DateUtilities.now() + 85 * 24 * 3600);
assertTrue(date < DateUtilities.now() + 95 * 24 * 3600);
date = Task.initializeDueDate(Task.URGENCY_WITHIN_SIX_MONTHS);
assertTrue(date > DateUtilities.now() + 180 * 24 * 3600);
assertTrue(date < DateUtilities.now() + 185 * 24 * 3600);
date = Task.initializeDueDate(Task.URGENCY_WITHIN_A_YEAR);
assertTrue(date > DateUtilities.now() + 364 * 24 * 3600);
assertTrue(date < DateUtilities.now() + 367 * 24 * 3600);
}
}

@ -0,0 +1,425 @@
package com.todoroo.astrid.provider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import com.todoroo.astrid.api.AstridContentProvider;
import com.todoroo.astrid.api.AstridContentProvider.AstridTask;
import com.todoroo.astrid.test.DatabaseTestCase;
public class ProviderTests extends DatabaseTestCase {
String[] PROJECTION = new String[] {
AstridTask.ID,
AstridTask.TITLE,
};
/** Test CRUD over tasks with the ALL ITEMS cursor */
public void testAllItemsCrud() {
ContentResolver resolver = getContext().getContentResolver();
// fetch all tasks, get nothing
Uri uri = AstridContentProvider.allItemsUri();
Cursor cursor = resolver.query(uri, PROJECTION, "1", null, null);
assertEquals(0, cursor.getCount());
cursor.close();
// insert a task
ContentValues values = new ContentValues();
values.put(AstridTask.TITLE, "mf doom?");
resolver.insert(uri, values);
// fetch all tasks, get something
cursor = resolver.query(uri, PROJECTION, "1", null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("mf doom?", cursor.getString(1));
cursor.close();
// delete a task
assertEquals(1, resolver.delete(uri, "1", null));
// fetch all tasks, get nothing
cursor = resolver.query(uri, PROJECTION, null, null, null);
assertEquals(0, cursor.getCount());
cursor.close();
}
/** Test selecting data */
public void testSelection() {
ContentResolver resolver = getContext().getContentResolver();
Uri uri = AstridContentProvider.allItemsUri();
// insert some tasks
ContentValues values = new ContentValues();
values.put(AstridTask.TITLE, "tujiko noriko");
values.put(AstridTask.IMPORTANCE, AstridTask.IMPORTANCE_MUST_DO);
resolver.insert(uri, values);
values.clear();
values.put(AstridTask.TITLE, "miho asahi");
values.put(AstridTask.IMPORTANCE, AstridTask.IMPORTANCE_NONE);
resolver.insert(uri, values);
// fetch all tasks with various selection parameters
Cursor cursor = resolver.query(uri, PROJECTION, "1", null, null);
assertEquals(2, cursor.getCount());
cursor.close();
cursor = resolver.query(uri, PROJECTION, null, null, null);
assertEquals(2, cursor.getCount());
cursor.close();
cursor = resolver.query(uri, PROJECTION, AstridTask.IMPORTANCE + "=" +
AstridTask.IMPORTANCE_MUST_DO, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("tujiko noriko", cursor.getString(1));
cursor.close();
cursor = resolver.query(uri, PROJECTION, AstridTask.IMPORTANCE + "<" +
AstridTask.IMPORTANCE_MUST_DO, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("miho asahi", cursor.getString(1));
cursor.close();
cursor = resolver.query(uri, PROJECTION, AstridTask.IMPORTANCE + "=" +
AstridTask.IMPORTANCE_DO_OR_DIE, null, null);
assertEquals(0, cursor.getCount());
cursor.close();
}
/** Test selecting data with metadata */
public void testSelectionWithMetadata() {
ContentResolver resolver = getContext().getContentResolver();
Uri uri = AstridContentProvider.allItemsUri();
// insert some tasks
ContentValues values = new ContentValues();
values.put(AstridTask.TITLE, "turkoglu");
values.put("suave-factor", "10");
resolver.insert(uri, values);
values.clear();
values.put(AstridTask.TITLE, "ichiro");
values.put("suave-factor", "30");
resolver.insert(uri, values);
values.clear();
values.put(AstridTask.TITLE, "oprah");
values.put("suave-factor", "-10");
resolver.insert(uri, values);
values.clear();
values.put(AstridTask.TITLE, "cruise");
values.put("suave-factor", "-10");
resolver.insert(uri, values);
values.clear();
values.put(AstridTask.TITLE, "oprah");
resolver.insert(uri, values);
String[] projection = new String[] {
AstridTask.ID,
AstridTask.TITLE,
"suave-factor"
};
// fetch all tasks with various selection parameters
Cursor cursor = resolver.query(uri, projection, "1", null, null);
assertEquals(3, cursor.getCount());
cursor.close();
cursor = resolver.query(uri, projection, "'suave-factor' = 30", null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("ichiro", cursor.getString(1));
cursor.close();
cursor = resolver.query(uri, projection, "'suave-factor' < 5", null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("cruise", cursor.getString(1));
cursor.close();
cursor = resolver.query(uri, projection, "'suave-factor' ISNULL", null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("oprah", cursor.getString(1));
cursor.close();
}
/** Test updating */
public void testUpdating() {
ContentResolver resolver = getContext().getContentResolver();
Uri allItemsUri = AstridContentProvider.allItemsUri();
// insert some tasks
ContentValues values = new ContentValues();
values.put(AstridTask.TITLE, "carlos silva");
values.put("suckitude", "acute");
Uri carlosUri = resolver.insert(allItemsUri, values);
values.clear();
values.put(AstridTask.TITLE, "felix hernandez");
values.put(AstridTask.URGENCY, AstridTask.URGENCY_WITHIN_A_YEAR);
resolver.insert(allItemsUri, values);
String[] projection = new String[] {
AstridTask.ID,
AstridTask.TITLE,
"suckitude"
};
// test updating with single item URI
Cursor cursor = resolver.query(allItemsUri, projection, "'suckitude' = 'carlos who?'", null, null);
assertEquals(0, cursor.getCount());
cursor.close();
values.clear();
values.put("suckitude", "carlos who?");
assertEquals(1, resolver.update(carlosUri, values, null, null));
cursor = resolver.query(allItemsUri, projection, "'suckitude' = 'carlos who?'", null, null);
assertEquals(1, cursor.getCount());
cursor.close();
// test updating with all items uri
cursor = resolver.query(allItemsUri, PROJECTION, AstridTask.URGENCY + " = " +
AstridTask.URGENCY_NONE, null, null);
assertEquals(0, cursor.getCount());
cursor.close();
values.clear();
values.put(AstridTask.URGENCY, AstridTask.URGENCY_NONE);
assertEquals(1, resolver.update(allItemsUri, values, AstridTask.URGENCY +
"=" + AstridTask.URGENCY_WITHIN_A_YEAR, null));
cursor = resolver.query(allItemsUri, PROJECTION, AstridTask.URGENCY + " = " +
AstridTask.URGENCY_NONE, null, null);
assertEquals(1, cursor.getCount());
cursor.close();
// test updating with group by uri
try {
Uri groupByUri = AstridContentProvider.groupByUri(AstridTask.TITLE);
resolver.update(groupByUri, values, null, null);
fail("Able to update using groupby uri");
} catch (Exception e) {
// expected
}
}
/** Test deleting */
public void testDeleting() {
ContentResolver resolver = getContext().getContentResolver();
Uri allItemsUri = AstridContentProvider.allItemsUri();
// insert some tasks
ContentValues values = new ContentValues();
values.put(AstridTask.TITLE, "modest mouse");
values.put(AstridTask.IMPORTANCE, AstridTask.IMPORTANCE_DO_OR_DIE);
Uri modestMouse = resolver.insert(allItemsUri, values);
values.clear();
values.put(AstridTask.TITLE, "death cab");
values.put(AstridTask.IMPORTANCE, AstridTask.IMPORTANCE_MUST_DO);
resolver.insert(allItemsUri, values);
values.clear();
values.put(AstridTask.TITLE, "murder city devils");
values.put(AstridTask.IMPORTANCE, AstridTask.IMPORTANCE_SHOULD_DO);
resolver.insert(allItemsUri, values);
// test deleting with single URI
Cursor cursor = resolver.query(allItemsUri, PROJECTION, AstridTask.TITLE +
" = 'modest mouse'", null, null);
assertEquals(1, cursor.getCount());
cursor.close();
assertEquals(1, resolver.delete(modestMouse, null, null));
cursor = resolver.query(allItemsUri, PROJECTION, AstridTask.TITLE +
" = 'modest mouse'", null, null);
assertEquals(0, cursor.getCount());
cursor.close();
// test updating with all items uri
cursor = resolver.query(allItemsUri, PROJECTION, AstridTask.TITLE +
" = 'murder city devils'", null, null);
assertEquals(1, cursor.getCount());
cursor.close();
assertEquals(1, resolver.delete(allItemsUri, AstridTask.IMPORTANCE +
"<" + AstridTask.IMPORTANCE_MUST_DO, null));
cursor = resolver.query(allItemsUri, PROJECTION, AstridTask.TITLE +
" = 'murder city devils'", null, null);
assertEquals(0, cursor.getCount());
cursor.close();
// test with group by uri
try {
Uri groupByUri = AstridContentProvider.groupByUri(AstridTask.TITLE);
resolver.delete(groupByUri, null, null);
fail("Able to delete using groupby uri");
} catch (Exception e) {
// expected
}
}
/** Test CRUD over SINGLE ITEM uri */
public void testSingleItemCrud() {
ContentResolver resolver = getContext().getContentResolver();
Uri uri = AstridContentProvider.allItemsUri();
ContentValues values = new ContentValues();
values.put(AstridTask.TITLE, "mf doom?");
Uri firstUri = resolver.insert(uri, values);
values.put(AstridTask.TITLE, "gm grimm!");
Uri secondUri = resolver.insert(uri, values);
assertNotSame(firstUri, secondUri);
Cursor cursor = resolver.query(uri, PROJECTION, "1", null, null);
assertEquals(2, cursor.getCount());
cursor.close();
cursor = resolver.query(firstUri, PROJECTION, null, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("mf doom?", cursor.getString(1));
cursor.close();
values.put(AstridTask.TITLE, "danger mouse.");
resolver.update(firstUri, values, null, null);
cursor = resolver.query(firstUri, PROJECTION, null, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToFirst();
assertEquals("danger mouse.", cursor.getString(1));
cursor.close();
assertEquals(1, resolver.delete(firstUri, null, null));
cursor = resolver.query(uri, PROJECTION, null, null, null);
assertEquals(1, cursor.getCount());
cursor.close();
}
/** Test GROUP BY uri */
public void testGroupByCrud() {
ContentResolver resolver = getContext().getContentResolver();
Uri uri = AstridContentProvider.allItemsUri();
ContentValues values = new ContentValues();
values.put(AstridTask.TITLE, "catwoman");
resolver.insert(uri, values);
values.put(AstridTask.TITLE, "the joker");
resolver.insert(uri, values);
resolver.insert(uri, values);
resolver.insert(uri, values);
values.put(AstridTask.TITLE, "deep freeze");
resolver.insert(uri, values);
resolver.insert(uri, values);
Uri groupByUri = AstridContentProvider.groupByUri(AstridTask.TITLE);
Cursor cursor = resolver.query(groupByUri, PROJECTION, null, null, AstridTask.TITLE);
assertEquals(3, cursor.getCount());
cursor.moveToFirst();
assertEquals("catwoman", cursor.getString(1));
cursor.moveToNext();
assertEquals("deep freeze", cursor.getString(1));
cursor.moveToNext();
assertEquals("the joker", cursor.getString(1));
cursor.close();
// test "group-by" with metadata
values.put("age", 50);
values.put("size", "large");
resolver.insert(uri, values);
values.put("age", 40);
values.put("size", "large");
resolver.insert(uri, values);
values.put("age", 20);
values.put("size", "small");
resolver.insert(uri, values);
Uri groupByAgeUri = AstridContentProvider.groupByUri("size");
cursor = resolver.query(groupByUri, PROJECTION, null, null, AstridTask.TITLE);
assertEquals(3, cursor.getCount());
}
/** Test updating and deleting with metadata */
public void testMetadataUpdateDelete() {
ContentResolver resolver = getContext().getContentResolver();
Uri allItemsUri = AstridContentProvider.allItemsUri();
// insert some tasks
ContentValues values = new ContentValues();
values.put(AstridTask.TITLE, "chicago");
values.put("pizza", "delicious");
values.put("temperature", 20);
resolver.insert(allItemsUri, values);
values.clear();
values.put(AstridTask.TITLE, "san francisco");
values.put("pizza", "meh");
values.put("temperature", 60);
resolver.insert(allItemsUri, values);
values.clear();
values.put(AstridTask.TITLE, "new york");
values.put("pizza", "yum");
values.put("temperature", 30);
resolver.insert(allItemsUri, values);
// test updating with standard URI (shouldn't work)
values.clear();
values.put("pizza", "nonexistent, the city is underwater");
assertEquals(0, resolver.update(allItemsUri, values,
"'pizza'='yum'", null));
String[] projection = new String[] {
AstridTask.ID,
AstridTask.TITLE,
"pizza",
"temperature"
};
Cursor cursor = resolver.query(allItemsUri, projection, "'pizza' = 'yum'", null, null);
assertEquals(1, cursor.getCount());
cursor.close();
// test updating with metadata uri
Uri pizzaUri = AstridContentProvider.allItemsWithMetadataUri(new String[] {"pizza"});
assertEquals(1, resolver.update(pizzaUri, values,
"'pizza'='yum'", null));
cursor = resolver.query(allItemsUri, projection, "'pizza' = 'yum'", null, null);
assertEquals(0, cursor.getCount());
cursor.close();
// test deleting with metadata uri
Uri pizzaTempUri = AstridContentProvider.allItemsWithMetadataUri(new String[] {"pizza",
"temperature"});
cursor = resolver.query(allItemsUri, projection, AstridTask.TITLE + " = 'chicago'", null, null);
assertEquals(0, cursor.getCount());
cursor.close();
// SQLITE: the deliverer of BAD NEWS
assertEquals(0, resolver.delete(pizzaTempUri, "pizza='delicious' AND temperature > 50", null));
assertEquals(1, resolver.delete(pizzaTempUri, "pizza='delicious' AND temperature < 50", null));
cursor = resolver.query(allItemsUri, projection, AstridTask.TITLE + " = 'chicago'", null, null);
assertEquals(0, cursor.getCount());
cursor.close();
}
}

@ -0,0 +1,90 @@
package com.todoroo.astrid.service;
import android.test.AndroidTestCase;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.AbstractDependencyInjector;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DialogUtilities;
public class AstridDependencyInjectorTests extends AndroidTestCase {
protected static class Helper {
public Object getObject() {
return null;
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
// in case some state from other unit tests overwrote injector
DependencyInjectionService.getInstance().setInjectors(new AbstractDependencyInjector[] {
new AstridDependencyInjector()
});
}
public void testWithString() {
Helper helper = new Helper() {
@Autowired
public String applicationName;
@Override
public Object getObject() {
return applicationName;
};
};
DependencyInjectionService.getInstance().inject(helper);
assertTrue(((String)helper.getObject()).length() > 0);
}
public void testWithInteger() {
Helper helper = new Helper() {
@Autowired
public Integer informationDialogTitleResource;
@Override
public Object getObject() {
return informationDialogTitleResource;
};
};
DependencyInjectionService.getInstance().inject(helper);
assertEquals(R.string.DLG_information_title, helper.getObject());
}
public void testWithClass() {
Helper helper = new Helper() {
@Autowired
public DialogUtilities dialogUtilities;
@Override
public Object getObject() {
return dialogUtilities;
};
};
DependencyInjectionService.getInstance().inject(helper);
assertTrue(helper.getObject() instanceof DialogUtilities);
Helper helper2 = new Helper() {
@Autowired
public DialogUtilities dialogUtilities;
@Override
public Object getObject() {
return dialogUtilities;
};
};
DependencyInjectionService.getInstance().inject(helper2);
assertEquals(helper.getObject(), helper2.getObject());
}
}

@ -0,0 +1,139 @@
package com.todoroo.astrid.service;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.test.data.TodorooCursor;
import com.todoroo.andlib.test.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.test.DatabaseTestCase;
public class TaskServiceTests extends DatabaseTestCase {
@Autowired
TaskService taskService;
@Autowired
TaskDao taskDao;
/**
* Since a lot of the service-layer methods call through to the dao,
* we don't need to do a lot of extensive testing here, just various
* boundary conditions.
*/
public void testTaskDaoExposedMethods() {
Task task = new Task();
task.setValue(Task.TITLE, "normal");
assertTrue(taskService.save(task, false));
// empty fetch w/ filter
Filter filter = new Filter("bla", "bla", "WHERE 1", null);
TodorooCursor<Task> cursor =
taskService.fetchFiltered(TaskService.idProperties(), filter);
assertEquals(1, cursor.getCount());
cursor.close();
filter.sqlQuery = "WHERE " + Task.TITLE + " = \"bob\"";
cursor = taskService.fetchFiltered(TaskService.idProperties(), filter);
assertEquals(0, cursor.getCount());
cursor.close();
Task fetched = taskService.fetchById(TaskService.idProperties(), task.getId());
assertNotNull(fetched);
fetched = taskService.fetchById(TaskService.idProperties(), task.getId() + 1);
assertNull(fetched);
taskService.setComplete(task, true);
assertTrue(task.isCompleted());
fetched = taskService.fetchById(Task.PROPERTIES, task.getId());
assertTrue(fetched.isCompleted());
taskService.setComplete(task, false);
fetched = taskService.fetchById(Task.PROPERTIES, task.getId());
assertFalse(fetched.isCompleted());
assertFalse(task.isCompleted());
long id = task.getId();
taskService.delete(id);
fetched = taskService.fetchById(Task.PROPERTIES, id);
assertNull(fetched);
}
/**
* Test cleanup
*/
public void testCleanup() throws Exception {
TodorooCursor<Task> cursor;
// save task with a name
Task task1 = new Task();
task1.setValue(Task.TITLE, "normal");
assertTrue(taskService.save(task1, false));
// save task without a name
Task task2 = new Task();
task2.setValue(Task.TITLE, "");
task2.setValue(Task.HIDDEN_UNTIL, DateUtilities.now() + 10000);
assertTrue(taskService.save(task2, false));
// save task #2 without a name
Task task3 = new Task();
task3.setValue(Task.TITLE, "");
task3.setValue(Task.DUE_DATE, DateUtilities.now() + 10000);
assertTrue(taskService.save(task3, false));
cursor = taskDao.fetch(database, Task.PROPERTIES, null, null);
assertEquals(3, cursor.getCount());
cursor.close();
taskService.cleanup();
cursor = taskDao.fetch(database, Task.PROPERTIES, null, null);
assertEquals(1, cursor.getCount());
cursor.close();
}
/**
* Test the sql invoked from a filter on new task creation
*/
public void testInvokeNewTaskSql() {
TodorooCursor<Task> cursor;
Filter filter = new Filter("bla", "bla", "WHERE 1", null);
Task task = new Task();
task.setValue(Task.TITLE, "evils");
assertTrue(taskService.save(task, false));
cursor = taskDao.fetch(database, Task.PROPERTIES, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
task = new Task(cursor, Task.PROPERTIES);
assertEquals("evils", task.getValue(Task.TITLE));
cursor.close();
filter.sqlForNewTasks = String.format("UPDATE %s set %s = \"good\"",
Database.TASK_TABLE, Task.TITLE);
taskService.invokeSqlForNewTask(filter, task);
cursor = taskDao.fetch(database, Task.PROPERTIES, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
task = new Task(cursor, Task.PROPERTIES);
assertEquals("good", task.getValue(Task.TITLE));
cursor.close();
filter.sqlForNewTasks = String.format("UPDATE %s set %s = \"yum\" WHERE %s = $ID",
Database.TASK_TABLE, Task.TITLE, Task.ID);
taskService.invokeSqlForNewTask(filter, task);
cursor = taskDao.fetch(database, Task.PROPERTIES, null, null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
task = new Task(cursor, Task.PROPERTIES);
assertEquals("yum", task.getValue(Task.TITLE));
cursor.close();
}
}

@ -0,0 +1,96 @@
package com.todoroo.astrid.test;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.test.ActivityInstrumentationTestCase2;
import android.view.MotionEvent;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.TestDependencyInjector;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.test.DatabaseTestCase.AstridTestDatabase;
/**
* ActivityTestCase is a helper for testing Todoroo Activities.
* <p>
* It initializes a test database before the activity is created.
*
* @author Tim Su <tim@todoroo.com>
*
* @param <T>
*/
public class AstridActivityTestCase<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
@Autowired
public Database database;
static {
AstridDependencyInjector.initialize();
TestDependencyInjector.initialize("db").addInjectable("database",
new AstridTestDatabase());
}
public AstridActivityTestCase(String packageName, Class<T> activityClass) {
super(packageName, activityClass);
DependencyInjectionService.getInstance().inject(this);
}
@Override
protected void setUp() throws Exception {
super.setUp();
// create new test database
AstridTestDatabase.dropTables(getInstrumentation().getTargetContext());
database.openForWriting();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
if(database != null)
database.close();
}
/**
* Call to just tear this activity down
*/
protected void tearActivityDown() throws Exception {
super.tearDown();
}
/**
* Call to just set this activity up
*/
protected void setActivityUp() throws Exception {
super.setUp();
}
/** Calls various lifecycle methods, makes sure all of them work */
public void testLifecycle() throws Exception {
T activity = getActivity();
getInstrumentation().callActivityOnPause(activity);
Thread.sleep(500);
getInstrumentation().callActivityOnResume(activity);
getInstrumentation().sendPointerSync(MotionEvent.obtain(500, 500, MotionEvent.ACTION_DOWN, 10, 30, 0));
getInstrumentation().callActivityOnPause(activity);
getInstrumentation().callActivityOnStop(activity);
Thread.sleep(500);
getInstrumentation().callActivityOnRestart(activity);
getInstrumentation().callActivityOnStart(activity);
getInstrumentation().callActivityOnResume(activity);
getInstrumentation().sendPointerSync(MotionEvent.obtain(500, 500, MotionEvent.ACTION_DOWN, 10, 30, 0));
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Thread.sleep(500);
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Thread.sleep(1000);
}
}

@ -0,0 +1,70 @@
package com.todoroo.astrid.test;
import android.content.Context;
import android.database.sqlite.SQLiteOpenHelper;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.TestDependencyInjector;
import com.todoroo.andlib.test.TodorooTestCase;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.Database.AstridSQLiteOpenHelper;
import com.todoroo.astrid.service.AstridDependencyInjector;
/**
* Test case that automatically sets up and tears down a test database
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class DatabaseTestCase extends TodorooTestCase {
@Autowired
public Database database;
static {
AstridDependencyInjector.initialize();
TestDependencyInjector.initialize("db").addInjectable("database",
new AstridTestDatabase());
}
@Override
protected void setUp() throws Exception {
// create new test database
AstridTestDatabase.dropTables(getContext());
database.openForWriting();
super.setUp();
}
@Override
protected void tearDown() throws Exception {
database.close();
}
public static class AstridTestDatabase extends Database {
private static final String NAME = "todoroo-test";
@Override
public synchronized void openForWriting() {
if(helper == null)
helper = new AstridSQLiteOpenHelper(context, NAME, null, VERSION);
super.openForWriting();
}
@Override
public synchronized void openForReading() {
if(helper == null)
helper = new AstridSQLiteOpenHelper(context, NAME, null, VERSION);
super.openForWriting();
}
public static void dropTables(Context context) {
// force drop database
SQLiteOpenHelper helper = new AstridSQLiteOpenHelper(context, NAME, null, VERSION);
helper.onUpgrade(helper.getWritableDatabase(),
0, VERSION);
helper.close();
}
}
}

@ -0,0 +1,7 @@
package com.todoroo.astrid.upgrade;
import com.todoroo.astrid.test.DatabaseTestCase;
public class Astrid2To3UpgradeTests extends DatabaseTestCase {
}
Loading…
Cancel
Save