Squashed 'api/' content from commit fdb4b31

git-subtree-dir: api
git-subtree-split: fdb4b31e548e92c66a8db44de57579017311f1bc
pull/14/head
Tim Su 14 years ago
commit 94a57ce751

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

8
.gitignore vendored

@ -0,0 +1,8 @@
*~
.metadata
*.class
org.eclipse.ltk.core.refactoring.prefs
gen
local.properties
ecbuild

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

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

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

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.todoroo.astrid.api"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="3" />
</manifest>

@ -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 <organization> 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 <COPYRIGHT HOLDER> 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.

@ -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

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="." default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked in in Version
Control Systems. -->
<property file="local.properties" />
<!-- The build.properties file can be created by you and is never touched
by the 'android' tool. This is the place to change some of the default property values
used by the Ant rules.
Here are some properties you may want to change/update:
application.package
the name of your application package as defined in the manifest. Used by the
'uninstall' rule.
source.dir
the name of the source directory. Default is 'src'.
out.dir
the name of the output directory. Default is 'bin'.
Properties related to the SDK location or the project target should be updated
using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your application and
should be checked in in Version Control Systems.
-->
<property file="build.properties" />
<!-- The default.properties file is created and updated by the 'android' tool, as well
as ADT.
This file is an integral part of the build system for your application and
should be checked in in Version Control Systems. -->
<property file="default.properties" />
<!-- Custom Android task to deal with the project target, and import the proper rules.
This requires ant 1.6.0 or above. -->
<path id="android.antlibs">
<pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
<pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
<pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
<pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
<pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
</path>
<taskdef name="setup"
classname="com.android.ant.SetupTask"
classpathref="android.antlibs" />
<!-- Execute the Android Setup task that will setup some properties specific to the target,
and import the build rules files.
The rules file is imported from
<SDK>/platforms/<target_platform>/templates/android_rules.xml
To customize some build steps for your project:
- copy the content of the main node <project> from android_rules.xml
- paste it in this build.xml below the <setup /> task.
- disable the import by changing the setup task below to <setup import="false" />
This will ensure that the properties are setup correctly but that your customized
build steps are used.
-->
<setup />
<target name="javadoc">
<javadoc access="public" author="true" classpath="." destdir="doc"
doctitle="Astrid API Project" nodeprecated="false"
nodeprecatedlist="false" noindex="false" nonavbar="false"
notree="false" source="1.6" splitindex="true"
use="true" version="true">
<sourcefiles>
<fileset dir="." includes="src/**/*" />
</sourcefiles>
<classpath>
<fileset dir="${sdk.dir}/platforms/${target}" includes="android.jar" />
</classpath>
</javadoc>
</target>
</project>

@ -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

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Layout for a visually child-like Preference in a PreferenceActivity. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<TextView android:id="@+android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<TextView android:id="@+android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignLeft="@android:id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:maxLines="2"
android:textColor="?android:attr/textColorSecondary" />
</RelativeLayout>
<!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="@+android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:orientation="vertical" />
</LinearLayout>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 Any</item>
<!-- plurals: years -->
<item quantity="other">%d Anys</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 Mes</item>
<!-- plurals: months -->
<item quantity="other">%d Mesos</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 Setmana</item>
<!-- plurals: days -->
<item quantity="other">%d Setmanes</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 dia</item>
<!-- plurals: days -->
<item quantity="other">%d Dies</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 Hora</item>
<!-- plurals: hours -->
<item quantity="other">%d Hores</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 Minut</item>
<!-- plurals: minutes -->
<item quantity="other">%d Minuts</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 Segon</item>
<!-- plurals: seconds -->
<item quantity="other">%d Segons</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 Hr</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d Hrs</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 Min</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d Min</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 Seg</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d Seg</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 tasca</item>
<!-- plurals: tasks -->
<item quantity="other">%d tasques</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Confirmar?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Pregunta:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Informació</string>
<!-- error dialog title -->
<string name="DLG_error_title">Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes"></string>
<!-- general dialog no-->
<string name="DLG_no">No</string>
<!-- general dialog close-->
<string name="DLG_close">Tancar</string>
<!-- general dialog done -->
<string name="DLG_done">Fet</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">¡Ui, sembla que hi ha hagut un problema! Això es el que ha passat:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">¡Ui, sembla que hi ha hagut un problema!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Si us plau, espera...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Sincronitzant les seves tasques...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Sincronitzant...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Sincronització</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Error de conexió! Verifiqui la conexió d\'internet.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Estat</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">No conectat!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Sincronització en curs...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Última sincronització: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Fallida el: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Última sincronització correcte: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Mai sincronitzat!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Opcions</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Sincronitzar en segon pla</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Desactivada la sincronització en segon pla</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Actualment configurat en: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Configuració Wifi</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">La sincronització en segon pla només funciona amb el Wifi activat.</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Sempre es produirà la sincronització en segon pla</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Accions</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Sincronitzar Ara!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Ingressar &amp; Sincronitzar!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Surt</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Esborra tota la informació de sincronització</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Tancar sessió / esborra la informació de sincronització?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>desactivat</item>
<item>cada quince minuts</item>
<item>cada trenta minuts</item>
<item>cada hora</item>
<item>cada tres hores</item>
<item>cada sis hores</item>
<item>cada dotze hores</item>
<item>diàriament</item>
<item>cada tres dies</item>
<item>setmanalment</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 rok</item>
<!-- plurals: years -->
<item quantity="other">%d Roky</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 měsíc</item>
<!-- plurals: months -->
<item quantity="other">%d Měsíce</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 týden</item>
<!-- plurals: days -->
<item quantity="other">%d Týdny</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 den</item>
<!-- plurals: days -->
<item quantity="other">%d Dnů</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 hodina</item>
<!-- plurals: hours -->
<item quantity="other">%d hodin</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 minuta</item>
<!-- plurals: minutes -->
<item quantity="other">%d minut</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 vteřina</item>
<!-- plurals: seconds -->
<item quantity="other">%d vteřin</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 hod.</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d hod.</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 min.</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d min.</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 s</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d s</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 úkol</item>
<!-- plurals: tasks -->
<item quantity="other">%d úkolů</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Potvrdit?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Otázka:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Informace</string>
<!-- error dialog title -->
<string name="DLG_error_title">Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Ano</string>
<!-- general dialog no-->
<string name="DLG_no">Ne</string>
<!-- general dialog close-->
<string name="DLG_close">Zavřít</string>
<!-- general dialog done -->
<string name="DLG_done">Hotovo</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Prosím čekejte...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Probíhá synchronizace Vašich úkolů...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Sychronizuji...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Synchronizace</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Connection Error! Check your Internet connection.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Stav</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Not Logged In!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Probíhá synchronizace...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Poslední synchronizace: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Selhalo: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Poslední úspěšná synchronizace: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Nikdo nesynchronizováno!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Možnosti</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Synchronizace na pozadí</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Synchronizace na pozadí je zakázána</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Současně nastaveno na: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Nastavení jen pro Wifi</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Synchronizovat na pozadí se bude pouze při zapnuté Wifi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Synchronizovat na pozadí se bude vždy</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Činnosti</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Synchronizuj teď!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Přihlásit se &amp; Synchronizovat!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Odhlásit se</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Clears all synchronization data</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Odhlásit se / vymazat synchronizační data?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>zakázat</item>
<item>každých patnáct minut</item>
<item>každých třicet minut</item>
<item>každou hodinu</item>
<item>každé tři hodiny</item>
<item>každých šest hodin</item>
<item>každých dvanáct hodin</item>
<item>každý den</item>
<item>každé tři dny</item>
<item>každý týden</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">Ein Jahr</item>
<!-- plurals: years -->
<item quantity="other">%d Jahre</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">Ein Monat</item>
<!-- plurals: months -->
<item quantity="other">%d Monate</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">Eine Woche</item>
<!-- plurals: days -->
<item quantity="other">%d Wochen</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 Tag</item>
<!-- plurals: days -->
<item quantity="other">%d Tage</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 Stunde</item>
<!-- plurals: hours -->
<item quantity="other">%d Stunden</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 Minute</item>
<!-- plurals: minutes -->
<item quantity="other">%d Minuten</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 Sekunde</item>
<!-- plurals: seconds -->
<item quantity="other">%d Sekunden</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 Std</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d Std</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 Min</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d Min</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 Sek</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d Sek</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 Aufgabe</item>
<!-- plurals: tasks -->
<item quantity="other">%d Aufgaben</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Bestätigen</string>
<!-- question dialog title -->
<string name="DLG_question_title">Frage:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Information</string>
<!-- error dialog title -->
<string name="DLG_error_title">Fehler!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Ja</string>
<!-- general dialog no-->
<string name="DLG_no">Nein</string>
<!-- general dialog close-->
<string name="DLG_close">Schließen</string>
<!-- general dialog done -->
<string name="DLG_done">Erledigt</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Ups, sieht aus, als ob ein Fehler passiert ist! Hier, was passiert ist:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Ups, sieht aus, als ob ein Fehler passiert ist!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Bitte warten...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synchronisiere deine Aufgaben</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Synchronisiere…</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Abgleich</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Verbindungsfehler! Überprüfen Sie Ihre Internetverbindung.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Status</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Nicht angemeldet!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Synchronisierung läuft...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Letzte Synchronisierung: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Fehlgeschlagen am: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Letzte erfolgreiche Synchronisierung: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Noch nie synchronisiert!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Einstellungen</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Hintergrund-Synchronisierung</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Hintergrund-Synchronisierung ist deaktiviert</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Gesetzt auf: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">WLAN Einstellungen</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Hintergrund-Synchronisierung nur bei WLAN-Verbindung</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Hintergrund-Synchronisierung findet immer statt</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Aktionen</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Jetzt abgleichen!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Einloggen &amp; Synchroniseren!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Abmelden</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Alle Synchronisationsdaten löschen</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Ausloggen / synchronisierte Daten löschen?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>deaktivieren</item>
<item>alle 15 Minuten</item>
<item>alle 30 Minuten</item>
<item>stündlich</item>
<item>alle 3 Stunden</item>
<item>alle 6 Stunden</item>
<item>alle 12 Stunden</item>
<item>täglich</item>
<item>jeden dritten Tag</item>
<item>wöchentlich</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 año</item>
<!-- plurals: years -->
<item quantity="other">%d años</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 Mes</item>
<!-- plurals: months -->
<item quantity="other">%d meses</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 semana</item>
<!-- plurals: days -->
<item quantity="other">%d semanas</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 día</item>
<!-- plurals: days -->
<item quantity="other">%d días</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 hora</item>
<!-- plurals: hours -->
<item quantity="other">%d horas</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 minuto</item>
<!-- plurals: minutes -->
<item quantity="other">%d minutos</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 segundo</item>
<!-- plurals: seconds -->
<item quantity="other">%d segundos</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 hora</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d horas</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 minuto</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d minutos</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 segundo</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d segundos</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 tarea</item>
<!-- plurals: tasks -->
<item quantity="other">%d tareas</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">¿Confirmar?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Pregunta:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Información</string>
<!-- error dialog title -->
<string name="DLG_error_title">¡Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes"></string>
<!-- general dialog no-->
<string name="DLG_no">No</string>
<!-- general dialog close-->
<string name="DLG_close">Cerrar</string>
<!-- general dialog done -->
<string name="DLG_done">Listo</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">¡Uy, al parecer hay algún problema! Esto es lo que pasó:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">¡Uy, al parecer hay algún problema!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Espere por favor...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Sincronizando sus tareas...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Sincronizando...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Sincronización</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Error de conexión! Verifique su conexión a internet.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Estado</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">No conectado!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Sincronización en curso...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Última sincronización: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Falló el: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Última sincronización exitosa: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">¡Jamás se sincronizó!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Opciones</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Sincronizar en segundo plano</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Sincronización en segundo plano desactivada</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Actualmente configurado para: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Sólo Configuración Wifi</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">La sincronización en segundo plano sólo funciona con el Wifi activado</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">La sincronización en segundo plano funciona siempre</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Acciones</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">¡Sincronizar ahora!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Iniciar sesión y sincronizar!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Cerrar sesión</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Borra todos los datos de sincronización</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Cierre de sesión / cancelar la sincronización de datos?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>desactivar</item>
<item>cada quince minutos</item>
<item>cada treinta minutos</item>
<item>cada hora</item>
<item>cada tres horas</item>
<item>cada seis horas</item>
<item>cada doce horas</item>
<item>cada día</item>
<item>cada tres días</item>
<item>cada semana</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 année</item>
<!-- plurals: years -->
<item quantity="other">%d années</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 mois</item>
<!-- plurals: months -->
<item quantity="other">%d mois</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 semaine</item>
<!-- plurals: days -->
<item quantity="other">%d semaines</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 jour</item>
<!-- plurals: days -->
<item quantity="other">%d jours</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 heure</item>
<!-- plurals: hours -->
<item quantity="other">%d heures</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 minute</item>
<!-- plurals: minutes -->
<item quantity="other">%d minutes</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 seconde</item>
<!-- plurals: seconds -->
<item quantity="other">%d secondes</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 h</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d h</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 min</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d min</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 s</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d s</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 tâche</item>
<!-- plurals: tasks -->
<item quantity="other">%d tâches</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Confirmer ?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Question :</string>
<!-- information dialog title -->
<string name="DLG_information_title">Information</string>
<!-- error dialog title -->
<string name="DLG_error_title">Erreur !</string>
<!-- general dialog yes-->
<string name="DLG_yes">Oui</string>
<!-- general dialog no-->
<string name="DLG_no">Non</string>
<!-- general dialog close-->
<string name="DLG_close">Fermer</string>
<!-- general dialog done -->
<string name="DLG_done">Terminé</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Veuillez patienter...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synchronisation de vos tâches...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Synchronisation...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Synchronisation</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Connection Error! Check your Internet connection.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Statut</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Not Logged In!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Synchronisation en cours...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Dernière synchro. : %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Échec sur : %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Dernière synchro. réussie : %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Jamais synchronisé !</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Options</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Synchro. en arrière-plan</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Synchronisation en arrière-plan désactivée</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Actuellement configuré sur : %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Paramètre Wifi seul</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">La synchronisation en arrière-plan ne s\'effectue uniquement sous Wifi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">La synchronisation en arrière-plan s\'effectuera toujours</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Actions</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Synchroniser maintenant !</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Se connecter et synchroniser !</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Se déconnecter</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Clears all synchronization data</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Se déconnecter/purger les données de synchronisation ?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>désactiver</item>
<item>toutes les quinze minutes</item>
<item>toutes les trente minutes</item>
<item>toutes les heures</item>
<item>toutes les trois heures</item>
<item>toutes les six heures</item>
<item>toutes les douze heures</item>
<item>tous les jours</item>
<item>tous les trois jours</item>
<item>toutes les semaines</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 Year</item>
<!-- plurals: years -->
<item quantity="other">%d Years</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 Month</item>
<!-- plurals: months -->
<item quantity="other">%d Months</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 Week</item>
<!-- plurals: days -->
<item quantity="other">%d Weeks</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 Hari</item>
<!-- plurals: days -->
<item quantity="other">%d Hari</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 Jam</item>
<!-- plurals: hours -->
<item quantity="other">%d Jam</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 Menit</item>
<!-- plurals: minutes -->
<item quantity="other">%d Menit</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 Detik</item>
<!-- plurals: seconds -->
<item quantity="other">%d Detik</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 Jam</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d Jam</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 Mnt</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d Mnt</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 Dtk</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d Dtk</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 task</item>
<!-- plurals: tasks -->
<item quantity="other">%d tasks</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Confirm?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Question:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Informasi</string>
<!-- error dialog title -->
<string name="DLG_error_title">Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Yes</string>
<!-- general dialog no-->
<string name="DLG_no">No</string>
<!-- general dialog close-->
<string name="DLG_close">Close</string>
<!-- general dialog done -->
<string name="DLG_done">Selesai</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Please wait...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synchronizing your tasks...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Synchronizing...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Sinkronisasi</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Connection Error! Check your Internet connection.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Status</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Not Logged In!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Sync Ongoing...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Last Sync: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Failed On: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Last Successful Sync: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Never Synchronized!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Pilihan</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Background Sync</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Background synchronization is disabled</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Currently set to: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Wifi Only Setting</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Background synchronization only happens when on Wifi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Background synchronization will always occur</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Aksi</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Sinkronkan Sekarang!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Log In &amp; Synchronize!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Log Out</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Clears all synchronization data</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Log out / clear synchronization data?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>tidak difungsikan</item>
<item>every fifteen minutes</item>
<item>every thirty minutes</item>
<item>every hour</item>
<item>every three hours</item>
<item>every six hours</item>
<item>every twelve hours</item>
<item>every day</item>
<item>every three days</item>
<item>every week</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 Anno</item>
<!-- plurals: years -->
<item quantity="other">%d Anni</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 Mese</item>
<!-- plurals: months -->
<item quantity="other">%d Mesi</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 Settimana</item>
<!-- plurals: days -->
<item quantity="other">%d Settimane</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 giorno</item>
<!-- plurals: days -->
<item quantity="other">%d Giorni</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 ora</item>
<!-- plurals: hours -->
<item quantity="other">%d ore</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 minuto</item>
<!-- plurals: minutes -->
<item quantity="other">%d minuti</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 secondo</item>
<!-- plurals: seconds -->
<item quantity="other">%d secondi</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 ora</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d ore</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 min</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d min</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 sec</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d sec</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 Attività</item>
<!-- plurals: tasks -->
<item quantity="other">%d attività</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Conferma?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Domanda:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Informazioni</string>
<!-- error dialog title -->
<string name="DLG_error_title">Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes"></string>
<!-- general dialog no-->
<string name="DLG_no">No</string>
<!-- general dialog close-->
<string name="DLG_close">Chiudi</string>
<!-- general dialog done -->
<string name="DLG_done">Completata</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Attendere per favore...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Sincronizzando le tue attività...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Sincronizzando...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Sincronizzazione</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Connection Error! Check your Internet connection.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Stato</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Not Logged In!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Sincronizzazione in corso ...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Ultima Sincronizzazione: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Fallita Su: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Ultima sincronizzazione eseguita con successo in data: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Mai sincronizzato!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Opzioni</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Sincronizzazione eseguita in background</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">La sincronizzazione in background è disattivata</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Attualmente impostata su: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Unica Impostazione Wifi</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">la sincronizzazione in background avviene solo quando la rete Wifi è abilitata</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">La sincronizzazione in background avviene sempre</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Azioni</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Sincronizza Ora!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Esegui l\'accesso &amp; Sincronizza!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Esci</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Clears all synchronization data</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Esci / cancella i file di sincronizzazione?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>disabilita</item>
<item>ogni quindici minuti</item>
<item>ogni trenta minuti</item>
<item>ogni ora</item>
<item>ogni tre ore</item>
<item>ogni sei ore</item>
<item>ogni dodici ore</item>
<item>ogni giorno</item>
<item>ogni tre giorni</item>
<item>Ogni settimana</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1年</item>
<!-- plurals: years -->
<item quantity="other">%d 年</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1か月</item>
<!-- plurals: months -->
<item quantity="other">%d か月</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1週間</item>
<!-- plurals: days -->
<item quantity="other">%d 週間</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 日</item>
<!-- plurals: days -->
<item quantity="other">%d 日</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 時間</item>
<!-- plurals: hours -->
<item quantity="other">%d 時間</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 分</item>
<!-- plurals: minutes -->
<item quantity="other">%d 分</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 秒</item>
<!-- plurals: seconds -->
<item quantity="other">%d 秒</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 時間</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d 時間</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 分</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d 分</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 秒</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d 秒</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">タスク 1 件</item>
<!-- plurals: tasks -->
<item quantity="other">タスク %d 件</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">確認</string>
<!-- question dialog title -->
<string name="DLG_question_title">確認</string>
<!-- information dialog title -->
<string name="DLG_information_title">インフォメーション</string>
<!-- error dialog title -->
<string name="DLG_error_title">エラー</string>
<!-- general dialog yes-->
<string name="DLG_yes">はい</string>
<!-- general dialog no-->
<string name="DLG_no">いいえ</string>
<!-- general dialog close-->
<string name="DLG_close">閉じる</string>
<!-- general dialog done -->
<string name="DLG_done">完了</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">お待ちください</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">タスクの同期中...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">同期中...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">同期</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Connection Error! Check your Internet connection.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">状況</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">ログインしていません</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">同期中</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">前回の同期: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">失敗: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">最後の同期: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">同期していません</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">オプション</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">バックグラウンド同期</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">バックグラウンド同期は無効になっています</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">現在の設定: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Wi-Fi のみ</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Wi-Fi が有効なときだけバックグラウンドで同期する</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Background synchronization will always occur</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">アクション</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">すぐに同期!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">ログインと同期</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">ログアウト</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">すべての同期データを消去します</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">ログアウトと同期データを消去しますか?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>無効</item>
<item>15分毎</item>
<item>30分毎</item>
<item>1時間毎</item>
<item>3時間毎</item>
<item>6時間毎</item>
<item>12時間毎</item>
<item>毎日</item>
<item>3日に一度</item>
<item>毎週</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1년</item>
<!-- plurals: years -->
<item quantity="other">%d 년</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1개월</item>
<!-- plurals: months -->
<item quantity="other">%d 개월</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1주</item>
<!-- plurals: days -->
<item quantity="other">%d 주</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1일</item>
<!-- plurals: days -->
<item quantity="other">%d 일</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1시간</item>
<!-- plurals: hours -->
<item quantity="other">%d 시간</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 분</item>
<!-- plurals: minutes -->
<item quantity="other">%d 분</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 초</item>
<!-- plurals: seconds -->
<item quantity="other">%d 초</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 시간</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d 시간</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 분</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d 분</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 초</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d 초</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 작업</item>
<!-- plurals: tasks -->
<item quantity="other">%d 작업</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">확인?</string>
<!-- question dialog title -->
<string name="DLG_question_title">질문:</string>
<!-- information dialog title -->
<string name="DLG_information_title">정보</string>
<!-- error dialog title -->
<string name="DLG_error_title">오류!</string>
<!-- general dialog yes-->
<string name="DLG_yes"></string>
<!-- general dialog no-->
<string name="DLG_no">아니오</string>
<!-- general dialog close-->
<string name="DLG_close">닫기</string>
<!-- general dialog done -->
<string name="DLG_done">마침</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">오류가 발생한 것 같습니다! 원인은 다음과 같습니다:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">오류가 발생한 것 같습니다!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">잠시만 기다려주세요...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">작업 동기화 중입니다...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">동기화하는 중...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">동기화</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">연결 오류! 인터넷 연결을 확인하세요.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">상태</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">로그인 되지 않았습니다!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">동기화 진행중...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">마지막 동기화: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">실패: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">마지막 동기화 성공시간: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">한번도 동기화 되지 않았습니다!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">옵션</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">백그라운드 동기화</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">백그라운드 동기화가 설정되지 않았습니다.</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">현재 설정: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">WiFi 일때만 설정</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">백그라운드 동기화는 WiFi 지역에서만 작동합니다.</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">백그라운드 동기화는 항상 작동합니다.</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">작업</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">동기화 시작!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">로그인 &amp; 동기화!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">로그아웃</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">모든 동기화 데이터 삭제</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">로그아웃 / 모든 동기화 데이터 삭제?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>사용안함</item>
<item>매 15분 마다</item>
<item>매 30분마다</item>
<item>매 시간</item>
<item>매 3시간마다</item>
<item>매 6시간마다</item>
<item>매 12시간마다</item>
<item>매일</item>
<item>매 3일마다</item>
<item>매주</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 år</item>
<!-- plurals: years -->
<item quantity="other">%d år</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 måned</item>
<!-- plurals: months -->
<item quantity="other">%d måneder</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 uke</item>
<!-- plurals: days -->
<item quantity="other">%d uker</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 dag</item>
<!-- plurals: days -->
<item quantity="other">%d dager</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 time</item>
<!-- plurals: hours -->
<item quantity="other">%d timer</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 minutt</item>
<!-- plurals: minutes -->
<item quantity="other">%d minutter</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 sekund</item>
<!-- plurals: seconds -->
<item quantity="other">%d sekunder</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 t</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d t</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 min</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d min</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 s</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d s</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 oppgave</item>
<!-- plurals: tasks -->
<item quantity="other">%d oppgaver</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Bekreft?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Spørsmål:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Informasjon</string>
<!-- error dialog title -->
<string name="DLG_error_title">Feil!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Ja</string>
<!-- general dialog no-->
<string name="DLG_no">Nei</string>
<!-- general dialog close-->
<string name="DLG_close">Lukk</string>
<!-- general dialog done -->
<string name="DLG_done">Utført</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oi sann! Det ser ut for at en feil oppstod. Her er hva som skjedde:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oi sann! Det ser ut for at en feil oppstod</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Vennligst vent...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synkroniserer oppgavene dine...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Synkroniserer...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Synkronisering</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Tilkoblingsfeil! Kontroller at du er koblet til Internett</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Status</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Not Logged In!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Synkronisering pågår...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Siste synkronisering: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Feilet: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Siste vellykkede synkronisering: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Aldri synkronisert!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Alternativer</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Bakgrunnssynkronisering</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Bakgrunnssynkronisering er deaktivert</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Foreløpig satt til %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Bare Wifi Innstilling</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Synkronisering i bakgrunnen skal kun utføres med WiFi-tilkobling</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Synkronisering i bakgrunnen skal alltid utføres</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Handlinger</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Synkroniser nå!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Logg Inn &amp; Synkroniser!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Logg av</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Clears all synchronization data</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Logge ut / slette synkroniserings data?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>deaktiver</item>
<item>hvert kvarter</item>
<item>hver halvtime</item>
<item>hver time</item>
<item>hver tredje time</item>
<item>hver sjette time</item>
<item>hver tolvte time</item>
<item>daglig</item>
<item>hver tredje dag</item>
<item>hver uke</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 jaar</item>
<!-- plurals: years -->
<item quantity="other">%d jaren</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 maand</item>
<!-- plurals: months -->
<item quantity="other">%d maanden</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 week</item>
<!-- plurals: days -->
<item quantity="other">%d weken</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 dag</item>
<!-- plurals: days -->
<item quantity="other">%d Dagen</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 Uur</item>
<!-- plurals: hours -->
<item quantity="other">%d Uren</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 Minuut</item>
<!-- plurals: minutes -->
<item quantity="other">%d Minuten</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 Seconde</item>
<!-- plurals: seconds -->
<item quantity="other">%d Seconden</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 U</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d Uur</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 Min</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d Min</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 Sec</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d Sec</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 task</item>
<!-- plurals: tasks -->
<item quantity="other">%d tasks</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Confirm?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Vraag:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Informatie</string>
<!-- error dialog title -->
<string name="DLG_error_title">Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Ja</string>
<!-- general dialog no-->
<string name="DLG_no">Nee</string>
<!-- general dialog close-->
<string name="DLG_close">Sluiten</string>
<!-- general dialog done -->
<string name="DLG_done">Voltooid</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Even geduld alstublieft...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synchronizing your tasks...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Bezig met synchroniseren...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Synchronisatie</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Connection Error! Check your Internet connection.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Status</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Not Logged In!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Sync Ongoing...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Last Sync: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Failed On: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Last Successful Sync: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Never Synchronized!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Opties</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Background Sync</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Background synchronization is disabled</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Currently set to: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Wifi Only Setting</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Background synchronization only happens when on Wifi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Background synchronization will always occur</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Acties</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Synchroniseer nu!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Log In &amp; Synchronize!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Afmelden</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Clears all synchronization data</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Log out / clear synchronization data?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>uit</item>
<item>elke 15 minuten</item>
<item>elke 30 minuten</item>
<item>elk uur</item>
<item>elke 3 uur</item>
<item>elke 6 uur</item>
<item>elke 12 uur</item>
<item>elke dag</item>
<item>elke 3 dagen</item>
<item>elke week</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 rok</item>
<!-- plurals: years -->
<item quantity="other">%d lat</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 miesiąc</item>
<!-- plurals: months -->
<item quantity="other">%d miesięcy</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 tydzień</item>
<!-- plurals: days -->
<item quantity="other">%d tygodni</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 dzień</item>
<!-- plurals: days -->
<item quantity="other">%d dni</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 godzina</item>
<!-- plurals: hours -->
<item quantity="other">%d godzin</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 minuta</item>
<!-- plurals: minutes -->
<item quantity="other">%d minut</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 sekunda</item>
<!-- plurals: seconds -->
<item quantity="other">%d sekund</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 godz.</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d godz.</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 min.</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d min.</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 sek.</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d sek.</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 zadanie</item>
<!-- plurals: tasks -->
<item quantity="other">%d zadań</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Potwierdzić?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Pytanie:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Informacja</string>
<!-- error dialog title -->
<string name="DLG_error_title">Błąd!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Tak</string>
<!-- general dialog no-->
<string name="DLG_no">Nie</string>
<!-- general dialog close-->
<string name="DLG_close">Zamknij</string>
<!-- general dialog done -->
<string name="DLG_done">Gotowe</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Ups! Wygląda na to, że wystąpił jakiś błąd! Oto, co się stało:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Ups! Wygląda na to, że wystąpił jakiś błąd!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Proszę czekać...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synchronizowanie Twoich zadań...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Synchronizacja...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Synchronizacja</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Błąd połączenia! Sprawdź swoje połączenie z Internetem!</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Stan</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Nie zalogowano!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Synchronizacja trwa...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Ostatnia synchronizacja: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Nieudana: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Ostatnia udana synchronizacja: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Nigdy nie synchronizowano!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Ustawienia</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Synchronizacja w tle</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Synchronizacja w tle wyłączona</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Aktualnie ustawione na: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Tylko połączenie Wi-Fi</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Synchronizacja w tle przebiega tylko poprzez Wi-Fi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Synchronizowanie w tle zawsze, niezależnie od rodzaju połączenia</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Działania</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Synchronizuj teraz!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Zaloguj &amp; Synchronizuj!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Wyloguj</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Czyści wszystkie dane synchronizacji</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Wyloguj / wyczyść dane synchronizacji?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>Wyłączone</item>
<item>co 15 minut</item>
<item>co 30 minut</item>
<item>co godzinę</item>
<item>co 3 godziny</item>
<item>co 6 godzin</item>
<item>co 12 godzin</item>
<item>raz dziennie</item>
<item>co 3 dni</item>
<item>co tydzień</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 Ano</item>
<!-- plurals: years -->
<item quantity="other">%d Anos</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 Mês</item>
<!-- plurals: months -->
<item quantity="other">%d Meses</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 Semana</item>
<!-- plurals: days -->
<item quantity="other">%d Semanas</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 Dia</item>
<!-- plurals: days -->
<item quantity="other">%d Dias</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 Hora</item>
<!-- plurals: hours -->
<item quantity="other">%d Horas</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 Minuto</item>
<!-- plurals: minutes -->
<item quantity="other">%d Minutos</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 Segundo</item>
<!-- plurals: seconds -->
<item quantity="other">%d Segundos</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 h</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d h</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 min</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d min</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 s</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d s</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 tarefa</item>
<!-- plurals: tasks -->
<item quantity="other">%d tarefas</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Confirma?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Pergunta:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Informação</string>
<!-- error dialog title -->
<string name="DLG_error_title">Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Sim</string>
<!-- general dialog no-->
<string name="DLG_no">Não</string>
<!-- general dialog close-->
<string name="DLG_close">Fechar</string>
<!-- general dialog done -->
<string name="DLG_done">Concluído</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Por favor aguarde...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synchronizing your tasks...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">A Sincronizar...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Sincronização</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Connection Error! Check your Internet connection.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Estado</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Not Logged In!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Sync Ongoing...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Last Sync: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Failed On: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Last Successful Sync: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Never Synchronized!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Opções</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Background Sync</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Background synchronization is disabled</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Currently set to: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Wifi Only Setting</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Background synchronization only happens when on Wifi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Background synchronization will always occur</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Acções</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Sincronizar Agora!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Log In &amp; Synchronize!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Terminar sessão</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Clears all synchronization data</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Log out / clear synchronization data?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>desactivar</item>
<item>every fifteen minutes</item>
<item>every thirty minutes</item>
<item>every hour</item>
<item>every three hours</item>
<item>every six hours</item>
<item>every twelve hours</item>
<item>every day</item>
<item>every three days</item>
<item>every week</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 год</item>
<!-- plurals: years -->
<item quantity="other">%d года/лет</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 месяц</item>
<!-- plurals: months -->
<item quantity="other">%d месяца/месяцев</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 неделя</item>
<!-- plurals: days -->
<item quantity="other">%d недели/недель</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 день</item>
<!-- plurals: days -->
<item quantity="other">%d для/дней</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 час</item>
<!-- plurals: hours -->
<item quantity="other">%d часа/часов</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 минута</item>
<!-- plurals: minutes -->
<item quantity="other">%d минуты/минут</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 секунда</item>
<!-- plurals: seconds -->
<item quantity="other">%d секунды/секунд</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 час</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d ч</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 мин</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d мин</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 с</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d с</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 задача</item>
<!-- plurals: tasks -->
<item quantity="other">%d задач(а/и)</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Подтвердить?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Вопрос:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Информация</string>
<!-- error dialog title -->
<string name="DLG_error_title">Ошибка!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Да</string>
<!-- general dialog no-->
<string name="DLG_no">Нет</string>
<!-- general dialog close-->
<string name="DLG_close">Закрыть</string>
<!-- general dialog done -->
<string name="DLG_done">Готово</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Ой, похоже произошла ошибка! Подробности ниже:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Ой, похоже произошла ошибка!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Пожалуйста, подождите…</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Синхронизация задач…</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Синхронизация…</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Синхронизация</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Ошибка соединения! Проверьте подключение к интернету.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Состояние</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Вы не вошли в систему!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Процесс синхронизации…</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Последняя синхронизация: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Ошибка: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Последняя успешная синхронизация: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Синхронизаций не выполнялось!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Параметры</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Фоновая синхронизация</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Фоновая синхронизация отключена</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Сейчас установлено: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Только через Wifi</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Фоновая синхронизация происходит только через Wifi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Фоновая синхронизация происходит всегда</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Действия</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Синхронизировать!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Войти и синхронизировать!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Выход</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Очищает все данные синхронизации</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Выйти / очистить данные синхронизации?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>отключить</item>
<item>каждые 15 минут</item>
<item>каждые 30 минут</item>
<item>каждый час</item>
<item>каждые 3 часа</item>
<item>каждые 6 часов</item>
<item>каждые 12 часов</item>
<item>каждый день</item>
<item>каждые 3 дня</item>
<item>каждую неделю</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 år</item>
<!-- plurals: years -->
<item quantity="other">%d år</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 månad</item>
<!-- plurals: months -->
<item quantity="other">%d månader</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 vecka</item>
<!-- plurals: days -->
<item quantity="other">%d veckor</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 dag</item>
<!-- plurals: days -->
<item quantity="other">%d dagar</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 timme</item>
<!-- plurals: hours -->
<item quantity="other">%d timmar</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 minut</item>
<!-- plurals: minutes -->
<item quantity="other">%d minuter</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 sekund</item>
<!-- plurals: seconds -->
<item quantity="other">%d sekunder</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 tim</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d tim</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 min</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d min</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 sek</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d sek</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 uppgift</item>
<!-- plurals: tasks -->
<item quantity="other">%d uppgifter</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Bekräfta?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Fråga:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Information</string>
<!-- error dialog title -->
<string name="DLG_error_title">Fel!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Ja</string>
<!-- general dialog no-->
<string name="DLG_no">Nej</string>
<!-- general dialog close-->
<string name="DLG_close">Stäng</string>
<!-- general dialog done -->
<string name="DLG_done">Klar</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oj, det uppstod ett fel! Detta hände:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oj, det uppstod ett fel!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Var god vänta...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synkroniserar dina uppgifter...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Synkroniserar...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Synkronisering</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Tillkopplingsfel! Kontrollera din tillkoppling till internet.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Status</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Ej inloggad!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Synkronisering pågår...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Synkroniserades senast: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Misslyckades: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Synkronisering lyckades senast: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Aldrig synkroniserad!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Alternativ</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Bakgrundssynkronisering</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Bakgrundssynkronisering är inaktiverad</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Aktuell inställning: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Endast Wi-Fi Inställning</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Bakgrundssynkronisering sker endast när du är ansluten till Wi-Fi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Bakgrundssynkronisering sker alltid</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Åtgärder</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Synkronisera nu!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Logga in &amp; synkronisera!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Logga ut</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Rensar alla synkroniseringsdata</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Logga ut / rensa synkroniseringsdata?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>inaktivera</item>
<item>varje kvartstimme</item>
<item>varje halvtimme</item>
<item>varje timme</item>
<item>var tredje timme</item>
<item>var sjätte timme</item>
<item>var tolfte timme</item>
<item>varje dag</item>
<item>var tredje dag</item>
<item>varje vecka</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 Year</item>
<!-- plurals: years -->
<item quantity="other">%d Years</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 Month</item>
<!-- plurals: months -->
<item quantity="other">%d Months</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 Week</item>
<!-- plurals: days -->
<item quantity="other">%d Weeks</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 Gün</item>
<!-- plurals: days -->
<item quantity="other">%d gün</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 Saat</item>
<!-- plurals: hours -->
<item quantity="other">%d saat</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 Dakika</item>
<!-- plurals: minutes -->
<item quantity="other">%d dakika</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 Saniye</item>
<!-- plurals: seconds -->
<item quantity="other">%d saniye</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 saat</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d saat</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 dakika</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d dakika</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 saniye</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d saniye</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 task</item>
<!-- plurals: tasks -->
<item quantity="other">%d tasks</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Confirm?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Question:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Bilgi</string>
<!-- error dialog title -->
<string name="DLG_error_title">Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Yes</string>
<!-- general dialog no-->
<string name="DLG_no">No</string>
<!-- general dialog close-->
<string name="DLG_close">Close</string>
<!-- general dialog done -->
<string name="DLG_done">Tamamlandı</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Please wait...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synchronizing your tasks...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Synchronizing...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Senkronizasyon</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Connection Error! Check your Internet connection.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Status</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Not Logged In!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Sync Ongoing...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Last Sync: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Failed On: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Last Successful Sync: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Never Synchronized!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Ayarlar</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Background Sync</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Background synchronization is disabled</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Currently set to: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Wifi Only Setting</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Background synchronization only happens when on Wifi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Background synchronization will always occur</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Eylemler</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Senkronize et</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Log In &amp; Synchronize!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Log Out</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Clears all synchronization data</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Log out / clear synchronization data?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>devre dışı bırak</item>
<item>every fifteen minutes</item>
<item>every thirty minutes</item>
<item>every hour</item>
<item>every three hours</item>
<item>every six hours</item>
<item>every twelve hours</item>
<item>every day</item>
<item>every three days</item>
<item>every week</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1年</item>
<!-- plurals: years -->
<item quantity="other">%d年</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1个月</item>
<!-- plurals: months -->
<item quantity="other">%d个月</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1周</item>
<!-- plurals: days -->
<item quantity="other">%d周</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1天</item>
<!-- plurals: days -->
<item quantity="other">%d天</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1小时</item>
<!-- plurals: hours -->
<item quantity="other">%d小时</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1分钟</item>
<!-- plurals: minutes -->
<item quantity="other">%d分钟</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1秒</item>
<!-- plurals: seconds -->
<item quantity="other">%d秒</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1小时</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d小时</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1分钟</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d分钟</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1秒</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d秒</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1任务</item>
<!-- plurals: tasks -->
<item quantity="other">%d任务</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">确认?</string>
<!-- question dialog title -->
<string name="DLG_question_title">问题:</string>
<!-- information dialog title -->
<string name="DLG_information_title">信息</string>
<!-- error dialog title -->
<string name="DLG_error_title">错误!</string>
<!-- general dialog yes-->
<string name="DLG_yes"></string>
<!-- general dialog no-->
<string name="DLG_no"></string>
<!-- general dialog close-->
<string name="DLG_close">关闭</string>
<!-- general dialog done -->
<string name="DLG_done">完成</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">哇,看来出错了!情况如下:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">哇,看来出错了!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">请稍候...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">正在同步任务...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">正在同步...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">同步</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">连接错误!查看您的网络连接。</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">状态</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">未登录!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">正在同步...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">上一次同步:%s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">同步失败:%s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">上一次成功同步:%s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">从未同步!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">选项</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">后台同步</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">后台同步已禁用</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">当前设置为:%s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">仅使用 Wifi</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">仅当 Wifi 打开时使用后台同步</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">总是使用后台同步</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">行动</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">现在同步!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">登录&amp;同步</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">注销</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">清除所有同步数据</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">注销并清除同步数据?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>禁用</item>
<item>每十五分钟</item>
<item>每半小时</item>
<item>每小时</item>
<item>每三小时</item>
<item>每六小时</item>
<item>每十二小时</item>
<item>每天</item>
<item>每三天</item>
<item>每周</item>
</string-array>
</resources>

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 年</item>
<!-- plurals: years -->
<item quantity="other">%d 年</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 個月</item>
<!-- plurals: months -->
<item quantity="other">%d 月</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 週</item>
<!-- plurals: days -->
<item quantity="other">%d 週</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 天</item>
<!-- plurals: days -->
<item quantity="other">%d 天</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 小時</item>
<!-- plurals: hours -->
<item quantity="other">%d 小時</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 分鐘</item>
<!-- plurals: minutes -->
<item quantity="other">%d 分鐘</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 秒</item>
<!-- plurals: seconds -->
<item quantity="other">%d 秒</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 小時</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d 小時</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 分鐘</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d 分鐘</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 秒</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d 秒</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 個工作</item>
<!-- plurals: tasks -->
<item quantity="other">%d 個工作</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">確認?</string>
<!-- question dialog title -->
<string name="DLG_question_title">問題:</string>
<!-- information dialog title -->
<string name="DLG_information_title">資訊</string>
<!-- error dialog title -->
<string name="DLG_error_title">Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes">確定</string>
<!-- general dialog no-->
<string name="DLG_no">取消</string>
<!-- general dialog close-->
<string name="DLG_close">關閉</string>
<!-- general dialog done -->
<string name="DLG_done">完成</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">請稍候...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">同步工作中...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">正在同步中...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">同步</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">連結錯誤! 檢查您的網際網路連線.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">狀態</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">未登入!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">同步中...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">上次同步: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">失敗: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">上次成功同步: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">未同步過!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">選項</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">背景同步</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">背景同步關閉</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">目前同步設定: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Wifi 才可使用之設定</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">使用Wifi才啟動背景同步</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">總是使用背景同步</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">動作</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">現在同步!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">登入並同步!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">登出</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">清除所有同步資料</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">登出 / 清除同步資料?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>停用</item>
<item>每15分</item>
<item>每30分</item>
<item>每小時</item>
<item>每3小時</item>
<item>每6小時</item>
<item>每12小時</item>
<item>每天</item>
<item>每3天</item>
<item>每週</item>
</string-array>
</resources>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<color name="importance_1">#ffff5555</color>
<color name="importance_2">#fffea400</color>
<color name="importance_3">#ff33a5e8</color>
<color name="importance_4">#ff808080</color>
<color name="importance_5">#ff505050</color>
<color name="importance_6">#ff202020</color>
</resources>

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains preference keys and preference list values.
These should not be translated
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- ============================================================ SYNC == -->
<string-array name="sync_SPr_interval_values">
<!-- sync_SPr_interval_values: interval in seconds for sync entries (do not edit) -->
<item>0</item>
<item>900</item>
<item>1800</item>
<item>3600</item>
<item>10800</item>
<item>21600</item>
<item>43200</item>
<item>86400</item>
<item>259200</item>
<item>604800</item>
</string-array>
<!-- Preference Key (do not translate) -->
<string name="sync_SPr_status_key">sync_status</string>
<!-- Preference Key (do not translate) -->
<string name="sync_SPr_bgwifi_key">sync_bgwifi</string>
<!-- Preference Key (do not translate) -->
<string name="sync_SPr_sync_key">sync_sync</string>
<!-- Preference Key (do not translate) -->
<string name="sync_SPr_forget_key">sync_forget</string>
</resources>

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
<item quantity="one">1 Year</item>
<!-- plurals: years -->
<item quantity="other">%d Years</item>
</plurals>
<plurals name="DUt_months">
<!-- plurals: months -->
<item quantity="one">1 Month</item>
<!-- plurals: months -->
<item quantity="other">%d Months</item>
</plurals>
<plurals name="DUt_weeks">
<!-- plurals: days -->
<item quantity="one">1 Week</item>
<!-- plurals: days -->
<item quantity="other">%d Weeks</item>
</plurals>
<plurals name="DUt_days">
<!-- plurals: days -->
<item quantity="one">1 Day</item>
<!-- plurals: days -->
<item quantity="other">%d Days</item>
</plurals>
<plurals name="DUt_weekdays">
<!-- plurals: days -->
<item quantity="one">1 Weekday</item>
<!-- plurals: days -->
<item quantity="other">%d Weekdays</item>
</plurals>
<plurals name="DUt_hours">
<!-- plurals: hours -->
<item quantity="one">1 Hour</item>
<!-- plurals: hours -->
<item quantity="other">%d Hours</item>
</plurals>
<plurals name="DUt_minutes">
<!-- plurals: minutes -->
<item quantity="one">1 Minute</item>
<!-- plurals: minutes -->
<item quantity="other">%d Minutes</item>
</plurals>
<plurals name="DUt_seconds">
<!-- plurals: seconds -->
<item quantity="one">1 Second</item>
<!-- plurals: seconds -->
<item quantity="other">%d Seconds</item>
</plurals>
<plurals name="DUt_hoursShort">
<!-- plurals: hours (abbreviated) -->
<item quantity="one">1 Hr</item>
<!-- plurals: hours (abbreviated) -->
<item quantity="other">%d Hrs</item>
</plurals>
<plurals name="DUt_minutesShort">
<!-- plurals: minutes (abbreviated) -->
<item quantity="one">1 Min</item>
<!-- plurals: minutes (abbreviated) -->
<item quantity="other">%d Min</item>
</plurals>
<plurals name="DUt_secondsShort">
<!-- plurals: seconds (abbreviated) -->
<item quantity="one">1 Sec</item>
<!-- plurals: seconds (abbreviated) -->
<item quantity="other">%d Sec</item>
</plurals>
<plurals name="Ntasks">
<!-- plurals: tasks -->
<item quantity="one">1 task</item>
<!-- plurals: tasks -->
<item quantity="other">%d tasks</item>
</plurals>
<!-- ================================================== Generic Dialogs == -->
<!-- confirmation dialog title -->
<string name="DLG_confirm_title">Confirm?</string>
<!-- question dialog title -->
<string name="DLG_question_title">Question:</string>
<!-- information dialog title -->
<string name="DLG_information_title">Information</string>
<!-- error dialog title -->
<string name="DLG_error_title">Error!</string>
<!-- general dialog yes-->
<string name="DLG_yes">Yes</string>
<!-- general dialog no-->
<string name="DLG_no">No</string>
<!-- general dialog close-->
<string name="DLG_close">Close</string>
<!-- general dialog done -->
<string name="DLG_done">Done</string>
<!-- error dialog (%s => error message)-->
<string name="DLG_error">Oops, looks like an error occurred! Here\'s what happened:\n\n%s</string>
<!-- error dialog (no message indicated)-->
<string name="DLG_error_generic">Oops, looks like an error occurred!</string>
<!-- Progress dialog shown when doing something slow -->
<string name="DLG_wait">Please wait...</string>
<!-- ====================================================== SyncProvider == -->
<!-- Sync Notification: message when sync service active -->
<string name="SyP_progress">Synchronizing your tasks...</string>
<!-- Sync Notification: toast when sync activated from activity -->
<string name="SyP_progress_toast">Synchronizing...</string>
<!-- Sync Label: used in menu to denote synchronization -->
<string name="SyP_label">Synchronization</string>
<!-- Error msg when io exception -->
<string name="SyP_ioerror">Connection Error! Check your Internet connection.</string>
<!-- ================================================== SyncPreferences == -->
<!-- Status Group Label -->
<string name="sync_SPr_group_status">Status</string>
<!-- Sync Status: log in -->
<string name="sync_status_loggedout">Not Logged In!</string>
<!-- Status: ongoing -->
<string name="sync_status_ongoing">Sync Ongoing...</string>
<!-- Sync Status: success status (%s -> last sync date). Keep it short!-->
<string name="sync_status_success">Last Sync: %s</string>
<!-- Sync Status: error status (%s -> last attempted sync date) -->
<string name="sync_status_failed">Failed On: %s</string>
<!-- Sync Status: error subtitle (%s -> last successful sync date) -->
<string name="sync_status_failed_subtitle">Last Successful Sync: %s</string>
<!-- Sync Status: never sync'd -->
<string name="sync_status_never">Never Synchronized!</string>
<!-- Options Group Label -->
<string name="sync_SPr_group_options">Options</string>
<!-- Preference: Synchronization Interval Title -->
<string name="sync_SPr_interval_title">Background Sync</string>
<!-- Preference: Synchronization Interval Description (when disabled) -->
<string name="sync_SPr_interval_desc_disabled">Background synchronization is disabled</string>
<!-- Preference: Synchronization Interval Description (%s => setting) -->
<string name="sync_SPr_interval_desc">Currently set to: %s</string>
<!-- Preference: Background Wifi Title -->
<string name="sync_SPr_bgwifi_title">Wifi Only Setting</string>
<!-- Preference: Background Wifi Description (enabled) -->
<string name="sync_SPr_bgwifi_desc_enabled">Background synchronization only happens when on Wifi</string>
<!-- Preference: Background Wifi Description (disabled) -->
<string name="sync_SPr_bgwifi_desc_disabled">Background synchronization will always occur</string>
<!-- Actions Group Label -->
<string name="sync_SPr_group_actions">Actions</string>
<!-- Synchronize Now Button -->
<string name="sync_SPr_sync">Synchronize Now!</string>
<!-- Synchronize Now Button if not logged in-->
<string name="sync_SPr_sync_log_in">Log In &amp; Synchronize!</string>
<!-- Sync: Clear Data Title -->
<string name="sync_SPr_forget">Log Out</string>
<!-- Sync: Clear Data Description -->
<string name="sync_SPr_forget_description">Clears all synchronization data</string>
<!-- confirmation dialog for sync log out -->
<string name="sync_forget_confirm">Log out / clear synchronization data?</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>disable</item>
<item>every fifteen minutes</item>
<item>every thirty minutes</item>
<item>every hour</item>
<item>every three hours</item>
<item>every six hours</item>
<item>every twelve hours</item>
<item>every day</item>
<item>every three days</item>
<item>every week</item>
</string-array>
</resources>

@ -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.
* <p>
* 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.
* <p>
* 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 <tim@todoroo.com>
*
*/
@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 <tim@todoroo.com>
*
*/
public static class SqlConstructorVisitor implements PropertyVisitor<String, Void> {
public String visitDouble(Property<Double> property, Void data) {
return String.format("%s REAL", property.name);
}
public String visitInteger(Property<Integer> property, Void data) {
return String.format("%s INTEGER", property.name);
}
public String visitLong(Property<Long> property, Void data) {
return String.format("%s INTEGER", property.name);
}
public String visitString(Property<String> property, Void data) {
return String.format("%s TEXT", property.name);
}
}
}

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

@ -0,0 +1,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 <tim@todoroo.com>
*
* @param <TYPE> model type
*/
public class ContentResolverDao<TYPE extends AbstractModel> {
/** class of model */
private final Class<TYPE> modelClass;
/** base content uri */
private final Uri baseUri;
/** content resolver */
private final ContentResolver cr;
@Autowired
protected Boolean debug;
public ContentResolverDao(Class<TYPE> 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<TYPE> query(Query query) {
if(debug)
Log.i("SQL-" + modelClass.getSimpleName(), query.toString()); //$NON-NLS-1$
Cursor cursor = query.queryContentResolver(cr, baseUri);
return new TodorooCursor<TYPE>(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<TYPE> cursor = query(
Query.select(properties).where(AbstractModel.ID_PROPERTY.eq(id)));
try {
if (cursor.getCount() == 0)
return null;
cursor.moveToFirst();
Constructor<TYPE> 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
}
}
}
}

@ -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 <tim@todoroo.com>
*
*/
public class DatabaseDao<TYPE extends AbstractModel> {
private final Class<TYPE> modelClass;
private Table table;
private AbstractDatabase database;
@Autowired
protected Boolean debug;
public DatabaseDao(Class<TYPE> modelClass) {
DependencyInjectionService.getInstance().inject(this);
this.modelClass = modelClass;
if(debug == null)
debug = false;
}
public DatabaseDao(Class<TYPE> 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<TYPE> 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<TYPE>(cursor, query.getFields());
}
/**
* Construct a query with raw SQL
*
* @param properties
* @param selection
* @param selectionArgs
* @return
*/
public TodorooCursor<TYPE> 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<TYPE>(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<TYPE> cursor = fetchItem(id, properties);
try {
if (cursor.getCount() == 0)
return null;
Constructor<TYPE> 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.
* <p>
* 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<TYPE> fetchItem(long id, Property<?>... properties) {
TodorooCursor<TYPE> cursor = query(
Query.select(properties).where(AbstractModel.ID_PROPERTY.eq(id)));
cursor.moveToFirst();
return new TodorooCursor<TYPE>(cursor, properties);
}
}

@ -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 <tim@todoroo.com>
*
* @param <TYPE>
* a database supported type, such as String or Integer
*/
@SuppressWarnings("nls")
public abstract class Property<TYPE> extends Field implements Cloneable {
// --- implementation
/** The database table name this property */
public final Table table;
/** The database column name for this property */
public final String name;
/**
* Create a property by table and column name. Uses the default property
* expression which is derived from default table name
*/
protected Property(Table table, String columnName) {
this(table, columnName, (table == null) ? (columnName) : (table.name + "." + columnName));
}
/**
* Create a property by table and column name, manually specifying an
* expression to use in SQL
*/
protected Property(Table table, String columnName, String expression) {
super(expression);
this.table = table;
this.name = columnName;
}
/**
* Accept a visitor
*/
abstract public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data);
/**
* Return a clone of this property
*/
@Override
public Property<TYPE> clone() {
try {
return (Property<TYPE>) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
// --- helper classes and interfaces
/**
* Visitor interface for property classes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface PropertyVisitor<RETURN, PARAMETER> {
public RETURN visitInteger(Property<Integer> property, PARAMETER data);
public RETURN visitLong(Property<Long> property, PARAMETER data);
public RETURN visitDouble(Property<Double> property, PARAMETER data);
public RETURN visitString(Property<String> property, PARAMETER data);
}
// --- children
/**
* Integer property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class IntegerProperty extends Property<Integer> {
public IntegerProperty(Table table, String name) {
super(table, name);
}
protected IntegerProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitInteger(this, data);
}
}
/**
* String property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class StringProperty extends Property<String> {
public StringProperty(Table table, String name) {
super(table, name);
}
protected StringProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitString(this, data);
}
}
/**
* Double property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class DoubleProperty extends Property<Double> {
public DoubleProperty(Table table, String name) {
super(table, name);
}
protected DoubleProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitDouble(this, data);
}
}
/**
* Long property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class LongProperty extends Property<Long> {
public LongProperty(Table table, String name) {
super(table, name);
}
protected LongProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitLong(this, data);
}
}
// --- pseudo-properties
/** Runs a SQL function and returns the result as a string */
public static class StringFunctionProperty extends StringProperty {
public StringFunctionProperty(String function, String columnName) {
super(null, columnName, function);
alias = columnName;
}
}
/** Runs a SQL function and returns the result as a string */
public static class IntegerFunctionProperty extends IntegerProperty {
public IntegerFunctionProperty(String function, String columnName) {
super(null, columnName, function);
alias = columnName;
}
}
/** Counting in aggregated tables. Returns the result of COUNT(1) */
public static final class CountProperty extends IntegerFunctionProperty {
public CountProperty() {
super("COUNT(1)", "count");
}
}
}

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

@ -0,0 +1,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 <tim@todoroo.com>
*
* @param <TYPE> a model type that is returned by this cursor
*/
public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
/** Properties read by this cursor */
private final Property<?>[] properties;
/** Weakly cache field name to column id references for this cursor.
* Because it's a weak hash map, entire keys can be discarded by GC */
private final WeakHashMap<String, Integer> columnIndexCache;
/** Property reading visitor */
private static final CursorReadingVisitor reader = new CursorReadingVisitor();
/**
* Create an <code>AstridCursor</code> from the supplied {@link Cursor}
* object.
*
* @param cursor
* @param properties properties read from this cursor
*/
public TodorooCursor(Cursor cursor, Property<?>[] properties) {
super(cursor);
this.properties = properties;
columnIndexCache = new WeakHashMap<String, Integer>();
}
/**
* Get the value for the given property on the underlying {@link Cursor}
*
* @param <PROPERTY_TYPE> type to return
* @param property to retrieve
* @return
*/
public <PROPERTY_TYPE> PROPERTY_TYPE get(Property<PROPERTY_TYPE> property) {
return (PROPERTY_TYPE)property.accept(reader, this);
}
/**
* Gets entire property list
* @return
*/
public Property<?>[] getProperties() {
return properties;
}
/**
* Use cache to get the column index for the given field name
*/
public synchronized int getColumnIndexFromCache(String field) {
Integer index = columnIndexCache.get(field);
if(index == null) {
index = getColumnIndexOrThrow(field);
columnIndexCache.put(field, index);
}
return index;
}
/**
* Visitor that reads the given property from a cursor
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class CursorReadingVisitor implements PropertyVisitor<Object, TodorooCursor<?>> {
public Object visitDouble(Property<Double> property,
TodorooCursor<?> cursor) {
return cursor.getDouble(cursor.getColumnIndexFromCache(property.name));
}
public Object visitInteger(Property<Integer> property,
TodorooCursor<?> cursor) {
return cursor.getInt(cursor.getColumnIndexFromCache(property.name));
}
public Object visitLong(Property<Long> property, TodorooCursor<?> cursor) {
return cursor.getLong(cursor.getColumnIndexFromCache(property.name));
}
public Object visitString(Property<String> property,
TodorooCursor<?> cursor) {
return cursor.getString(cursor.getColumnIndexFromCache(property.name));
}
}
}

@ -0,0 +1,5 @@
/**
* Todoroo Library classes for the data layer: accessing data from a database or
* {@link ContentResolver}
*/
package com.todoroo.andlib.data;

@ -0,0 +1,100 @@
package com.todoroo.andlib.service;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.HashMap;
import com.todoroo.andlib.service.ExceptionService.ErrorReporter;
/**
* A Dependency Injector knows how to inject certain dependencies based
* on the field that is passed in. You will need to write your own initialization
* code to insert this dependency injector into the DI chain.
*
* @author Tim Su <tim@todoroo.com>
*
*/
abstract public class AbstractDependencyInjector {
/**
* Initialize list of injection variables. Special care must used when
* instantiating classes that themselves depend on dependency injection
* (i.e. {@link ErrorReporter}.
*/
protected void addInjectables() {
// your injectables here
}
// ---
/**
* Constructor
*/
protected AbstractDependencyInjector() {
addInjectables();
}
/**
* Dependencies this class knows how to handle
*/
protected final HashMap<String, Object> injectables = new HashMap<String, Object>();
/**
* Cache of classes that were instantiated by the injector
*/
protected final HashMap<Class<?>, WeakReference<Object>> createdObjects =
new HashMap<Class<?>, WeakReference<Object>>();
/**
* Gets the injected object for this field. If implementing class does not
* know how to handle this dependency, it should return null
*
* @param object
* object to perform dependency injection on
* @param field
* field tagged with {link Autowired} annotation
* @return object to assign to this field, or null
*/
public Object getInjection(Object object, Field field) {
if(injectables.containsKey(field.getName())) {
Object injection = injectables.get(field.getName());
// if it's a class, instantiate the class
if(injection instanceof Class<?>) {
if(createdObjects.containsKey(injection) &&
createdObjects.get(injection).get() != null) {
injection = createdObjects.get(injection).get();
} else {
Class<?> cls = (Class<?>)injection;
try {
injection = cls.newInstance();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
createdObjects.put(cls,
new WeakReference<Object>(injection));
}
}
return injection;
}
return null;
}
@Override
public String toString() {
return getClass().getSimpleName();
}
/**
* Flush dependency injection cache. Useful for unit tests.
*/
protected void flushCreated() {
createdObjects.clear();
}
}

@ -0,0 +1,19 @@
package com.todoroo.andlib.service;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Autowired is an annotation that tells the dependency injector to
* set up the class as appropriate.
*
* @author Tim Su <tim@todoroo.com>
*
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
//
}

@ -0,0 +1,63 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.service;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
/**
* Singleton class to manage current application context
* b
* @author Tim Su <tim@todoroo.com>
*
*/
public final class ContextManager {
/**
* Global application context
*/
private static Context context = null;
/**
* Sets the global context
*
* @param context
*/
public static void setContext(Context context) {
if(context == null || context.getApplicationContext() == null)
return;
if(ContextManager.context != null && !(context instanceof Activity))
return;
ContextManager.context = context;
}
/**
* Gets the global context
*/
public static Context getContext() {
return context;
}
/**
* Convenience method to read a string from the resources
*
* @param resId resource
* @param parameters % arguments
* @return resource string
*/
public static String getString(int resId, Object... formatArgs) {
return context.getString(resId, formatArgs);
}
/**
* Convenience method to read resources
*
* @return resources object
*/
public static Resources getResources() {
return context.getResources();
}
}

@ -0,0 +1,165 @@
package com.todoroo.andlib.service;
import java.lang.reflect.Field;
import java.util.LinkedList;
/**
* Simple Dependency Injection Service for Android.
* <p>
* Add dependency injectors to the injector chain, then invoke this method
* against classes you wish to perform dependency injection for.
* <p>
* All errors encountered are handled as warnings, so if dependency injection
* seems to be failing, check the logs for more information.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class DependencyInjectionService {
/**
* Dependency injectors. Use getters and setters to modify this list
*/
private final LinkedList<AbstractDependencyInjector> injectors = new LinkedList<AbstractDependencyInjector>();
/**
* Perform dependency injection in the caller object
*
* @param caller
* object to perform DI on
*/
@SuppressWarnings("nls")
public void inject(Object caller) {
// Traverse through class and all parent classes, looking for
// fields declared with the @Autowired annotation and using
// dependency injection to set them as appropriate
Class<?> cls = caller.getClass();
while(cls != null) {
String packageName = cls.getPackage().getName();
if(!isQualifiedPackage(packageName))
break;
for(Field field : cls.getDeclaredFields()) {
if(field.getAnnotation(Autowired.class) != null) {
field.setAccessible(true);
try {
handleField(caller, field);
} catch (IllegalStateException e) {
throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'",
field.getName(), field.getType()), e);
} catch (IllegalArgumentException e) {
throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'",
field.getName(), field.getType()), e);
} catch (IllegalAccessException e) {
throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'",
field.getName(), field.getType()), e);
}
}
}
cls = cls.getSuperclass();
}
}
@SuppressWarnings("nls")
private boolean isQualifiedPackage(String packageName) {
if(packageName.startsWith("com.todoroo"))
return true;
if(packageName.startsWith("com.timsu"))
return true;
if(packageName.startsWith("org.weloveastrid"))
return true;
return false;
}
/**
* This method returns the appropriate dependency object based on the type
* that this autowired field accepts
*
* @param caller
* calling object
* @param field
* field to inject
*/
@SuppressWarnings("nls")
private synchronized void handleField(Object caller, Field field)
throws IllegalStateException, IllegalArgumentException,
IllegalAccessException {
if(field.getType().isPrimitive())
throw new IllegalStateException(String.format(
"Tried to dependency-inject primative field '%s' of type '%s'",
field.getName(), field.getType()));
// field has already been processed, ignore
if (field.get(caller) != null) {
return;
}
for (AbstractDependencyInjector injector : injectors) {
Object injection = injector.getInjection(caller, field);
if (injection != null) {
field.set(caller, injection);
return;
}
}
throw new IllegalStateException(
String.format("No dependency injector found for autowired " +
"field '%s' in class '%s'. Injectors: %s",
field.getName(), caller.getClass().getName(),
injectors));
}
// --- default dependency injector
private class DefaultDependencyInjector extends AbstractDependencyInjector {
@SuppressWarnings("nls")
@Override
protected void addInjectables() {
injectables.put("debug", false);
injectables.put("exceptionService", ExceptionService.class);
}
}
// --- static methods
private static DependencyInjectionService instance = null;
DependencyInjectionService() {
// prevent instantiation
injectors.add(new DefaultDependencyInjector());
}
/**
* Gets the singleton instance of the dependency injection service.
* @return
*/
public synchronized static DependencyInjectionService getInstance() {
if(instance == null)
instance = new DependencyInjectionService();
return instance;
}
/**
* Removes the supplied injector
* @return
*/
public synchronized void removeInjector(AbstractDependencyInjector injector) {
injectors.remove(injector);
}
/**
* Adds a Dependency Injector to the front of the list
* @param injectors
*/
public synchronized void addInjector(AbstractDependencyInjector injector) {
removeInjector(injector);
this.injectors.addFirst(injector);
}
}

@ -0,0 +1,162 @@
package com.todoroo.andlib.service;
import java.lang.Thread.UncaughtExceptionHandler;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import com.todoroo.astrid.api.R;
/**
* Exception handling utility class - reports and logs errors
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class ExceptionService {
@Autowired
public ErrorReporter[] errorReporters;
public ExceptionService() {
DependencyInjectionService.getInstance().inject(this);
if(errorReporters == null) {
errorReporters = new ErrorReporter[] {
new AndroidLogReporter()
};
}
}
/**
* Report the error via registered error handlers
*
* @param name Internal error name. Not displayed to user
* @param error Exception encountered. Message will be displayed to user
*/
public void reportError(String name, Throwable error) {
if(errorReporters == null)
return;
for(ErrorReporter reporter : errorReporters) {
try {
reporter.handleError(name, error);
} catch (Exception e) {
Log.e("astrid-exception-service", "Exception handling error", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
/**
* Display error dialog if context is activity and report error
*
* @param context Application Context
* @param name Internal error name. Not displayed to user
* @param error Exception encountered. Message will be displayed to user
*/
public void displayAndReportError(final Context context, String name, Throwable error) {
if(context instanceof Activity) {
final String messageToDisplay;
// pretty up the message when displaying to user
if(error == null)
messageToDisplay = context.getString(R.string.DLG_error_generic);
else
messageToDisplay = context.getString(R.string.DLG_error, error);
((Activity)context).runOnUiThread(new Runnable() {
public void run() {
try {
new AlertDialog.Builder(context)
.setTitle(R.string.DLG_error_title)
.setMessage(messageToDisplay)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, null)
.show();
} catch (Exception e) {
// suppress errors during dialog creation
}
}
});
}
reportError(name, error);
}
/**
* Error reporter interface
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface ErrorReporter {
public void handleError(String name, Throwable error);
}
/**
* AndroidLogReporter reports errors to LogCat
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class AndroidLogReporter implements ErrorReporter {
/**
* Report the error to the logs
*
* @param name
* @param error
*/
public void handleError(String name, Throwable error) {
String tag = null;
if(ContextManager.getContext() != null) {
PackageManager pm = ContextManager.getContext().getPackageManager();
try {
String appName = pm.getApplicationInfo(ContextManager.getContext().
getPackageName(), 0).loadLabel(pm).toString();
tag = appName + "-" + name; //$NON-NLS-1$
} catch (NameNotFoundException e) {
// give up
}
}
if(tag == null)
tag = "unknown-" + name; //$NON-NLS-1$
if(error == null)
Log.e(tag, "Exception: " + name); //$NON-NLS-1$
else
Log.e(tag, error.toString(), error);
}
}
/**
* Uncaught exception handler uses the exception utilities class to
* report errors
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class TodorooUncaughtExceptionHandler implements UncaughtExceptionHandler {
private final UncaughtExceptionHandler defaultUEH;
@Autowired
protected ExceptionService exceptionService;
public TodorooUncaughtExceptionHandler() {
defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
DependencyInjectionService.getInstance().inject(this);
}
public void uncaughtException(Thread thread, Throwable ex) {
if(exceptionService != null)
exceptionService.reportError("uncaught", ex); //$NON-NLS-1$
defaultUEH.uncaughtException(thread, ex);
}
}
}

@ -0,0 +1,13 @@
package com.todoroo.andlib.service;
import java.io.IOException;
public class HttpErrorException extends IOException {
private static final long serialVersionUID = 5373340422464657279L;
public HttpErrorException(int code, String message) {
super(String.format("%d %s", code, message)); //$NON-NLS-1$
}
}

@ -0,0 +1,164 @@
package com.todoroo.andlib.service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import android.util.Log;
/**
* RestClient allows Android to consume web requests.
* <p>
* Portions by Praeda:
* http://senior.ceng.metu.edu.tr/2009/praeda/2009/01/11/a-simple
* -restful-client-at-android/
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class HttpRestClient implements RestClient {
private static final int HTTP_UNAVAILABLE_END = 599;
private static final int HTTP_UNAVAILABLE_START = 500;
private static final int HTTP_OK = 200;
private static final int TIMEOUT_MILLIS = 30000;
private static WeakReference<HttpClient> httpClient = null;
protected boolean debug = false;
public HttpRestClient() {
DependencyInjectionService.getInstance().inject(this);
}
private static String convertStreamToString(InputStream is) {
/*
* To convert the InputStream to String we use the
* BufferedReader.readLine() method. We iterate until the BufferedReader
* return null which means there's no more data to read. Each line will
* appended to a StringBuilder and returned as String.
*/
BufferedReader reader = new BufferedReader(new InputStreamReader(is), 16384);
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n"); //$NON-NLS-1$
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
private synchronized static HttpClient getClient() {
if (httpClient == null || httpClient.get() == null) {
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, TIMEOUT_MILLIS);
HttpConnectionParams.setSoTimeout(params, TIMEOUT_MILLIS);
HttpClient client = new DefaultHttpClient(params);
httpClient = new WeakReference<HttpClient>(client);
return client;
} else {
return httpClient.get();
}
}
private String processHttpResponse(HttpResponse response) throws IOException {
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode >= HTTP_UNAVAILABLE_START && statusCode <= HTTP_UNAVAILABLE_END) {
throw new HttpUnavailableException();
} else if(statusCode != HTTP_OK) {
throw new HttpErrorException(response.getStatusLine().getStatusCode(),
response.getStatusLine().getReasonPhrase());
}
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream contentStream = entity.getContent();
try {
return convertStreamToString(contentStream);
} finally {
contentStream.close();
}
}
return null;
}
/**
* Issue an HTTP GET for the given URL, return the response
*
* @param url url with url-encoded params
* @return response, or null if there was no response
* @throws IOException
*/
public synchronized String get(String url) throws IOException {
if(debug)
Log.d("http-rest-client-get", url); //$NON-NLS-1$
try {
HttpGet httpGet = new HttpGet(url);
HttpResponse response = getClient().execute(httpGet);
return processHttpResponse(response);
} catch (IOException e) {
throw e;
} catch (Exception e) {
IOException ioException = new IOException(e.getMessage());
ioException.initCause(e);
throw ioException;
}
}
/**
* Issue an HTTP POST for the given URL, return the response
*
* @param url
* @param data
* url-encoded data
* @throws IOException
*/
public synchronized String post(String url, String data) throws IOException {
if(debug)
Log.d("http-rest-client-post", url + " | " + data); //$NON-NLS-1$ //$NON-NLS-2$
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(data));
HttpResponse response = getClient().execute(httpPost);
return processHttpResponse(response);
} catch (IOException e) {
throw e;
} catch (Exception e) {
IOException ioException = new IOException(e.getMessage());
ioException.initCause(e);
throw ioException;
}
}
}

@ -0,0 +1,25 @@
package com.todoroo.andlib.service;
import java.io.IOException;
/**
* Exception displayed when a 500 error is received on an HTTP invocation
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class HttpUnavailableException extends IOException {
private static final long serialVersionUID = 5373340422464657279L;
public HttpUnavailableException() {
super();
DependencyInjectionService.getInstance().inject(this);
}
@Override
public String getMessage() {
return "Sorry, our servers are experiencing some issues. Please try again later!"; //$NON-NLS-1$ // FIXME
}
}

@ -0,0 +1,49 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.service;
import android.app.Notification;
import android.content.Context;
/**
* Notification Manager stub
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface NotificationManager {
public void cancel(int id);
public void cancelAll();
public void notify(int id, Notification notification);
/**
* Instantiation of notification manager that passes through to
* Android's notification manager
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class AndroidNotificationManager implements NotificationManager {
private final android.app.NotificationManager nm;
public AndroidNotificationManager(Context context) {
nm = (android.app.NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
}
public void cancel(int id) {
nm.cancel(id);
}
public void cancelAll() {
nm.cancelAll();
}
public void notify(int id, Notification notification) {
nm.notify(id, notification);
}
}
}

@ -0,0 +1,14 @@
package com.todoroo.andlib.service;
import java.io.IOException;
/**
* RestClient stub invokes the HTML requests as desired
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface RestClient {
public String get(String url) throws IOException;
public String post(String url, String data) throws IOException;
}

@ -0,0 +1,5 @@
/**
* Todoroo Library classes for the service layer: dependency injection,
* exceptions, HTTP requests, etc.
*/
package com.todoroo.andlib.service;

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

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

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

@ -0,0 +1,112 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AND;
import static com.todoroo.andlib.sql.SqlConstants.BETWEEN;
import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Field extends DBObject<Field> {
protected Field(String expression) {
super(expression);
}
public static Field field(String expression) {
return new Field(expression);
}
public Criterion eq(Object value) {
if(value == null)
return UnaryCriterion.isNull(this);
return UnaryCriterion.eq(this, value);
}
/**
* Adds the criterion that the field must be equal to the given string, ignoring case.
*
* Thanks to a sqlite bug, this will only work for ASCII values.
*
* @param value string which field must equal
* @return the criterion
*/
@SuppressWarnings("nls")
public Criterion eqCaseInsensitive(String value) {
String escaped = value.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_");
return UnaryCriterion.like(this, escaped, "\\");
}
public Criterion neq(Object value) {
if(value == null)
return UnaryCriterion.isNotNull(this);
return UnaryCriterion.neq(this, value);
}
public Criterion gt(Object value) {
return UnaryCriterion.gt(this, value);
}
public Criterion lt(final Object value) {
return UnaryCriterion.lt(this, value);
}
public Criterion lte(final Object value) {
return UnaryCriterion.lte(this, value);
}
public Criterion isNull() {
return UnaryCriterion.isNull(this);
}
public Criterion isNotNull() {
return UnaryCriterion.isNotNull(this);
}
public Criterion between(final Object lower, final Object upper) {
final Field field = this;
return new Criterion(null) {
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(BETWEEN).append(SPACE).append(lower).append(SPACE).append(AND)
.append(SPACE).append(upper);
}
};
}
public Criterion like(final String value) {
return UnaryCriterion.like(this, value);
}
public Criterion like(String value, String escape) {
return UnaryCriterion.like(this, value, escape);
}
public <T> Criterion in(final T... value) {
final Field field = this;
return new Criterion(Operator.in) {
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(SPACE);
for (T t : value) {
sb.append(t.toString()).append(COMMA);
}
sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS);
}
};
}
public Criterion in(final Query query) {
final Field field = this;
return new Criterion(Operator.in) {
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(query)
.append(RIGHT_PARENTHESIS);
}
};
}
}

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

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

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

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

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

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

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

@ -0,0 +1,296 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.ALL;
import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.DISTINCT;
import static com.todoroo.andlib.sql.SqlConstants.FROM;
import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY;
import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.LIMIT;
import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY;
import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.SELECT;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import static com.todoroo.andlib.sql.SqlConstants.WHERE;
import static com.todoroo.andlib.sql.SqlTable.table;
import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import com.todoroo.andlib.data.Property;
import com.todoroo.astrid.api.AstridApiConstants;
public final class Query {
private SqlTable table;
private String queryTemplate = null;
private final ArrayList<Criterion> criterions = new ArrayList<Criterion>();
private final ArrayList<Field> fields = new ArrayList<Field>();
private final ArrayList<Join> joins = new ArrayList<Join>();
private final ArrayList<Field> groupBies = new ArrayList<Field>();
private final ArrayList<Order> orders = new ArrayList<Order>();
private final ArrayList<Criterion> havings = new ArrayList<Criterion>();
private int limits = -1;
private boolean distinct = false;
private Query(Field... fields) {
this.fields.addAll(asList(fields));
}
public static Query select(Field... fields) {
return new Query(fields);
}
public static Query selectDistinct(Field... fields) {
Query query = new Query(fields);
query.distinct = true;
return query;
}
public Query from(SqlTable fromTable) {
this.table = fromTable;
return this;
}
public Query join(Join... join) {
joins.addAll(asList(join));
return this;
}
public Query where(Criterion criterion) {
criterions.add(criterion);
return this;
}
public Query groupBy(Field... groupBy) {
groupBies.addAll(asList(groupBy));
return this;
}
public Query orderBy(Order... order) {
orders.addAll(asList(order));
return this;
}
public Query limit(int limit) {
limits = limit;
return this;
}
public Query appendSelectFields(Property<?>... selectFields) {
this.fields.addAll(asList(selectFields));
return this;
}
@Override
public boolean equals(Object o) {
return this == o || !(o == null || getClass() != o.getClass()) && this.toString().equals(o.toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public String toString() {
StringBuilder sql = new StringBuilder();
visitSelectClause(sql);
visitFromClause(sql);
visitJoinClause(sql);
if(queryTemplate == null) {
visitWhereClause(sql);
visitGroupByClause(sql);
visitOrderByClause(sql);
visitLimitClause(sql);
} else {
if(groupBies.size() > 0 || orders.size() > 0 ||
havings.size() > 0)
throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$
sql.append(queryTemplate);
}
return sql.toString();
}
private void visitOrderByClause(StringBuilder sql) {
if (orders.isEmpty()) {
return;
}
sql.append(ORDER_BY);
for (Order order : orders) {
sql.append(SPACE).append(order).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
@SuppressWarnings("nls")
private void visitGroupByClause(StringBuilder sql) {
if (groupBies.isEmpty()) {
return;
}
sql.append(GROUP_BY);
for (Field groupBy : groupBies) {
sql.append(SPACE).append(groupBy).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
if (havings.isEmpty()) {
return;
}
sql.append("HAVING");
for (Criterion havingCriterion : havings) {
sql.append(SPACE).append(havingCriterion).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) {
return;
}
sql.append(WHERE);
for (Criterion criterion : criterions) {
sql.append(SPACE).append(criterion).append(SPACE);
}
}
private void visitJoinClause(StringBuilder sql) {
for (Join join : joins) {
sql.append(join).append(SPACE);
}
}
private void visitFromClause(StringBuilder sql) {
if (table == null) {
return;
}
sql.append(FROM).append(SPACE).append(table).append(SPACE);
}
private void visitSelectClause(StringBuilder sql) {
sql.append(SELECT).append(SPACE);
if(distinct)
sql.append(DISTINCT).append(SPACE);
if (fields.isEmpty()) {
sql.append(ALL).append(SPACE);
return;
}
for (Field field : fields) {
sql.append(field.toStringInSelect()).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
private void visitLimitClause(StringBuilder sql) {
if(limits > -1)
sql.append(LIMIT).append(SPACE).append(limits).append(SPACE);
}
public SqlTable as(String alias) {
return table(LEFT_PARENTHESIS + this.toString() + RIGHT_PARENTHESIS).as(alias);
}
public Query having(Criterion criterion) {
this.havings.add(criterion);
return this;
}
/**
* Gets a list of fields returned by this query
* @return
*/
public Property<?>[] getFields() {
return fields.toArray(new Property<?>[fields.size()]);
}
/**
* Add the SQL query template (comes after the "from")
* @param template
* @return query
*/
public Query withQueryTemplate(String template) {
queryTemplate = template;
return this;
}
/**
* Parse out properties and run query
* @param cr
* @param baseUri
* @return
*/
public Cursor queryContentResolver(ContentResolver cr, Uri baseUri) {
Uri uri = baseUri;
if(joins.size() != 0)
throw new UnsupportedOperationException("can't perform join in content resolver query"); //$NON-NLS-1$
String[] projection = new String[fields.size()];
for(int i = 0; i < projection.length; i++)
projection[i] = fields.get(i).toString();
StringBuilder groupByClause = new StringBuilder();
StringBuilder selectionClause = new StringBuilder();
StringBuilder orderClause = new StringBuilder();
if(queryTemplate != null) {
QueryTemplateHelper.queryForContentResolver(queryTemplate,
selectionClause, orderClause, groupByClause);
} else {
if(groupBies.size() > 0) {
for (Field groupBy : groupBies)
groupByClause.append(SPACE).append(groupBy).append(COMMA);
if(groupByClause.length() > 0)
groupByClause.deleteCharAt(groupByClause.length() - 1);
}
for (Criterion criterion : criterions)
selectionClause.append(criterion).append(SPACE);
for (Order order : orders)
orderClause.append(SPACE).append(order).append(COMMA);
if(orderClause.length() > 0)
orderClause.deleteCharAt(orderClause.length() - 1);
}
if(groupByClause.length() > 0)
uri = Uri.withAppendedPath(baseUri, AstridApiConstants.GROUP_BY_URI +
groupByClause.toString().trim());
return cr.query(uri, projection, selectionClause.toString(), null,
orderClause.toString());
}
/** query template helper */
public static class QueryTemplateHelper {
/** build a content resolver query */
@SuppressWarnings("nls")
public static void queryForContentResolver(String queryTemplate,
StringBuilder selectionClause, StringBuilder orderClause,
StringBuilder groupByClause) {
Pattern where = Pattern.compile("WHERE (.*?)(LIMIT|HAVING|GROUP|ORDER|\\Z)");
Matcher whereMatcher = where.matcher(queryTemplate);
if(whereMatcher.find())
selectionClause.append(whereMatcher.group(1).trim());
Pattern group = Pattern.compile("GROUP BY (.*?)(LIMIT|HAVING|ORDER|\\Z)");
Matcher groupMatcher = group.matcher(queryTemplate);
if(groupMatcher.find())
groupByClause.append(groupMatcher.group(1).trim());
Pattern order = Pattern.compile("ORDER BY (.*?)(LIMIT|HAVING|\\Z)");
Matcher orderMatcher = order.matcher(queryTemplate);
if(orderMatcher.find())
orderClause.append(orderMatcher.group(1).trim());
}
}
}

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

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

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

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

@ -0,0 +1,4 @@
/**
* Fork of <a href="http://code.google.com/p/sql-dsl/">sql-dsl</a>
*/
package com.todoroo.andlib.sql;

@ -0,0 +1,547 @@
package com.todoroo.andlib.utility;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.text.InputType;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnTouchListener;
import android.widget.TextView;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.service.ExceptionService;
/**
* Android Utility Classes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class AndroidUtilities {
public static final String SEPARATOR_ESCAPE = "!PIPE!"; //$NON-NLS-1$
public static final String SERIALIZATION_SEPARATOR = "|"; //$NON-NLS-1$
// --- utility methods
/** Suppress virtual keyboard until user's first tap */
public static void suppressVirtualKeyboard(final TextView editor) {
final int inputType = editor.getInputType();
editor.setInputType(InputType.TYPE_NULL);
editor.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
editor.setInputType(inputType);
editor.setOnTouchListener(null);
return false;
}
});
}
/**
* @return true if we're connected to the internet
*/
public static boolean isConnected(Context context) {
ConnectivityManager manager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = manager.getActiveNetworkInfo();
if (info == null)
return false;
if (info.getState() != State.CONNECTED)
return false;
return true;
}
/** Fetch the image specified by the given url */
public static Bitmap fetchImage(URL url) throws IOException {
InputStream is = null;
try {
URLConnection conn = url.openConnection();
conn.connect();
is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is, 16384);
try {
Bitmap bitmap = BitmapFactory.decodeStream(bis);
return bitmap;
} finally {
bis.close();
}
} finally {
if(is != null)
is.close();
}
}
/**
* Start the given intent, handling security exceptions if they arise
*
* @param context
* @param intent
* @param request request code. if negative, no request.
*/
public static void startExternalIntent(Context context, Intent intent, int request) {
try {
if(request > -1 && context instanceof Activity)
((Activity)context).startActivityForResult(intent, request);
else
context.startActivity(intent);
} catch (Exception e) {
getExceptionService().displayAndReportError(context,
"start-external-intent-" + intent.toString(), //$NON-NLS-1$
e);
}
}
/**
* Start the given intent, handling security exceptions if they arise
*
* @param activity
* @param intent
* @param requestCode
*/
public static void startExternalIntentForResult(
Activity activity, Intent intent, int requestCode) {
try {
activity.startActivityForResult(intent, requestCode);
} catch (SecurityException e) {
getExceptionService().displayAndReportError(activity,
"start-external-intent-" + intent.toString(), //$NON-NLS-1$
e);
}
}
/**
* Put an arbitrary object into a {@link ContentValues}
* @param target
* @param key
* @param value
*/
public static void putInto(ContentValues target, String key, Object value) {
if(value instanceof String)
target.put(key, (String) value);
else if(value instanceof Long)
target.put(key, (Long) value);
else if(value instanceof Integer)
target.put(key, (Integer) value);
else if(value instanceof Double)
target.put(key, (Double) value);
else
throw new UnsupportedOperationException("Could not handle type " + //$NON-NLS-1$
value.getClass());
}
/**
* Rips apart a content value into two string arrays, keys and value
*/
public static String[][] contentValuesToStringArrays(ContentValues source) {
String[][] result = new String[2][source.size()];
int i = 0;
for(Entry<String, Object> entry : source.valueSet()) {
result[0][i] = entry.getKey();
result[1][i++] = entry.getValue().toString();
}
return result;
}
/**
* Return index of value in array
* @param array array to search
* @param value value to look for
* @return
*/
public static <TYPE> int indexOf(TYPE[] array, TYPE value) {
for(int i = 0; i < array.length; i++)
if(array[i].equals(value))
return i;
return -1;
}
/**
* Serializes a content value into a string
*/
public static String contentValuesToSerializedString(ContentValues source) {
StringBuilder result = new StringBuilder();
for(Entry<String, Object> entry : source.valueSet()) {
result.append(entry.getKey().replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE)).append(
SERIALIZATION_SEPARATOR);
Object value = entry.getValue();
if(value instanceof Integer)
result.append('i').append(value);
else if(value instanceof Double)
result.append('d').append(value);
else if(value instanceof Long)
result.append('l').append(value);
else if(value instanceof String)
result.append('s').append(value.toString());
else
throw new UnsupportedOperationException(value.getClass().toString());
result.append(SERIALIZATION_SEPARATOR);
}
return result.toString();
}
/**
* Turn ContentValues into a string
* @param string
* @return
*/
public static ContentValues contentValuesFromSerializedString(String string) {
if(string == null)
return new ContentValues();
String[] pairs = string.split("\\" + SERIALIZATION_SEPARATOR); //$NON-NLS-1$
ContentValues result = new ContentValues();
for(int i = 0; i < pairs.length; i += 2) {
String key = pairs[i].replaceAll(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR);
String value = pairs[i+1].substring(1);
try {
switch(pairs[i+1].charAt(0)) {
case 'i':
result.put(key, Integer.parseInt(value));
break;
case 'd':
result.put(key, Double.parseDouble(value));
break;
case 'l':
result.put(key, Long.parseLong(value));
break;
case 's':
result.put(key, value.replace(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR));
break;
}
} catch (NumberFormatException e) {
// failed parse to number, try to put a string
result.put(key, value);
}
}
return result;
}
/**
* Turn ContentValues into a string
* @param string
* @return
*/
@SuppressWarnings("nls")
public static ContentValues contentValuesFromString(String string) {
if(string == null)
return null;
String[] pairs = string.split("=");
ContentValues result = new ContentValues();
String key = null;
for(int i = 0; i < pairs.length; i++) {
String newKey = null;
int lastSpace = pairs[i].lastIndexOf(' ');
if(lastSpace != -1) {
newKey = pairs[i].substring(lastSpace + 1);
pairs[i] = pairs[i].substring(0, lastSpace);
} else {
newKey = pairs[i];
}
if(key != null)
result.put(key.trim(), pairs[i].trim());
key = newKey;
}
return result;
}
/**
* Returns true if a and b or null or a.equals(b)
* @param a
* @param b
* @return
*/
public static boolean equals(Object a, Object b) {
if(a == null && b == null)
return true;
if(a == null)
return false;
return a.equals(b);
}
/**
* Copy a file from one place to another
*
* @param in
* @param out
* @throws Exception
*/
public static void copyFile(File in, File out) throws Exception {
FileInputStream fis = new FileInputStream(in);
FileOutputStream fos = new FileOutputStream(out);
try {
byte[] buf = new byte[1024];
int i = 0;
while ((i = fis.read(buf)) != -1) {
fos.write(buf, 0, i);
}
} catch (Exception e) {
throw e;
} finally {
fis.close();
fos.close();
}
}
/**
* Find a child view of a certain type
* @param view
* @param type
* @return first view (by DFS) if found, or null if none
*/
public static <TYPE> TYPE findViewByType(View view, Class<TYPE> type) {
if(view == null)
return null;
if(type.isInstance(view))
return (TYPE) view;
if(view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
for(int i = 0; i < group.getChildCount(); i++) {
TYPE v = findViewByType(group.getChildAt(i), type);
if(v != null)
return v;
}
}
return null;
}
/**
* @return Android SDK version as an integer. Works on all versions
*/
public static int getSdkVersion() {
return Integer.parseInt(android.os.Build.VERSION.SDK);
}
/**
* Copy databases to a given folder. Useful for debugging
* @param folder
*/
public static void copyDatabases(Context context, String folder) {
File folderFile = new File(folder);
if(!folderFile.exists())
folderFile.mkdir();
for(String db : context.databaseList()) {
File dbFile = context.getDatabasePath(db);
try {
copyFile(dbFile, new File(folderFile.getAbsolutePath() +
File.separator + db));
} catch (Exception e) {
Log.e("ERROR", "ERROR COPYING DB " + db, e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
/**
* Sort files by date so the newest file is on top
* @param files
*/
public static void sortFilesByDateDesc(File[] files) {
Arrays.sort(files, new Comparator<File>() {
public int compare(File o1, File o2) {
return Long.valueOf(o2.lastModified()).compareTo(Long.valueOf(o1.lastModified()));
}
});
}
/**
* Search for the given value in the map, returning key if found
* @param map
* @param value
* @return null if not found, otherwise key
*/
public static <KEY, VALUE> KEY findKeyInMap(Map<KEY, VALUE> map, VALUE value){
for (Entry<KEY, VALUE> entry: map.entrySet()) {
if(entry.getValue().equals(value))
return entry.getKey();
}
return null;
}
/**
* Sleep, ignoring interruption. Before using this method, think carefully
* about why you are ignoring interruptions.
*
* @param l
*/
public static void sleepDeep(long l) {
try {
Thread.sleep(l);
} catch (InterruptedException e) {
// ignore
}
}
/**
* Call a method via reflection if API level is at least minSdk
* @param minSdk minimum sdk number (i.e. 8)
* @param receiver object to call method on
* @param methodName method name to call
* @param params method parameter types
* @param args arguments
* @return method return value, or null if nothing was called or exception
*/
@SuppressWarnings("nls")
public static Object callApiMethod(int minSdk, Object receiver,
String methodName, Class<?>[] params, Object... args) {
if(getSdkVersion() < minSdk)
return null;
Method method;
try {
method = receiver.getClass().getMethod(methodName, params);
return method.invoke(receiver, args);
} catch (SecurityException e) {
getExceptionService().reportError("call-method", e);
} catch (NoSuchMethodException e) {
getExceptionService().reportError("call-method", e);
} catch (IllegalArgumentException e) {
getExceptionService().reportError("call-method", e);
} catch (IllegalAccessException e) {
getExceptionService().reportError("call-method", e);
} catch (InvocationTargetException e) {
getExceptionService().reportError("call-method", e);
}
return null;
}
/**
* From Android MyTracks project (http://mytracks.googlecode.com/).
* Licensed under the Apache Public License v2
* @param activity
* @param id
* @return
*/
public static CharSequence readFile(Context activity, int id) {
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(
activity.getResources().openRawResource(id)));
String line;
StringBuilder buffer = new StringBuilder();
while ((line = in.readLine()) != null) {
buffer.append(line).append('\n');
}
return buffer;
} catch (IOException e) {
return ""; //$NON-NLS-1$
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// Ignore
}
}
}
}
/**
* Performs an md5 hash on the input string
* @param input
* @return
*/
@SuppressWarnings("nls")
public static String md5(String input) {
try {
byte[] bytesOfMessage = input.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(bytesOfMessage);
BigInteger bigInt = new BigInteger(1,digest);
String hashtext = bigInt.toString(16);
while(hashtext.length() < 32 ){
hashtext = "0" + hashtext;
}
return hashtext;
} catch (Exception e) {
return "";
}
}
/**
* Create an intent to a remote activity
* @param appPackage
* @param activityClass
* @return
*/
public static Intent remoteIntent(String appPackage, String activityClass) {
Intent intent = new Intent();
intent.setClassName(appPackage, activityClass);
return intent;
}
/**
* Gets application signature
* @return application signature, or null if an error was encountered
*/
public static String getSignature(Context context, String packageName) {
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
return packageInfo.signatures[0].toCharsString();
} catch (Exception e) {
return null;
}
}
/**
* Join items to a list
* @param <TYPE>
* @param list
* @param newList
* @param newItems
* @return
*/
public static Property<?>[] addToArray(Property<?>[] list, Property<?>... newItems) {
Property<?>[] newList = new Property<?>[list.length + newItems.length];
for(int i = 0; i < list.length; i++)
newList[i] = list[i];
for(int i = 0; i < newItems.length; i++)
newList[list.length + i] = newItems[i];
return newList;
}
// --- internal
private static ExceptionService exceptionService = null;
private static ExceptionService getExceptionService() {
if(exceptionService == null)
synchronized(AndroidUtilities.class) {
if(exceptionService == null)
exceptionService = new ExceptionService();
}
return exceptionService;
}
}

@ -0,0 +1,136 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.andlib.utility;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import android.content.Context;
import android.text.format.DateUtils;
public class DateUtilities {
/* ======================================================================
* ============================================================ long time
* ====================================================================== */
/** Convert unixtime into date */
public static final Date unixtimeToDate(long millis) {
if(millis == 0)
return null;
return new Date(millis);
}
/** Convert date into unixtime */
public static final long dateToUnixtime(Date date) {
if(date == null)
return 0;
return date.getTime();
}
/** Returns unixtime for current time */
public static final long now() {
return System.currentTimeMillis();
}
/** Returns unixtime one month from now */
public static final long oneMonthFromNow() {
Date date = new Date();
date.setMonth(date.getMonth() + 1);
return date.getTime();
}
/** Represents a single hour */
public static long ONE_HOUR = 3600000L;
/** Represents a single day */
public static long ONE_DAY = 24 * ONE_HOUR;
/** Represents a single week */
public static long ONE_WEEK = 7 * ONE_DAY;
/* ======================================================================
* =========================================================== formatters
* ====================================================================== */
static Boolean is24HourOverride = null;
@SuppressWarnings("nls")
public static boolean is24HourFormat(Context context) {
if(is24HourOverride != null)
return is24HourOverride;
String value = android.provider.Settings.System.getString(context.getContentResolver(),
android.provider.Settings.System.TIME_12_24);
boolean b24 = !(value == null || value.equals("12"));
return b24;
}
/**
* @param context android context
* @param date time to format
* @return time, with hours and minutes
*/
@SuppressWarnings("nls")
public static String getTimeString(Context context, Date date) {
String value;
if (is24HourFormat(context)) {
value = "H:mm";
} else {
value = "h:mm a";
}
return new SimpleDateFormat(value).format(date);
}
/**
* @param context android context
* @param date date to format
* @return date, with month, day, and year
*/
@SuppressWarnings("nls")
public static String getDateString(Context context, Date date) {
String month = DateUtils.getMonthString(date.getMonth() +
Calendar.JANUARY, DateUtils.LENGTH_MEDIUM);
String value;
// united states, you are special
if (Locale.US.equals(Locale.getDefault())
|| Locale.CANADA.equals(Locale.getDefault()))
value = "'#' d yyyy";
else
value = "d '#' yyyy";
return new SimpleDateFormat(value).format(date).replace("#", month);
}
/**
* @return date format as getDateFormat with weekday
*/
@SuppressWarnings("nls")
public static String getDateStringWithWeekday(Context context, Date date) {
String weekday = DateUtils.getDayOfWeekString(date.getDay() + Calendar.SUNDAY,
DateUtils.LENGTH_LONG);
return weekday + ", " + getDateString(context, date);
}
/**
* @return date format as getDateFormat with weekday
*/
@SuppressWarnings("nls")
public static String getDateStringWithTimeAndWeekday(Context context, Date date) {
return getDateStringWithWeekday(context, date) + " " + getTimeString(context, date);
}
/**
* @return date with time at the end
*/
@SuppressWarnings("nls")
public static String getDateStringWithTime(Context context, Date date) {
return getDateString(context, date) + " " + getTimeString(context, date);
}
}

@ -0,0 +1,217 @@
package com.todoroo.andlib.utility;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.webkit.WebView;
import com.todoroo.astrid.api.R;
public class DialogUtilities {
/**
* Displays a dialog box with a EditText and an ok / cancel
*
* @param activity
* @param text
* @param okListener
*/
public static void viewDialog(final Activity activity, final String text,
final View view, final DialogInterface.OnClickListener okListener,
final DialogInterface.OnClickListener cancelListener) {
if(activity.isFinishing())
return;
tryOnUiThread(activity, new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(R.string.DLG_question_title)
.setMessage(text)
.setView(view)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, okListener)
.setNegativeButton(android.R.string.cancel, cancelListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Display an OK dialog with HTML content
*
* @param context
* @param html
* @param title
*/
public static void htmlDialog(Context context, String html, int title) {
WebView webView = new WebView(context);
webView.loadData(html, "text/html", "utf-8"); //$NON-NLS-1$ //$NON-NLS-2$
webView.setBackgroundColor(0);
new AlertDialog.Builder(context)
.setTitle(title)
.setView(webView)
.setIcon(android.R.drawable.ic_dialog_info)
.setPositiveButton(android.R.string.ok, null)
.show();
}
/**
* Displays a dialog box with an OK button
*
* @param activity
* @param text
* @param okListener
*/
public static void okDialog(final Activity activity, final String text,
final DialogInterface.OnClickListener okListener) {
if(activity.isFinishing())
return;
tryOnUiThread(activity, new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(R.string.DLG_information_title)
.setMessage(text)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, okListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Displays a dialog box with an OK button
*
* @param activity
* @param text
* @param okListener
*/
public static void okDialog(final Activity activity, final String title,
final int icon, final CharSequence text,
final DialogInterface.OnClickListener okListener) {
if(activity.isFinishing())
return;
tryOnUiThread(activity, new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(text)
.setIcon(icon)
.setPositiveButton(android.R.string.ok, okListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Displays a dialog box with OK and Cancel buttons and custom title
*
* @param activity
* @param title
* @param text
* @param okListener
* @param cancelListener
*/
public static void okCancelDialog(final Activity activity, final String title,
final String text, final DialogInterface.OnClickListener okListener,
final DialogInterface.OnClickListener cancelListener) {
if(activity.isFinishing())
return;
tryOnUiThread(activity, new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(text)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, okListener)
.setNegativeButton(android.R.string.cancel, cancelListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Displays a dialog box with OK and Cancel buttons
*
* @param activity
* @param text
* @param okListener
* @param cancelListener
*/
public static void okCancelDialog(final Activity activity, final String text,
final DialogInterface.OnClickListener okListener,
final DialogInterface.OnClickListener cancelListener) {
if(activity.isFinishing())
return;
tryOnUiThread(activity, new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(R.string.DLG_confirm_title)
.setMessage(text)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, okListener)
.setNegativeButton(android.R.string.cancel, cancelListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Displays a progress dialog. Must be run on the UI thread
* @param context
* @param text
* @return
*/
public static ProgressDialog progressDialog(Context context, String text) {
ProgressDialog dialog = new ProgressDialog(context);
dialog.setIndeterminate(true);
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setMessage(text);
dialog.show();
dialog.setOwnerActivity((Activity)context);
return dialog;
}
/**
* Dismiss a dialog off the UI thread
*
* @param activity
* @param dialog
*/
public static void dismissDialog(Activity activity, final Dialog dialog) {
if(dialog == null)
return;
tryOnUiThread(activity, new Runnable() {
public void run() {
try {
dialog.dismiss();
} catch (Exception e) {
// could have killed activity
}
}
});
}
private static void tryOnUiThread(Activity activity, final Runnable runnable) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
runnable.run();
} catch (Exception e) {
// probably window was closed
}
}
});
}
}

@ -0,0 +1,56 @@
package com.todoroo.andlib.utility;
/**
* Pair utility class
*
* @author Tim Su <tim@todoroo.com>
*
* @param <L>
* @param <R>
*/
public class Pair<L, R> {
private final L left;
private final R right;
public R getRight() {
return right;
}
public L getLeft() {
return left;
}
public Pair(final L left, final R right) {
this.left = left;
this.right = right;
}
public static <A, B> Pair<A, B> create(A left, B right) {
return new Pair<A, B>(left, right);
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof Pair<?, ?>))
return false;
final Pair<?, ?> other = (Pair<?, ?>) o;
return equal(getLeft(), other.getLeft()) && equal(getRight(), other.getRight());
}
public static final boolean equal(Object o1, Object o2) {
if (o1 == null) {
return o2 == null;
}
return o1.equals(o2);
}
@Override
public int hashCode() {
int hLeft = getLeft() == null ? 0 : getLeft().hashCode();
int hRight = getRight() == null ? 0 : getRight().hashCode();
return hLeft + (57 * hRight);
}
}

@ -0,0 +1,255 @@
package com.todoroo.andlib.utility;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Resources;
import android.preference.PreferenceManager;
import com.todoroo.andlib.service.ContextManager;
/**
* Helper class for reading and writing SharedPreferences
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class Preferences {
/**
* Helper to write to editor if key specified is null. Writes a String
* property with the given integer
*
* @param prefs
* @param editor
* @param r
* @param keyResource
* @param value
*/
public static void setIfUnset(SharedPreferences prefs, Editor editor, Resources r, int keyResource, int value) {
String key = r.getString(keyResource);
if(!prefs.contains(key))
editor.putString(key, Integer.toString(value));
}
/**
* Helper to write to editor if key specified is null
* @param prefs
* @param editor
* @param r
* @param keyResource
* @param value
*/
public static void setIfUnset(SharedPreferences prefs, Editor editor, Resources r, int keyResource, boolean value) {
String key = r.getString(keyResource);
if(!prefs.contains(key) || !(prefs.getAll().get(key) instanceof Boolean))
editor.putBoolean(key, value);
}
/* ======================================================================
* ======================================================= helper methods
* ====================================================================== */
/** Get preferences object from the context */
public static SharedPreferences getPrefs(Context context) {
context = context.getApplicationContext();
return PreferenceManager.getDefaultSharedPreferences(context);
}
/** @return true if given preference is set */
public static boolean isSet(String key) {
Context context = ContextManager.getContext();
return getPrefs(context).contains(key);
}
// --- preference fetching (string)
/** Gets an string value from a string preference. Returns null
* if the value is not set
*
* @param context
* @param key
* @return integer value, or null on error
*/
public static String getStringValue(String key) {
Context context = ContextManager.getContext();
return getPrefs(context).getString(key, null);
}
/** Gets an string value from a string preference. Returns null
* if the value is not set
*
* @param context
* @param key
* @return integer value, or null on error
*/
public static String getStringValue(int keyResource) {
Context context = ContextManager.getContext();
return getPrefs(context).getString(context.getResources().getString(keyResource), null);
}
/** Gets an integer value from a string preference. Returns null
* if the value is not set or not an integer.
*
* @param keyResource resource from string.xml
* @return integer value, or null on error
*/
public static int getIntegerFromString(int keyResource, int defaultValue) {
Context context = ContextManager.getContext();
Resources r = context.getResources();
String value = getPrefs(context).getString(r.getString(keyResource), null);
if(value == null)
return defaultValue;
try {
return Integer.parseInt(value);
} catch (Exception e) {
return defaultValue;
}
}
/** Gets an float value from a string preference. Returns null
* if the value is not set or not an flat.
*
* @param keyResource resource from string.xml
* @return
*/
public static Float getFloatFromString(int keyResource) {
Context context = ContextManager.getContext();
Resources r = context.getResources();
String value = getPrefs(context).getString(r.getString(keyResource), ""); //$NON-NLS-1$
try {
return Float.parseFloat(value);
} catch (Exception e) {
return null;
}
}
/**
* Sets string preference
*/
public static void setString(int keyResource, String newValue) {
Context context = ContextManager.getContext();
setString(context.getString(keyResource), newValue);
}
/**
* Sets string preference
*/
public static void setString(String key, String newValue) {
Context context = ContextManager.getContext();
Editor editor = getPrefs(context).edit();
editor.putString(key, newValue);
editor.commit();
}
/**
* Sets string preference from integer value
*/
public static void setStringFromInteger(int keyResource, int newValue) {
Context context = ContextManager.getContext();
Editor editor = getPrefs(context).edit();
editor.putString(context.getString(keyResource), Integer.toString(newValue));
editor.commit();
}
// --- preference fetching (boolean)
/** Gets a boolean preference (e.g. a CheckBoxPreference setting)
*
* @param key
* @param defValue
* @return default if value is unset otherwise the value
*/
public static boolean getBoolean(String key, boolean defValue) {
Context context = ContextManager.getContext();
try {
return getPrefs(context).getBoolean(key, defValue);
} catch (ClassCastException e) {
return defValue;
}
}
/** Gets a boolean preference (e.g. a CheckBoxPreference setting)
*
* @param keyResource
* @param defValue
* @return default if value is unset otherwise the value
*/
public static boolean getBoolean(int keyResources, boolean defValue) {
return getBoolean(ContextManager.getString(keyResources), defValue);
}
/**
* Sets boolean preference
* @param key
* @param value
*/
public static void setBoolean(int keyResource, boolean value) {
setBoolean(ContextManager.getString(keyResource), value);
}
/**
* Sets boolean preference
* @param key
* @param value
*/
public static void setBoolean(String key, boolean value) {
Context context = ContextManager.getContext();
Editor editor = getPrefs(context).edit();
editor.putBoolean(key, value);
editor.commit();
}
// --- preference fetching (int)
/** Gets a int preference
*
* @param key
* @param defValue
* @return default if value is unset otherwise the value
*/
public static int getInt(String key, int defValue) {
Context context = ContextManager.getContext();
return getPrefs(context).getInt(key, defValue);
}
/**
* Sets int preference
* @param key
* @param value
*/
public static void setInt(String key, int value) {
Context context = ContextManager.getContext();
Editor editor = getPrefs(context).edit();
editor.putInt(key, value);
editor.commit();
}
// --- preference fetching (long)
/** Gets a long preference
*
* @param key
* @param defValue
* @return default if value is unset otherwise the value
*/
public static long getLong(String key, long defValue) {
Context context = ContextManager.getContext();
return getPrefs(context).getLong(key, defValue);
}
/**
* Sets long preference
* @param key
* @param value
*/
public static void setLong(String key, long value) {
Context context = ContextManager.getContext();
Editor editor = getPrefs(context).edit();
editor.putLong(key, value);
editor.commit();
}
}

@ -0,0 +1,85 @@
package com.todoroo.andlib.utility;
/**
* See the file "LICENSE" for the full license governing this code.
*/
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.RingtonePreference;
import com.todoroo.andlib.service.ContextManager;
/**
* Displays a preference screen for users to edit their preferences. Override
* updatePreferences to update the summary with preference values.
*
* @author Tim Su <tim@todoroo.com>
*
*/
abstract public class TodorooPreferenceActivity extends PreferenceActivity {
// --- abstract methods
public abstract int getPreferenceResource();
/**
* Update preferences for the given preference
* @param preference
* @param value setting. may be null.
*/
public abstract void updatePreferences(Preference preference, Object value);
// --- implementation
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ContextManager.setContext(this);
addPreferencesFromResource(getPreferenceResource());
}
protected void initializePreference(Preference preference) {
if(preference instanceof PreferenceGroup) {
PreferenceGroup group = (PreferenceGroup)preference;
for(int i = 0; i < group.getPreferenceCount(); i++) {
initializePreference(group.getPreference(i));
}
} else {
Object value = null;
if(preference instanceof ListPreference)
value = ((ListPreference)preference).getValue();
else if(preference instanceof CheckBoxPreference)
value = ((CheckBoxPreference)preference).isChecked();
else if(preference instanceof EditTextPreference)
value = ((EditTextPreference)preference).getText();
else if(preference instanceof RingtonePreference)
value = getPreferenceManager().getSharedPreferences().getString(preference.getKey(), null);
updatePreferences(preference, value);
preference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference myPreference, Object newValue) {
updatePreferences(myPreference, newValue);
return true;
}
});
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus) {
initializePreference(getPreferenceScreen());
}
}
}

@ -0,0 +1,4 @@
/**
* Todoroo Library utility classes
*/
package com.todoroo.andlib.utility;

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

@ -0,0 +1,270 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.content.Intent;
import android.widget.RemoteViews;
/**
* Constants for interfacing with Astrid.
*
* @author Tim Su <tim@todoroo.com>
*/
@SuppressWarnings("nls")
public class AstridApiConstants {
// --- General Constants
/**
* Astrid broadcast base package name
*/
public static final String PACKAGE = "com.todoroo.astrid";
/**
* Permission for reading tasks and receiving to GET_FILTERS intent
*/
public static final String PERMISSION_READ = PACKAGE + ".READ";
/**
* Permission for writing and creating tasks
*/
public static final String PERMISSION_WRITE = PACKAGE + ".WRITE";
/**
* Name of Astrid's publicly readable preference store
*/
public static final String PUBLIC_PREFS = "public";
// --- Content Provider
/**
* URI to append to base content URI for making group-by queries
*/
public static final String GROUP_BY_URI = "/groupby/";
// --- Broadcast Extras
/**
* Extras name for task id
*/
public static final String EXTRAS_TASK_ID = "task";
/**
* Extras name for a response item broadcast to astrid
*/
public static final String EXTRAS_RESPONSE = "response";
/**
* Extras name for plug-in identifier
*/
public static final String EXTRAS_ADDON = "addon";
/**
* Extras name for whether task detail request is extended
*/
public static final String EXTRAS_EXTENDED = "extended";
/**
* Extras name for old task due date
*/
public static final String EXTRAS_OLD_DUE_DATE= "oldDueDate";
/**
* Extras name for new task due date
*/
public static final String EXTRAS_NEW_DUE_DATE = "newDueDate";
/**
* Extras name for sync provider name
*/
public static final String EXTRAS_NAME = "name";
// --- Add-ons API
/**
* Action name for broadcast intent requesting add-ons
*/
public static final String BROADCAST_REQUEST_ADDONS = PACKAGE + ".REQUEST_ADDONS";
/**
* Action name for broadcast intent sending add-ons back to Astrid
* <li> EXTRAS_RESPONSE an {@link Addon} object
*/
public static final String BROADCAST_SEND_ADDONS = PACKAGE + ".SEND_ADDONS";
// --- Filters API
/**
* Action name for broadcast intent requesting filters
*/
public static final String BROADCAST_REQUEST_FILTERS = PACKAGE + ".REQUEST_FILTERS";
/**
* Action name for broadcast intent sending filters back to Astrid
* <li> EXTRAS_ADDON your add-on identifier </li>
* <li> EXTRAS_RESPONSE an array of {@link FilterListItem}s </li>
*/
public static final String BROADCAST_SEND_FILTERS = PACKAGE + ".SEND_FILTERS";
// -- Custom criteria API
/**
* Action name for a broadcast intent requesting custom filter criteria (e.g. "Due by, Tagged, Tag contains", etc.)
*/
public static final String BROADCAST_REQUEST_CUSTOM_FILTER_CRITERIA = PACKAGE + ".REQUEST_CUSTOM_FILTER_CRITERIA";
/**
* Action name for broadcast intent sending custom filter criteria back to Astrid
* <li> EXTRAS_ADDON you add-on identifier
* <li> EXTRAS_RESPONSE an array of {@link CustomFilterCriterion} </li>
*/
public static final String BROADCAST_SEND_CUSTOM_FILTER_CRITERIA = PACKAGE + ".SEND_CUSTOM_FILTER_CRITERIA";
// --- Edit Controls API
/**
* Action name for broadcast intent requesting task edit controls
* <li> EXTRAS_TASK_ID id of the task user is editing
*/
public static final String BROADCAST_REQUEST_EDIT_CONTROLS = PACKAGE + ".REQUEST_EDIT_CONTROLS";
/**
* Action name for broadcast intent sending task edit controls back to Astrid
* <li> EXTRAS_ADDON your add-on identifier
* <li> EXTRAS_RESPONSE a {@link RemoteViews} with your edit controls
*/
public static final String BROADCAST_SEND_EDIT_CONTROLS = PACKAGE + ".SEND_EDIT_CONTROLS";
// --- Task Details API
/**
* Action name for broadcast intent requesting details for a task.
* Extended details are displayed when a user presses on a task.
*
* <li> EXTRAS_TASK_ID id of the task
* <li> EXTRAS_EXTENDED whether request is for standard or extended details
*/
public static final String BROADCAST_REQUEST_DETAILS = PACKAGE + ".REQUEST_DETAILS";
/**
* Action name for broadcast intent sending details back to Astrid
* <li> EXTRAS_ADDON your add-on identifier
* <li> EXTRAS_TASK_ID id of the task
* <li> EXTRAS_EXTENDED whether request is for standard or extended details
* <li> EXTRAS_RESPONSE a String
*/
public static final String BROADCAST_SEND_DETAILS = PACKAGE + ".SEND_DETAILS";
// --- Sync Action API
/**
* Action name for broadcast intent requesting a listing of active
* sync actions users can activate from the menu
*/
public static final String BROADCAST_REQUEST_SYNC_ACTIONS = PACKAGE + ".REQUEST_SYNC_ACTIONS";
/**
* Action name for broadcast intent sending sync provider information back to Astrid
* <li> EXTRAS_ADDON your add-on identifier
* <li> EXTRAS_RESPONSE a {@link SyncAction} to invoke synchronization
*/
public static final String BROADCAST_SEND_SYNC_ACTIONS = PACKAGE + ".SEND_SYNC_ACTIONS";
// --- Task Actions API
/**
* Action name for broadcast intent requesting actions for a task
* <li> EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_REQUEST_ACTIONS = PACKAGE + ".REQUEST_ACTIONS";
/**
* Action name for broadcast intent sending actions back to Astrid
* <li> EXTRAS_ADDON your add-on identifier
* <li> EXTRAS_TASK_ID id of the task
* <li> EXTRAS_RESPONSE a String
*/
public static final String BROADCAST_SEND_ACTIONS = PACKAGE + ".SEND_ACTIONS";
// --- Task Decorations API
/**
* Action name for broadcast intent requesting task list decorations for a task
* <li> EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_REQUEST_DECORATIONS = PACKAGE + ".REQUEST_DECORATIONS";
/**
* Action name for broadcast intent sending decorations back to Astrid
* <li> EXTRAS_ADDON your add-on identifier
* <li> EXTRAS_TASK_ID id of the task
* <li> EXTRAS_RESPONSE a {@link TaskDecoration}
*/
public static final String BROADCAST_SEND_DECORATIONS = PACKAGE + ".SEND_DECORATIONS";
// --- Actions API
/**
* Action name for intents to be displayed on task context menu
* <li> EXTRAS_TASK_ID id of the task
*/
public static final String ACTION_TASK_CONTEXT_MENU = PACKAGE + ".CONTEXT_MENU";
/**
* Action name for intents to be displayed on Astrid's task list menu
* <li> EXTRAS_ADDON your add-on identifier
* <li> EXTRAS_RESPONSE an array of {@link Intent}s
*/
public static final String ACTION_TASK_LIST_MENU = PACKAGE + ".TASK_LIST_MENU";
/**
* Action name for intents to be displayed in Astrid's settings. By default,
* your application will be put into the category named by your application,
* but you can add a string meta-data with name "category" to override this.
*/
public static final String ACTION_SETTINGS = PACKAGE + ".SETTINGS";
// --- Events API
/**
* Action name for broadcast intent notifying add-ons that Astrid started up
*/
public static final String BROADCAST_EVENT_STARTUP = PACKAGE + ".STARTUP";
/**
* Action name for broadcast intent notifying Astrid task list to refresh
*/
public static final String BROADCAST_EVENT_REFRESH = PACKAGE + ".REFRESH";
/**
* Action name for broadcast intent notifying Astrid to clear detail cache
* because an event occurred that potentially affects all tasks (e.g.
* logging out of a sync provider). Use this call carefully, as loading
* details can degrade the performance of Astrid.
*/
public static final String BROADCAST_EVENT_FLUSH_DETAILS = PACKAGE + ".FLUSH_DETAILS";
/**
* Action name for broadcast intent notifying that task was created or
* title was changed
* <li> EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_EVENT_TASK_LIST_UPDATED = PACKAGE + ".TASK_LIST_UPDATED";
/**
* Action name for broadcast intent notifying that task was completed
* <li> EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_EVENT_TASK_COMPLETED = PACKAGE + ".TASK_COMPLETED";
/**
* Action name for broadcast intent notifying that task was created from repeating template
* <li> EXTRAS_TASK_ID id of the task
* <li> EXTRAS_OLD_DUE_DATE task old due date (could be 0)
* <li> EXTRAS_NEW_DUE_DATE task new due date (will not be 0)
*/
public static final String BROADCAST_EVENT_TASK_REPEATED = PACKAGE + ".TASK_REPEATED";
}

@ -0,0 +1,91 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
/**
* CustomFilterCriteria allow users to build a custom filter by chaining
* together criteria
*
* @author Tim Su <tim@todoroo.com>
*
*/
abstract public class CustomFilterCriterion implements Parcelable {
/**
* Criteria Identifier. This identifier allows saved filters to be reloaded.
* <p>
* e.g "duedate"
*/
public String identifier;
/**
* Criteria Title. If the title contains ?, this is replaced by the entry
* label string selected.
* <p>
* e.g "Due: ?"
*/
public String text;
/**
* Criterion SQL. This query should return task id's. If this contains
* ?, it will be replaced by the entry value
* <p>
* Examples:
* <ul>
* <li><code>SELECT _id FROM tasks WHERE dueDate <= ?</code>
* <li><code>SELECT task FROM metadata WHERE value = '?'</code>
* </ul>
*/
public String sql;
/**
* Values to apply to a task when quick-adding a task from a filter
* created from this criterion. ? will be replaced with the entry value.
* For example, when a user views tasks tagged 'ABC', the
* tasks they create should also be tagged 'ABC'. If set to null, no
* additional values will be stored for a task.
*/
public ContentValues valuesForNewTasks = null;
/**
* Icon for this criteria. Can be null for no bitmap
*/
public Bitmap icon;
/**
* Criteria name. This is displayed when users are selecting a criteria
*/
public String name;
// --- parcelable utilities
/**
* Utility method to write to parcel
*/
public void writeToParcel(Parcel dest) {
dest.writeString(identifier);
dest.writeString(text);
dest.writeString(sql);
dest.writeParcelable(valuesForNewTasks, 0);
dest.writeParcelable(icon, 0);
dest.writeString(name);
}
/**
* Utility method to read from parcel
*/
public void readFromParcel(Parcel source) {
identifier = source.readString();
text = source.readString();
sql = source.readString();
valuesForNewTasks = (ContentValues)source.readParcelable(ContentValues.class.getClassLoader());
icon = (Bitmap)source.readParcelable(Bitmap.class.getClassLoader());
name = source.readString();
}
}

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

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

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

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

@ -0,0 +1,83 @@
package com.todoroo.astrid.api;
import android.content.ComponentName;
import android.content.ContentValues;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import com.todoroo.andlib.sql.QueryTemplate;
public class FilterWithCustomIntent extends Filter {
public ComponentName customTaskList = null;
public Bundle customExtras = null;
protected FilterWithCustomIntent() {
super();
}
public FilterWithCustomIntent(String listingTitle, String title,
QueryTemplate sqlQuery, ContentValues valuesForNewTasks) {
super(listingTitle, title, sqlQuery, valuesForNewTasks);
}
public FilterWithCustomIntent(String listingTitle, String title,
String sqlQuery, ContentValues valuesForNewTasks) {
super(listingTitle, title, sqlQuery, valuesForNewTasks);
}
// --- parcelable
/**
* {@inheritDoc}
*/
@Override
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeParcelable(customTaskList, 0);
dest.writeParcelable(customExtras, 0);
}
@Override
public void readFromParcel(Parcel source) {
super.readFromParcel(source);
customTaskList = source.readParcelable(ComponentName.class.getClassLoader());
customExtras = source.readParcelable(Bundle.class.getClassLoader());
}
/**
* Parcelable Creator Object
*/
@SuppressWarnings("hiding")
public static final Parcelable.Creator<FilterWithCustomIntent> CREATOR = new Parcelable.Creator<FilterWithCustomIntent>() {
/**
* {@inheritDoc}
*/
public FilterWithCustomIntent createFromParcel(Parcel source) {
FilterWithCustomIntent item = new FilterWithCustomIntent();
item.readFromParcel(source);
return item;
}
/**
* {@inheritDoc}
*/
public FilterWithCustomIntent[] newArray(int size) {
return new FilterWithCustomIntent[size];
}
};
}

@ -0,0 +1,83 @@
package com.todoroo.astrid.api;
import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Special filter that launches a PendingIntent when accessed.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class IntentFilter extends FilterListItem implements Parcelable {
/**
* PendingIntent to trigger when pressed
*/
public PendingIntent intent;
/**
* Constructor for creating a new IntentFilter
*
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
* @param intent
* intent to load
*/
public IntentFilter(String listingTitle, PendingIntent intent) {
this.listingTitle = listingTitle;
this.intent = intent;
}
/**
* Constructor for creating a new IntentFilter used internally
*/
protected IntentFilter(PendingIntent intent) {
this.intent = intent;
}
// --- parcelable
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(intent, 0);
super.writeToParcel(dest, flags);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<IntentFilter> CREATOR = new Parcelable.Creator<IntentFilter>() {
/**
* {@inheritDoc}
*/
public IntentFilter createFromParcel(Parcel source) {
IntentFilter item = new IntentFilter((PendingIntent) source.readParcelable(
PendingIntent.class.getClassLoader()));
item.readFromParcel(source);
return item;
}
/**
* {@inheritDoc}
*/
public IntentFilter[] newArray(int size) {
return new IntentFilter[size];
}
};
}

@ -0,0 +1,102 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
/**
* CustomFilterCriteria allow users to build a custom filter by chaining
* together criteria
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MultipleSelectCriterion extends CustomFilterCriterion implements Parcelable {
/**
* Array of entries for user to select from
*/
public String[] entryTitles;
/**
* Array of entry values corresponding to entries
*/
public String[] entryValues;
/**
* Create a new CustomFilterCriteria object
*
* @param title
* @param sql
* @param valuesForNewTasks
* @param entryTitles
* @param entryValues
* @param icon
* @param name
*/
public MultipleSelectCriterion(String identifier, String title, String sql,
ContentValues valuesForNewTasks, String[] entryTitles,
String[] entryValues, Bitmap icon, String name) {
this.identifier = identifier;
this.text = title;
this.sql = sql;
this.valuesForNewTasks = valuesForNewTasks;
this.entryTitles = entryTitles;
this.entryValues = entryValues;
this.icon = icon;
this.name = name;
}
protected MultipleSelectCriterion() {
// constructor for inflating from parceling
}
// --- parcelable
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(entryTitles);
dest.writeStringArray(entryValues);
super.writeToParcel(dest);
}
/**
* Parcelable Creator Object
*/
public static final Parcelable.Creator<MultipleSelectCriterion> CREATOR = new Parcelable.Creator<MultipleSelectCriterion>() {
/**
* {@inheritDoc}
*/
public MultipleSelectCriterion createFromParcel(Parcel source) {
MultipleSelectCriterion item = new MultipleSelectCriterion();
item.entryTitles = source.createStringArray();
item.entryValues = source.createStringArray();
item.readFromParcel(source);
return item;
}
/**
* {@inheritDoc}
*/
public MultipleSelectCriterion[] newArray(int size) {
return new MultipleSelectCriterion[size];
}
};
}

@ -0,0 +1,65 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import java.util.Date;
import com.todoroo.andlib.utility.DateUtilities;
/**
* PermaSql allows for creating SQL statements that can be saved and used
* later without dates getting stale. It also allows these values to be
* used in
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class PermaSql {
// --- placeholder strings
/** value to be replaced with the current time as long */
public static final String VALUE_NOW = "NOW()"; //$NON-NLS-1$
/** value to be replaced by end of day as long */
public static final String VALUE_EOD = "EOD()"; //$NON-NLS-1$
/** value to be replaced by end of day yesterday as long */
public static final String VALUE_EOD_YESTERDAY = "EODY()"; //$NON-NLS-1$
/** value to be replaced by end of day tomorrow as long */
public static final String VALUE_EOD_TOMORROW = "EODT()"; //$NON-NLS-1$
/** value to be replaced by end of day day after tomorrow as long */
public static final String VALUE_EOD_DAY_AFTER = "EODTT()"; //$NON-NLS-1$
/** value to be replaced by end of day next week as long */
public static final String VALUE_EOD_NEXT_WEEK = "EODW()"; //$NON-NLS-1$
/** value to be replaced by approximate end of day next month as long */
public static final String VALUE_EOD_NEXT_MONTH = "EODM()"; //$NON-NLS-1$
/** Replace placeholder strings with actual */
public static String replacePlaceholders(String value) {
if(value.contains(VALUE_NOW))
value = value.replace(VALUE_NOW, Long.toString(DateUtilities.now()));
if(value.contains(VALUE_EOD) || value.contains(VALUE_EOD_DAY_AFTER) ||
value.contains(VALUE_EOD_NEXT_WEEK) || value.contains(VALUE_EOD_TOMORROW) ||
value.contains(VALUE_EOD_YESTERDAY) || value.contains(VALUE_EOD_NEXT_MONTH)) {
Date date = new Date();
date.setHours(23);
date.setMinutes(59);
date.setSeconds(59);
long time = date.getTime() / 1000l * 1000l; // chop milliseconds off
value = value.replace(VALUE_EOD_YESTERDAY, Long.toString(time - DateUtilities.ONE_DAY));
value = value.replace(VALUE_EOD, Long.toString(time));
value = value.replace(VALUE_EOD_TOMORROW, Long.toString(time + DateUtilities.ONE_DAY));
value = value.replace(VALUE_EOD_DAY_AFTER, Long.toString(time + 2 * DateUtilities.ONE_DAY));
value = value.replace(VALUE_EOD_NEXT_WEEK, Long.toString(time + 7 * DateUtilities.ONE_DAY));
value = value.replace(VALUE_EOD_NEXT_MONTH, Long.toString(time + 30 * DateUtilities.ONE_DAY));
}
return value;
}
}

@ -0,0 +1,105 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents an intent that can be called to perform synchronization
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class SyncAction implements Parcelable {
/**
* Label
*/
public String label = null;
/**
* Intent to call when invoking this operation
*/
public PendingIntent intent = null;
/**
* Create an EditOperation object
*
* @param label
* label to display
* @param intent
* intent to invoke
*/
public SyncAction(String label, PendingIntent intent) {
super();
this.label = label;
this.intent = intent;
}
/**
* Returns the label of this action
*/
@Override
public String toString() {
return label;
}
@Override
public int hashCode() {
return label.hashCode() ^ intent.getTargetPackage().hashCode();
}
/**
* We consider two sync actions equal if target package is identical
* and the labels are the same. This prevents duplicate pendingIntents
* from creating multiple SyncAction objects.
*/
@Override
public boolean equals(Object o) {
if(!(o instanceof SyncAction))
return false;
SyncAction other = (SyncAction) o;
return label.equals(other.label) && intent.getTargetPackage().equals(other.intent.getTargetPackage());
}
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(label);
dest.writeParcelable(intent, 0);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<SyncAction> CREATOR = new Parcelable.Creator<SyncAction>() {
/**
* {@inheritDoc}
*/
public SyncAction createFromParcel(Parcel source) {
return new SyncAction(source.readString(), (PendingIntent)source.readParcelable(
PendingIntent.class.getClassLoader()));
}
/**
* {@inheritDoc}
*/
public SyncAction[] newArray(int size) {
return new SyncAction[size];
};
};
}

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

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

@ -0,0 +1,103 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
/**
* CustomFilterCriteria allow users to build a custom filter by chaining
* together criteria
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TextInputCriterion extends CustomFilterCriterion implements Parcelable {
/**
* Text area prompt
*/
public String prompt;
/**
* Text area hint
*/
public String hint;
/**
* Create a new CustomFilterCriteria object
*
* @param identifier
* @param title
* @param sql
* @param valuesForNewTasks
* @param prompt
* @param hint
* @param icon
* @param name
*/
public TextInputCriterion(String identifier, String title, String sql,
ContentValues valuesForNewTasks, String prompt, String hint,
Bitmap icon, String name) {
this.identifier = identifier;
this.text = title;
this.sql = sql;
this.valuesForNewTasks = valuesForNewTasks;
this.prompt = prompt;
this.hint = hint;
this.icon = icon;
this.name = name;
}
protected TextInputCriterion() {
// constructor for inflating from parceling
}
// --- parcelable
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(prompt);
dest.writeString(hint);
super.writeToParcel(dest);
}
/**
* Parcelable Creator Object
*/
public static final Parcelable.Creator<TextInputCriterion> CREATOR = new Parcelable.Creator<TextInputCriterion>() {
/**
* {@inheritDoc}
*/
public TextInputCriterion createFromParcel(Parcel source) {
TextInputCriterion item = new TextInputCriterion();
item.prompt = source.readString();
item.hint = source.readString();
item.readFromParcel(source);
return item;
}
/**
* {@inheritDoc}
*/
public TextInputCriterion[] newArray(int size) {
return new TextInputCriterion[size];
}
};
}

@ -0,0 +1,4 @@
/**
* Astrid API constants and container packages
*/
package com.todoroo.astrid.api;

@ -0,0 +1,73 @@
package com.todoroo.astrid.core;
import android.os.Parcel;
import android.os.Parcelable;
import com.todoroo.astrid.api.FilterListItem;
/**
* Special filter that triggers the search functionality when accessed.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class SearchFilter extends FilterListItem {
/**
* Constructor for creating a new SearchFilter
*
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
*/
public SearchFilter(String listingTitle) {
this.listingTitle = listingTitle;
}
/**
* Constructor for creating a new SearchFilter
*/
protected SearchFilter() {
//
}
// --- parcelable
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<SearchFilter> CREATOR = new Parcelable.Creator<SearchFilter>() {
/**
* {@inheritDoc}
*/
public SearchFilter createFromParcel(Parcel source) {
SearchFilter item = new SearchFilter();
item.readFromParcel(source);
return item;
}
/**
* {@inheritDoc}
*/
public SearchFilter[] newArray(int size) {
return new SearchFilter[size];
}
};
}

@ -0,0 +1,103 @@
package com.todoroo.astrid.core;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskApiDao.TaskCriteria;
/**
* Helpers for sorting a list of tasks
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class SortHelper {
public static final int FLAG_REVERSE_SORT = 1 << 0;
public static final int FLAG_SHOW_COMPLETED = 1 << 1;
public static final int FLAG_SHOW_HIDDEN = 1 << 2;
public static final int FLAG_SHOW_DELETED = 1 << 3;
public static final int SORT_AUTO = 0;
public static final int SORT_ALPHA = 1;
public static final int SORT_DUE = 2;
public static final int SORT_IMPORTANCE = 3;
public static final int SORT_MODIFIED = 4;
/** preference key for sort flags. stored in public prefs */
public static final String PREF_SORT_FLAGS = "sort_flags"; //$NON-NLS-1$
/** preference key for sort sort. stored in public prefs */
public static final String PREF_SORT_SORT = "sort_sort"; //$NON-NLS-1$
/**
* Takes a SQL query, and if there isn't already an order, creates an order.
* @param originalSql
* @param flags
* @param sort
* @return
*/
@SuppressWarnings("nls")
public static String adjustQueryForFlagsAndSort(String originalSql, int flags, int sort) {
// sort
if(originalSql == null)
originalSql = "";
if(!originalSql.toUpperCase().contains("ORDER BY")) {
Order order;
switch(sort) {
case SORT_ALPHA:
order = Order.asc(Functions.upper(Task.TITLE));
break;
case SORT_DUE:
order = Order.asc(Functions.caseStatement(Task.DUE_DATE.eq(0),
DateUtilities.now()*2, Task.DUE_DATE) + "+" + Task.IMPORTANCE +
"+3*" + Task.COMPLETION_DATE);
break;
case SORT_IMPORTANCE:
order = Order.asc(Task.IMPORTANCE + "*" + (2*DateUtilities.now()) + //$NON-NLS-1$
"+" + Functions.caseStatement(Task.DUE_DATE.eq(0), //$NON-NLS-1$
Functions.now() + "+" + DateUtilities.ONE_WEEK, //$NON-NLS-1$
Task.DUE_DATE) + "+8*" + Task.COMPLETION_DATE);
break;
case SORT_MODIFIED:
order = Order.desc(Task.MODIFICATION_DATE);
break;
default:
order = defaultTaskOrder();
}
if((flags & FLAG_REVERSE_SORT) > 0)
order = order.reverse();
originalSql += " ORDER BY " + order;
}
// flags
if((flags & FLAG_SHOW_COMPLETED) > 0)
originalSql = originalSql.replace(Task.COMPLETION_DATE.eq(0).toString(),
Criterion.all.toString());
if((flags & FLAG_SHOW_HIDDEN) > 0)
originalSql = originalSql.replace(TaskCriteria.isVisible().toString(),
Criterion.all.toString());
if((flags & FLAG_SHOW_DELETED) > 0)
originalSql = originalSql.replace(Task.DELETION_DATE.eq(0).toString(),
Criterion.all.toString());
return originalSql;
}
/**
* Returns SQL task ordering that is astrid's default algorithm
* @return
*/
@SuppressWarnings("nls")
public static Order defaultTaskOrder() {
return Order.asc(Functions.caseStatement(Task.DUE_DATE.eq(0),
DateUtilities.now() + DateUtilities.ONE_WEEK,
Task.DUE_DATE) + " + 200000000 * " +
Task.IMPORTANCE + " + 2*" + Task.COMPLETION_DATE);
}
}

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

@ -0,0 +1,101 @@
package com.todoroo.astrid.data;
import java.util.ArrayList;
import java.util.HashSet;
import android.content.ContentValues;
import android.content.Context;
import com.todoroo.andlib.data.ContentResolverDao;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
/**
* Data access object for accessing Astrid's {@link Metadata} table. A
* piece of Metadata is information about a task, for example a tag or a
* note. It operates in a one-to-many relation with tasks.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class MetadataApiDao extends ContentResolverDao<Metadata> {
public MetadataApiDao(Context context) {
super(Metadata.class, context, Metadata.CONTENT_URI);
}
/**
* Generates SQL clauses
*/
public static class MetadataCriteria {
/** Returns all metadata associated with a given task */
public static Criterion byTask(long taskId) {
return Metadata.TASK.eq(taskId);
}
/** Returns all metadata associated with a given key */
public static Criterion withKey(String key) {
return Metadata.KEY.eq(key);
}
/** Returns all metadata associated with a given key */
public static Criterion byTaskAndwithKey(long taskId, String key) {
return Criterion.and(withKey(key), byTask(taskId));
}
}
/**
* Synchronize metadata for given task id. Deletes rows in database that
* are not identical to those in the metadata list, creates rows that
* have no match.
*
* @param taskId id of task to perform synchronization on
* @param metadata list of new metadata items to save
* @param metadataCriteria criteria to load data for comparison from metadata
*/
public void synchronizeMetadata(long taskId, ArrayList<Metadata> metadata,
Criterion metadataCriteria) {
HashSet<ContentValues> newMetadataValues = new HashSet<ContentValues>();
for(Metadata metadatum : metadata) {
metadatum.setValue(Metadata.TASK, taskId);
metadatum.clearValue(Metadata.ID);
newMetadataValues.add(metadatum.getMergedValues());
}
Metadata item = new Metadata();
TodorooCursor<Metadata> cursor = query(Query.select(Metadata.PROPERTIES).where(Criterion.and(MetadataCriteria.byTask(taskId),
metadataCriteria)));
try {
// try to find matches within our metadata list
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
item.readFromCursor(cursor);
long id = item.getId();
// clear item id when matching with incoming values
item.clearValue(Metadata.ID);
ContentValues itemMergedValues = item.getMergedValues();
if(newMetadataValues.contains(itemMergedValues)) {
newMetadataValues.remove(itemMergedValues);
continue;
}
// not matched. cut it
delete(id);
}
} finally {
cursor.close();
}
// everything that remains shall be written
for(ContentValues values : newMetadataValues) {
item.clear();
item.mergeWith(values);
save(item);
}
}
}

@ -0,0 +1,112 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.data;
import android.content.ContentValues;
import android.net.Uri;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.api.AstridApiConstants;
/**
* Data Model which represents a piece of data unrelated to a task
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public class StoreObject extends AbstractModel {
// --- table
/** table for this model */
public static final Table TABLE = new Table("store", StoreObject.class);
/** content uri for this model */
public static final Uri CONTENT_URI = Uri.parse("content://" + AstridApiConstants.PACKAGE + "/" +
TABLE.name);
// --- properties
/** ID */
public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME);
/** Store Type Key */
public static final StringProperty TYPE = new StringProperty(
TABLE, "type");
/** Store Item Key */
public static final StringProperty ITEM= new StringProperty(
TABLE, "item");
/** Store Value Column 1 */
public static final StringProperty VALUE1 = new StringProperty(
TABLE, "value");
/** Store Value Column 2 */
public static final StringProperty VALUE2 = new StringProperty(
TABLE, "value2");
/** Store Value Column 3 */
public static final StringProperty VALUE3 = new StringProperty(
TABLE, "value3");
/** Store Value Column 4 */
public static final StringProperty VALUE4 = new StringProperty(
TABLE, "value4");
/** Store Value Column 5 */
public static final StringProperty VALUE5 = new StringProperty(
TABLE, "value5");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(StoreObject.class);
// --- defaults
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- data access boilerplate
public StoreObject() {
super();
}
public StoreObject(TodorooCursor<StoreObject> cursor) {
this();
readPropertiesFromCursor(cursor);
}
public void readFromCursor(TodorooCursor<StoreObject> cursor) {
super.readPropertiesFromCursor(cursor);
}
@Override
public long getId() {
return getIdHelper(ID);
};
// --- parcelable helpers
private static final Creator<StoreObject> CREATOR = new ModelCreator<StoreObject>(StoreObject.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {
return CREATOR;
}
}

@ -0,0 +1,40 @@
package com.todoroo.astrid.data;
import android.content.Context;
import com.todoroo.andlib.data.ContentResolverDao;
import com.todoroo.andlib.sql.Criterion;
/**
* Data access object for accessing Astrid's {@link StoreObject} table. A
* StoreObject is an arbitrary piece of data stored inside of Astrid.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class StoreObjectApiDao extends ContentResolverDao<StoreObject> {
public StoreObjectApiDao(Context context) {
super(StoreObject.class, context, StoreObject.CONTENT_URI);
}
// --- SQL clause generators
/**
* Generates SQL clauses
*/
public static class StoreObjectCriteria {
/** Returns all store objects with given type */
public static Criterion byType(String type) {
return StoreObject.TYPE.eq(type);
}
/** Returns store object with type and key */
public static Criterion byTypeAndItem(String type, String item) {
return Criterion.and(byType(type), StoreObject.ITEM.eq(item));
}
}
}

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

@ -0,0 +1,185 @@
package com.todoroo.astrid.data;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import com.todoroo.andlib.data.ContentResolverDao;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.PermaSql;
/**
* Data access object for accessing Astrid's {@link Task} table. If you
* are looking to store extended information about a Task, you probably
* want to use the {@link MetadataApiDao} object.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TaskApiDao extends ContentResolverDao<Task> {
public TaskApiDao(Context context) {
super(Task.class, context, Task.CONTENT_URI);
}
/**
* Generates SQL clauses
*/
public static class TaskCriteria {
/** @return tasks by id */
public static Criterion byId(long id) {
return Task.ID.eq(id);
}
/** @return tasks that were deleted */
public static Criterion isDeleted() {
return Task.DELETION_DATE.neq(0);
}
/** @return tasks that were not deleted */
public static Criterion notDeleted() {
return Task.DELETION_DATE.eq(0);
}
/** @return tasks that have not yet been completed or deleted */
public static Criterion activeAndVisible() {
return Criterion.and(Task.COMPLETION_DATE.eq(0),
Task.DELETION_DATE.eq(0),
Task.HIDE_UNTIL.lt(Functions.now()));
}
/** @return tasks that have not yet been completed or deleted */
public static Criterion isActive() {
return Criterion.and(Task.COMPLETION_DATE.eq(0),
Task.DELETION_DATE.eq(0));
}
/** @return tasks that are not hidden at current time */
public static Criterion isVisible() {
return Task.HIDE_UNTIL.lt(Functions.now());
}
/** @return tasks that have a due date */
public static Criterion hasDeadlines() {
return Task.DUE_DATE.neq(0);
}
/** @return tasks that are due before a certain unixtime */
public static Criterion dueBeforeNow() {
return Criterion.and(Task.DUE_DATE.gt(0), Task.DUE_DATE.lt(Functions.now()));
}
/** @return tasks that are due after a certain unixtime */
public static Criterion dueAfterNow() {
return Task.DUE_DATE.gt(Functions.now());
}
/** @return tasks completed before a given unixtime */
public static Criterion completed() {
return Criterion.and(Task.COMPLETION_DATE.gt(0), Task.COMPLETION_DATE.lt(Functions.now()));
}
/** @return tasks that have a blank or null title */
@SuppressWarnings("nls")
public static Criterion hasNoTitle() {
return Criterion.or(Task.TITLE.isNull(), Task.TITLE.eq(""));
}
}
/**
* Count tasks matching criterion
* @param criterion
* @return # of tasks matching
*/
public int countTasks(Criterion criterion) {
TodorooCursor<Task> cursor = query(Query.select(Task.ID).where(criterion));
try {
return cursor.getCount();
} finally {
cursor.close();
}
}
/**
* Count tasks matching query tepmlate
* @param queryTemplate
* @return # of tasks matching
*/
public int countTasks(String queryTemplate) {
queryTemplate = PermaSql.replacePlaceholders(queryTemplate);
TodorooCursor<Task> cursor = query(Query.select(Task.ID).withQueryTemplate(queryTemplate));
try {
return cursor.getCount();
} finally {
cursor.close();
}
}
@Override
public boolean save(Task model) {
ContentValues setValues = model.getSetValues();
if(super.save(model)) {
afterSave(model, setValues);
return true;
}
return false;
}
/** @return true if task change shouldn't be broadcast */
public static boolean insignificantChange(ContentValues values) {
if(values == null || values.size() == 0)
return true;
if(values.containsKey(Task.DETAILS_DATE.name) &&
values.containsKey(Task.DETAILS.name) &&
values.size() == 2)
return true;
if(values.containsKey(Task.REMINDER_LAST.name) &&
values.size() == 1)
return true;
return false;
}
/**
* Send broadcasts on task change (triggers things like task repeats)
* @param task task that was saved
* @param values values that were updated
*/
public static void afterSave(Task task, ContentValues values) {
if(insignificantChange(values))
return;
if(values.containsKey(Task.COMPLETION_DATE.name) && task.isCompleted()) {
Context context = ContextManager.getContext();
if(context != null) {
Intent broadcastIntent;
broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_COMPLETED);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
context.sendOrderedBroadcast(broadcastIntent, null);
}
}
afterTaskListChanged();
}
/**
* Send broadcast when task list changes. Widgets should update.
*/
public static void afterTaskListChanged() {
Context context = ContextManager.getContext();
if(context != null) {
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_LIST_UPDATED);
context.sendOrderedBroadcast(broadcastIntent, null);
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save