diff --git a/api/.classpath b/api/.classpath
new file mode 100644
index 0000000000..52ae72c838
--- /dev/null
+++ b/api/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/api/.gitignore b/api/.gitignore
new file mode 100644
index 0000000000..b5c234f8ed
--- /dev/null
+++ b/api/.gitignore
@@ -0,0 +1,8 @@
+*~
+.metadata
+*.class
+org.eclipse.ltk.core.refactoring.prefs
+gen
+local.properties
+ecbuild
+
diff --git a/api/.project b/api/.project
new file mode 100644
index 0000000000..3ce3a857b6
--- /dev/null
+++ b/api/.project
@@ -0,0 +1,33 @@
+
+
+ astridApi
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/api/.settings/org.eclipse.jdt.core.prefs b/api/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..070dbff7de
--- /dev/null
+++ b/api/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,332 @@
+#Wed Jul 07 18:43:41 PDT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/api/.settings/org.eclipse.jdt.ui.prefs b/api/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..ace644cbc6
--- /dev/null
+++ b/api/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,109 @@
+#Tue Jun 29 14:53:46 PDT 2010
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=true
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=true
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=true
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=_Astrid
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Astrid
+formatter_settings_version=11
+org.eclipse.jdt.ui.text.custom_code_templates=
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=true
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/api/AndroidManifest.xml b/api/AndroidManifest.xml
new file mode 100644
index 0000000000..2fd098d1a2
--- /dev/null
+++ b/api/AndroidManifest.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/api/LICENSE b/api/LICENSE
new file mode 100644
index 0000000000..c0c5ea9335
--- /dev/null
+++ b/api/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2010, Todoroo, Inc
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/api/README.md b/api/README.md
new file mode 100644
index 0000000000..a732c92679
--- /dev/null
+++ b/api/README.md
@@ -0,0 +1,35 @@
+Astrid API Library - libraries for writing add-ons to [Astrid](http://www.weloveastrid.com/) -
+================================
+
+This code is licensed under the New BSD License (see LICENSE)
+
+Note that this is a beta release of the API - things may (and probably will) change from now until the official release. Documentation is also being written.
+
+If you are planning on using this project, make sure to watch it for changes. Your feedback is also appreciated.
+
+Getting Started With Development
+---------------
+
+1. Install the following:
+ • *[git](http://git.or.cz/)*
+ • *[Eclipse](http://eclipse.org)* (preferred: Eclipse IDE for Java Developers)
+ • *[Android SDK](http://developer.android.com/sdk/index.html)* - version 0.9.7 of Eclipse ADT is required
+
+2. Use **git** to clone this repository (see Github's instructions if you need help).
+
+2b. mkdir libs (in case your Android SDK is [not up to date](http://comments.gmane.org/gmane.comp.handhelds.android.devel/101722))
+
+3. Open up **eclipse** and import the *astridApi* project.
+
+4. If you are creating a new add-on for Astrid, create a new project in **eclipse**
+ • in the Android tab of the project, indicate astridApi as a library reference
+
+5. Check out the [wiki](http://wiki.github.com/todoroo/astridApi) and [javadoc](http://todoroo.github.com/astridApi)
+
+Contact
+-------
+For support requests, use the Astrid issue tracker. For development questions, contact [timsu](http://github.com/timsu) via e-mail.
+
+Astrid also has an IRC channel, irc.freenode.net #astrid
+
+
diff --git a/api/build.xml b/api/build.xml
new file mode 100644
index 0000000000..e471fa5027
--- /dev/null
+++ b/api/build.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/default.properties b/api/default.properties
new file mode 100644
index 0000000000..2262a388ea
--- /dev/null
+++ b/api/default.properties
@@ -0,0 +1,12 @@
+# 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.
+
+android.library=true
+# Project target.
+target=android-3
diff --git a/api/res/.gitignore b/api/res/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/api/res/layout/status_preference.xml b/api/res/layout/status_preference.xml
new file mode 100644
index 0000000000..65bf255aab
--- /dev/null
+++ b/api/res/layout/status_preference.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/res/values-ca/strings.xml b/api/res/values-ca/strings.xml
new file mode 100644
index 0000000000..627fbec465
--- /dev/null
+++ b/api/res/values-ca/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 Any
+
+ %d Anys
+
+
+
+ 1 Mes
+
+ %d Mesos
+
+
+
+ 1 Setmana
+
+ %d Setmanes
+
+
+
+ 1 dia
+
+ %d Dies
+
+
+
+ 1 Hora
+
+ %d Hores
+
+
+
+ 1 Minut
+
+ %d Minuts
+
+
+
+ 1 Segon
+
+ %d Segons
+
+
+
+ 1 Hr
+
+ %d Hrs
+
+
+
+ 1 Min
+
+ %d Min
+
+
+
+ 1 Seg
+
+ %d Seg
+
+
+
+ 1 tasca
+
+ %d tasques
+
+
+
+
+
+ Confirmar?
+
+
+ Pregunta:
+
+
+ Informació
+
+
+ Error!
+
+
+ Sí
+
+
+ No
+
+
+ Tancar
+
+
+ Fet
+
+
+ ¡Ui, sembla que hi ha hagut un problema! Això es el que ha passat:\n\n%s
+
+
+ ¡Ui, sembla que hi ha hagut un problema!
+
+
+ Si us plau, espera...
+
+
+
+
+ Sincronitzant les seves tasques...
+
+
+ Sincronitzant...
+
+
+ Sincronització
+
+
+ Error de conexió! Verifiqui la conexió d\'internet.
+
+
+
+
+ Estat
+
+
+ No conectat!
+
+ Sincronització en curs...
+
+ Última sincronització: %s
+
+ Fallida el: %s
+
+ Última sincronització correcte: %s
+
+ Mai sincronitzat!
+
+
+ Opcions
+
+
+ Sincronitzar en segon pla
+
+ Desactivada la sincronització en segon pla
+
+ Actualment configurat en: %s
+
+
+ Configuració Wifi
+
+ La sincronització en segon pla només funciona amb el Wifi activat.
+
+ Sempre es produirà la sincronització en segon pla
+
+
+ Accions
+
+
+ Sincronitzar Ara!
+
+ Ingressar & Sincronitzar!
+
+
+ Surt
+
+ Esborra tota la informació de sincronització
+
+
+ Tancar sessió / esborra la informació de sincronització?
+
+
+
+ desactivat
+ cada quince minuts
+ cada trenta minuts
+ cada hora
+ cada tres hores
+ cada sis hores
+ cada dotze hores
+ diàriament
+ cada tres dies
+ setmanalment
+
+
+
+
diff --git a/api/res/values-cs/strings.xml b/api/res/values-cs/strings.xml
new file mode 100644
index 0000000000..6e0f9ad473
--- /dev/null
+++ b/api/res/values-cs/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 rok
+
+ %d Roky
+
+
+
+ 1 měsíc
+
+ %d Měsíce
+
+
+
+ 1 týden
+
+ %d Týdny
+
+
+
+ 1 den
+
+ %d Dnů
+
+
+
+ 1 hodina
+
+ %d hodin
+
+
+
+ 1 minuta
+
+ %d minut
+
+
+
+ 1 vteřina
+
+ %d vteřin
+
+
+
+ 1 hod.
+
+ %d hod.
+
+
+
+ 1 min.
+
+ %d min.
+
+
+
+ 1 s
+
+ %d s
+
+
+
+ 1 úkol
+
+ %d úkolů
+
+
+
+
+
+ Potvrdit?
+
+
+ Otázka:
+
+
+ Informace
+
+
+ Error!
+
+
+ Ano
+
+
+ Ne
+
+
+ Zavřít
+
+
+ Hotovo
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ Prosím čekejte...
+
+
+
+
+ Probíhá synchronizace Vašich úkolů...
+
+
+ Sychronizuji...
+
+
+ Synchronizace
+
+
+ Connection Error! Check your Internet connection.
+
+
+
+
+ Stav
+
+
+ Not Logged In!
+
+ Probíhá synchronizace...
+
+ Poslední synchronizace: %s
+
+ Selhalo: %s
+
+ Poslední úspěšná synchronizace: %s
+
+ Nikdo nesynchronizováno!
+
+
+ Možnosti
+
+
+ Synchronizace na pozadí
+
+ Synchronizace na pozadí je zakázána
+
+ Současně nastaveno na: %s
+
+
+ Nastavení jen pro Wifi
+
+ Synchronizovat na pozadí se bude pouze při zapnuté Wifi
+
+ Synchronizovat na pozadí se bude vždy
+
+
+ Činnosti
+
+
+ Synchronizuj teď!
+
+ Přihlásit se & Synchronizovat!
+
+
+ Odhlásit se
+
+ Clears all synchronization data
+
+
+ Odhlásit se / vymazat synchronizační data?
+
+
+
+ zakázat
+ každých patnáct minut
+ každých třicet minut
+ každou hodinu
+ každé tři hodiny
+ každých šest hodin
+ každých dvanáct hodin
+ každý den
+ každé tři dny
+ každý týden
+
+
+
+
diff --git a/api/res/values-de/strings.xml b/api/res/values-de/strings.xml
new file mode 100644
index 0000000000..a765d70fbd
--- /dev/null
+++ b/api/res/values-de/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ Ein Jahr
+
+ %d Jahre
+
+
+
+ Ein Monat
+
+ %d Monate
+
+
+
+ Eine Woche
+
+ %d Wochen
+
+
+
+ 1 Tag
+
+ %d Tage
+
+
+
+ 1 Stunde
+
+ %d Stunden
+
+
+
+ 1 Minute
+
+ %d Minuten
+
+
+
+ 1 Sekunde
+
+ %d Sekunden
+
+
+
+ 1 Std
+
+ %d Std
+
+
+
+ 1 Min
+
+ %d Min
+
+
+
+ 1 Sek
+
+ %d Sek
+
+
+
+ 1 Aufgabe
+
+ %d Aufgaben
+
+
+
+
+
+ Bestätigen
+
+
+ Frage:
+
+
+ Information
+
+
+ Fehler!
+
+
+ Ja
+
+
+ Nein
+
+
+ Schließen
+
+
+ Erledigt
+
+
+ Ups, sieht aus, als ob ein Fehler passiert ist! Hier, was passiert ist:\n\n%s
+
+
+ Ups, sieht aus, als ob ein Fehler passiert ist!
+
+
+ Bitte warten...
+
+
+
+
+ Synchronisiere deine Aufgaben
+
+
+ Synchronisiere…
+
+
+ Abgleich
+
+
+ Verbindungsfehler! Überprüfen Sie Ihre Internetverbindung.
+
+
+
+
+ Status
+
+
+ Nicht angemeldet!
+
+ Synchronisierung läuft...
+
+ Letzte Synchronisierung: %s
+
+ Fehlgeschlagen am: %s
+
+ Letzte erfolgreiche Synchronisierung: %s
+
+ Noch nie synchronisiert!
+
+
+ Einstellungen
+
+
+ Hintergrund-Synchronisierung
+
+ Hintergrund-Synchronisierung ist deaktiviert
+
+ Gesetzt auf: %s
+
+
+ WLAN Einstellungen
+
+ Hintergrund-Synchronisierung nur bei WLAN-Verbindung
+
+ Hintergrund-Synchronisierung findet immer statt
+
+
+ Aktionen
+
+
+ Jetzt abgleichen!
+
+ Einloggen & Synchroniseren!
+
+
+ Abmelden
+
+ Alle Synchronisationsdaten löschen
+
+
+ Ausloggen / synchronisierte Daten löschen?
+
+
+
+ deaktivieren
+ alle 15 Minuten
+ alle 30 Minuten
+ stündlich
+ alle 3 Stunden
+ alle 6 Stunden
+ alle 12 Stunden
+ täglich
+ jeden dritten Tag
+ wöchentlich
+
+
+
+
diff --git a/api/res/values-es/strings.xml b/api/res/values-es/strings.xml
new file mode 100644
index 0000000000..ee8985b981
--- /dev/null
+++ b/api/res/values-es/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 año
+
+ %d años
+
+
+
+ 1 Mes
+
+ %d meses
+
+
+
+ 1 semana
+
+ %d semanas
+
+
+
+ 1 día
+
+ %d días
+
+
+
+ 1 hora
+
+ %d horas
+
+
+
+ 1 minuto
+
+ %d minutos
+
+
+
+ 1 segundo
+
+ %d segundos
+
+
+
+ 1 hora
+
+ %d horas
+
+
+
+ 1 minuto
+
+ %d minutos
+
+
+
+ 1 segundo
+
+ %d segundos
+
+
+
+ 1 tarea
+
+ %d tareas
+
+
+
+
+
+ ¿Confirmar?
+
+
+ Pregunta:
+
+
+ Información
+
+
+ ¡Error!
+
+
+ Sí
+
+
+ No
+
+
+ Cerrar
+
+
+ Listo
+
+
+ ¡Uy, al parecer hay algún problema! Esto es lo que pasó:\n\n%s
+
+
+ ¡Uy, al parecer hay algún problema!
+
+
+ Espere por favor...
+
+
+
+
+ Sincronizando sus tareas...
+
+
+ Sincronizando...
+
+
+ Sincronización
+
+
+ Error de conexión! Verifique su conexión a internet.
+
+
+
+
+ Estado
+
+
+ No conectado!
+
+ Sincronización en curso...
+
+ Última sincronización: %s
+
+ Falló el: %s
+
+ Última sincronización exitosa: %s
+
+ ¡Jamás se sincronizó!
+
+
+ Opciones
+
+
+ Sincronizar en segundo plano
+
+ Sincronización en segundo plano desactivada
+
+ Actualmente configurado para: %s
+
+
+ Sólo Configuración Wifi
+
+ La sincronización en segundo plano sólo funciona con el Wifi activado
+
+ La sincronización en segundo plano funciona siempre
+
+
+ Acciones
+
+
+ ¡Sincronizar ahora!
+
+ Iniciar sesión y sincronizar!
+
+
+ Cerrar sesión
+
+ Borra todos los datos de sincronización
+
+
+ Cierre de sesión / cancelar la sincronización de datos?
+
+
+
+ desactivar
+ cada quince minutos
+ cada treinta minutos
+ cada hora
+ cada tres horas
+ cada seis horas
+ cada doce horas
+ cada día
+ cada tres días
+ cada semana
+
+
+
+
diff --git a/api/res/values-fr/strings.xml b/api/res/values-fr/strings.xml
new file mode 100644
index 0000000000..9541fb4665
--- /dev/null
+++ b/api/res/values-fr/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 année
+
+ %d années
+
+
+
+ 1 mois
+
+ %d mois
+
+
+
+ 1 semaine
+
+ %d semaines
+
+
+
+ 1 jour
+
+ %d jours
+
+
+
+ 1 heure
+
+ %d heures
+
+
+
+ 1 minute
+
+ %d minutes
+
+
+
+ 1 seconde
+
+ %d secondes
+
+
+
+ 1 h
+
+ %d h
+
+
+
+ 1 min
+
+ %d min
+
+
+
+ 1 s
+
+ %d s
+
+
+
+ 1 tâche
+
+ %d tâches
+
+
+
+
+
+ Confirmer ?
+
+
+ Question :
+
+
+ Information
+
+
+ Erreur !
+
+
+ Oui
+
+
+ Non
+
+
+ Fermer
+
+
+ Terminé
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ Veuillez patienter...
+
+
+
+
+ Synchronisation de vos tâches...
+
+
+ Synchronisation...
+
+
+ Synchronisation
+
+
+ Connection Error! Check your Internet connection.
+
+
+
+
+ Statut
+
+
+ Not Logged In!
+
+ Synchronisation en cours...
+
+ Dernière synchro. : %s
+
+ Échec sur : %s
+
+ Dernière synchro. réussie : %s
+
+ Jamais synchronisé !
+
+
+ Options
+
+
+ Synchro. en arrière-plan
+
+ Synchronisation en arrière-plan désactivée
+
+ Actuellement configuré sur : %s
+
+
+ Paramètre Wifi seul
+
+ La synchronisation en arrière-plan ne s\'effectue uniquement sous Wifi
+
+ La synchronisation en arrière-plan s\'effectuera toujours
+
+
+ Actions
+
+
+ Synchroniser maintenant !
+
+ Se connecter et synchroniser !
+
+
+ Se déconnecter
+
+ Clears all synchronization data
+
+
+ Se déconnecter/purger les données de synchronisation ?
+
+
+
+ désactiver
+ toutes les quinze minutes
+ toutes les trente minutes
+ toutes les heures
+ toutes les trois heures
+ toutes les six heures
+ toutes les douze heures
+ tous les jours
+ tous les trois jours
+ toutes les semaines
+
+
+
+
diff --git a/api/res/values-id/strings.xml b/api/res/values-id/strings.xml
new file mode 100644
index 0000000000..e689e1fbf4
--- /dev/null
+++ b/api/res/values-id/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 Year
+
+ %d Years
+
+
+
+ 1 Month
+
+ %d Months
+
+
+
+ 1 Week
+
+ %d Weeks
+
+
+
+ 1 Hari
+
+ %d Hari
+
+
+
+ 1 Jam
+
+ %d Jam
+
+
+
+ 1 Menit
+
+ %d Menit
+
+
+
+ 1 Detik
+
+ %d Detik
+
+
+
+ 1 Jam
+
+ %d Jam
+
+
+
+ 1 Mnt
+
+ %d Mnt
+
+
+
+ 1 Dtk
+
+ %d Dtk
+
+
+
+ 1 task
+
+ %d tasks
+
+
+
+
+
+ Confirm?
+
+
+ Question:
+
+
+ Informasi
+
+
+ Error!
+
+
+ Yes
+
+
+ No
+
+
+ Close
+
+
+ Selesai
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ Please wait...
+
+
+
+
+ Synchronizing your tasks...
+
+
+ Synchronizing...
+
+
+ Sinkronisasi
+
+
+ Connection Error! Check your Internet connection.
+
+
+
+
+ Status
+
+
+ Not Logged In!
+
+ Sync Ongoing...
+
+ Last Sync: %s
+
+ Failed On: %s
+
+ Last Successful Sync: %s
+
+ Never Synchronized!
+
+
+ Pilihan
+
+
+ Background Sync
+
+ Background synchronization is disabled
+
+ Currently set to: %s
+
+
+ Wifi Only Setting
+
+ Background synchronization only happens when on Wifi
+
+ Background synchronization will always occur
+
+
+ Aksi
+
+
+ Sinkronkan Sekarang!
+
+ Log In & Synchronize!
+
+
+ Log Out
+
+ Clears all synchronization data
+
+
+ Log out / clear synchronization data?
+
+
+
+ tidak difungsikan
+ every fifteen minutes
+ every thirty minutes
+ every hour
+ every three hours
+ every six hours
+ every twelve hours
+ every day
+ every three days
+ every week
+
+
+
+
diff --git a/api/res/values-it/strings.xml b/api/res/values-it/strings.xml
new file mode 100644
index 0000000000..018d2bc041
--- /dev/null
+++ b/api/res/values-it/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 Anno
+
+ %d Anni
+
+
+
+ 1 Mese
+
+ %d Mesi
+
+
+
+ 1 Settimana
+
+ %d Settimane
+
+
+
+ 1 giorno
+
+ %d Giorni
+
+
+
+ 1 ora
+
+ %d ore
+
+
+
+ 1 minuto
+
+ %d minuti
+
+
+
+ 1 secondo
+
+ %d secondi
+
+
+
+ 1 ora
+
+ %d ore
+
+
+
+ 1 min
+
+ %d min
+
+
+
+ 1 sec
+
+ %d sec
+
+
+
+ 1 Attività
+
+ %d attività
+
+
+
+
+
+ Conferma?
+
+
+ Domanda:
+
+
+ Informazioni
+
+
+ Error!
+
+
+ Sì
+
+
+ No
+
+
+ Chiudi
+
+
+ Completata
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ Attendere per favore...
+
+
+
+
+ Sincronizzando le tue attività...
+
+
+ Sincronizzando...
+
+
+ Sincronizzazione
+
+
+ Connection Error! Check your Internet connection.
+
+
+
+
+ Stato
+
+
+ Not Logged In!
+
+ Sincronizzazione in corso ...
+
+ Ultima Sincronizzazione: %s
+
+ Fallita Su: %s
+
+ Ultima sincronizzazione eseguita con successo in data: %s
+
+ Mai sincronizzato!
+
+
+ Opzioni
+
+
+ Sincronizzazione eseguita in background
+
+ La sincronizzazione in background è disattivata
+
+ Attualmente impostata su: %s
+
+
+ Unica Impostazione Wifi
+
+ la sincronizzazione in background avviene solo quando la rete Wifi è abilitata
+
+ La sincronizzazione in background avviene sempre
+
+
+ Azioni
+
+
+ Sincronizza Ora!
+
+ Esegui l\'accesso & Sincronizza!
+
+
+ Esci
+
+ Clears all synchronization data
+
+
+ Esci / cancella i file di sincronizzazione?
+
+
+
+ disabilita
+ ogni quindici minuti
+ ogni trenta minuti
+ ogni ora
+ ogni tre ore
+ ogni sei ore
+ ogni dodici ore
+ ogni giorno
+ ogni tre giorni
+ Ogni settimana
+
+
+
+
diff --git a/api/res/values-ja/strings.xml b/api/res/values-ja/strings.xml
new file mode 100644
index 0000000000..70cba6c77c
--- /dev/null
+++ b/api/res/values-ja/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1年
+
+ %d 年
+
+
+
+ 1か月
+
+ %d か月
+
+
+
+ 1週間
+
+ %d 週間
+
+
+
+ 1 日
+
+ %d 日
+
+
+
+ 1 時間
+
+ %d 時間
+
+
+
+ 1 分
+
+ %d 分
+
+
+
+ 1 秒
+
+ %d 秒
+
+
+
+ 1 時間
+
+ %d 時間
+
+
+
+ 1 分
+
+ %d 分
+
+
+
+ 1 秒
+
+ %d 秒
+
+
+
+ タスク 1 件
+
+ タスク %d 件
+
+
+
+
+
+ 確認
+
+
+ 確認
+
+
+ インフォメーション
+
+
+ エラー
+
+
+ はい
+
+
+ いいえ
+
+
+ 閉じる
+
+
+ 完了
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ お待ちください
+
+
+
+
+ タスクの同期中...
+
+
+ 同期中...
+
+
+ 同期
+
+
+ Connection Error! Check your Internet connection.
+
+
+
+
+ 状況
+
+
+ ログインしていません
+
+ 同期中
+
+ 前回の同期: %s
+
+ 失敗: %s
+
+ 最後の同期: %s
+
+ 同期していません
+
+
+ オプション
+
+
+ バックグラウンド同期
+
+ バックグラウンド同期は無効になっています
+
+ 現在の設定: %s
+
+
+ Wi-Fi のみ
+
+ Wi-Fi が有効なときだけバックグラウンドで同期する
+
+ Background synchronization will always occur
+
+
+ アクション
+
+
+ すぐに同期!
+
+ ログインと同期
+
+
+ ログアウト
+
+ すべての同期データを消去します
+
+
+ ログアウトと同期データを消去しますか?
+
+
+
+ 無効
+ 15分毎
+ 30分毎
+ 1時間毎
+ 3時間毎
+ 6時間毎
+ 12時間毎
+ 毎日
+ 3日に一度
+ 毎週
+
+
+
+
diff --git a/api/res/values-ko/strings.xml b/api/res/values-ko/strings.xml
new file mode 100644
index 0000000000..a540627639
--- /dev/null
+++ b/api/res/values-ko/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1년
+
+ %d 년
+
+
+
+ 1개월
+
+ %d 개월
+
+
+
+ 1주
+
+ %d 주
+
+
+
+ 1일
+
+ %d 일
+
+
+
+ 1시간
+
+ %d 시간
+
+
+
+ 1 분
+
+ %d 분
+
+
+
+ 1 초
+
+ %d 초
+
+
+
+ 1 시간
+
+ %d 시간
+
+
+
+ 1 분
+
+ %d 분
+
+
+
+ 1 초
+
+ %d 초
+
+
+
+ 1 작업
+
+ %d 작업
+
+
+
+
+
+ 확인?
+
+
+ 질문:
+
+
+ 정보
+
+
+ 오류!
+
+
+ 네
+
+
+ 아니오
+
+
+ 닫기
+
+
+ 마침
+
+
+ 오류가 발생한 것 같습니다! 원인은 다음과 같습니다:\n\n%s
+
+
+ 오류가 발생한 것 같습니다!
+
+
+ 잠시만 기다려주세요...
+
+
+
+
+ 작업 동기화 중입니다...
+
+
+ 동기화하는 중...
+
+
+ 동기화
+
+
+ 연결 오류! 인터넷 연결을 확인하세요.
+
+
+
+
+ 상태
+
+
+ 로그인 되지 않았습니다!
+
+ 동기화 진행중...
+
+ 마지막 동기화: %s
+
+ 실패: %s
+
+ 마지막 동기화 성공시간: %s
+
+ 한번도 동기화 되지 않았습니다!
+
+
+ 옵션
+
+
+ 백그라운드 동기화
+
+ 백그라운드 동기화가 설정되지 않았습니다.
+
+ 현재 설정: %s
+
+
+ WiFi 일때만 설정
+
+ 백그라운드 동기화는 WiFi 지역에서만 작동합니다.
+
+ 백그라운드 동기화는 항상 작동합니다.
+
+
+ 작업
+
+
+ 동기화 시작!
+
+ 로그인 & 동기화!
+
+
+ 로그아웃
+
+ 모든 동기화 데이터 삭제
+
+
+ 로그아웃 / 모든 동기화 데이터 삭제?
+
+
+
+ 사용안함
+ 매 15분 마다
+ 매 30분마다
+ 매 시간
+ 매 3시간마다
+ 매 6시간마다
+ 매 12시간마다
+ 매일
+ 매 3일마다
+ 매주
+
+
+
+
diff --git a/api/res/values-nb/strings.xml b/api/res/values-nb/strings.xml
new file mode 100644
index 0000000000..a9378784d4
--- /dev/null
+++ b/api/res/values-nb/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 år
+
+ %d år
+
+
+
+ 1 måned
+
+ %d måneder
+
+
+
+ 1 uke
+
+ %d uker
+
+
+
+ 1 dag
+
+ %d dager
+
+
+
+ 1 time
+
+ %d timer
+
+
+
+ 1 minutt
+
+ %d minutter
+
+
+
+ 1 sekund
+
+ %d sekunder
+
+
+
+ 1 t
+
+ %d t
+
+
+
+ 1 min
+
+ %d min
+
+
+
+ 1 s
+
+ %d s
+
+
+
+ 1 oppgave
+
+ %d oppgaver
+
+
+
+
+
+ Bekreft?
+
+
+ Spørsmål:
+
+
+ Informasjon
+
+
+ Feil!
+
+
+ Ja
+
+
+ Nei
+
+
+ Lukk
+
+
+ Utført
+
+
+ Oi sann! Det ser ut for at en feil oppstod. Her er hva som skjedde:\n\n%s
+
+
+ Oi sann! Det ser ut for at en feil oppstod
+
+
+ Vennligst vent...
+
+
+
+
+ Synkroniserer oppgavene dine...
+
+
+ Synkroniserer...
+
+
+ Synkronisering
+
+
+ Tilkoblingsfeil! Kontroller at du er koblet til Internett
+
+
+
+
+ Status
+
+
+ Not Logged In!
+
+ Synkronisering pågår...
+
+ Siste synkronisering: %s
+
+ Feilet: %s
+
+ Siste vellykkede synkronisering: %s
+
+ Aldri synkronisert!
+
+
+ Alternativer
+
+
+ Bakgrunnssynkronisering
+
+ Bakgrunnssynkronisering er deaktivert
+
+ Foreløpig satt til %s
+
+
+ Bare Wifi Innstilling
+
+ Synkronisering i bakgrunnen skal kun utføres med WiFi-tilkobling
+
+ Synkronisering i bakgrunnen skal alltid utføres
+
+
+ Handlinger
+
+
+ Synkroniser nå!
+
+ Logg Inn & Synkroniser!
+
+
+ Logg av
+
+ Clears all synchronization data
+
+
+ Logge ut / slette synkroniserings data?
+
+
+
+ deaktiver
+ hvert kvarter
+ hver halvtime
+ hver time
+ hver tredje time
+ hver sjette time
+ hver tolvte time
+ daglig
+ hver tredje dag
+ hver uke
+
+
+
+
diff --git a/api/res/values-nl/strings.xml b/api/res/values-nl/strings.xml
new file mode 100644
index 0000000000..01d34df456
--- /dev/null
+++ b/api/res/values-nl/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 jaar
+
+ %d jaren
+
+
+
+ 1 maand
+
+ %d maanden
+
+
+
+ 1 week
+
+ %d weken
+
+
+
+ 1 dag
+
+ %d Dagen
+
+
+
+ 1 Uur
+
+ %d Uren
+
+
+
+ 1 Minuut
+
+ %d Minuten
+
+
+
+ 1 Seconde
+
+ %d Seconden
+
+
+
+ 1 U
+
+ %d Uur
+
+
+
+ 1 Min
+
+ %d Min
+
+
+
+ 1 Sec
+
+ %d Sec
+
+
+
+ 1 task
+
+ %d tasks
+
+
+
+
+
+ Confirm?
+
+
+ Vraag:
+
+
+ Informatie
+
+
+ Error!
+
+
+ Ja
+
+
+ Nee
+
+
+ Sluiten
+
+
+ Voltooid
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ Even geduld alstublieft...
+
+
+
+
+ Synchronizing your tasks...
+
+
+ Bezig met synchroniseren...
+
+
+ Synchronisatie
+
+
+ Connection Error! Check your Internet connection.
+
+
+
+
+ Status
+
+
+ Not Logged In!
+
+ Sync Ongoing...
+
+ Last Sync: %s
+
+ Failed On: %s
+
+ Last Successful Sync: %s
+
+ Never Synchronized!
+
+
+ Opties
+
+
+ Background Sync
+
+ Background synchronization is disabled
+
+ Currently set to: %s
+
+
+ Wifi Only Setting
+
+ Background synchronization only happens when on Wifi
+
+ Background synchronization will always occur
+
+
+ Acties
+
+
+ Synchroniseer nu!
+
+ Log In & Synchronize!
+
+
+ Afmelden
+
+ Clears all synchronization data
+
+
+ Log out / clear synchronization data?
+
+
+
+ uit
+ elke 15 minuten
+ elke 30 minuten
+ elk uur
+ elke 3 uur
+ elke 6 uur
+ elke 12 uur
+ elke dag
+ elke 3 dagen
+ elke week
+
+
+
+
diff --git a/api/res/values-pl/strings.xml b/api/res/values-pl/strings.xml
new file mode 100644
index 0000000000..1d8362a665
--- /dev/null
+++ b/api/res/values-pl/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 rok
+
+ %d lat
+
+
+
+ 1 miesiąc
+
+ %d miesięcy
+
+
+
+ 1 tydzień
+
+ %d tygodni
+
+
+
+ 1 dzień
+
+ %d dni
+
+
+
+ 1 godzina
+
+ %d godzin
+
+
+
+ 1 minuta
+
+ %d minut
+
+
+
+ 1 sekunda
+
+ %d sekund
+
+
+
+ 1 godz.
+
+ %d godz.
+
+
+
+ 1 min.
+
+ %d min.
+
+
+
+ 1 sek.
+
+ %d sek.
+
+
+
+ 1 zadanie
+
+ %d zadań
+
+
+
+
+
+ Potwierdzić?
+
+
+ Pytanie:
+
+
+ Informacja
+
+
+ Błąd!
+
+
+ Tak
+
+
+ Nie
+
+
+ Zamknij
+
+
+ Gotowe
+
+
+ Ups! Wygląda na to, że wystąpił jakiś błąd! Oto, co się stało:\n\n%s
+
+
+ Ups! Wygląda na to, że wystąpił jakiś błąd!
+
+
+ Proszę czekać...
+
+
+
+
+ Synchronizowanie Twoich zadań...
+
+
+ Synchronizacja...
+
+
+ Synchronizacja
+
+
+ Błąd połączenia! Sprawdź swoje połączenie z Internetem!
+
+
+
+
+ Stan
+
+
+ Nie zalogowano!
+
+ Synchronizacja trwa...
+
+ Ostatnia synchronizacja: %s
+
+ Nieudana: %s
+
+ Ostatnia udana synchronizacja: %s
+
+ Nigdy nie synchronizowano!
+
+
+ Ustawienia
+
+
+ Synchronizacja w tle
+
+ Synchronizacja w tle wyłączona
+
+ Aktualnie ustawione na: %s
+
+
+ Tylko połączenie Wi-Fi
+
+ Synchronizacja w tle przebiega tylko poprzez Wi-Fi
+
+ Synchronizowanie w tle zawsze, niezależnie od rodzaju połączenia
+
+
+ Działania
+
+
+ Synchronizuj teraz!
+
+ Zaloguj & Synchronizuj!
+
+
+ Wyloguj
+
+ Czyści wszystkie dane synchronizacji
+
+
+ Wyloguj / wyczyść dane synchronizacji?
+
+
+
+ Wyłączone
+ co 15 minut
+ co 30 minut
+ co godzinę
+ co 3 godziny
+ co 6 godzin
+ co 12 godzin
+ raz dziennie
+ co 3 dni
+ co tydzień
+
+
+
+
diff --git a/api/res/values-pt/strings.xml b/api/res/values-pt/strings.xml
new file mode 100644
index 0000000000..f6e9ce04b7
--- /dev/null
+++ b/api/res/values-pt/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 Ano
+
+ %d Anos
+
+
+
+ 1 Mês
+
+ %d Meses
+
+
+
+ 1 Semana
+
+ %d Semanas
+
+
+
+ 1 Dia
+
+ %d Dias
+
+
+
+ 1 Hora
+
+ %d Horas
+
+
+
+ 1 Minuto
+
+ %d Minutos
+
+
+
+ 1 Segundo
+
+ %d Segundos
+
+
+
+ 1 h
+
+ %d h
+
+
+
+ 1 min
+
+ %d min
+
+
+
+ 1 s
+
+ %d s
+
+
+
+ 1 tarefa
+
+ %d tarefas
+
+
+
+
+
+ Confirma?
+
+
+ Pergunta:
+
+
+ Informação
+
+
+ Error!
+
+
+ Sim
+
+
+ Não
+
+
+ Fechar
+
+
+ Concluído
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ Por favor aguarde...
+
+
+
+
+ Synchronizing your tasks...
+
+
+ A Sincronizar...
+
+
+ Sincronização
+
+
+ Connection Error! Check your Internet connection.
+
+
+
+
+ Estado
+
+
+ Not Logged In!
+
+ Sync Ongoing...
+
+ Last Sync: %s
+
+ Failed On: %s
+
+ Last Successful Sync: %s
+
+ Never Synchronized!
+
+
+ Opções
+
+
+ Background Sync
+
+ Background synchronization is disabled
+
+ Currently set to: %s
+
+
+ Wifi Only Setting
+
+ Background synchronization only happens when on Wifi
+
+ Background synchronization will always occur
+
+
+ Acções
+
+
+ Sincronizar Agora!
+
+ Log In & Synchronize!
+
+
+ Terminar sessão
+
+ Clears all synchronization data
+
+
+ Log out / clear synchronization data?
+
+
+
+ desactivar
+ every fifteen minutes
+ every thirty minutes
+ every hour
+ every three hours
+ every six hours
+ every twelve hours
+ every day
+ every three days
+ every week
+
+
+
+
diff --git a/api/res/values-ru/strings.xml b/api/res/values-ru/strings.xml
new file mode 100644
index 0000000000..34b75bba86
--- /dev/null
+++ b/api/res/values-ru/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 год
+
+ %d года/лет
+
+
+
+ 1 месяц
+
+ %d месяца/месяцев
+
+
+
+ 1 неделя
+
+ %d недели/недель
+
+
+
+ 1 день
+
+ %d для/дней
+
+
+
+ 1 час
+
+ %d часа/часов
+
+
+
+ 1 минута
+
+ %d минуты/минут
+
+
+
+ 1 секунда
+
+ %d секунды/секунд
+
+
+
+ 1 час
+
+ %d ч
+
+
+
+ 1 мин
+
+ %d мин
+
+
+
+ 1 с
+
+ %d с
+
+
+
+ 1 задача
+
+ %d задач(а/и)
+
+
+
+
+
+ Подтвердить?
+
+
+ Вопрос:
+
+
+ Информация
+
+
+ Ошибка!
+
+
+ Да
+
+
+ Нет
+
+
+ Закрыть
+
+
+ Готово
+
+
+ Ой, похоже произошла ошибка! Подробности ниже:\n\n%s
+
+
+ Ой, похоже произошла ошибка!
+
+
+ Пожалуйста, подождите…
+
+
+
+
+ Синхронизация задач…
+
+
+ Синхронизация…
+
+
+ Синхронизация
+
+
+ Ошибка соединения! Проверьте подключение к интернету.
+
+
+
+
+ Состояние
+
+
+ Вы не вошли в систему!
+
+ Процесс синхронизации…
+
+ Последняя синхронизация: %s
+
+ Ошибка: %s
+
+ Последняя успешная синхронизация: %s
+
+ Синхронизаций не выполнялось!
+
+
+ Параметры
+
+
+ Фоновая синхронизация
+
+ Фоновая синхронизация отключена
+
+ Сейчас установлено: %s
+
+
+ Только через Wifi
+
+ Фоновая синхронизация происходит только через Wifi
+
+ Фоновая синхронизация происходит всегда
+
+
+ Действия
+
+
+ Синхронизировать!
+
+ Войти и синхронизировать!
+
+
+ Выход
+
+ Очищает все данные синхронизации
+
+
+ Выйти / очистить данные синхронизации?
+
+
+
+ отключить
+ каждые 15 минут
+ каждые 30 минут
+ каждый час
+ каждые 3 часа
+ каждые 6 часов
+ каждые 12 часов
+ каждый день
+ каждые 3 дня
+ каждую неделю
+
+
+
+
diff --git a/api/res/values-sv/strings.xml b/api/res/values-sv/strings.xml
new file mode 100644
index 0000000000..4468a27a40
--- /dev/null
+++ b/api/res/values-sv/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 år
+
+ %d år
+
+
+
+ 1 månad
+
+ %d månader
+
+
+
+ 1 vecka
+
+ %d veckor
+
+
+
+ 1 dag
+
+ %d dagar
+
+
+
+ 1 timme
+
+ %d timmar
+
+
+
+ 1 minut
+
+ %d minuter
+
+
+
+ 1 sekund
+
+ %d sekunder
+
+
+
+ 1 tim
+
+ %d tim
+
+
+
+ 1 min
+
+ %d min
+
+
+
+ 1 sek
+
+ %d sek
+
+
+
+ 1 uppgift
+
+ %d uppgifter
+
+
+
+
+
+ Bekräfta?
+
+
+ Fråga:
+
+
+ Information
+
+
+ Fel!
+
+
+ Ja
+
+
+ Nej
+
+
+ Stäng
+
+
+ Klar
+
+
+ Oj, det uppstod ett fel! Detta hände:\n\n%s
+
+
+ Oj, det uppstod ett fel!
+
+
+ Var god vänta...
+
+
+
+
+ Synkroniserar dina uppgifter...
+
+
+ Synkroniserar...
+
+
+ Synkronisering
+
+
+ Tillkopplingsfel! Kontrollera din tillkoppling till internet.
+
+
+
+
+ Status
+
+
+ Ej inloggad!
+
+ Synkronisering pågår...
+
+ Synkroniserades senast: %s
+
+ Misslyckades: %s
+
+ Synkronisering lyckades senast: %s
+
+ Aldrig synkroniserad!
+
+
+ Alternativ
+
+
+ Bakgrundssynkronisering
+
+ Bakgrundssynkronisering är inaktiverad
+
+ Aktuell inställning: %s
+
+
+ Endast Wi-Fi Inställning
+
+ Bakgrundssynkronisering sker endast när du är ansluten till Wi-Fi
+
+ Bakgrundssynkronisering sker alltid
+
+
+ Åtgärder
+
+
+ Synkronisera nu!
+
+ Logga in & synkronisera!
+
+
+ Logga ut
+
+ Rensar alla synkroniseringsdata
+
+
+ Logga ut / rensa synkroniseringsdata?
+
+
+
+ inaktivera
+ varje kvartstimme
+ varje halvtimme
+ varje timme
+ var tredje timme
+ var sjätte timme
+ var tolfte timme
+ varje dag
+ var tredje dag
+ varje vecka
+
+
+
+
diff --git a/api/res/values-tr/strings.xml b/api/res/values-tr/strings.xml
new file mode 100644
index 0000000000..1fd6997d10
--- /dev/null
+++ b/api/res/values-tr/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 Year
+
+ %d Years
+
+
+
+ 1 Month
+
+ %d Months
+
+
+
+ 1 Week
+
+ %d Weeks
+
+
+
+ 1 Gün
+
+ %d gün
+
+
+
+ 1 Saat
+
+ %d saat
+
+
+
+ 1 Dakika
+
+ %d dakika
+
+
+
+ 1 Saniye
+
+ %d saniye
+
+
+
+ 1 saat
+
+ %d saat
+
+
+
+ 1 dakika
+
+ %d dakika
+
+
+
+ 1 saniye
+
+ %d saniye
+
+
+
+ 1 task
+
+ %d tasks
+
+
+
+
+
+ Confirm?
+
+
+ Question:
+
+
+ Bilgi
+
+
+ Error!
+
+
+ Yes
+
+
+ No
+
+
+ Close
+
+
+ Tamamlandı
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ Please wait...
+
+
+
+
+ Synchronizing your tasks...
+
+
+ Synchronizing...
+
+
+ Senkronizasyon
+
+
+ Connection Error! Check your Internet connection.
+
+
+
+
+ Status
+
+
+ Not Logged In!
+
+ Sync Ongoing...
+
+ Last Sync: %s
+
+ Failed On: %s
+
+ Last Successful Sync: %s
+
+ Never Synchronized!
+
+
+ Ayarlar
+
+
+ Background Sync
+
+ Background synchronization is disabled
+
+ Currently set to: %s
+
+
+ Wifi Only Setting
+
+ Background synchronization only happens when on Wifi
+
+ Background synchronization will always occur
+
+
+ Eylemler
+
+
+ Senkronize et
+
+ Log In & Synchronize!
+
+
+ Log Out
+
+ Clears all synchronization data
+
+
+ Log out / clear synchronization data?
+
+
+
+ devre dışı bırak
+ every fifteen minutes
+ every thirty minutes
+ every hour
+ every three hours
+ every six hours
+ every twelve hours
+ every day
+ every three days
+ every week
+
+
+
+
diff --git a/api/res/values-zh-rCN/strings.xml b/api/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000000..715a029de0
--- /dev/null
+++ b/api/res/values-zh-rCN/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1年
+
+ %d年
+
+
+
+ 1个月
+
+ %d个月
+
+
+
+ 1周
+
+ %d周
+
+
+
+ 1天
+
+ %d天
+
+
+
+ 1小时
+
+ %d小时
+
+
+
+ 1分钟
+
+ %d分钟
+
+
+
+ 1秒
+
+ %d秒
+
+
+
+ 1小时
+
+ %d小时
+
+
+
+ 1分钟
+
+ %d分钟
+
+
+
+ 1秒
+
+ %d秒
+
+
+
+ 1任务
+
+ %d任务
+
+
+
+
+
+ 确认?
+
+
+ 问题:
+
+
+ 信息
+
+
+ 错误!
+
+
+ 是
+
+
+ 否
+
+
+ 关闭
+
+
+ 完成
+
+
+ 哇,看来出错了!情况如下:\n\n%s
+
+
+ 哇,看来出错了!
+
+
+ 请稍候...
+
+
+
+
+ 正在同步任务...
+
+
+ 正在同步...
+
+
+ 同步
+
+
+ 连接错误!查看您的网络连接。
+
+
+
+
+ 状态
+
+
+ 未登录!
+
+ 正在同步...
+
+ 上一次同步:%s
+
+ 同步失败:%s
+
+ 上一次成功同步:%s
+
+ 从未同步!
+
+
+ 选项
+
+
+ 后台同步
+
+ 后台同步已禁用
+
+ 当前设置为:%s
+
+
+ 仅使用 Wifi
+
+ 仅当 Wifi 打开时使用后台同步
+
+ 总是使用后台同步
+
+
+ 行动
+
+
+ 现在同步!
+
+ 登录&同步
+
+
+ 注销
+
+ 清除所有同步数据
+
+
+ 注销并清除同步数据?
+
+
+
+ 禁用
+ 每十五分钟
+ 每半小时
+ 每小时
+ 每三小时
+ 每六小时
+ 每十二小时
+ 每天
+ 每三天
+ 每周
+
+
+
+
diff --git a/api/res/values-zh-rTW/strings.xml b/api/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000000..accf33169f
--- /dev/null
+++ b/api/res/values-zh-rTW/strings.xml
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+ 1 年
+
+ %d 年
+
+
+
+ 1 個月
+
+ %d 月
+
+
+
+ 1 週
+
+ %d 週
+
+
+
+ 1 天
+
+ %d 天
+
+
+
+ 1 小時
+
+ %d 小時
+
+
+
+ 1 分鐘
+
+ %d 分鐘
+
+
+
+ 1 秒
+
+ %d 秒
+
+
+
+ 1 小時
+
+ %d 小時
+
+
+
+ 1 分鐘
+
+ %d 分鐘
+
+
+
+ 1 秒
+
+ %d 秒
+
+
+
+ 1 個工作
+
+ %d 個工作
+
+
+
+
+
+ 確認?
+
+
+ 問題:
+
+
+ 資訊
+
+
+ Error!
+
+
+ 確定
+
+
+ 取消
+
+
+ 關閉
+
+
+ 完成
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ 請稍候...
+
+
+
+
+ 同步工作中...
+
+
+ 正在同步中...
+
+
+ 同步
+
+
+ 連結錯誤! 檢查您的網際網路連線.
+
+
+
+
+ 狀態
+
+
+ 未登入!
+
+ 同步中...
+
+ 上次同步: %s
+
+ 失敗: %s
+
+ 上次成功同步: %s
+
+ 未同步過!
+
+
+ 選項
+
+
+ 背景同步
+
+ 背景同步關閉
+
+ 目前同步設定: %s
+
+
+ Wifi 才可使用之設定
+
+ 使用Wifi才啟動背景同步
+
+ 總是使用背景同步
+
+
+ 動作
+
+
+ 現在同步!
+
+ 登入並同步!
+
+
+ 登出
+
+ 清除所有同步資料
+
+
+ 登出 / 清除同步資料?
+
+
+
+ 停用
+ 每15分
+ 每30分
+ 每小時
+ 每3小時
+ 每6小時
+ 每12小時
+ 每天
+ 每3天
+ 每週
+
+
+
+
diff --git a/api/res/values/colors.xml b/api/res/values/colors.xml
new file mode 100644
index 0000000000..cfa0c5dd7a
--- /dev/null
+++ b/api/res/values/colors.xml
@@ -0,0 +1,12 @@
+
+
+
+ #ffff5555
+ #fffea400
+ #ff33a5e8
+ #ff808080
+ #ff505050
+ #ff202020
+
+
+
diff --git a/api/res/values/keys.xml b/api/res/values/keys.xml
new file mode 100644
index 0000000000..ed660ae6b5
--- /dev/null
+++ b/api/res/values/keys.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+ 0
+ 900
+ 1800
+ 3600
+ 10800
+ 21600
+ 43200
+ 86400
+ 259200
+ 604800
+
+
+
+ sync_status
+
+ sync_bgwifi
+
+ sync_sync
+
+ sync_forget
+
+
diff --git a/api/res/values/strings.xml b/api/res/values/strings.xml
new file mode 100644
index 0000000000..63decbc80c
--- /dev/null
+++ b/api/res/values/strings.xml
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+ 1 Year
+
+ %d Years
+
+
+
+ 1 Month
+
+ %d Months
+
+
+
+ 1 Week
+
+ %d Weeks
+
+
+
+ 1 Day
+
+ %d Days
+
+
+
+ 1 Weekday
+
+ %d Weekdays
+
+
+
+ 1 Hour
+
+ %d Hours
+
+
+
+ 1 Minute
+
+ %d Minutes
+
+
+
+ 1 Second
+
+ %d Seconds
+
+
+
+ 1 Hr
+
+ %d Hrs
+
+
+
+ 1 Min
+
+ %d Min
+
+
+
+ 1 Sec
+
+ %d Sec
+
+
+
+ 1 task
+
+ %d tasks
+
+
+
+
+
+ Confirm?
+
+
+ Question:
+
+
+ Information
+
+
+ Error!
+
+
+ Yes
+
+
+ No
+
+
+ Close
+
+
+ Done
+
+
+ Oops, looks like an error occurred! Here\'s what happened:\n\n%s
+
+
+ Oops, looks like an error occurred!
+
+
+ Please wait...
+
+
+
+
+ Synchronizing your tasks...
+
+
+ Synchronizing...
+
+
+ Synchronization
+
+
+ Connection Error! Check your Internet connection.
+
+
+
+
+ Status
+
+
+ Not Logged In!
+
+ Sync Ongoing...
+
+ Last Sync: %s
+
+ Failed On: %s
+
+ Last Successful Sync: %s
+
+ Never Synchronized!
+
+
+ Options
+
+
+ Background Sync
+
+ Background synchronization is disabled
+
+ Currently set to: %s
+
+
+ Wifi Only Setting
+
+ Background synchronization only happens when on Wifi
+
+ Background synchronization will always occur
+
+
+ Actions
+
+
+ Synchronize Now!
+
+ Log In & Synchronize!
+
+
+ Log Out
+
+ Clears all synchronization data
+
+
+ Log out / clear synchronization data?
+
+
+
+ disable
+ every fifteen minutes
+ every thirty minutes
+ every hour
+ every three hours
+ every six hours
+ every twelve hours
+ every day
+ every three days
+ every week
+
+
+
diff --git a/api/src/com/todoroo/andlib/data/AbstractDatabase.java b/api/src/com/todoroo/andlib/data/AbstractDatabase.java
new file mode 100644
index 0000000000..05937031ba
--- /dev/null
+++ b/api/src/com/todoroo/andlib/data/AbstractDatabase.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2009, Todoroo Inc
+ * All Rights Reserved
+ * http://www.todoroo.com
+ */
+package com.todoroo.andlib.data;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.util.Log;
+
+import com.todoroo.andlib.data.Property.PropertyVisitor;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.ContextManager;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.service.ExceptionService;
+import com.todoroo.andlib.utility.AndroidUtilities;
+
+/**
+ * AbstractDatabase is a database abstraction which wraps a SQLite database.
+ *
+ * Users of this class are in charge of the database's lifecycle - ensuring that
+ * the database is open when needed and closed when usage is finished. Within an
+ * activity, this is typically accomplished through the onResume and onPause
+ * methods, though if the database is not needed for the activity's entire
+ * lifecycle, it can be closed earlier.
+ *
+ * Direct querying is not recommended for type safety reasons. Instead, use one
+ * of the service classes to issue the request and return a {@link TodorooCursor}.
+ *
+ * @author Tim Su
+ *
+ */
+@SuppressWarnings("nls")
+abstract public class AbstractDatabase {
+
+ // --- abstract methods
+
+ /**
+ * @return database name
+ */
+ protected abstract String getName();
+
+ /**
+ * @return all tables in this database
+ */
+ protected abstract Table[] getTables();
+
+ /**
+ * @return database version
+ */
+ protected abstract int getVersion();
+
+ /**
+ * Called after database and tables are created. Use this method to
+ * create indices and perform other database maintenance
+ */
+ protected abstract void onCreateTables();
+
+ /**
+ * Upgrades an open database from one version to the next
+ * @param oldVersion
+ * @param newVersion
+ * @return true if upgrade was handled, false otherwise
+ */
+ protected abstract boolean onUpgrade(int oldVersion, int newVersion);
+
+ // --- protected variables
+
+ /**
+ * SQLiteOpenHelper that takes care of database operations
+ */
+ protected SQLiteOpenHelper helper = null;
+
+ /**
+ * Internal pointer to open database. Hides the fact that there is a
+ * database and a wrapper by making a single monolithic interface
+ */
+ protected SQLiteDatabase database = null;
+
+ // --- internal implementation
+
+ @Autowired
+ private ExceptionService exceptionService;
+
+ public AbstractDatabase() {
+ DependencyInjectionService.getInstance().inject(this);
+ }
+
+ /**
+ * Return the name of the table containing these models
+ * @param modelType
+ * @return
+ */
+ public final Table getTable(Class extends AbstractModel> modelType) {
+ for(Table table : getTables()) {
+ if(table.modelClass.equals(modelType))
+ return table;
+ }
+ throw new UnsupportedOperationException("Unknown model class " + modelType); //$NON-NLS-1$
+ }
+
+ protected synchronized final void initializeHelper() {
+ if(helper == null) {
+ if(ContextManager.getContext() == null)
+ throw new NullPointerException("Null context creating database helper");
+ helper = new DatabaseHelper(ContextManager.getContext(),
+ getName(), null, getVersion());
+ }
+ }
+
+ /**
+ * Open the database for writing. Must be closed afterwards. If user is
+ * out of disk space, database may be opened for reading instead
+ */
+ public synchronized final void openForWriting() {
+ initializeHelper();
+
+ if(database != null && !database.isReadOnly() && database.isOpen())
+ return;
+
+ try {
+ database = helper.getWritableDatabase();
+ } catch (NullPointerException e) {
+ // don't know why this happens
+ throw new IllegalStateException(e);
+ } catch (final RuntimeException original) {
+ Log.e("database-" + getName(), "Error opening db",
+ original);
+ try {
+ // provide read-only database
+ openForReading();
+ } catch (Exception readException) {
+ exceptionService.reportError("database-open-" + getName(), original);
+
+ // throw original write exception
+ throw original;
+ }
+ }
+ }
+
+ /**
+ * Open the database for reading. Must be closed afterwards
+ */
+ public synchronized final void openForReading() {
+ initializeHelper();
+ if(database != null && database.isOpen())
+ return;
+ database = helper.getReadableDatabase();
+ }
+
+ /**
+ * Close the database if it has been opened previously
+ */
+ public synchronized final void close() {
+ if(database != null) {
+ database.close();
+ }
+ database = null;
+ }
+
+ /**
+ * Clear all data in database. Warning: this does what it says. Any open
+ * database resources will be abruptly closed.
+ */
+ public synchronized final void clear() {
+ close();
+ ContextManager.getContext().deleteDatabase(getName());
+ }
+
+ /**
+ * @return sql database. opens database if not yet open
+ */
+ public synchronized final SQLiteDatabase getDatabase() {
+ if(database == null) {
+ AndroidUtilities.sleepDeep(300L);
+ openForWriting();
+ }
+ return database;
+ }
+
+ /**
+ * @return human-readable database name for debugging
+ */
+ @Override
+ public String toString() {
+ return "DB:" + getName();
+ }
+
+ // --- database wrapper
+
+ /*
+ * @see android.database.sqlite.SQLiteDatabase#rawQuery(String sql, String[] selectionArgs)
+ */
+ public synchronized Cursor rawQuery(String sql, String[] selectionArgs) {
+ return getDatabase().rawQuery(sql, selectionArgs);
+ }
+
+ /*
+ * @see android.database.sqlite.SQLiteDatabase#insert(String table, String nullColumnHack, ContentValues values)
+ */
+ public synchronized long insert(String table, String nullColumnHack, ContentValues values) {
+ return getDatabase().insert(table, nullColumnHack, values);
+ }
+
+ /*
+ * @see android.database.sqlite.SQLiteDatabase#delete(String table, String whereClause, String[] whereArgs)
+ */
+ public synchronized int delete(String table, String whereClause, String[] whereArgs) {
+ return getDatabase().delete(table, whereClause, whereArgs);
+ }
+
+ /*
+ * @see android.database.sqlite.SQLiteDatabase#update(String table, ContentValues values, String whereClause, String[] whereArgs)
+ */
+ public synchronized int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
+ return getDatabase().update(table, values, whereClause, whereArgs);
+ }
+
+ // --- helper classes
+
+ /**
+ * Default implementation of Astrid database helper
+ */
+ private class DatabaseHelper extends SQLiteOpenHelper {
+
+ public DatabaseHelper(Context context, String name,
+ CursorFactory factory, int version) {
+ super(context, name, factory, version);
+ }
+
+ /**
+ * Called to create the database tables
+ */
+ @Override
+ public synchronized void onCreate(SQLiteDatabase db) {
+ StringBuilder sql = new StringBuilder();
+ SqlConstructorVisitor sqlVisitor = new SqlConstructorVisitor();
+
+ // create tables
+ for(Table table : getTables()) {
+ sql.append("CREATE TABLE IF NOT EXISTS ").append(table.name).append('(').
+ append(AbstractModel.ID_PROPERTY).append(" INTEGER PRIMARY KEY AUTOINCREMENT");
+ for(Property> property : table.getProperties()) {
+ if(AbstractModel.ID_PROPERTY.name.equals(property.name))
+ continue;
+ sql.append(',').append(property.accept(sqlVisitor, null));
+ }
+ sql.append(')');
+ db.execSQL(sql.toString());
+ sql.setLength(0);
+ }
+
+ // post-table-creation
+ database = db;
+ onCreateTables();
+ }
+
+ /**
+ * Called to upgrade the database to a new version
+ */
+ @Override
+ public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w("database-" + getName(), String.format("Upgrading database from version %d to %d.",
+ oldVersion, newVersion));
+
+ database = db;
+ try {
+ if(!AbstractDatabase.this.onUpgrade(oldVersion, newVersion)) {
+ // We don't know how to handle this case because someone forgot to
+ // implement the upgrade. We can't drop tables, we can only
+ // throw a nasty exception at this time
+
+ throw new IllegalStateException("Missing database migration " +
+ "from " + oldVersion + " to " + newVersion);
+ }
+ } catch (Exception e) {
+ exceptionService.reportError(String.format("database-upgrade-%s-%d-%d",
+ getName(), oldVersion, newVersion), e);
+ }
+ }
+ }
+
+ /**
+ * Visitor that returns SQL constructor for this property
+ *
+ * @author Tim Su
+ *
+ */
+ public static class SqlConstructorVisitor implements PropertyVisitor {
+
+ public String visitDouble(Property property, Void data) {
+ return String.format("%s REAL", property.name);
+ }
+
+ public String visitInteger(Property property, Void data) {
+ return String.format("%s INTEGER", property.name);
+ }
+
+ public String visitLong(Property property, Void data) {
+ return String.format("%s INTEGER", property.name);
+ }
+
+ public String visitString(Property property, Void data) {
+ return String.format("%s TEXT", property.name);
+ }
+ }
+}
+
diff --git a/api/src/com/todoroo/andlib/data/AbstractModel.java b/api/src/com/todoroo/andlib/data/AbstractModel.java
new file mode 100644
index 0000000000..a17aa8b23e
--- /dev/null
+++ b/api/src/com/todoroo/andlib/data/AbstractModel.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2009, Todoroo Inc
+ * All Rights Reserved
+ * http://www.todoroo.com
+ */
+package com.todoroo.andlib.data;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import android.content.ContentValues;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.todoroo.andlib.data.Property.DoubleProperty;
+import com.todoroo.andlib.data.Property.IntegerProperty;
+import com.todoroo.andlib.data.Property.LongProperty;
+import com.todoroo.andlib.data.Property.PropertyVisitor;
+
+/**
+ * AbstractModel represents a row in a database.
+ *
+ * A single database can be represented by multiple AbstractModels
+ * corresponding to different queries that return a different set of columns.
+ * Each model exposes a set of properties that it contains.
+ *
+ * @author Tim Su
+ *
+ */
+public abstract class AbstractModel implements Parcelable {
+
+ // --- static variables
+
+ private static final ContentValuesSavingVisitor saver = new ContentValuesSavingVisitor();
+
+ // --- constants
+
+ /** id property common to all models */
+ protected static final String ID_PROPERTY_NAME = "_id"; //$NON-NLS-1$
+
+ /** id field common to all models */
+ public static final IntegerProperty ID_PROPERTY = new IntegerProperty(null, ID_PROPERTY_NAME);
+
+ /** sentinel for objects without an id */
+ public static final long NO_ID = 0;
+
+ // --- abstract methods
+
+ /** Get the default values for this object */
+ abstract public ContentValues getDefaultValues();
+
+ // --- data store variables and management
+
+ /* Data Source Ordering:
+ *
+ * In order to return the best data, we want to check first what the user
+ * has explicitly set (setValues), then the values we have read out of
+ * the database (values), then defaults (getDefaultValues)
+ */
+
+ /** User set values */
+ protected ContentValues setValues = null;
+
+ /** Values from database */
+ protected ContentValues values = null;
+
+ /** Get database-read values for this object */
+ public ContentValues getDatabaseValues() {
+ return values;
+ }
+
+ /** Get the user-set values for this object */
+ public ContentValues getSetValues() {
+ return setValues;
+ }
+
+ /** Get a list of all field/value pairs merged across data sources */
+ public ContentValues getMergedValues() {
+ ContentValues mergedValues = new ContentValues();
+
+ ContentValues defaultValues = getDefaultValues();
+ if(defaultValues != null)
+ mergedValues.putAll(defaultValues);
+ if(values != null)
+ mergedValues.putAll(values);
+ if(setValues != null)
+ mergedValues.putAll(setValues);
+
+ return mergedValues;
+ }
+
+ /**
+ * Clear all data on this model
+ */
+ public void clear() {
+ values = null;
+ setValues = null;
+ }
+
+ /**
+ * Transfers all set values into values. This occurs when a task is
+ * saved - future saves will not need to write all the data as before.
+ */
+ public void markSaved() {
+ if(values == null)
+ values = setValues;
+ else if(setValues != null)
+ values.putAll(setValues);
+ setValues = null;
+ }
+
+ /**
+ * Use merged values to compare two models to each other. Must be of
+ * exactly the same class.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if(other == null || other.getClass() != getClass())
+ return false;
+
+ return getMergedValues().equals(((AbstractModel)other).getMergedValues());
+ }
+
+ @Override
+ public int hashCode() {
+ return getMergedValues().hashCode() ^ getClass().hashCode();
+ }
+
+ // --- data retrieval
+
+ /**
+ * Reads all properties from the supplied cursor and store
+ */
+ protected synchronized void readPropertiesFromCursor(TodorooCursor extends AbstractModel> cursor) {
+ if (values == null)
+ values = new ContentValues();
+
+ // clears user-set values
+ setValues = null;
+
+ for (Property> property : cursor.getProperties()) {
+ saver.save(property, values, cursor.get(property));
+ }
+ }
+
+ /**
+ * Reads the given property. Make sure this model has this property!
+ */
+ public synchronized TYPE getValue(Property property) {
+ Object value;
+ if(setValues != null && setValues.containsKey(property.name))
+ value = setValues.get(property.name);
+
+ else if(values != null && values.containsKey(property.name))
+ value = values.get(property.name);
+
+ else if(getDefaultValues().containsKey(property.name))
+ value = getDefaultValues().get(property.name);
+
+ else
+ throw new UnsupportedOperationException(
+ "Model Error: Did not read property " + property.name); //$NON-NLS-1$
+
+ // resolve properties that were retrieved with a different type than accessed
+ if(value instanceof String && property instanceof LongProperty)
+ return (TYPE) Long.valueOf((String)value);
+ else if(value instanceof String && property instanceof IntegerProperty)
+ return (TYPE) Integer.valueOf((String)value);
+ else if(value instanceof String && property instanceof DoubleProperty)
+ return (TYPE) Double.valueOf((String)value);
+ else if(value instanceof Integer && property instanceof LongProperty)
+ return (TYPE) Long.valueOf(((Number)value).longValue());
+ return (TYPE) value;
+ }
+
+ /**
+ * Utility method to get the identifier of the model, if it exists.
+ *
+ * @return {@value #NO_ID} if this model was not added to the database
+ */
+ abstract public long getId();
+
+ protected long getIdHelper(LongProperty id) {
+ if(setValues != null && setValues.containsKey(id.name))
+ return setValues.getAsLong(id.name);
+ else if(values != null && values.containsKey(id.name))
+ return values.getAsLong(id.name);
+ else
+ return NO_ID;
+ }
+
+ public void setId(long id) {
+ if (setValues == null)
+ setValues = new ContentValues();
+
+ if(id == NO_ID)
+ setValues.remove(ID_PROPERTY_NAME);
+ else
+ setValues.put(ID_PROPERTY_NAME, id);
+ }
+
+ /**
+ * @return true if this model has found Jesus (i.e. the database)
+ */
+ public boolean isSaved() {
+ return getId() != NO_ID;
+ }
+
+ /**
+ * @param property
+ * @return true if setValues or values contains this property
+ */
+ public boolean containsValue(Property> property) {
+ if(setValues != null && setValues.containsKey(property.name))
+ return true;
+ if(values != null && values.containsKey(property.name))
+ return true;
+ return false;
+ }
+
+ /**
+ * @param property
+ * @return true if setValues or values contains this property, and the value
+ * stored is not null
+ */
+ public boolean containsNonNullValue(Property> property) {
+ if(setValues != null && setValues.containsKey(property.name))
+ return setValues.get(property.name) != null;
+ if(values != null && values.containsKey(property.name))
+ return values.get(property.name) != null;
+ return false;
+ }
+
+ // --- data storage
+
+ /**
+ * Check whether the user has changed this property value and it should be
+ * stored for saving in the database
+ */
+ protected synchronized boolean shouldSaveValue(
+ Property property, TYPE newValue) {
+
+ // we've already decided to save it, so overwrite old value
+ if (setValues.containsKey(property.name))
+ return true;
+
+ // values contains this key, we should check it out
+ if(values != null && values.containsKey(property.name)) {
+ TYPE value = getValue(property);
+ if (value == null) {
+ if (newValue == null)
+ return false;
+ } else if (value.equals(newValue))
+ return false;
+ }
+
+ // otherwise, good to save
+ return true;
+ }
+
+ /**
+ * Sets the given property. Make sure this model has this property!
+ */
+ public synchronized void setValue(Property property,
+ TYPE value) {
+ if (setValues == null)
+ setValues = new ContentValues();
+ if (!shouldSaveValue(property, value))
+ return;
+
+ saver.save(property, setValues, value);
+ }
+
+ /**
+ * Merges content values with those coming from another source
+ */
+ public synchronized void mergeWith(ContentValues other) {
+ if (setValues == null)
+ setValues = new ContentValues();
+ setValues.putAll(other);
+ }
+
+ /**
+ * Clear the key for the given property
+ * @param property
+ */
+ public synchronized void clearValue(Property> property) {
+ if(setValues != null && setValues.containsKey(property.name))
+ setValues.remove(property.name);
+ else if(values != null && values.containsKey(property.name))
+ values.remove(property.name);
+ else if(getDefaultValues().containsKey(property.name))
+ throw new IllegalArgumentException("Property has a default value"); //$NON-NLS-1$
+ }
+
+ // --- property management
+
+ /**
+ * Looks inside the given class and finds all declared properties
+ */
+ protected static Property>[] generateProperties(Class extends AbstractModel> cls) {
+ ArrayList> properties = new ArrayList>();
+ if(cls.getSuperclass() != AbstractModel.class)
+ properties.addAll(Arrays.asList(generateProperties(
+ (Class extends AbstractModel>) cls.getSuperclass())));
+
+ // a property is public, static & extends Property
+ for(Field field : cls.getFields()) {
+ if((field.getModifiers() & Modifier.STATIC) == 0)
+ continue;
+ if(!Property.class.isAssignableFrom(field.getType()))
+ continue;
+ try {
+ properties.add((Property>) field.get(null));
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return properties.toArray(new Property>[properties.size()]);
+ }
+
+ /**
+ * Visitor that saves a value into a content values store
+ *
+ * @author Tim Su
+ *
+ */
+ public static class ContentValuesSavingVisitor implements PropertyVisitor {
+
+ private ContentValues store;
+
+ public synchronized void save(Property> property, ContentValues newStore, Object value) {
+ this.store = newStore;
+
+ // we don't allow null values, as they indicate unset properties
+ // when the database was written
+
+ if(value != null)
+ property.accept(this, value);
+ }
+
+ public Void visitDouble(Property property, Object value) {
+ store.put(property.name, (Double) value);
+ return null;
+ }
+
+ public Void visitInteger(Property property, Object value) {
+ store.put(property.name, (Integer) value);
+ return null;
+ }
+
+ public Void visitLong(Property property, Object value) {
+ store.put(property.name, (Long) value);
+ return null;
+ }
+
+ public Void visitString(Property property, Object value) {
+ store.put(property.name, (String) value);
+ return null;
+ }
+ }
+
+ // --- parcelable helpers
+
+ /**
+ * {@inheritDoc}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(setValues, 0);
+ dest.writeParcelable(values, 0);
+ }
+
+ /**
+ * In addition to overriding this class, model classes should create
+ * a static final variable named "CREATOR" in order to satisfy the
+ * requirements of the Parcelable interface.
+ */
+ abstract protected Parcelable.Creator extends AbstractModel> getCreator();
+
+ /**
+ * Parcelable creator helper
+ */
+ protected static final class ModelCreator
+ implements Parcelable.Creator {
+
+ private final Class cls;
+
+ public ModelCreator(Class cls) {
+ super();
+ this.cls = cls;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TYPE createFromParcel(Parcel source) {
+ TYPE model;
+ try {
+ model = cls.newInstance();
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ }
+ model.setValues = source.readParcelable(ContentValues.class.getClassLoader());
+ model.values = source.readParcelable(ContentValues.class.getClassLoader());
+ return model;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TYPE[] newArray(int size) {
+ return (TYPE[]) Array.newInstance(cls, size);
+ };
+ };
+
+}
diff --git a/api/src/com/todoroo/andlib/data/ContentResolverDao.java b/api/src/com/todoroo/andlib/data/ContentResolverDao.java
new file mode 100644
index 0000000000..2cf61e0594
--- /dev/null
+++ b/api/src/com/todoroo/andlib/data/ContentResolverDao.java
@@ -0,0 +1,148 @@
+package com.todoroo.andlib.data;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.util.Log;
+
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Query;
+
+
+/**
+ * DAO for reading and writing values from an Android ContentResolver
+ *
+ * @author Tim Su
+ *
+ * @param model type
+ */
+public class ContentResolverDao {
+
+ /** class of model */
+ private final Class modelClass;
+
+ /** base content uri */
+ private final Uri baseUri;
+
+ /** content resolver */
+ private final ContentResolver cr;
+
+ @Autowired
+ protected Boolean debug;
+
+ public ContentResolverDao(Class modelClass, Context context, Uri baseUri) {
+ DependencyInjectionService.getInstance().inject(this);
+ this.modelClass = modelClass;
+ if(debug == null)
+ debug = false;
+ this.baseUri = baseUri;
+
+ cr = context.getContentResolver();
+ }
+
+ /**
+ * Returns a URI for a single id
+ * @param id
+ * @return
+ */
+ private Uri uriWithId(long id) {
+ return Uri.withAppendedPath(baseUri, Long.toString(id));
+ }
+
+ /**
+ * Delete specific item from the given table
+ * @param id
+ * @return number of rows affected
+ */
+ public int delete(long id) {
+ return cr.delete(uriWithId(id), null, null);
+ }
+
+ /**
+ * Delete by criteria
+ * @param where
+ * @return number of rows affected
+ */
+ public int deleteWhere(Criterion where) {
+ return cr.delete(baseUri, where.toString(), null);
+ }
+
+ /**
+ * Query content provider
+ * @param query
+ * @return
+ */
+ public TodorooCursor query(Query query) {
+ if(debug)
+ Log.i("SQL-" + modelClass.getSimpleName(), query.toString()); //$NON-NLS-1$
+ Cursor cursor = query.queryContentResolver(cr, baseUri);
+ return new TodorooCursor(cursor, query.getFields());
+ }
+
+ /**
+ * Create new or save existing model
+ * @param model
+ * @return true if data was written to the db, false otherwise
+ */
+ public boolean save(TYPE model) {
+ if(model.isSaved()) {
+ if(model.getSetValues() == null)
+ return false;
+ if(cr.update(uriWithId(model.getId()), model.getSetValues(), null, null) != 0)
+ return true;
+ }
+ Uri uri = cr.insert(baseUri, model.getMergedValues());
+ long id = Long.parseLong(uri.getLastPathSegment());
+ model.setId(id);
+ model.markSaved();
+ return true;
+ }
+
+ /**
+ * Returns object corresponding to the given identifier
+ *
+ * @param database
+ * @param table
+ * name of table
+ * @param properties
+ * properties to read
+ * @param id
+ * id of item
+ * @return null if no item found
+ */
+ public TYPE fetch(long id, Property>... properties) {
+ TodorooCursor cursor = query(
+ Query.select(properties).where(AbstractModel.ID_PROPERTY.eq(id)));
+ try {
+ if (cursor.getCount() == 0)
+ return null;
+ cursor.moveToFirst();
+ Constructor constructor = modelClass.getConstructor(TodorooCursor.class);
+ return constructor.newInstance(cursor);
+ } catch (SecurityException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } finally {
+ try {
+ cursor.close();
+ } catch (NullPointerException e) {
+ // cursor was not open
+ }
+ }
+ }
+}
diff --git a/api/src/com/todoroo/andlib/data/DatabaseDao.java b/api/src/com/todoroo/andlib/data/DatabaseDao.java
new file mode 100644
index 0000000000..ac8fc3d25a
--- /dev/null
+++ b/api/src/com/todoroo/andlib/data/DatabaseDao.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2009, Todoroo Inc
+ * All Rights Reserved
+ * http://www.todoroo.com
+ */
+package com.todoroo.andlib.data;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.util.Log;
+
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Query;
+
+
+
+/**
+ * DAO for reading data from an instance of {@link AbstractDatabase}. If you
+ * are writing an add-on for Astrid, you probably want to be using a subclass
+ * of {@link ContentResolverDao} instead.
+ *
+ * @author Tim Su
+ *
+ */
+public class DatabaseDao {
+
+ private final Class modelClass;
+
+ private Table table;
+
+ private AbstractDatabase database;
+
+ @Autowired
+ protected Boolean debug;
+
+ public DatabaseDao(Class modelClass) {
+ DependencyInjectionService.getInstance().inject(this);
+ this.modelClass = modelClass;
+ if(debug == null)
+ debug = false;
+ }
+
+ public DatabaseDao(Class modelClass, AbstractDatabase database) {
+ this(modelClass);
+ setDatabase(database);
+ }
+
+ /** Gets table associated with this DAO */
+ public Table getTable() {
+ return table;
+ }
+
+ /**
+ * Sets database accessed by this DAO. Used for dependency-injected
+ * initialization by child classes and unit tests
+ *
+ * @param database
+ */
+ public void setDatabase(AbstractDatabase database) {
+ if(database == this.database)
+ return;
+ this.database = database;
+ table = database.getTable(modelClass);
+ }
+
+ // --- dao methods
+
+ /**
+ * Construct a query with SQL DSL objects
+ *
+ * @param query
+ * @return
+ */
+ public TodorooCursor query(Query query) {
+ query.from(table);
+ if(debug)
+ Log.i("SQL-" + modelClass.getSimpleName(), query.toString()); //$NON-NLS-1$
+ Cursor cursor = database.rawQuery(query.toString(), null);
+ return new TodorooCursor(cursor, query.getFields());
+ }
+
+ /**
+ * Construct a query with raw SQL
+ *
+ * @param properties
+ * @param selection
+ * @param selectionArgs
+ * @return
+ */
+ public TodorooCursor rawQuery(String selection, String[] selectionArgs, Property>... properties) {
+ String[] fields = new String[properties.length];
+ for(int i = 0; i < properties.length; i++)
+ fields[i] = properties[i].name;
+ return new TodorooCursor(database.getDatabase().query(table.name,
+ fields, selection, selectionArgs, null, null, null),
+ properties);
+ }
+
+ /**
+ * Returns object corresponding to the given identifier
+ *
+ * @param database
+ * @param table
+ * name of table
+ * @param properties
+ * properties to read
+ * @param id
+ * id of item
+ * @return null if no item found
+ */
+ public TYPE fetch(long id, Property>... properties) {
+ TodorooCursor cursor = fetchItem(id, properties);
+ try {
+ if (cursor.getCount() == 0)
+ return null;
+ Constructor constructor = modelClass.getConstructor(TodorooCursor.class);
+ return constructor.newInstance(cursor);
+ } catch (SecurityException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Delete the given id
+ *
+ * @param database
+ * @param id
+ * @return true if delete was successful
+ */
+ public boolean delete(long id) {
+ return database.delete(table.name,
+ AbstractModel.ID_PROPERTY.eq(id).toString(), null) > 0;
+ }
+
+ /**
+ * Delete all matching a clause
+ * @param where predicate for deletion
+ * @return # of deleted items
+ */
+ public int deleteWhere(Criterion where) {
+ return database.delete(table.name,
+ where.toString(), null);
+ }
+
+ /**
+ * Update all matching a clause to have the values set on template object.
+ *
+ * Example (updates "joe" => "bob" in metadata value1):
+ * {code}
+ * Metadata item = new Metadata();
+ * item.setValue(Metadata.VALUE1, "bob");
+ * update(item, Metadata.VALUE1.eq("joe"));
+ * {code}
+ * @param where sql criteria
+ * @param template set fields on this object in order to set them in the db.
+ * @return # of updated items
+ */
+ public int update(Criterion where, TYPE template) {
+ return database.update(table.name, template.getSetValues(),
+ where.toString(), null);
+ }
+
+ /**
+ * Save the given object to the database. Creates a new object if
+ * model id property has not been set
+ *
+ * @return true on success.
+ */
+ public boolean persist(TYPE item) {
+ if (item.getId() == AbstractModel.NO_ID) {
+ return createNew(item);
+ } else {
+ ContentValues values = item.getSetValues();
+
+ if (values.size() == 0) // nothing changed
+ return true;
+
+ return saveExisting(item);
+ }
+ }
+
+ /**
+ * Creates the given item.
+ *
+ * @param database
+ * @param table
+ * table name
+ * @param item
+ * item model
+ * @return returns true on success.
+ */
+ public boolean createNew(TYPE item) {
+ long newRow = database.insert(table.name,
+ AbstractModel.ID_PROPERTY.name, item.getMergedValues());
+ boolean result = newRow >= 0;
+ if(result) {
+ item.markSaved();
+ item.setId(newRow);
+ }
+ return result;
+ }
+
+ /**
+ * Saves the given item. Will not create a new item!
+ *
+ * @param database
+ * @param table
+ * table name
+ * @param item
+ * item model
+ * @return returns true on success.
+ */
+ public boolean saveExisting(TYPE item) {
+ ContentValues values = item.getSetValues();
+ if(values == null || values.size() == 0) // nothing changed
+ return true;
+ boolean result = database.update(table.name, values,
+ AbstractModel.ID_PROPERTY.eq(item.getId()).toString(), null) > 0;
+ if(result)
+ item.markSaved();
+ return result;
+ }
+
+ /**
+ * Updates multiple rows of the database based on model set values
+ *
+ * @param item
+ * item model
+ * @param criterion
+ * @return returns true on success.
+ */
+ public int updateMultiple(ContentValues values, Criterion criterion) {
+ if(values.size() == 0) // nothing changed
+ return 0;
+ return database.update(table.name, values, criterion.toString(), null);
+ }
+
+ // --- helper methods
+
+
+ /**
+ * Returns cursor to object corresponding to the given identifier
+ *
+ * @param database
+ * @param table
+ * name of table
+ * @param properties
+ * properties to read
+ * @param id
+ * id of item
+ * @return
+ */
+ protected TodorooCursor fetchItem(long id, Property>... properties) {
+ TodorooCursor cursor = query(
+ Query.select(properties).where(AbstractModel.ID_PROPERTY.eq(id)));
+ cursor.moveToFirst();
+ return new TodorooCursor(cursor, properties);
+ }
+}
diff --git a/api/src/com/todoroo/andlib/data/Property.java b/api/src/com/todoroo/andlib/data/Property.java
new file mode 100644
index 0000000000..9a4665bf2f
--- /dev/null
+++ b/api/src/com/todoroo/andlib/data/Property.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2009, Todoroo Inc
+ * All Rights Reserved
+ * http://www.todoroo.com
+ */
+package com.todoroo.andlib.data;
+
+import com.todoroo.andlib.sql.Field;
+
+/**
+ * Property represents a typed column in a database.
+ *
+ * Within a given database row, the parameter may not exist, in which case the
+ * value is null, it may be of an incorrect type, in which case an exception is
+ * thrown, or the correct type, in which case the value is returned.
+ *
+ * @author Tim Su
+ *
+ * @param
+ * a database supported type, such as String or Integer
+ */
+@SuppressWarnings("nls")
+public abstract class Property extends Field implements Cloneable {
+
+ // --- implementation
+
+ /** The database table name this property */
+ public final Table table;
+
+ /** The database column name for this property */
+ public final String name;
+
+ /**
+ * Create a property by table and column name. Uses the default property
+ * expression which is derived from default table name
+ */
+ protected Property(Table table, String columnName) {
+ this(table, columnName, (table == null) ? (columnName) : (table.name + "." + columnName));
+ }
+
+ /**
+ * Create a property by table and column name, manually specifying an
+ * expression to use in SQL
+ */
+ protected Property(Table table, String columnName, String expression) {
+ super(expression);
+ this.table = table;
+ this.name = columnName;
+ }
+
+ /**
+ * Accept a visitor
+ */
+ abstract public RETURN accept(
+ PropertyVisitor visitor, PARAMETER data);
+
+ /**
+ * Return a clone of this property
+ */
+ @Override
+ public Property clone() {
+ try {
+ return (Property) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // --- helper classes and interfaces
+
+ /**
+ * Visitor interface for property classes
+ *
+ * @author Tim Su
+ *
+ */
+ public interface PropertyVisitor {
+ public RETURN visitInteger(Property property, PARAMETER data);
+
+ public RETURN visitLong(Property property, PARAMETER data);
+
+ public RETURN visitDouble(Property property, PARAMETER data);
+
+ public RETURN visitString(Property property, PARAMETER data);
+ }
+
+ // --- children
+
+ /**
+ * Integer property type. See {@link Property}
+ *
+ * @author Tim Su
+ *
+ */
+ public static class IntegerProperty extends Property {
+
+ public IntegerProperty(Table table, String name) {
+ super(table, name);
+ }
+
+ protected IntegerProperty(Table table, String name, String expression) {
+ super(table, name, expression);
+ }
+
+ @Override
+ public RETURN accept(
+ PropertyVisitor visitor, PARAMETER data) {
+ return visitor.visitInteger(this, data);
+ }
+ }
+
+ /**
+ * String property type. See {@link Property}
+ *
+ * @author Tim Su
+ *
+ */
+ public static class StringProperty extends Property {
+
+ public StringProperty(Table table, String name) {
+ super(table, name);
+ }
+
+ protected StringProperty(Table table, String name, String expression) {
+ super(table, name, expression);
+ }
+
+ @Override
+ public RETURN accept(
+ PropertyVisitor visitor, PARAMETER data) {
+ return visitor.visitString(this, data);
+ }
+ }
+
+ /**
+ * Double property type. See {@link Property}
+ *
+ * @author Tim Su
+ *
+ */
+ public static class DoubleProperty extends Property {
+
+ public DoubleProperty(Table table, String name) {
+ super(table, name);
+ }
+
+ protected DoubleProperty(Table table, String name, String expression) {
+ super(table, name, expression);
+ }
+
+
+ @Override
+ public RETURN accept(
+ PropertyVisitor visitor, PARAMETER data) {
+ return visitor.visitDouble(this, data);
+ }
+ }
+
+ /**
+ * Long property type. See {@link Property}
+ *
+ * @author Tim Su
+ *
+ */
+ public static class LongProperty extends Property {
+
+ public LongProperty(Table table, String name) {
+ super(table, name);
+ }
+
+ protected LongProperty(Table table, String name, String expression) {
+ super(table, name, expression);
+ }
+
+ @Override
+ public RETURN accept(
+ PropertyVisitor visitor, PARAMETER data) {
+ return visitor.visitLong(this, data);
+ }
+ }
+
+ // --- pseudo-properties
+
+ /** Runs a SQL function and returns the result as a string */
+ public static class StringFunctionProperty extends StringProperty {
+ public StringFunctionProperty(String function, String columnName) {
+ super(null, columnName, function);
+ alias = columnName;
+ }
+ }
+
+ /** Runs a SQL function and returns the result as a string */
+ public static class IntegerFunctionProperty extends IntegerProperty {
+ public IntegerFunctionProperty(String function, String columnName) {
+ super(null, columnName, function);
+ alias = columnName;
+ }
+ }
+
+ /** Counting in aggregated tables. Returns the result of COUNT(1) */
+ public static final class CountProperty extends IntegerFunctionProperty {
+ public CountProperty() {
+ super("COUNT(1)", "count");
+ }
+ }
+
+}
diff --git a/api/src/com/todoroo/andlib/data/Table.java b/api/src/com/todoroo/andlib/data/Table.java
new file mode 100644
index 0000000000..3d2cb88808
--- /dev/null
+++ b/api/src/com/todoroo/andlib/data/Table.java
@@ -0,0 +1,68 @@
+package com.todoroo.andlib.data;
+
+import com.todoroo.andlib.sql.Field;
+import com.todoroo.andlib.sql.SqlTable;
+
+/**
+ * Table class. Most fields are final, so methods such as as will
+ * clone the table when it returns.
+ *
+ * @author Tim Su
+ *
+ */
+public final class Table extends SqlTable {
+ public final String name;
+ public final Class extends AbstractModel> modelClass;
+
+ public Table(String name, Class extends AbstractModel> modelClass) {
+ this(name, modelClass, null);
+ }
+
+ public Table(String name, Class extends AbstractModel> modelClass, String alias) {
+ super(name);
+ this.name = name;
+ this.alias = alias;
+ this.modelClass = modelClass;
+ }
+
+ /**
+ * Reads a list of properties from model class by reflection
+ * @return property array
+ */
+ @SuppressWarnings("nls")
+ public Property>[] getProperties() {
+ try {
+ return (Property>[])modelClass.getField("PROPERTIES").get(null);
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (SecurityException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // --- for sql-dsl
+
+ /**
+ * Create a new join table based on this table, but with an alias
+ */
+ @Override
+ public Table as(String newAlias) {
+ return new Table(name, modelClass, newAlias);
+ }
+
+ /**
+ * Create a field object based on the given property
+ * @param property
+ * @return
+ */
+ @SuppressWarnings("nls")
+ public Field field(Property> property) {
+ if(alias != null)
+ return Field.field(alias + "." + property.name);
+ return Field.field(name + "." + property.name);
+ }
+}
\ No newline at end of file
diff --git a/api/src/com/todoroo/andlib/data/TodorooCursor.java b/api/src/com/todoroo/andlib/data/TodorooCursor.java
new file mode 100644
index 0000000000..4ef2c8ccc9
--- /dev/null
+++ b/api/src/com/todoroo/andlib/data/TodorooCursor.java
@@ -0,0 +1,109 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.andlib.data;
+
+import java.util.WeakHashMap;
+
+import android.database.Cursor;
+import android.database.CursorWrapper;
+
+import com.todoroo.andlib.data.Property.PropertyVisitor;
+
+/**
+ * AstridCursor wraps a cursor and allows users to query for individual
+ * {@link Property} types or read an entire {@link AbstractModel} from
+ * a database row.
+ *
+ * @author Tim Su
+ *
+ * @param a model type that is returned by this cursor
+ */
+public class TodorooCursor extends CursorWrapper {
+
+ /** Properties read by this cursor */
+ private final Property>[] properties;
+
+ /** Weakly cache field name to column id references for this cursor.
+ * Because it's a weak hash map, entire keys can be discarded by GC */
+ private final WeakHashMap columnIndexCache;
+
+ /** Property reading visitor */
+ private static final CursorReadingVisitor reader = new CursorReadingVisitor();
+
+ /**
+ * Create an AstridCursor from the supplied {@link Cursor}
+ * object.
+ *
+ * @param cursor
+ * @param properties properties read from this cursor
+ */
+ public TodorooCursor(Cursor cursor, Property>[] properties) {
+ super(cursor);
+
+ this.properties = properties;
+ columnIndexCache = new WeakHashMap();
+ }
+
+ /**
+ * Get the value for the given property on the underlying {@link Cursor}
+ *
+ * @param type to return
+ * @param property to retrieve
+ * @return
+ */
+ public PROPERTY_TYPE get(Property property) {
+ return (PROPERTY_TYPE)property.accept(reader, this);
+ }
+
+ /**
+ * Gets entire property list
+ * @return
+ */
+ public Property>[] getProperties() {
+ return properties;
+ }
+
+ /**
+ * Use cache to get the column index for the given field name
+ */
+ public synchronized int getColumnIndexFromCache(String field) {
+ Integer index = columnIndexCache.get(field);
+ if(index == null) {
+ index = getColumnIndexOrThrow(field);
+ columnIndexCache.put(field, index);
+ }
+
+ return index;
+ }
+
+ /**
+ * Visitor that reads the given property from a cursor
+ *
+ * @author Tim Su
+ *
+ */
+ public static class CursorReadingVisitor implements PropertyVisitor