forked from upstream-mirrors/OpenTracks
Compare commits
130 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c4235b1961 | |||
| 2b73139051 | |||
| 1a2d263945 | |||
| e69bea802f | |||
| 4baaff8374 | |||
| 1ed0a11c2f | |||
| 971b0196da | |||
| 45f41d6401 | |||
| 42234515cb | |||
| 10e479ceb9 | |||
| 88d8a808f3 | |||
| f15ae48846 | |||
| 5bf4cef57d | |||
| 52226c8ef4 | |||
| c254a6ec75 | |||
| 0ecf4863a7 | |||
| 325196159d | |||
| 6adbf39e52 | |||
| db1a37f015 | |||
| 7ca9423fbf | |||
| 11c5ec43e2 | |||
| 3f891f7fb4 | |||
| 49260a0517 | |||
| 1392c5d92a | |||
| c641b436ce | |||
| 6948c2db41 | |||
| fb904f4a0b | |||
| 6c006cc9c7 | |||
| 7a73757ab1 | |||
| 9725cca2bf | |||
| 252f71e75f | |||
| fc2341515c | |||
| 77719f5bcd | |||
| 565ef7f12b | |||
| f4208e2f6b | |||
| 4b3ff45609 | |||
| 6dd9630bae | |||
| a4ec68def6 | |||
| d80be8b2a9 | |||
| 7d7ab5778a | |||
| 1749c776ab | |||
| 336b9eaa9f | |||
| 0fc2209ceb | |||
| 477819d730 | |||
| 425ea0f216 | |||
| 779681088c | |||
| f824b31891 | |||
| d316a63fca | |||
| 55cf60ef2f | |||
| 01598d0a55 | |||
| 4bcba07706 | |||
| ed979bdc80 | |||
| 1d81070d9e | |||
| 357a1de07d | |||
| 84380d8373 | |||
| 97c3e5a938 | |||
| 8fbcf5dcc0 | |||
| 8c364ba7e2 | |||
| 0c5c49cd92 | |||
| 62d9e81b23 | |||
| 1a5acc8f2d | |||
| 7e9b99ae1c | |||
| 1f86250e46 | |||
| 7f6e3f7c69 | |||
| e0b84df8f6 | |||
| a431850793 | |||
| 5c17c8ffcf | |||
| 0691524dfa | |||
| 1c825b052e | |||
| eb8d28dfc1 | |||
| 9f1ea4d745 | |||
| dbd64a8075 | |||
| 75a88ca0e1 | |||
| 2af19b00e5 | |||
| 11db0d1a4f | |||
| 6a4adee2f2 | |||
| d04b420c56 | |||
| 0122539d0f | |||
| 0b8c2a0ef6 | |||
| cef363562c | |||
| e681531186 | |||
| 65c4b85bc4 | |||
| d7022a07a2 | |||
| 81b090893b | |||
| 3dca9304c4 | |||
| 7fc6492e13 | |||
| 80d968dd0f | |||
| f336a1da12 | |||
| 1657bf5ec1 | |||
| d046ca1cd8 | |||
| 6c852091ce | |||
| 4906d3f942 | |||
| 0395394e26 | |||
| ebcc7dc46e | |||
| 1e4246e73a | |||
| 3ca8381504 | |||
| b92457cb23 | |||
| 4baec1e672 | |||
| c702c5a4a0 | |||
| 24c2c5fc1b | |||
| 8797b033a3 | |||
| b2c4cdc4aa | |||
| ae37ec9c33 | |||
| ccc18739a4 | |||
| f48c4806b5 | |||
| 3aadf6d5d8 | |||
| 2f2679c775 | |||
| 442e23b106 | |||
| 65249f11b2 | |||
| 34b5c2ab18 | |||
| 93919183be | |||
| 1915641d78 | |||
| c2c7cad3eb | |||
| e4957e45af | |||
| 77bce9a5eb | |||
| 6538f0d587 | |||
| 5b0e7e5065 | |||
| d6cf8ba90e | |||
| 29568d0e3f | |||
| a571c4c93b | |||
| 073fed6c84 | |||
| 6847e1f899 | |||
| 427048ac11 | |||
| 240ea47c43 | |||
| 068d56a850 | |||
| 76d02da973 | |||
| 4664895ae3 | |||
| 5235c8cf83 | |||
| 2eea93a4a3 | |||
| 55e419475a |
+965
@@ -0,0 +1,965 @@
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = false
|
||||
max_line_length = 100
|
||||
tab_width = 4
|
||||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = false
|
||||
ij_smart_tabs = false
|
||||
ij_visual_guides = none
|
||||
ij_wrap_on_typing = false
|
||||
|
||||
[*.java]
|
||||
ij_java_align_consecutive_assignments = false
|
||||
ij_java_align_consecutive_variable_declarations = false
|
||||
ij_java_align_group_field_declarations = false
|
||||
ij_java_align_multiline_annotation_parameters = false
|
||||
ij_java_align_multiline_array_initializer_expression = false
|
||||
ij_java_align_multiline_assignment = false
|
||||
ij_java_align_multiline_binary_operation = false
|
||||
ij_java_align_multiline_chained_methods = false
|
||||
ij_java_align_multiline_deconstruction_list_components = true
|
||||
ij_java_align_multiline_extends_list = false
|
||||
ij_java_align_multiline_for = true
|
||||
ij_java_align_multiline_method_parentheses = false
|
||||
ij_java_align_multiline_parameters = true
|
||||
ij_java_align_multiline_parameters_in_calls = false
|
||||
ij_java_align_multiline_parenthesized_expression = false
|
||||
ij_java_align_multiline_records = true
|
||||
ij_java_align_multiline_resources = true
|
||||
ij_java_align_multiline_ternary_operation = false
|
||||
ij_java_align_multiline_text_blocks = false
|
||||
ij_java_align_multiline_throws_list = false
|
||||
ij_java_align_subsequent_simple_methods = false
|
||||
ij_java_align_throws_keyword = false
|
||||
ij_java_align_types_in_multi_catch = true
|
||||
ij_java_annotation_parameter_wrap = off
|
||||
ij_java_array_initializer_new_line_after_left_brace = false
|
||||
ij_java_array_initializer_right_brace_on_new_line = false
|
||||
ij_java_array_initializer_wrap = off
|
||||
ij_java_assert_statement_colon_on_next_line = false
|
||||
ij_java_assert_statement_wrap = off
|
||||
ij_java_assignment_wrap = off
|
||||
ij_java_binary_operation_sign_on_next_line = false
|
||||
ij_java_binary_operation_wrap = off
|
||||
ij_java_blank_lines_after_anonymous_class_header = 0
|
||||
ij_java_blank_lines_after_class_header = 0
|
||||
ij_java_blank_lines_after_imports = 1
|
||||
ij_java_blank_lines_after_package = 1
|
||||
ij_java_blank_lines_around_class = 1
|
||||
ij_java_blank_lines_around_field = 0
|
||||
ij_java_blank_lines_around_field_in_interface = 0
|
||||
ij_java_blank_lines_around_initializer = 1
|
||||
ij_java_blank_lines_around_method = 1
|
||||
ij_java_blank_lines_around_method_in_interface = 1
|
||||
ij_java_blank_lines_before_class_end = 0
|
||||
ij_java_blank_lines_before_imports = 1
|
||||
ij_java_blank_lines_before_method_body = 0
|
||||
ij_java_blank_lines_before_package = 0
|
||||
ij_java_block_brace_style = end_of_line
|
||||
ij_java_block_comment_add_space = false
|
||||
ij_java_block_comment_at_first_column = true
|
||||
ij_java_builder_methods = none
|
||||
ij_java_call_parameters_new_line_after_left_paren = false
|
||||
ij_java_call_parameters_right_paren_on_new_line = false
|
||||
ij_java_call_parameters_wrap = off
|
||||
ij_java_case_statement_on_separate_line = true
|
||||
ij_java_catch_on_new_line = false
|
||||
ij_java_class_annotation_wrap = split_into_lines
|
||||
ij_java_class_brace_style = end_of_line
|
||||
ij_java_class_count_to_use_import_on_demand = 99
|
||||
ij_java_class_names_in_javadoc = 1
|
||||
ij_java_deconstruction_list_wrap = normal
|
||||
ij_java_do_not_indent_top_level_class_members = false
|
||||
ij_java_do_not_wrap_after_single_annotation = false
|
||||
ij_java_do_not_wrap_after_single_annotation_in_parameter = false
|
||||
ij_java_do_while_brace_force = never
|
||||
ij_java_doc_add_blank_line_after_description = true
|
||||
ij_java_doc_add_blank_line_after_param_comments = false
|
||||
ij_java_doc_add_blank_line_after_return = false
|
||||
ij_java_doc_add_p_tag_on_empty_lines = true
|
||||
ij_java_doc_align_exception_comments = true
|
||||
ij_java_doc_align_param_comments = true
|
||||
ij_java_doc_do_not_wrap_if_one_line = false
|
||||
ij_java_doc_enable_formatting = true
|
||||
ij_java_doc_enable_leading_asterisks = true
|
||||
ij_java_doc_indent_on_continuation = false
|
||||
ij_java_doc_keep_empty_lines = true
|
||||
ij_java_doc_keep_empty_parameter_tag = true
|
||||
ij_java_doc_keep_empty_return_tag = true
|
||||
ij_java_doc_keep_empty_throws_tag = true
|
||||
ij_java_doc_keep_invalid_tags = true
|
||||
ij_java_doc_param_description_on_new_line = false
|
||||
ij_java_doc_preserve_line_breaks = false
|
||||
ij_java_doc_use_throws_not_exception_tag = true
|
||||
ij_java_else_on_new_line = false
|
||||
ij_java_enum_constants_wrap = off
|
||||
ij_java_extends_keyword_wrap = off
|
||||
ij_java_extends_list_wrap = off
|
||||
ij_java_field_annotation_wrap = split_into_lines
|
||||
ij_java_finally_on_new_line = false
|
||||
ij_java_for_brace_force = never
|
||||
ij_java_for_statement_new_line_after_left_paren = false
|
||||
ij_java_for_statement_right_paren_on_new_line = false
|
||||
ij_java_for_statement_wrap = off
|
||||
ij_java_generate_final_locals = false
|
||||
ij_java_generate_final_parameters = false
|
||||
ij_java_if_brace_force = never
|
||||
ij_java_imports_layout = $android.**,$androidx.**,$com.**,$junit.**,$net.**,$org.**,$java.**,$javax.**,$*,|,android.**,|,androidx.**,|,com.**,|,junit.**,|,net.**,|,org.**,|,java.**,|,javax.**,|,*,|
|
||||
ij_java_indent_case_from_switch = true
|
||||
ij_java_insert_inner_class_imports = false
|
||||
ij_java_insert_override_annotation = true
|
||||
ij_java_keep_blank_lines_before_right_brace = 2
|
||||
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
|
||||
ij_java_keep_blank_lines_in_code = 2
|
||||
ij_java_keep_blank_lines_in_declarations = 2
|
||||
ij_java_keep_builder_methods_indents = false
|
||||
ij_java_keep_control_statement_in_one_line = true
|
||||
ij_java_keep_first_column_comment = true
|
||||
ij_java_keep_indents_on_empty_lines = false
|
||||
ij_java_keep_line_breaks = true
|
||||
ij_java_keep_multiple_expressions_in_one_line = false
|
||||
ij_java_keep_simple_blocks_in_one_line = false
|
||||
ij_java_keep_simple_classes_in_one_line = false
|
||||
ij_java_keep_simple_lambdas_in_one_line = false
|
||||
ij_java_keep_simple_methods_in_one_line = false
|
||||
ij_java_label_indent_absolute = false
|
||||
ij_java_label_indent_size = 0
|
||||
ij_java_lambda_brace_style = end_of_line
|
||||
ij_java_layout_static_imports_separately = true
|
||||
ij_java_line_comment_add_space = false
|
||||
ij_java_line_comment_add_space_on_reformat = false
|
||||
ij_java_line_comment_at_first_column = true
|
||||
ij_java_method_annotation_wrap = split_into_lines
|
||||
ij_java_method_brace_style = end_of_line
|
||||
ij_java_method_call_chain_wrap = off
|
||||
ij_java_method_parameters_new_line_after_left_paren = false
|
||||
ij_java_method_parameters_right_paren_on_new_line = false
|
||||
ij_java_method_parameters_wrap = off
|
||||
ij_java_modifier_list_wrap = false
|
||||
ij_java_multi_catch_types_wrap = normal
|
||||
ij_java_names_count_to_use_import_on_demand = 99
|
||||
ij_java_new_line_after_lparen_in_annotation = false
|
||||
ij_java_new_line_after_lparen_in_deconstruction_pattern = true
|
||||
ij_java_new_line_after_lparen_in_record_header = false
|
||||
ij_java_parameter_annotation_wrap = off
|
||||
ij_java_parentheses_expression_new_line_after_left_paren = false
|
||||
ij_java_parentheses_expression_right_paren_on_new_line = false
|
||||
ij_java_place_assignment_sign_on_next_line = false
|
||||
ij_java_prefer_longer_names = true
|
||||
ij_java_prefer_parameters_wrap = false
|
||||
ij_java_record_components_wrap = normal
|
||||
ij_java_repeat_synchronized = true
|
||||
ij_java_replace_instanceof_and_cast = false
|
||||
ij_java_replace_null_check = true
|
||||
ij_java_replace_sum_lambda_with_method_ref = true
|
||||
ij_java_resource_list_new_line_after_left_paren = false
|
||||
ij_java_resource_list_right_paren_on_new_line = false
|
||||
ij_java_resource_list_wrap = off
|
||||
ij_java_rparen_on_new_line_in_annotation = false
|
||||
ij_java_rparen_on_new_line_in_deconstruction_pattern = true
|
||||
ij_java_rparen_on_new_line_in_record_header = false
|
||||
ij_java_space_after_closing_angle_bracket_in_type_argument = false
|
||||
ij_java_space_after_colon = true
|
||||
ij_java_space_after_comma = true
|
||||
ij_java_space_after_comma_in_type_arguments = true
|
||||
ij_java_space_after_for_semicolon = true
|
||||
ij_java_space_after_quest = true
|
||||
ij_java_space_after_type_cast = true
|
||||
ij_java_space_before_annotation_array_initializer_left_brace = false
|
||||
ij_java_space_before_annotation_parameter_list = false
|
||||
ij_java_space_before_array_initializer_left_brace = false
|
||||
ij_java_space_before_catch_keyword = true
|
||||
ij_java_space_before_catch_left_brace = true
|
||||
ij_java_space_before_catch_parentheses = true
|
||||
ij_java_space_before_class_left_brace = true
|
||||
ij_java_space_before_colon = true
|
||||
ij_java_space_before_colon_in_foreach = true
|
||||
ij_java_space_before_comma = false
|
||||
ij_java_space_before_deconstruction_list = false
|
||||
ij_java_space_before_do_left_brace = true
|
||||
ij_java_space_before_else_keyword = true
|
||||
ij_java_space_before_else_left_brace = true
|
||||
ij_java_space_before_finally_keyword = true
|
||||
ij_java_space_before_finally_left_brace = true
|
||||
ij_java_space_before_for_left_brace = true
|
||||
ij_java_space_before_for_parentheses = true
|
||||
ij_java_space_before_for_semicolon = false
|
||||
ij_java_space_before_if_left_brace = true
|
||||
ij_java_space_before_if_parentheses = true
|
||||
ij_java_space_before_method_call_parentheses = false
|
||||
ij_java_space_before_method_left_brace = true
|
||||
ij_java_space_before_method_parentheses = false
|
||||
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
|
||||
ij_java_space_before_quest = true
|
||||
ij_java_space_before_switch_left_brace = true
|
||||
ij_java_space_before_switch_parentheses = true
|
||||
ij_java_space_before_synchronized_left_brace = true
|
||||
ij_java_space_before_synchronized_parentheses = true
|
||||
ij_java_space_before_try_left_brace = true
|
||||
ij_java_space_before_try_parentheses = true
|
||||
ij_java_space_before_type_parameter_list = false
|
||||
ij_java_space_before_while_keyword = true
|
||||
ij_java_space_before_while_left_brace = true
|
||||
ij_java_space_before_while_parentheses = true
|
||||
ij_java_space_inside_one_line_enum_braces = false
|
||||
ij_java_space_within_empty_array_initializer_braces = false
|
||||
ij_java_space_within_empty_method_call_parentheses = false
|
||||
ij_java_space_within_empty_method_parentheses = false
|
||||
ij_java_spaces_around_additive_operators = true
|
||||
ij_java_spaces_around_annotation_eq = true
|
||||
ij_java_spaces_around_assignment_operators = true
|
||||
ij_java_spaces_around_bitwise_operators = true
|
||||
ij_java_spaces_around_equality_operators = true
|
||||
ij_java_spaces_around_lambda_arrow = true
|
||||
ij_java_spaces_around_logical_operators = true
|
||||
ij_java_spaces_around_method_ref_dbl_colon = false
|
||||
ij_java_spaces_around_multiplicative_operators = true
|
||||
ij_java_spaces_around_relational_operators = true
|
||||
ij_java_spaces_around_shift_operators = true
|
||||
ij_java_spaces_around_type_bounds_in_type_parameters = true
|
||||
ij_java_spaces_around_unary_operator = false
|
||||
ij_java_spaces_within_angle_brackets = false
|
||||
ij_java_spaces_within_annotation_parentheses = false
|
||||
ij_java_spaces_within_array_initializer_braces = false
|
||||
ij_java_spaces_within_braces = false
|
||||
ij_java_spaces_within_brackets = false
|
||||
ij_java_spaces_within_cast_parentheses = false
|
||||
ij_java_spaces_within_catch_parentheses = false
|
||||
ij_java_spaces_within_deconstruction_list = false
|
||||
ij_java_spaces_within_for_parentheses = false
|
||||
ij_java_spaces_within_if_parentheses = false
|
||||
ij_java_spaces_within_method_call_parentheses = false
|
||||
ij_java_spaces_within_method_parentheses = false
|
||||
ij_java_spaces_within_parentheses = false
|
||||
ij_java_spaces_within_record_header = false
|
||||
ij_java_spaces_within_switch_parentheses = false
|
||||
ij_java_spaces_within_synchronized_parentheses = false
|
||||
ij_java_spaces_within_try_parentheses = false
|
||||
ij_java_spaces_within_while_parentheses = false
|
||||
ij_java_special_else_if_treatment = true
|
||||
ij_java_subclass_name_suffix = Impl
|
||||
ij_java_ternary_operation_signs_on_next_line = false
|
||||
ij_java_ternary_operation_wrap = off
|
||||
ij_java_test_name_suffix = Test
|
||||
ij_java_throws_keyword_wrap = off
|
||||
ij_java_throws_list_wrap = off
|
||||
ij_java_use_external_annotations = false
|
||||
ij_java_use_fq_class_names = false
|
||||
ij_java_use_relative_indents = false
|
||||
ij_java_use_single_class_imports = true
|
||||
ij_java_variable_annotation_wrap = off
|
||||
ij_java_visibility = public
|
||||
ij_java_while_brace_force = never
|
||||
ij_java_while_on_new_line = false
|
||||
ij_java_wrap_comments = false
|
||||
ij_java_wrap_first_method_in_call_chain = false
|
||||
ij_java_wrap_long_lines = false
|
||||
|
||||
[*.properties]
|
||||
ij_properties_align_group_field_declarations = false
|
||||
ij_properties_keep_blank_lines = false
|
||||
ij_properties_key_value_delimiter = equals
|
||||
ij_properties_spaces_around_key_value_delimiter = false
|
||||
|
||||
[.editorconfig]
|
||||
ij_editorconfig_align_group_field_declarations = false
|
||||
ij_editorconfig_space_after_colon = false
|
||||
ij_editorconfig_space_after_comma = true
|
||||
ij_editorconfig_space_before_colon = false
|
||||
ij_editorconfig_space_before_comma = false
|
||||
ij_editorconfig_spaces_around_assignment_operators = true
|
||||
|
||||
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
|
||||
ij_continuation_indent_size = 4
|
||||
ij_xml_align_attributes = false
|
||||
ij_xml_align_text = false
|
||||
ij_xml_attribute_wrap = normal
|
||||
ij_xml_block_comment_add_space = false
|
||||
ij_xml_block_comment_at_first_column = true
|
||||
ij_xml_keep_blank_lines = 2
|
||||
ij_xml_keep_indents_on_empty_lines = false
|
||||
ij_xml_keep_line_breaks = false
|
||||
ij_xml_keep_line_breaks_in_text = true
|
||||
ij_xml_keep_whitespaces = false
|
||||
ij_xml_keep_whitespaces_around_cdata = preserve
|
||||
ij_xml_keep_whitespaces_inside_cdata = false
|
||||
ij_xml_line_comment_at_first_column = true
|
||||
ij_xml_space_after_tag_name = false
|
||||
ij_xml_space_around_equals_in_attribute = false
|
||||
ij_xml_space_inside_empty_tag = true
|
||||
ij_xml_text_wrap = normal
|
||||
ij_xml_use_custom_settings = true
|
||||
|
||||
[{*.apinotes,*.yaml,*.yml}]
|
||||
indent_size = 2
|
||||
ij_yaml_align_values_properties = do_not_align
|
||||
ij_yaml_autoinsert_sequence_marker = true
|
||||
ij_yaml_block_mapping_on_new_line = false
|
||||
ij_yaml_indent_sequence_value = true
|
||||
ij_yaml_keep_indents_on_empty_lines = false
|
||||
ij_yaml_keep_line_breaks = true
|
||||
ij_yaml_sequence_on_new_line = false
|
||||
ij_yaml_space_before_colon = false
|
||||
ij_yaml_spaces_within_braces = true
|
||||
ij_yaml_spaces_within_brackets = true
|
||||
|
||||
[{*.bash,*.sh,*.zsh}]
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
ij_shell_binary_ops_start_line = false
|
||||
ij_shell_keep_column_alignment_padding = false
|
||||
ij_shell_minify_program = false
|
||||
ij_shell_redirect_followed_by_space = false
|
||||
ij_shell_switch_cases_indented = false
|
||||
ij_shell_use_unix_line_separator = true
|
||||
|
||||
[{*.c,*.c++,*.cc,*.cp,*.cpp,*.cppm,*.cu,*.cuh,*.cxx,*.h,*.h++,*.hh,*.hp,*.hpp,*.hxx,*.i,*.icc,*.ii,*.inl,*.ino,*.ipp,*.ixx,*.m,*.mm,*.mxx,*.pch,*.tcc,*.tpp}]
|
||||
ij_c_add_brief_tag = false
|
||||
ij_c_add_getter_prefix = true
|
||||
ij_c_add_setter_prefix = true
|
||||
ij_c_align_dictionary_pair_values = false
|
||||
ij_c_align_group_field_declarations = false
|
||||
ij_c_align_init_list_in_columns = true
|
||||
ij_c_align_multiline_array_initializer_expression = true
|
||||
ij_c_align_multiline_assignment = true
|
||||
ij_c_align_multiline_binary_operation = true
|
||||
ij_c_align_multiline_chained_methods = false
|
||||
ij_c_align_multiline_for = true
|
||||
ij_c_align_multiline_ternary_operation = true
|
||||
ij_c_array_initializer_comma_on_next_line = false
|
||||
ij_c_array_initializer_new_line_after_left_brace = false
|
||||
ij_c_array_initializer_right_brace_on_new_line = false
|
||||
ij_c_array_initializer_wrap = normal
|
||||
ij_c_assignment_wrap = off
|
||||
ij_c_binary_operation_sign_on_next_line = false
|
||||
ij_c_binary_operation_wrap = normal
|
||||
ij_c_blank_lines_after_class_header = 0
|
||||
ij_c_blank_lines_after_imports = 1
|
||||
ij_c_blank_lines_around_class = 1
|
||||
ij_c_blank_lines_around_field = 0
|
||||
ij_c_blank_lines_around_field_in_interface = 0
|
||||
ij_c_blank_lines_around_method = 1
|
||||
ij_c_blank_lines_around_method_in_interface = 1
|
||||
ij_c_blank_lines_around_namespace = 0
|
||||
ij_c_blank_lines_around_properties_in_declaration = 0
|
||||
ij_c_blank_lines_around_properties_in_interface = 0
|
||||
ij_c_blank_lines_before_imports = 1
|
||||
ij_c_blank_lines_before_method_body = 0
|
||||
ij_c_block_brace_placement = end_of_line
|
||||
ij_c_block_brace_style = end_of_line
|
||||
ij_c_block_comment_at_first_column = true
|
||||
ij_c_catch_on_new_line = false
|
||||
ij_c_class_brace_style = end_of_line
|
||||
ij_c_class_constructor_init_list_align_multiline = true
|
||||
ij_c_class_constructor_init_list_comma_on_next_line = false
|
||||
ij_c_class_constructor_init_list_new_line_after_colon = never
|
||||
ij_c_class_constructor_init_list_new_line_before_colon = if_long
|
||||
ij_c_class_constructor_init_list_wrap = normal
|
||||
ij_c_copy_is_deep = false
|
||||
ij_c_create_interface_for_categories = true
|
||||
ij_c_declare_generated_methods = true
|
||||
ij_c_description_include_member_names = true
|
||||
ij_c_discharged_short_ternary_operator = false
|
||||
ij_c_do_not_add_breaks = false
|
||||
ij_c_do_while_brace_force = never
|
||||
ij_c_else_on_new_line = false
|
||||
ij_c_enum_constants_comma_on_next_line = false
|
||||
ij_c_enum_constants_wrap = on_every_item
|
||||
ij_c_for_brace_force = never
|
||||
ij_c_for_statement_new_line_after_left_paren = false
|
||||
ij_c_for_statement_right_paren_on_new_line = false
|
||||
ij_c_for_statement_wrap = off
|
||||
ij_c_function_brace_placement = end_of_line
|
||||
ij_c_function_call_arguments_align_multiline = true
|
||||
ij_c_function_call_arguments_align_multiline_pars = false
|
||||
ij_c_function_call_arguments_comma_on_next_line = false
|
||||
ij_c_function_call_arguments_new_line_after_lpar = false
|
||||
ij_c_function_call_arguments_new_line_before_rpar = false
|
||||
ij_c_function_call_arguments_wrap = normal
|
||||
ij_c_function_non_top_after_return_type_wrap = normal
|
||||
ij_c_function_parameters_align_multiline = true
|
||||
ij_c_function_parameters_align_multiline_pars = false
|
||||
ij_c_function_parameters_comma_on_next_line = false
|
||||
ij_c_function_parameters_new_line_after_lpar = false
|
||||
ij_c_function_parameters_new_line_before_rpar = false
|
||||
ij_c_function_parameters_wrap = normal
|
||||
ij_c_function_top_after_return_type_wrap = normal
|
||||
ij_c_generate_additional_eq_operators = true
|
||||
ij_c_generate_additional_rel_operators = true
|
||||
ij_c_generate_class_constructor = true
|
||||
ij_c_generate_comparison_operators_use_std_tie = false
|
||||
ij_c_generate_instance_variables_for_properties = ask
|
||||
ij_c_generate_operators_as_members = true
|
||||
ij_c_header_guard_style_pattern = ${PROJECT_NAME}_${FILE_NAME}_${EXT}
|
||||
ij_c_if_brace_force = never
|
||||
ij_c_in_line_short_ternary_operator = true
|
||||
ij_c_indent_block_comment = true
|
||||
ij_c_indent_c_struct_members = 4
|
||||
ij_c_indent_case_from_switch = true
|
||||
ij_c_indent_class_members = 4
|
||||
ij_c_indent_directive_as_code = false
|
||||
ij_c_indent_implementation_members = 0
|
||||
ij_c_indent_inside_code_block = 4
|
||||
ij_c_indent_interface_members = 0
|
||||
ij_c_indent_interface_members_except_ivars_block = false
|
||||
ij_c_indent_namespace_members = 4
|
||||
ij_c_indent_preprocessor_directive = 0
|
||||
ij_c_indent_visibility_keywords = 0
|
||||
ij_c_insert_override = true
|
||||
ij_c_insert_virtual_with_override = false
|
||||
ij_c_introduce_auto_vars = false
|
||||
ij_c_introduce_const_params = false
|
||||
ij_c_introduce_const_vars = false
|
||||
ij_c_introduce_generate_property = false
|
||||
ij_c_introduce_generate_synthesize = true
|
||||
ij_c_introduce_globals_to_header = true
|
||||
ij_c_introduce_prop_to_private_category = false
|
||||
ij_c_introduce_static_consts = true
|
||||
ij_c_introduce_use_ns_types = false
|
||||
ij_c_ivars_prefix = _
|
||||
ij_c_keep_blank_lines_before_end = 2
|
||||
ij_c_keep_blank_lines_before_right_brace = 2
|
||||
ij_c_keep_blank_lines_in_code = 2
|
||||
ij_c_keep_blank_lines_in_declarations = 2
|
||||
ij_c_keep_case_expressions_in_one_line = false
|
||||
ij_c_keep_control_statement_in_one_line = true
|
||||
ij_c_keep_directive_at_first_column = true
|
||||
ij_c_keep_first_column_comment = true
|
||||
ij_c_keep_line_breaks = true
|
||||
ij_c_keep_nested_namespaces_in_one_line = false
|
||||
ij_c_keep_simple_blocks_in_one_line = true
|
||||
ij_c_keep_simple_methods_in_one_line = true
|
||||
ij_c_keep_structures_in_one_line = false
|
||||
ij_c_lambda_capture_list_align_multiline = false
|
||||
ij_c_lambda_capture_list_align_multiline_bracket = false
|
||||
ij_c_lambda_capture_list_comma_on_next_line = false
|
||||
ij_c_lambda_capture_list_new_line_after_lbracket = false
|
||||
ij_c_lambda_capture_list_new_line_before_rbracket = false
|
||||
ij_c_lambda_capture_list_wrap = off
|
||||
ij_c_line_comment_add_space = false
|
||||
ij_c_line_comment_at_first_column = true
|
||||
ij_c_method_brace_placement = end_of_line
|
||||
ij_c_method_call_arguments_align_by_colons = true
|
||||
ij_c_method_call_arguments_align_multiline = false
|
||||
ij_c_method_call_arguments_special_dictionary_pairs_treatment = true
|
||||
ij_c_method_call_arguments_wrap = off
|
||||
ij_c_method_call_chain_wrap = off
|
||||
ij_c_method_parameters_align_by_colons = true
|
||||
ij_c_method_parameters_align_multiline = false
|
||||
ij_c_method_parameters_wrap = off
|
||||
ij_c_namespace_brace_placement = end_of_line
|
||||
ij_c_parentheses_expression_new_line_after_left_paren = false
|
||||
ij_c_parentheses_expression_right_paren_on_new_line = false
|
||||
ij_c_place_assignment_sign_on_next_line = false
|
||||
ij_c_property_nonatomic = true
|
||||
ij_c_put_ivars_to_implementation = true
|
||||
ij_c_refactor_compatibility_aliases_and_classes = true
|
||||
ij_c_refactor_properties_and_ivars = true
|
||||
ij_c_release_style = ivar
|
||||
ij_c_retain_object_parameters_in_constructor = true
|
||||
ij_c_semicolon_after_method_signature = false
|
||||
ij_c_shift_operation_align_multiline = true
|
||||
ij_c_shift_operation_wrap = normal
|
||||
ij_c_show_non_virtual_functions = false
|
||||
ij_c_space_after_colon = true
|
||||
ij_c_space_after_colon_in_foreach = true
|
||||
ij_c_space_after_colon_in_selector = false
|
||||
ij_c_space_after_comma = true
|
||||
ij_c_space_after_cup_in_blocks = false
|
||||
ij_c_space_after_dictionary_literal_colon = true
|
||||
ij_c_space_after_for_semicolon = true
|
||||
ij_c_space_after_init_list_colon = true
|
||||
ij_c_space_after_method_parameter_type_parentheses = false
|
||||
ij_c_space_after_method_return_type_parentheses = false
|
||||
ij_c_space_after_pointer_in_declaration = false
|
||||
ij_c_space_after_quest = true
|
||||
ij_c_space_after_reference_in_declaration = false
|
||||
ij_c_space_after_reference_in_rvalue = false
|
||||
ij_c_space_after_structures_rbrace = true
|
||||
ij_c_space_after_superclass_colon = true
|
||||
ij_c_space_after_type_cast = true
|
||||
ij_c_space_after_visibility_sign_in_method_declaration = true
|
||||
ij_c_space_before_autorelease_pool_lbrace = true
|
||||
ij_c_space_before_catch_keyword = true
|
||||
ij_c_space_before_catch_left_brace = true
|
||||
ij_c_space_before_catch_parentheses = true
|
||||
ij_c_space_before_category_parentheses = true
|
||||
ij_c_space_before_chained_send_message = true
|
||||
ij_c_space_before_class_left_brace = true
|
||||
ij_c_space_before_colon = true
|
||||
ij_c_space_before_colon_in_foreach = false
|
||||
ij_c_space_before_comma = false
|
||||
ij_c_space_before_dictionary_literal_colon = false
|
||||
ij_c_space_before_do_left_brace = true
|
||||
ij_c_space_before_else_keyword = true
|
||||
ij_c_space_before_else_left_brace = true
|
||||
ij_c_space_before_export_lbrace = true
|
||||
ij_c_space_before_for_left_brace = true
|
||||
ij_c_space_before_for_parentheses = true
|
||||
ij_c_space_before_for_semicolon = false
|
||||
ij_c_space_before_if_left_brace = true
|
||||
ij_c_space_before_if_parentheses = true
|
||||
ij_c_space_before_init_list = false
|
||||
ij_c_space_before_init_list_colon = true
|
||||
ij_c_space_before_method_call_parentheses = false
|
||||
ij_c_space_before_method_left_brace = true
|
||||
ij_c_space_before_method_parentheses = false
|
||||
ij_c_space_before_namespace_lbrace = true
|
||||
ij_c_space_before_pointer_in_declaration = true
|
||||
ij_c_space_before_property_attributes_parentheses = false
|
||||
ij_c_space_before_protocols_brackets = true
|
||||
ij_c_space_before_quest = true
|
||||
ij_c_space_before_reference_in_declaration = true
|
||||
ij_c_space_before_superclass_colon = true
|
||||
ij_c_space_before_switch_left_brace = true
|
||||
ij_c_space_before_switch_parentheses = true
|
||||
ij_c_space_before_template_call_lt = false
|
||||
ij_c_space_before_template_declaration_lt = false
|
||||
ij_c_space_before_try_left_brace = true
|
||||
ij_c_space_before_while_keyword = true
|
||||
ij_c_space_before_while_left_brace = true
|
||||
ij_c_space_before_while_parentheses = true
|
||||
ij_c_space_between_adjacent_brackets = false
|
||||
ij_c_space_between_operator_and_punctuator = false
|
||||
ij_c_space_within_empty_array_initializer_braces = false
|
||||
ij_c_spaces_around_additive_operators = true
|
||||
ij_c_spaces_around_assignment_operators = true
|
||||
ij_c_spaces_around_bitwise_operators = true
|
||||
ij_c_spaces_around_equality_operators = true
|
||||
ij_c_spaces_around_lambda_arrow = true
|
||||
ij_c_spaces_around_logical_operators = true
|
||||
ij_c_spaces_around_multiplicative_operators = true
|
||||
ij_c_spaces_around_pm_operators = false
|
||||
ij_c_spaces_around_relational_operators = true
|
||||
ij_c_spaces_around_shift_operators = true
|
||||
ij_c_spaces_around_unary_operator = false
|
||||
ij_c_spaces_within_array_initializer_braces = false
|
||||
ij_c_spaces_within_braces = true
|
||||
ij_c_spaces_within_brackets = false
|
||||
ij_c_spaces_within_cast_parentheses = false
|
||||
ij_c_spaces_within_catch_parentheses = false
|
||||
ij_c_spaces_within_category_parentheses = false
|
||||
ij_c_spaces_within_empty_braces = false
|
||||
ij_c_spaces_within_empty_function_call_parentheses = false
|
||||
ij_c_spaces_within_empty_function_declaration_parentheses = false
|
||||
ij_c_spaces_within_empty_lambda_capture_list_bracket = false
|
||||
ij_c_spaces_within_empty_template_call_ltgt = false
|
||||
ij_c_spaces_within_empty_template_declaration_ltgt = false
|
||||
ij_c_spaces_within_for_parentheses = false
|
||||
ij_c_spaces_within_function_call_parentheses = false
|
||||
ij_c_spaces_within_function_declaration_parentheses = false
|
||||
ij_c_spaces_within_if_parentheses = false
|
||||
ij_c_spaces_within_lambda_capture_list_bracket = false
|
||||
ij_c_spaces_within_method_parameter_type_parentheses = false
|
||||
ij_c_spaces_within_method_return_type_parentheses = false
|
||||
ij_c_spaces_within_parentheses = false
|
||||
ij_c_spaces_within_property_attributes_parentheses = false
|
||||
ij_c_spaces_within_protocols_brackets = false
|
||||
ij_c_spaces_within_send_message_brackets = false
|
||||
ij_c_spaces_within_structured_binding_list_bracket = false
|
||||
ij_c_spaces_within_switch_parentheses = false
|
||||
ij_c_spaces_within_template_call_ltgt = false
|
||||
ij_c_spaces_within_template_declaration_ltgt = false
|
||||
ij_c_spaces_within_template_double_gt = true
|
||||
ij_c_spaces_within_while_parentheses = false
|
||||
ij_c_special_else_if_treatment = true
|
||||
ij_c_structured_binding_list_align_multiline = false
|
||||
ij_c_structured_binding_list_align_multiline_bracket = false
|
||||
ij_c_structured_binding_list_comma_on_next_line = false
|
||||
ij_c_structured_binding_list_new_line_after_lbracket = false
|
||||
ij_c_structured_binding_list_new_line_before_rbracket = false
|
||||
ij_c_structured_binding_list_wrap = off
|
||||
ij_c_superclass_list_after_colon = never
|
||||
ij_c_superclass_list_align_multiline = true
|
||||
ij_c_superclass_list_before_colon = if_long
|
||||
ij_c_superclass_list_comma_on_next_line = false
|
||||
ij_c_superclass_list_wrap = on_every_item
|
||||
ij_c_tag_prefix_of_block_comment = at
|
||||
ij_c_tag_prefix_of_line_comment = back_slash
|
||||
ij_c_template_call_arguments_align_multiline = false
|
||||
ij_c_template_call_arguments_align_multiline_pars = false
|
||||
ij_c_template_call_arguments_comma_on_next_line = false
|
||||
ij_c_template_call_arguments_new_line_after_lt = false
|
||||
ij_c_template_call_arguments_new_line_before_gt = false
|
||||
ij_c_template_call_arguments_wrap = off
|
||||
ij_c_template_declaration_function_body_indent = false
|
||||
ij_c_template_declaration_function_wrap = split_into_lines
|
||||
ij_c_template_declaration_struct_body_indent = false
|
||||
ij_c_template_declaration_struct_wrap = split_into_lines
|
||||
ij_c_template_parameters_align_multiline = false
|
||||
ij_c_template_parameters_align_multiline_pars = false
|
||||
ij_c_template_parameters_comma_on_next_line = false
|
||||
ij_c_template_parameters_new_line_after_lt = false
|
||||
ij_c_template_parameters_new_line_before_gt = false
|
||||
ij_c_template_parameters_wrap = off
|
||||
ij_c_ternary_operation_signs_on_next_line = true
|
||||
ij_c_ternary_operation_wrap = normal
|
||||
ij_c_type_qualifiers_placement = before
|
||||
ij_c_use_modern_casts = true
|
||||
ij_c_use_setters_in_constructor = true
|
||||
ij_c_while_brace_force = never
|
||||
ij_c_while_on_new_line = false
|
||||
ij_c_wrap_property_declaration = off
|
||||
|
||||
[{*.cmake,CMakeLists.txt}]
|
||||
ij_cmake_align_multiline_parameters_in_calls = false
|
||||
ij_cmake_force_commands_case = 2
|
||||
ij_cmake_keep_blank_lines_in_code = 2
|
||||
ij_cmake_space_before_for_parentheses = true
|
||||
ij_cmake_space_before_if_parentheses = true
|
||||
ij_cmake_space_before_method_call_parentheses = false
|
||||
ij_cmake_space_before_method_parentheses = false
|
||||
ij_cmake_space_before_while_parentheses = true
|
||||
ij_cmake_spaces_within_for_parentheses = false
|
||||
ij_cmake_spaces_within_if_parentheses = false
|
||||
ij_cmake_spaces_within_method_call_parentheses = false
|
||||
ij_cmake_spaces_within_method_parentheses = false
|
||||
ij_cmake_spaces_within_while_parentheses = false
|
||||
|
||||
[{*.gant,*.groovy,*.gy}]
|
||||
ij_groovy_align_group_field_declarations = false
|
||||
ij_groovy_align_multiline_array_initializer_expression = false
|
||||
ij_groovy_align_multiline_assignment = false
|
||||
ij_groovy_align_multiline_binary_operation = false
|
||||
ij_groovy_align_multiline_chained_methods = false
|
||||
ij_groovy_align_multiline_extends_list = false
|
||||
ij_groovy_align_multiline_for = true
|
||||
ij_groovy_align_multiline_list_or_map = true
|
||||
ij_groovy_align_multiline_method_parentheses = false
|
||||
ij_groovy_align_multiline_parameters = true
|
||||
ij_groovy_align_multiline_parameters_in_calls = false
|
||||
ij_groovy_align_multiline_resources = true
|
||||
ij_groovy_align_multiline_ternary_operation = false
|
||||
ij_groovy_align_multiline_throws_list = false
|
||||
ij_groovy_align_named_args_in_map = true
|
||||
ij_groovy_align_throws_keyword = false
|
||||
ij_groovy_array_initializer_new_line_after_left_brace = false
|
||||
ij_groovy_array_initializer_right_brace_on_new_line = false
|
||||
ij_groovy_array_initializer_wrap = off
|
||||
ij_groovy_assert_statement_wrap = off
|
||||
ij_groovy_assignment_wrap = off
|
||||
ij_groovy_binary_operation_wrap = off
|
||||
ij_groovy_blank_lines_after_class_header = 0
|
||||
ij_groovy_blank_lines_after_imports = 1
|
||||
ij_groovy_blank_lines_after_package = 1
|
||||
ij_groovy_blank_lines_around_class = 1
|
||||
ij_groovy_blank_lines_around_field = 0
|
||||
ij_groovy_blank_lines_around_field_in_interface = 0
|
||||
ij_groovy_blank_lines_around_method = 1
|
||||
ij_groovy_blank_lines_around_method_in_interface = 1
|
||||
ij_groovy_blank_lines_before_imports = 1
|
||||
ij_groovy_blank_lines_before_method_body = 0
|
||||
ij_groovy_blank_lines_before_package = 0
|
||||
ij_groovy_block_brace_style = end_of_line
|
||||
ij_groovy_block_comment_add_space = false
|
||||
ij_groovy_block_comment_at_first_column = true
|
||||
ij_groovy_call_parameters_new_line_after_left_paren = false
|
||||
ij_groovy_call_parameters_right_paren_on_new_line = false
|
||||
ij_groovy_call_parameters_wrap = off
|
||||
ij_groovy_catch_on_new_line = false
|
||||
ij_groovy_class_annotation_wrap = split_into_lines
|
||||
ij_groovy_class_brace_style = end_of_line
|
||||
ij_groovy_class_count_to_use_import_on_demand = 5
|
||||
ij_groovy_do_while_brace_force = never
|
||||
ij_groovy_else_on_new_line = false
|
||||
ij_groovy_enable_groovydoc_formatting = true
|
||||
ij_groovy_enum_constants_wrap = off
|
||||
ij_groovy_extends_keyword_wrap = off
|
||||
ij_groovy_extends_list_wrap = off
|
||||
ij_groovy_field_annotation_wrap = split_into_lines
|
||||
ij_groovy_finally_on_new_line = false
|
||||
ij_groovy_for_brace_force = never
|
||||
ij_groovy_for_statement_new_line_after_left_paren = false
|
||||
ij_groovy_for_statement_right_paren_on_new_line = false
|
||||
ij_groovy_for_statement_wrap = off
|
||||
ij_groovy_ginq_general_clause_wrap_policy = 2
|
||||
ij_groovy_ginq_having_wrap_policy = 1
|
||||
ij_groovy_ginq_indent_having_clause = true
|
||||
ij_groovy_ginq_indent_on_clause = true
|
||||
ij_groovy_ginq_on_wrap_policy = 1
|
||||
ij_groovy_ginq_space_after_keyword = true
|
||||
ij_groovy_if_brace_force = never
|
||||
ij_groovy_import_annotation_wrap = 2
|
||||
ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
|
||||
ij_groovy_indent_case_from_switch = true
|
||||
ij_groovy_indent_label_blocks = true
|
||||
ij_groovy_insert_inner_class_imports = false
|
||||
ij_groovy_keep_blank_lines_before_right_brace = 2
|
||||
ij_groovy_keep_blank_lines_in_code = 2
|
||||
ij_groovy_keep_blank_lines_in_declarations = 2
|
||||
ij_groovy_keep_control_statement_in_one_line = true
|
||||
ij_groovy_keep_first_column_comment = true
|
||||
ij_groovy_keep_indents_on_empty_lines = false
|
||||
ij_groovy_keep_line_breaks = true
|
||||
ij_groovy_keep_multiple_expressions_in_one_line = false
|
||||
ij_groovy_keep_simple_blocks_in_one_line = false
|
||||
ij_groovy_keep_simple_classes_in_one_line = true
|
||||
ij_groovy_keep_simple_lambdas_in_one_line = true
|
||||
ij_groovy_keep_simple_methods_in_one_line = true
|
||||
ij_groovy_label_indent_absolute = false
|
||||
ij_groovy_label_indent_size = 0
|
||||
ij_groovy_lambda_brace_style = end_of_line
|
||||
ij_groovy_layout_static_imports_separately = true
|
||||
ij_groovy_line_comment_add_space = false
|
||||
ij_groovy_line_comment_add_space_on_reformat = false
|
||||
ij_groovy_line_comment_at_first_column = true
|
||||
ij_groovy_method_annotation_wrap = split_into_lines
|
||||
ij_groovy_method_brace_style = end_of_line
|
||||
ij_groovy_method_call_chain_wrap = off
|
||||
ij_groovy_method_parameters_new_line_after_left_paren = false
|
||||
ij_groovy_method_parameters_right_paren_on_new_line = false
|
||||
ij_groovy_method_parameters_wrap = off
|
||||
ij_groovy_modifier_list_wrap = false
|
||||
ij_groovy_names_count_to_use_import_on_demand = 3
|
||||
ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
|
||||
ij_groovy_parameter_annotation_wrap = off
|
||||
ij_groovy_parentheses_expression_new_line_after_left_paren = false
|
||||
ij_groovy_parentheses_expression_right_paren_on_new_line = false
|
||||
ij_groovy_prefer_parameters_wrap = false
|
||||
ij_groovy_resource_list_new_line_after_left_paren = false
|
||||
ij_groovy_resource_list_right_paren_on_new_line = false
|
||||
ij_groovy_resource_list_wrap = off
|
||||
ij_groovy_space_after_assert_separator = true
|
||||
ij_groovy_space_after_colon = true
|
||||
ij_groovy_space_after_comma = true
|
||||
ij_groovy_space_after_comma_in_type_arguments = true
|
||||
ij_groovy_space_after_for_semicolon = true
|
||||
ij_groovy_space_after_quest = true
|
||||
ij_groovy_space_after_type_cast = true
|
||||
ij_groovy_space_before_annotation_parameter_list = false
|
||||
ij_groovy_space_before_array_initializer_left_brace = false
|
||||
ij_groovy_space_before_assert_separator = false
|
||||
ij_groovy_space_before_catch_keyword = true
|
||||
ij_groovy_space_before_catch_left_brace = true
|
||||
ij_groovy_space_before_catch_parentheses = true
|
||||
ij_groovy_space_before_class_left_brace = true
|
||||
ij_groovy_space_before_closure_left_brace = true
|
||||
ij_groovy_space_before_colon = true
|
||||
ij_groovy_space_before_comma = false
|
||||
ij_groovy_space_before_do_left_brace = true
|
||||
ij_groovy_space_before_else_keyword = true
|
||||
ij_groovy_space_before_else_left_brace = true
|
||||
ij_groovy_space_before_finally_keyword = true
|
||||
ij_groovy_space_before_finally_left_brace = true
|
||||
ij_groovy_space_before_for_left_brace = true
|
||||
ij_groovy_space_before_for_parentheses = true
|
||||
ij_groovy_space_before_for_semicolon = false
|
||||
ij_groovy_space_before_if_left_brace = true
|
||||
ij_groovy_space_before_if_parentheses = true
|
||||
ij_groovy_space_before_method_call_parentheses = false
|
||||
ij_groovy_space_before_method_left_brace = true
|
||||
ij_groovy_space_before_method_parentheses = false
|
||||
ij_groovy_space_before_quest = true
|
||||
ij_groovy_space_before_record_parentheses = false
|
||||
ij_groovy_space_before_switch_left_brace = true
|
||||
ij_groovy_space_before_switch_parentheses = true
|
||||
ij_groovy_space_before_synchronized_left_brace = true
|
||||
ij_groovy_space_before_synchronized_parentheses = true
|
||||
ij_groovy_space_before_try_left_brace = true
|
||||
ij_groovy_space_before_try_parentheses = true
|
||||
ij_groovy_space_before_while_keyword = true
|
||||
ij_groovy_space_before_while_left_brace = true
|
||||
ij_groovy_space_before_while_parentheses = true
|
||||
ij_groovy_space_in_named_argument = true
|
||||
ij_groovy_space_in_named_argument_before_colon = false
|
||||
ij_groovy_space_within_empty_array_initializer_braces = false
|
||||
ij_groovy_space_within_empty_method_call_parentheses = false
|
||||
ij_groovy_spaces_around_additive_operators = true
|
||||
ij_groovy_spaces_around_assignment_operators = true
|
||||
ij_groovy_spaces_around_bitwise_operators = true
|
||||
ij_groovy_spaces_around_equality_operators = true
|
||||
ij_groovy_spaces_around_lambda_arrow = true
|
||||
ij_groovy_spaces_around_logical_operators = true
|
||||
ij_groovy_spaces_around_multiplicative_operators = true
|
||||
ij_groovy_spaces_around_regex_operators = true
|
||||
ij_groovy_spaces_around_relational_operators = true
|
||||
ij_groovy_spaces_around_shift_operators = true
|
||||
ij_groovy_spaces_within_annotation_parentheses = false
|
||||
ij_groovy_spaces_within_array_initializer_braces = false
|
||||
ij_groovy_spaces_within_braces = true
|
||||
ij_groovy_spaces_within_brackets = false
|
||||
ij_groovy_spaces_within_cast_parentheses = false
|
||||
ij_groovy_spaces_within_catch_parentheses = false
|
||||
ij_groovy_spaces_within_for_parentheses = false
|
||||
ij_groovy_spaces_within_gstring_injection_braces = false
|
||||
ij_groovy_spaces_within_if_parentheses = false
|
||||
ij_groovy_spaces_within_list_or_map = false
|
||||
ij_groovy_spaces_within_method_call_parentheses = false
|
||||
ij_groovy_spaces_within_method_parentheses = false
|
||||
ij_groovy_spaces_within_parentheses = false
|
||||
ij_groovy_spaces_within_switch_parentheses = false
|
||||
ij_groovy_spaces_within_synchronized_parentheses = false
|
||||
ij_groovy_spaces_within_try_parentheses = false
|
||||
ij_groovy_spaces_within_tuple_expression = false
|
||||
ij_groovy_spaces_within_while_parentheses = false
|
||||
ij_groovy_special_else_if_treatment = true
|
||||
ij_groovy_ternary_operation_wrap = off
|
||||
ij_groovy_throws_keyword_wrap = off
|
||||
ij_groovy_throws_list_wrap = off
|
||||
ij_groovy_use_flying_geese_braces = false
|
||||
ij_groovy_use_fq_class_names = false
|
||||
ij_groovy_use_fq_class_names_in_javadoc = true
|
||||
ij_groovy_use_relative_indents = false
|
||||
ij_groovy_use_single_class_imports = true
|
||||
ij_groovy_variable_annotation_wrap = off
|
||||
ij_groovy_while_brace_force = never
|
||||
ij_groovy_while_on_new_line = false
|
||||
ij_groovy_wrap_chain_calls_after_dot = false
|
||||
ij_groovy_wrap_long_lines = false
|
||||
|
||||
[{*.har,*.json}]
|
||||
indent_size = 2
|
||||
ij_json_array_wrapping = split_into_lines
|
||||
ij_json_keep_blank_lines_in_code = 0
|
||||
ij_json_keep_indents_on_empty_lines = false
|
||||
ij_json_keep_line_breaks = true
|
||||
ij_json_keep_trailing_comma = false
|
||||
ij_json_object_wrapping = split_into_lines
|
||||
ij_json_property_alignment = do_not_align
|
||||
ij_json_space_after_colon = true
|
||||
ij_json_space_after_comma = true
|
||||
ij_json_space_before_colon = false
|
||||
ij_json_space_before_comma = false
|
||||
ij_json_spaces_within_braces = false
|
||||
ij_json_spaces_within_brackets = false
|
||||
ij_json_wrap_long_lines = false
|
||||
|
||||
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
|
||||
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
||||
ij_html_align_attributes = true
|
||||
ij_html_align_text = false
|
||||
ij_html_attribute_wrap = normal
|
||||
ij_html_block_comment_add_space = false
|
||||
ij_html_block_comment_at_first_column = true
|
||||
ij_html_do_not_align_children_of_min_lines = 0
|
||||
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
|
||||
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
|
||||
ij_html_enforce_quotes = false
|
||||
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
|
||||
ij_html_keep_blank_lines = 2
|
||||
ij_html_keep_indents_on_empty_lines = false
|
||||
ij_html_keep_line_breaks = true
|
||||
ij_html_keep_line_breaks_in_text = true
|
||||
ij_html_keep_whitespaces = false
|
||||
ij_html_keep_whitespaces_inside = span,pre,textarea
|
||||
ij_html_line_comment_at_first_column = true
|
||||
ij_html_new_line_after_last_attribute = never
|
||||
ij_html_new_line_before_first_attribute = never
|
||||
ij_html_quote_style = double
|
||||
ij_html_remove_new_line_before_tags = br
|
||||
ij_html_space_after_tag_name = false
|
||||
ij_html_space_around_equality_in_attribute = false
|
||||
ij_html_space_inside_empty_tag = false
|
||||
ij_html_text_wrap = normal
|
||||
|
||||
[{*.kt,*.kts}]
|
||||
ij_kotlin_align_in_columns_case_branch = false
|
||||
ij_kotlin_align_multiline_binary_operation = false
|
||||
ij_kotlin_align_multiline_extends_list = false
|
||||
ij_kotlin_align_multiline_method_parentheses = false
|
||||
ij_kotlin_align_multiline_parameters = true
|
||||
ij_kotlin_align_multiline_parameters_in_calls = false
|
||||
ij_kotlin_allow_trailing_comma = false
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = false
|
||||
ij_kotlin_assignment_wrap = off
|
||||
ij_kotlin_blank_lines_after_class_header = 0
|
||||
ij_kotlin_blank_lines_around_block_when_branches = 0
|
||||
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
|
||||
ij_kotlin_block_comment_add_space = false
|
||||
ij_kotlin_block_comment_at_first_column = true
|
||||
ij_kotlin_call_parameters_new_line_after_left_paren = false
|
||||
ij_kotlin_call_parameters_right_paren_on_new_line = false
|
||||
ij_kotlin_call_parameters_wrap = off
|
||||
ij_kotlin_catch_on_new_line = false
|
||||
ij_kotlin_class_annotation_wrap = split_into_lines
|
||||
ij_kotlin_continuation_indent_for_chained_calls = true
|
||||
ij_kotlin_continuation_indent_for_expression_bodies = true
|
||||
ij_kotlin_continuation_indent_in_argument_lists = true
|
||||
ij_kotlin_continuation_indent_in_elvis = true
|
||||
ij_kotlin_continuation_indent_in_if_conditions = true
|
||||
ij_kotlin_continuation_indent_in_parameter_lists = true
|
||||
ij_kotlin_continuation_indent_in_supertype_lists = true
|
||||
ij_kotlin_else_on_new_line = false
|
||||
ij_kotlin_enum_constants_wrap = off
|
||||
ij_kotlin_extends_list_wrap = off
|
||||
ij_kotlin_field_annotation_wrap = split_into_lines
|
||||
ij_kotlin_finally_on_new_line = false
|
||||
ij_kotlin_if_rparen_on_new_line = false
|
||||
ij_kotlin_import_nested_classes = false
|
||||
ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
|
||||
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
|
||||
ij_kotlin_keep_blank_lines_before_right_brace = 2
|
||||
ij_kotlin_keep_blank_lines_in_code = 2
|
||||
ij_kotlin_keep_blank_lines_in_declarations = 2
|
||||
ij_kotlin_keep_first_column_comment = true
|
||||
ij_kotlin_keep_indents_on_empty_lines = false
|
||||
ij_kotlin_keep_line_breaks = true
|
||||
ij_kotlin_lbrace_on_next_line = false
|
||||
ij_kotlin_line_break_after_multiline_when_entry = true
|
||||
ij_kotlin_line_comment_add_space = false
|
||||
ij_kotlin_line_comment_add_space_on_reformat = false
|
||||
ij_kotlin_line_comment_at_first_column = true
|
||||
ij_kotlin_method_annotation_wrap = split_into_lines
|
||||
ij_kotlin_method_call_chain_wrap = off
|
||||
ij_kotlin_method_parameters_new_line_after_left_paren = false
|
||||
ij_kotlin_method_parameters_right_paren_on_new_line = false
|
||||
ij_kotlin_method_parameters_wrap = off
|
||||
ij_kotlin_name_count_to_use_star_import = 2147483647
|
||||
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
||||
ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.**
|
||||
ij_kotlin_parameter_annotation_wrap = off
|
||||
ij_kotlin_space_after_comma = true
|
||||
ij_kotlin_space_after_extend_colon = true
|
||||
ij_kotlin_space_after_type_colon = true
|
||||
ij_kotlin_space_before_catch_parentheses = true
|
||||
ij_kotlin_space_before_comma = false
|
||||
ij_kotlin_space_before_extend_colon = true
|
||||
ij_kotlin_space_before_for_parentheses = true
|
||||
ij_kotlin_space_before_if_parentheses = true
|
||||
ij_kotlin_space_before_lambda_arrow = true
|
||||
ij_kotlin_space_before_type_colon = false
|
||||
ij_kotlin_space_before_when_parentheses = true
|
||||
ij_kotlin_space_before_while_parentheses = true
|
||||
ij_kotlin_spaces_around_additive_operators = true
|
||||
ij_kotlin_spaces_around_assignment_operators = true
|
||||
ij_kotlin_spaces_around_equality_operators = true
|
||||
ij_kotlin_spaces_around_function_type_arrow = true
|
||||
ij_kotlin_spaces_around_logical_operators = true
|
||||
ij_kotlin_spaces_around_multiplicative_operators = true
|
||||
ij_kotlin_spaces_around_range = false
|
||||
ij_kotlin_spaces_around_relational_operators = true
|
||||
ij_kotlin_spaces_around_unary_operator = false
|
||||
ij_kotlin_spaces_around_when_arrow = true
|
||||
ij_kotlin_use_custom_formatting_for_modifiers = true
|
||||
ij_kotlin_variable_annotation_wrap = off
|
||||
ij_kotlin_while_on_new_line = false
|
||||
ij_kotlin_wrap_elvis_expressions = 1
|
||||
ij_kotlin_wrap_expression_body_functions = 0
|
||||
ij_kotlin_wrap_first_method_in_call_chain = false
|
||||
|
||||
[{*.markdown,*.md}]
|
||||
ij_markdown_force_one_space_after_blockquote_symbol = true
|
||||
ij_markdown_force_one_space_after_header_symbol = true
|
||||
ij_markdown_force_one_space_after_list_bullet = true
|
||||
ij_markdown_force_one_space_between_words = true
|
||||
ij_markdown_format_tables = true
|
||||
ij_markdown_insert_quote_arrows_on_wrap = true
|
||||
ij_markdown_keep_indents_on_empty_lines = false
|
||||
ij_markdown_keep_line_breaks_inside_text_blocks = true
|
||||
ij_markdown_max_lines_around_block_elements = 1
|
||||
ij_markdown_max_lines_around_header = 1
|
||||
ij_markdown_max_lines_between_paragraphs = 1
|
||||
ij_markdown_min_lines_around_block_elements = 1
|
||||
ij_markdown_min_lines_around_header = 1
|
||||
ij_markdown_min_lines_between_paragraphs = 1
|
||||
ij_markdown_wrap_text_if_long = true
|
||||
ij_markdown_wrap_text_inside_blockquotes = true
|
||||
|
||||
[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}]
|
||||
ij_toml_keep_indents_on_empty_lines = false
|
||||
Generated
+5
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
+10
-3
@@ -1,18 +1,25 @@
|
||||
# How to contribute?
|
||||
|
||||
## Testing
|
||||
## Testing
|
||||
|
||||
Just use OpenTracks and tell us what can be improved by opening an issue [here](https://github.com/OpenTracksApp/OpenTracks/issues/new/choose)
|
||||
Just use OpenTracks and tell us what can be improved by opening an
|
||||
issue [here](https://github.com/OpenTracksApp/OpenTracks/issues/new/choose)
|
||||
|
||||
If we broke things (happens now and then), please tell us also.
|
||||
|
||||
## Translations
|
||||
|
||||
Translations are hosted on [hosted.weblate.org](https://hosted.weblate.org/engage/opentracks/).
|
||||
|
||||
## Coding new features
|
||||
|
||||
You have a cool idea for a feature that is missing in OpenTracks?
|
||||
And you would like to help by implementing it?
|
||||
If not, you can just take a look at the [issue tracker](https://github.com/OpenTracksApp/OpenTracks/issues).
|
||||
If not, you can just take a look at
|
||||
the [issue tracker](https://github.com/OpenTracksApp/OpenTracks/issues).
|
||||
|
||||
Advices:
|
||||
|
||||
* creating an issue beforehand might be a good idea
|
||||
Sometimes it is helpful to discuss your idea and also your implementation approach first.
|
||||
We may be able to give you some hints on how to tackle the problem.
|
||||
|
||||
+33
-11
@@ -59,16 +59,38 @@ We do not support type of movement (i.e., walking vs running).
|
||||
|
||||
## Tested Barometric Sensor Smartphones
|
||||
|
||||
There are smartphones that have a barometric sensor that OpenTracks uses to compute altitude gain and loss.
|
||||
OpenTracks derives the altitude gain and altitude loss using the internal barometric sensor of a device (if present).
|
||||
However, barometric sensor seem to be prone to a lot of noise.
|
||||
This may either be a malfunctioning sensor, a case that prevents the device from measuring, or something else.
|
||||
|
||||
In our tests there are smartphones whose barometric sensor works well and others don't. Here is a list of the smartphones tested:
|
||||
To check if the barometric sensor of a device is working with OpenTracks:
|
||||
1. Record a track (ca. 1min) with the device stationary.
|
||||
Expectation: 0m gain and 0m loss should.
|
||||
2. Record a track (ca. 1min) while shaking the device.
|
||||
Expectation: 0m gain and 0m loss should.
|
||||
3. Record a track (ca. 1min) while moving the device up (less than 3m difference between highest and lowest point).
|
||||
Expectation: 0m gain and 0m loss should.
|
||||
4. Record a track walking up some stairs or taking an elevator.
|
||||
Expectation: some gain and some loss is recorded that seems realistic.
|
||||
|
||||
| Brand | Model | Android Version | Tests |
|
||||
|--------|--------------------|-----------------|--------------------------------------------------------------------------------|
|
||||
| Google | Pixel | LineageOS 17.1 | Ok |
|
||||
| Google | Pixel 2 XL | LineageOS 17.1 | Ok on the bike handlebars but it fails when I carry it in a pocket or backpack |
|
||||
| Google | Pixel 3 | Android 11 | Failure |
|
||||
| Google | Pixel 3a | Android 11 | Ok |
|
||||
| Google | Pixel 3a | Lineage 18.1 | Ok |
|
||||
| Google | Pixel 4a | Android 11 | Ok |
|
||||
| Sony | Xperia XZ1 Compact | LineageOS 16.1 | Failure |
|
||||
If any of these tests do **not** show expected values for gain/loss, OpenTracks will not show useful values.
|
||||
|
||||
Please note that OpenTracks:
|
||||
* only records gain/loss if +/- 3m to the initial/previous of are measured to the previous
|
||||
* solely uses the barometric sensor for gain/loss; GPS is not used for this.
|
||||
|
||||
In our tests there are smartphones whose barometric sensor works well and others don't.
|
||||
|
||||
Here is a list of the smartphones tested:
|
||||
|
||||
| Brand | Model | Android Version | Tests |
|
||||
|---------|--------------------|-----------------|--------------------------------------------------------------------------------|
|
||||
| Google | Pixel | LineageOS 17.1 | Ok |
|
||||
| Google | Pixel 2 XL | LineageOS 17.1 | Ok on the bike handlebars but it fails when I carry it in a pocket or backpack |
|
||||
| Google | Pixel 3 | Android 11 | Failure |
|
||||
| Google | Pixel 3a | Android 11 | Ok |
|
||||
| Google | Pixel 3a | Lineage 18.1 | Ok |
|
||||
| Google | Pixel 4a | Android 11 | Ok |
|
||||
| Google | Nexus 4 | LineageOS 18.1 | Ok |
|
||||
| Samsung | S4 | Android 11 | Ok |
|
||||
| Sony | Xperia XZ1 Compact | LineageOS 16.1 | Failure |
|
||||
|
||||
+10
-10
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.1.0-beta01'
|
||||
classpath 'com.android.tools.build:gradle:8.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
compileSdk 33
|
||||
compileSdk 34
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
@@ -64,13 +64,13 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 5196
|
||||
versionName "v4.3.4"
|
||||
versionCode 5326
|
||||
versionName "v4.7.1"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
minSdk 21
|
||||
targetSdk 33
|
||||
minSdk 24
|
||||
targetSdk 34
|
||||
|
||||
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
@@ -128,20 +128,20 @@ dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation 'androidx.mediarouter:mediarouter:1.3.1'
|
||||
implementation 'androidx.mediarouter:mediarouter:1.4.0'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.5.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.3.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.4.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
v4.3.5: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Sensor parsing had a overflow one off bug (#1559)
|
||||
- Insert marker: don't crash if no camera app is installed (#1561)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.4.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Export all tracks as one file
|
||||
@@ -0,0 +1,14 @@
|
||||
v4.5.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Material3
|
||||
- App shortcut for aggregated statistics
|
||||
- New Unit: miles with meters
|
||||
|
||||
Bugfix:
|
||||
- GPS disabled warnings shown repeatedly (#1429)
|
||||
|
||||
Developer:
|
||||
- Use Java Records
|
||||
- Target API34
|
||||
- Refactor sensor handling
|
||||
@@ -0,0 +1,12 @@
|
||||
v4.6.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Voice announcements use ICU plural format (en, es, pt, zh, dr, de) due to fractional quanitites #1584
|
||||
- Requires Android 7.0 / SDK24+ due to ICU plurals
|
||||
- On Android 14+: use Android's AltitudeConverter instead of internal EGM2008
|
||||
|
||||
Bugfix:
|
||||
- Android 13 couldn't select images from gallery
|
||||
|
||||
Developer:
|
||||
- Split ActivityType and ActivityIcon
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.6.1: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Re-added aggregated statistics per selected tracks
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.6.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Crash on voice annoucement for imperial+meter
|
||||
- Crash on settings for imperial+meter
|
||||
- Fixed clipping of charts (Material3 upgrade)
|
||||
- Bluetooth settings handle missing permission
|
||||
- Aggregated stats didn't show imperial units for avg speed
|
||||
@@ -0,0 +1,11 @@
|
||||
v4.7.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Statistics: distance, total time, and moving time are included between automatic segments (created due to distance too previous location too long)
|
||||
- Recorded tracks show sensor statistics again
|
||||
|
||||
Bugfix:
|
||||
- GPX exporter: for automatic segments their might be no location
|
||||
- CSV exporter: distance didn't use decimal grouping
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.7.1: OpenTracks
|
||||
|
||||
Bugfixes:
|
||||
- Android 14: crash on recording start (missing permissions)
|
||||
- Android 14: crash on track deletion (ForegroundService without reason)
|
||||
|
||||
Developer:
|
||||
- AggregatedStats is now a RecyclerView
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.4.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Exportar todas as rutas nun único ficheiro
|
||||
@@ -0,0 +1,14 @@
|
||||
v4.5.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Material3
|
||||
- Atallo na app para estatísticas acumuladas
|
||||
- Nova unidade: millas con metros
|
||||
|
||||
Arranxo:
|
||||
- avisos repetidos do GPS desactivado (#1429)
|
||||
|
||||
Desenvolvemento:
|
||||
- Utilizar Java Records
|
||||
- Obxectivo API34
|
||||
- Refactorización da xestión dos sensores
|
||||
@@ -0,0 +1,12 @@
|
||||
v4.6.0: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Anuncios por voz usando o formato de plurais ICU (en, es, pt, zh, dr, de) debido ás cantidades en fraccións #1584
|
||||
- Require Android 7.0 / SDK24+ debido aos plurais ICU
|
||||
- En Android 14+: usar o conversor AltitudeConverter de Android no lugar do interno EGM2008
|
||||
|
||||
Arrranxo:
|
||||
- Non se podía escoller unha imaxe da galería en Android 13
|
||||
|
||||
Desenvolvemento:
|
||||
- Separamos ActivityType e ActivityIcon
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.6.1: OpenTracks
|
||||
|
||||
Arranxo:
|
||||
- Volvemos a engadir estatísticas conxuntas para as rutas seleccionadas
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.6.2: OpenTracks
|
||||
|
||||
Arranxos:
|
||||
- Fallo da app cos anuncios por voz para imperial+métrico
|
||||
- Fallo nos axustes para imperial+métrico
|
||||
- Arranxo da inconsistencia nas gráficas (actualización Material3)
|
||||
- Perda do permiso Bluetooth
|
||||
- Estatítiscas acumuladas da velocidade media non mostraban unidades imperiais
|
||||
@@ -0,0 +1,9 @@
|
||||
v4.7.0 OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Estatísticas: inclúese distancia, tempo total e tempo en movemento entre os segmentos automáticos (creado debido a que a distancia á localización anterior era demasiado grande)
|
||||
- As rutas gravadas volven a mostrar estatísticas do sensor outra vez
|
||||
|
||||
Arranxos:
|
||||
- GPX exporter: para os segmentos automáticos que poderían non ter localización
|
||||
- CSV exporter: a distancia non usaba o agrupamento con decimal
|
||||
@@ -0,0 +1,10 @@
|
||||
(versionName): OpenTracks
|
||||
|
||||
Cambios:
|
||||
-
|
||||
|
||||
Arranxos:
|
||||
-
|
||||
|
||||
Programadora:
|
||||
-
|
||||
@@ -0,0 +1,22 @@
|
||||
Gravador de actividades deportivas que respecta a túa privacidade.
|
||||
|
||||
As actividades deportivas e ao aire libre proporcionanche felicidade, destreza e confianza.
|
||||
|
||||
Dalle valor á túa saúde facendo un seguimento dos teus adestramentos.
|
||||
Grava mentras saes a correr ou camiñar, e será un ciclocomputador cunha pantalla máis grande e clara.
|
||||
Marca as localizacións interesantes na ruta podendo engadir unha imaxe.
|
||||
Garda un rexistro das túas estatísticas con gran detalle e análise.
|
||||
Comparte só os datos que outras persoas poden ver.
|
||||
|
||||
* Anuncios por voz.
|
||||
* Soporte para sensores Bluetooth LE: de frecuencia cardíaca, velocidade e distancia (ciclismo), cadencia (ciclismo), e de potencia (ciclismo).
|
||||
* Gañancia e perda de altitude: a través do sensor barométrico.
|
||||
* Altitude mostrada en EGM2008 (sobre o nivel do mar); exportada como WGS84.
|
||||
* Exporta os datos como rutas tanto como KMZ (inclúe fotos), KML, ou GPX.
|
||||
* Non precisa conexión a internet ou permisos extra.
|
||||
* Decorado escuro, claro, ou seguindo os axustes do sistema.
|
||||
* Sen publicidade.
|
||||
|
||||
Software Libre / Free Software / Open Source
|
||||
significa que podes usar, estudar, cambiar e compartir o código fonte.
|
||||
Licenza Apache 2.0
|
||||
@@ -0,0 +1 @@
|
||||
Gravador de actividades que respecta a túa privacidade.
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks
|
||||
@@ -2,13 +2,13 @@ v3.19.0: OpenTracks
|
||||
|
||||
Wijzigingen:
|
||||
- Ondersteuning voor Running Speed en Cadence (RCS) sensoren
|
||||
- Bij het delen van een track wordt een afbeelding gemaakt
|
||||
- Bij delen van een track: maakt een deel-afbeelding
|
||||
- Niet trillen bij starten van opnemen
|
||||
- Bij opnemen: UI toont recentst ontvangen hoogte, snelheid en richting (ook al is deze niet opgeslagen)
|
||||
- Bij opnemen: UI toont meest recente hoogte, snelheid en richting (zelfs als niet opgeslagen)
|
||||
|
||||
Bugfix:
|
||||
- Berekening van totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
|
||||
- (opnieuw) Gebruik aangepaste overzichten indien aanwezig
|
||||
- Berekening totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
|
||||
- (opnieuw) Gebruik aanwezige aangepaste overzichten
|
||||
|
||||
Ontwikkelaar:
|
||||
- Upgrade naar SDK31 en AGP7.0.0 (vereist JDK11)
|
||||
|
||||
@@ -5,4 +5,4 @@ Wijzigingen:
|
||||
- Gesproken meldingen van hartslag
|
||||
|
||||
Bugfix:
|
||||
- Crash due to race condition
|
||||
- Vastloper door race condities
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+2
-1
@@ -1,5 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,13 +80,10 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -143,12 +140,16 @@ fi
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -193,6 +194,10 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
|
||||
Vendored
+1
@@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
@@ -30,7 +31,7 @@ public class EspressoDeleteTrackTest {
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
@Test
|
||||
public void espressoDeleteTrackTest() {
|
||||
@@ -51,7 +52,15 @@ public class EspressoDeleteTrackTest {
|
||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
||||
|
||||
// open menu and delete selected track
|
||||
onView(allOf(withContentDescription("More options"), isDisplayed()))
|
||||
//TODO openActionBarOverflowOrOptionsMenu(); doesn't work
|
||||
onView(
|
||||
allOf(withContentDescription("More options"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(androidx.appcompat.R.id.action_mode_bar),
|
||||
1),
|
||||
2),
|
||||
isDisplayed()))
|
||||
.perform(click());
|
||||
|
||||
onView(withText("Delete")).perform(click());
|
||||
|
||||
@@ -38,7 +38,7 @@ public class EspressoEditTrackRecordingTest {
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
|
||||
@@ -31,8 +31,7 @@ public class EspressoUITest {
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
@LargeTest
|
||||
@Test
|
||||
public void record_stop_resume_stop_finish() {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.Manifest;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
public class TestUtil {
|
||||
|
||||
public static GrantPermissionRule createGrantPermissionRule() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
return GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.POST_NOTIFICATIONS);
|
||||
}
|
||||
return GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_CONNECT);
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ public class ChartPointTest {
|
||||
statistics.setTotalTime(Duration.ofSeconds(1000));
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL);
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL_FEET);
|
||||
|
||||
// then
|
||||
assertEquals(1000000, (long) point.getTimeOrDistance());
|
||||
|
||||
@@ -96,16 +96,7 @@ public class TestDataUtil {
|
||||
return new TrackData(track, trackPoints, markers);
|
||||
}
|
||||
|
||||
public static class TrackData {
|
||||
public final Track track;
|
||||
public final List<TrackPoint> trackPoints;
|
||||
public final List<Marker> markers;
|
||||
|
||||
public TrackData(Track track, List<TrackPoint> trackPoints, List<Marker> markers) {
|
||||
this.track = track;
|
||||
this.trackPoints = trackPoints;
|
||||
this.markers = markers;
|
||||
}
|
||||
public record TrackData(Track track, List<TrackPoint> trackPoints, List<Marker> markers) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -424,7 +424,7 @@ public class CustomContentProviderUtilsTest {
|
||||
Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
|
||||
marker.setId(markerId);
|
||||
ContentValues contentValues = contentProviderUtils.createContentValues(marker);
|
||||
assertEquals(markerId.getId(), contentValues.get(MarkerColumns._ID));
|
||||
assertEquals(markerId.id(), contentValues.get(MarkerColumns._ID));
|
||||
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
|
||||
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
|
||||
}
|
||||
@@ -458,7 +458,7 @@ public class CustomContentProviderUtilsTest {
|
||||
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
||||
|
||||
Marker marker = contentProviderUtils.createMarker(cursorMock);
|
||||
assertEquals(id, marker.getId().getId());
|
||||
assertEquals(id, marker.getId().id());
|
||||
assertEquals(name, marker.getName());
|
||||
assertEquals(trackId, marker.getTrackId().getId());
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class TrackSelectionTest extends TestCase {
|
||||
@Test
|
||||
public void testFilterBuildSelection_onlyOneCategory() {
|
||||
// given
|
||||
TrackSelection filter = new TrackSelection().addCategory("running");
|
||||
TrackSelection filter = new TrackSelection().addActivityType("running");
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
@@ -82,10 +82,10 @@ public class TrackSelectionTest extends TestCase {
|
||||
public void testFilterBuildSelection_severalCategories() {
|
||||
// given
|
||||
TrackSelection filter = new TrackSelection()
|
||||
.addCategory("running")
|
||||
.addCategory("road biking")
|
||||
.addCategory("mountain biking")
|
||||
.addCategory("trail walking");
|
||||
.addActivityType("running")
|
||||
.addActivityType("road biking")
|
||||
.addActivityType("mountain biking")
|
||||
.addActivityType("trail walking");
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
@@ -136,8 +136,8 @@ public class TrackSelectionTest extends TestCase {
|
||||
.addTrackId(trackId1)
|
||||
.addTrackId(trackId2)
|
||||
.addTrackId(trackId3)
|
||||
.addCategory("running")
|
||||
.addCategory("road biking");
|
||||
.addActivityType("running")
|
||||
.addActivityType("road biking");
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
@@ -188,8 +188,8 @@ public class TrackSelectionTest extends TestCase {
|
||||
long oneDay = 24 * 60 * 60 * 1000;
|
||||
|
||||
TrackSelection filter = new TrackSelection()
|
||||
.addCategory("running")
|
||||
.addCategory("road biking")
|
||||
.addActivityType("running")
|
||||
.addActivityType("road biking")
|
||||
.addDateRange(instant, instant.plusMillis(oneDay));
|
||||
|
||||
// when
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ public class DistanceFormatterTest {
|
||||
public void testFormatDistance_imperial() {
|
||||
DistanceFormatter formatter = DistanceFormatter.Builder()
|
||||
.setDecimalCount(2)
|
||||
.setUnit(UnitSystem.IMPERIAL)
|
||||
.setUnit(UnitSystem.IMPERIAL_FEET)
|
||||
.build(context);
|
||||
|
||||
// A large number in imperial
|
||||
|
||||
@@ -36,7 +36,7 @@ public class SpeedFormatterTest {
|
||||
public void testGetSpeedParts_pace_imperial() {
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||
.setDecimalCount(2)
|
||||
.setUnit(UnitSystem.IMPERIAL)
|
||||
.setUnit(UnitSystem.IMPERIAL_FEET)
|
||||
.setReportSpeedOrPace(false)
|
||||
.build(context);
|
||||
|
||||
@@ -72,7 +72,7 @@ public class SpeedFormatterTest {
|
||||
public void testGetSpeedParts_speed_imperial() {
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||
.setDecimalCount(2)
|
||||
.setUnit(UnitSystem.IMPERIAL)
|
||||
.setUnit(UnitSystem.IMPERIAL_FEET)
|
||||
.setReportSpeedOrPace(true)
|
||||
.build(context);
|
||||
|
||||
|
||||
+42
-48
@@ -46,6 +46,7 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TimezoneRule;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
@@ -57,7 +58,6 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
import de.dennisguse.opentracks.sensors.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||
@@ -96,8 +96,8 @@ public class ExportImportTest {
|
||||
|
||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
private static final String TRACK_ICON = "the track icon";
|
||||
private static final String TRACK_CATEGORY = "the category";
|
||||
private static final ActivityType TRACK_ACTIVITY_TYPE = ActivityType.MOUNTAIN_BIKING;
|
||||
private static final String TRACK_ACTIVITY_TYPE_LOCALIZED = "the activity type";
|
||||
private static final String TRACK_DESCRIPTION = "the description";
|
||||
|
||||
private File tmpFile;
|
||||
@@ -165,8 +165,7 @@ public class ExportImportTest {
|
||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||
BluetoothRemoteSensorManager mockRemoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class);
|
||||
trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager);
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
|
||||
service.endCurrentTrack();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||
@@ -179,12 +178,11 @@ public class ExportImportTest {
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:24Z");
|
||||
trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager);
|
||||
service.endCurrentTrack();
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
track.setIcon(TRACK_ICON);
|
||||
track.setCategory(TRACK_CATEGORY);
|
||||
track.setActivityType(TRACK_ACTIVITY_TYPE);
|
||||
track.setActivityTypeLocalized(TRACK_ACTIVITY_TYPE_LOCALIZED);
|
||||
track.setDescription(TRACK_DESCRIPTION);
|
||||
contentProviderUtils.updateTrack(track);
|
||||
|
||||
@@ -195,7 +193,7 @@ public class ExportImportTest {
|
||||
assertEquals(2, markers.size());
|
||||
}
|
||||
|
||||
//TODO Does not test images
|
||||
//TODO Does not test marker images
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata() throws TimeoutException, IOException {
|
||||
@@ -204,11 +202,11 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
|
||||
// 2. import
|
||||
@@ -219,10 +217,10 @@ public class ExportImportTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals(track.getCategory(), importedTrack.getCategory());
|
||||
assertEquals(track.getActivityTypeLocalized(), importedTrack.getActivityTypeLocalized());
|
||||
assertEquals(track.getDescription(), importedTrack.getDescription());
|
||||
assertEquals(track.getName(), importedTrack.getName());
|
||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||
assertEquals(track.getActivityType(), importedTrack.getActivityType());
|
||||
|
||||
// 2. trackpoints
|
||||
TrackPointAssert a = new TrackPointAssert();
|
||||
@@ -240,24 +238,24 @@ public class ExportImportTest {
|
||||
TrackStatistics originalTrackStatistics = track.getTrackStatistics();
|
||||
|
||||
assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(19), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(20), importedTrackStatistics.getTotalTime());
|
||||
|
||||
assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(3), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(4), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
|
||||
assertEquals(125.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(222238.70, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
|
||||
assertEquals(41.72, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(55559.67, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
|
||||
assertEquals(6.58, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(11111.93, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
|
||||
assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
|
||||
assertEquals(41.72, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(55559.67, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
@@ -290,7 +288,7 @@ public class ExportImportTest {
|
||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
|
||||
// 2. import
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(tmpFileUri);
|
||||
@@ -314,7 +312,7 @@ public class ExportImportTest {
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
|
||||
// 2. import
|
||||
@@ -326,7 +324,7 @@ public class ExportImportTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals(track.getCategory(), importedTrack.getCategory());
|
||||
assertEquals(track.getActivityTypeLocalized(), importedTrack.getActivityTypeLocalized());
|
||||
assertEquals(track.getDescription(), importedTrack.getDescription());
|
||||
assertEquals(track.getName(), importedTrack.getName());
|
||||
|
||||
@@ -396,16 +394,16 @@ public class ExportImportTest {
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:03:23Z"), importedTrackStatistics.getStopTime());
|
||||
|
||||
assertEquals(Duration.ofSeconds(15), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(15), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(125.16, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(222349.85, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(8.34, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(8.34, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8.34, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(2779.37, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(2779.37, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(2779.37, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
@@ -432,7 +430,7 @@ public class ExportImportTest {
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
|
||||
// 2. import
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(tmpFileUri);
|
||||
@@ -457,7 +455,7 @@ public class ExportImportTest {
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
|
||||
// then
|
||||
@@ -495,9 +493,6 @@ public class ExportImportTest {
|
||||
}
|
||||
|
||||
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
||||
BluetoothRemoteSensorManager remoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class);
|
||||
Mockito.when(remoteSensorManager.fill(Mockito.any())).thenAnswer(invocation -> {
|
||||
TrackPoint trackPoint = invocation.getArgument(0);
|
||||
|
||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
||||
@@ -508,21 +503,20 @@ public class ExportImportTest {
|
||||
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
|
||||
sensorDataSet.set(cyclingCadence);
|
||||
|
||||
if (distance != null && speed != null) {
|
||||
SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
|
||||
Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
|
||||
Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
|
||||
SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
|
||||
sensorDataSet.set(distanceSpeed);
|
||||
}
|
||||
if (distance != null && speed != null) {
|
||||
SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
|
||||
Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
|
||||
Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
|
||||
SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
|
||||
sensorDataSet.set(distanceSpeed);
|
||||
}
|
||||
|
||||
sensorDataSet.fillTrackPoint(trackPoint);
|
||||
return sensorDataSet;
|
||||
});
|
||||
trackPointCreator.getSensorManager().setBluetoothSensorManager(remoteSensorManager);
|
||||
trackPointCreator.onChange(new SensorDataSet());
|
||||
trackPointCreator.getSensorManager().sensorDataSet = sensorDataSet;
|
||||
trackPointCreator.onChange(sensorDataSet);
|
||||
|
||||
//TODO Might require: getSensorManager().sensorDataSet = new SensorDataSet()
|
||||
}
|
||||
|
||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
||||
@@ -545,6 +539,6 @@ public class ExportImportTest {
|
||||
mockAltitudeChange(trackPointCreator, altitudeGain);
|
||||
|
||||
trackPointCreator.setClock(time);
|
||||
trackPointCreator.getGpsHandler().onLocationChanged(location);
|
||||
trackPointCreator.onChange(location);
|
||||
}
|
||||
}
|
||||
+8
-7
@@ -28,6 +28,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -79,10 +80,10 @@ public class GPXTrackImporterTest {
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("the category", importedTrack.getCategory());
|
||||
assertEquals("the category", importedTrack.getActivityTypeLocalized());
|
||||
assertEquals("the description", importedTrack.getDescription());
|
||||
assertEquals("2021-01-07 22:51", importedTrack.getName());
|
||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
||||
|
||||
//TODO Check trackstatistics
|
||||
|
||||
@@ -133,10 +134,10 @@ public class GPXTrackImporterTest {
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getActivityTypeLocalized());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||
@@ -181,10 +182,10 @@ public class GPXTrackImporterTest {
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getActivityTypeLocalized());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||
@@ -232,7 +233,7 @@ public class GPXTrackImporterTest {
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||
trackExporter.writeTrack(importedTrack, outputStream);
|
||||
trackExporter.writeTrack(List.of(importedTrack), outputStream);
|
||||
|
||||
// then
|
||||
String expected = new BufferedReader(
|
||||
|
||||
+7
-6
@@ -22,6 +22,7 @@ import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -70,10 +71,10 @@ public class KMLTrackImporterTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getActivityTypeLocalized());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
||||
|
||||
// 2. markers
|
||||
assertEquals(0, contentProviderUtils.getMarkers(importTrackId).size());
|
||||
@@ -119,10 +120,10 @@ public class KMLTrackImporterTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getActivityTypeLocalized());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
||||
|
||||
// 2. markers
|
||||
assertEquals(0, contentProviderUtils.getMarkers(importTrackId).size());
|
||||
@@ -159,10 +160,10 @@ public class KMLTrackImporterTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("unknown", importedTrack.getCategory());
|
||||
assertEquals("unknown", importedTrack.getActivityTypeLocalized());
|
||||
assertEquals("Test Track", importedTrack.getDescription());
|
||||
assertEquals("2020-11-28 18:06", importedTrack.getName());
|
||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
||||
|
||||
// 2. markers
|
||||
assertEquals(0, contentProviderUtils.getMarkers(importTrackId).size());
|
||||
|
||||
@@ -1,47 +1,55 @@
|
||||
package de.dennisguse.opentracks.sensors;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
import de.dennisguse.opentracks.sensors.driver.BarometerInternal;
|
||||
|
||||
/**
|
||||
* NOTE: Test data is completely artificial.
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AltitudeSumManagerTest {
|
||||
|
||||
private final AltitudeSumManager subject = new AltitudeSumManager();
|
||||
|
||||
private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) {
|
||||
for (float f : values) {
|
||||
altitudeSumManager.onSensorValueChanged(f);
|
||||
altitudeSumManager.onSensorValueChanged(AtmosphericPressure.ofHPA(f));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
subject.reset();
|
||||
}
|
||||
@Mock
|
||||
BarometerInternal driver;
|
||||
|
||||
@Test
|
||||
public void getAltitudeGainLoss_downhill() {
|
||||
// given
|
||||
subject.setConnected(true);
|
||||
AltitudeSumManager subject = new AltitudeSumManager(driver);
|
||||
|
||||
when(driver.isConnected()).thenReturn(true);
|
||||
|
||||
// then
|
||||
addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1015.14f, 1015.15f});
|
||||
addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f});
|
||||
|
||||
// then
|
||||
Assert.assertEquals(0f, subject.getAltitudeGain_m(), 0.01);
|
||||
Assert.assertEquals(48.0, subject.getAltitudeLoss_m(), 0.01);
|
||||
Assert.assertEquals(15f, subject.getAltitudeLoss_m(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sensorUnavailable() {
|
||||
// given
|
||||
subject.setConnected(false);
|
||||
AltitudeSumManager subject = new AltitudeSumManager(driver);
|
||||
|
||||
when(driver.isConnected()).thenReturn(true);
|
||||
|
||||
// then
|
||||
subject.onSensorValueChanged(999f);
|
||||
subject.onSensorValueChanged(AtmosphericPressure.ofHPA(999f));
|
||||
|
||||
// then
|
||||
Assert.assertNull(subject.getAltitudeGain_m());
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
@@ -20,7 +21,7 @@ public class BluetoothUtilsTest {
|
||||
@Test
|
||||
public void parseHeartRate_uint8() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x02, 0x3C});
|
||||
|
||||
// when
|
||||
@@ -33,7 +34,7 @@ public class BluetoothUtilsTest {
|
||||
@Test
|
||||
public void parseHeartRate_uint16() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
|
||||
|
||||
// when
|
||||
@@ -43,9 +44,22 @@ public class BluetoothUtilsTest {
|
||||
assertEquals(HeartRate.of(257), heartRate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEnvironmentalSensing_Pa() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00});
|
||||
|
||||
// when
|
||||
AtmosphericPressure pressure = BluetoothUtils.parseEnvironmentalSensing(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(AtmosphericPressure.ofPA(100165), pressure);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCyclingSpeedCadence_crankOnly() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99});
|
||||
|
||||
// when
|
||||
@@ -58,7 +72,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingSpeedCadence_wheelOnly() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99});
|
||||
|
||||
// when
|
||||
@@ -71,7 +85,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingSpeedCadence_crankWheel() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99});
|
||||
|
||||
// when
|
||||
@@ -84,34 +98,34 @@ public class BluetoothUtilsTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingPower_power() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0, 0, 40, 0});
|
||||
|
||||
// when
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(40, powerCadence.getPower().getValue().getW(), 0.01);
|
||||
assertEquals(40, powerCadence.power().getValue().getW(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCyclingPower_power_with_cadence() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42});
|
||||
|
||||
// when
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(0, powerCadence.getPower().getValue().getW(), 0.01);
|
||||
assertEquals(0, powerCadence.power().getValue().getW(), 0.01);
|
||||
|
||||
assertEquals(12, powerCadence.getCadence().getCrankRevolutionsCount());
|
||||
assertEquals(17125, powerCadence.getCadence().getCrankRevolutionsTime());
|
||||
assertEquals(12, powerCadence.cadence().getCrankRevolutionsCount());
|
||||
assertEquals(17125, powerCadence.cadence().getCrankRevolutionsTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseRunningSpeedAndCadence_with_distance() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1});
|
||||
|
||||
// when
|
||||
|
||||
@@ -5,6 +5,8 @@ import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
|
||||
public class PressureSensorUtilsTest {
|
||||
|
||||
// real data at 500Hz stationary
|
||||
@@ -12,11 +14,11 @@ public class PressureSensorUtilsTest {
|
||||
public void altitudeChanges_none() {
|
||||
// given
|
||||
float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f};
|
||||
float firstSensorValue = sensorValues_hPa[0];
|
||||
AtmosphericPressure firstSensorValue = AtmosphericPressure.ofHPA(sensorValues_hPa[0]);
|
||||
|
||||
// when // then
|
||||
for (float v : sensorValues_hPa) {
|
||||
assertNull(PressureSensorUtils.computeChanges(firstSensorValue, v));
|
||||
assertNull(PressureSensorUtils.computeChanges(firstSensorValue, AtmosphericPressure.ofHPA(v)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,25 +27,25 @@ public class PressureSensorUtilsTest {
|
||||
@Test
|
||||
public void computeChanges_downhill() {
|
||||
float[] sensorValues_hPa = new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f};
|
||||
float firstSensorValue = sensorValues_hPa[0];
|
||||
AtmosphericPressure firstSensorValue = AtmosphericPressure.ofHPA(sensorValues_hPa[0]);
|
||||
|
||||
// when
|
||||
float altitudeGain_m = 0;
|
||||
float altitudeLoss = 0;
|
||||
float lastUsedPressureValue_hPa = firstSensorValue;
|
||||
AtmosphericPressure lastUsedPressureValue = firstSensorValue;
|
||||
|
||||
for (float v : sensorValues_hPa) {
|
||||
PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChanges(lastUsedPressureValue_hPa, v);
|
||||
PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChanges(lastUsedPressureValue, AtmosphericPressure.ofHPA(v));
|
||||
if (altitudeChange != null) {
|
||||
altitudeGain_m += altitudeChange.getAltitudeGain_m();
|
||||
altitudeLoss += altitudeChange.getAltitudeLoss_m();
|
||||
lastUsedPressureValue_hPa = altitudeChange.getCurrentSensorValue_hPa();
|
||||
lastUsedPressureValue = altitudeChange.currentSensorValue();
|
||||
}
|
||||
}
|
||||
|
||||
// then
|
||||
assertEquals(0f, altitudeGain_m, 0.01);
|
||||
assertEquals(15.0f, altitudeLoss, 0.01);
|
||||
assertEquals(1016.80f, lastUsedPressureValue_hPa, 0.01); //Expect exponential smoothing
|
||||
assertEquals(1016.80f, lastUsedPressureValue.getHPA(), 0.01); //Expect exponential smoothing
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,44 @@ public class UintUtilsTest {
|
||||
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT16_MAX));
|
||||
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT16_MAX));
|
||||
assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT16_MAX));
|
||||
assertEquals(65534, UintUtils.diff(1, 2, UintUtils.UINT16_MAX));
|
||||
|
||||
assertEquals(65535, UintUtils.diff(1, 2, UintUtils.UINT16_MAX)); /* unsigned 16 arithmetic is modulo UINT16_MAX + 1, not modulo UINT16_MAX */
|
||||
assertEquals(65530, UintUtils.diff(UintUtils.UINT16_MAX, 5, UintUtils.UINT16_MAX));
|
||||
|
||||
/* Test modulo arithmetic for arguments that are out of range */
|
||||
|
||||
if (false) { /* false means UintUtils.diff() throws if arguments out of range */
|
||||
assertEquals(0, UintUtils.diff(65537, 1, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(1, 65537, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(65537, 65537, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(-65535, 1, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(1, -65535, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(-65535, -65535, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(-65535, 65537, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(65537, -65535, UintUtils.UINT16_MAX));
|
||||
}
|
||||
|
||||
/* The following tests are the above, but for 32-bit unsigned. */
|
||||
|
||||
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT32_MAX));
|
||||
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT32_MAX));
|
||||
assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT32_MAX));
|
||||
assertEquals(4294967295L, UintUtils.diff(1, 2, UintUtils.UINT32_MAX));
|
||||
assertEquals(4294967290L, UintUtils.diff(UintUtils.UINT32_MAX, 5, UintUtils.UINT32_MAX));
|
||||
|
||||
if (false) { /* false means UintUtils.diff() throws if arguments out of range */
|
||||
assertEquals(0, UintUtils.diff(4294967297L, 1, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(1, 4294967297L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(4294967297L, 4294967297L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(-4294967295L, 1, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(1, -4294967295L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(-4294967295L, -4294967295L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(-4294967295L, 4294967297L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(65537, -4294967297L, UintUtils.UINT32_MAX));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void realData() {
|
||||
assertEquals(1, UintUtils.diff(381616, 381615, UintUtils.UINT32_MAX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -765,7 +765,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps2, 0, 1, 0, 0, 0f, 0f), contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new TrackStatistics(startTime, gps2, 11113.275390625, 4, 3, 3704.4251302083335f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
||||
|
||||
|
||||
// when
|
||||
@@ -774,8 +774,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 7, 0, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 11113.275390625, 10, 3, 3704.4251302083335f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
||||
|
||||
|
||||
// then
|
||||
|
||||
+2
-4
@@ -5,8 +5,6 @@ import android.location.Location;
|
||||
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@@ -16,7 +14,7 @@ public class TrackRecordingServiceTestUtils {
|
||||
//TODO Workaround as service is not stopped on API23; thus sharedpreferences are not reset between tests.
|
||||
//TODO Anyhow, the service should re-create all it's resources if a recording starts and makes sure that there is no leftovers from previous recordings.
|
||||
@Deprecated
|
||||
public static void resetService(ServiceTestRule mServiceRule, Context context) throws TimeoutException {
|
||||
public static void resetService(ServiceTestRule mServiceRule, Context context) {
|
||||
// Let's use default values.
|
||||
PreferencesUtils.clear();
|
||||
}
|
||||
@@ -30,6 +28,6 @@ public class TrackRecordingServiceTestUtils {
|
||||
location.setSpeed(speed);
|
||||
|
||||
trackPointCreator.setClock(time);
|
||||
trackPointCreator.getGpsHandler().onLocationChanged(location);
|
||||
trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
+63
-4
@@ -3,6 +3,7 @@ package de.dennisguse.opentracks.services.announcement;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.content.Context;
|
||||
import android.icu.text.MessageFormat;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@@ -15,6 +16,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import de.dennisguse.opentracks.LocaleRule;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
@@ -185,12 +187,54 @@ public class VoiceAnnouncementUtilsTest {
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_speed_1() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(Distance.ofMile(1.1));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_meter_speed_1() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(Distance.ofMile(1.1));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_METER, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_speed_1() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(Distance.ofKilometer(1.1));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 1.1 kilometers. 1 hour. Average moving speed 1.1 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_withInterval_imperial_speed() {
|
||||
// given
|
||||
@@ -207,7 +251,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
}
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||
@@ -223,7 +267,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||
@@ -245,7 +289,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
}
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||
@@ -306,4 +350,19 @@ public class VoiceAnnouncementUtilsTest {
|
||||
// then
|
||||
assertEquals(" Current heart rate 133 bpm.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ICUMessageDemo() {
|
||||
// Android 7's ICU MessageFormat; working
|
||||
String template = """
|
||||
{n, plural,
|
||||
one {1 mile}
|
||||
other {{n,number,#.#} miles}
|
||||
}""";
|
||||
|
||||
assertEquals("1.1 miles", MessageFormat.format(template, Map.of("n", 1.1)));
|
||||
assertEquals("1 mile", MessageFormat.format(template, Map.of("n", 1)));
|
||||
assertEquals("1.1 miles", MessageFormat.format(template, Map.of("n", 1.11)));
|
||||
assertEquals("1.2 miles", MessageFormat.format(template, Map.of("n", 1.18)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_DISABLED;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_ENABLED;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_NONE;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_SIGNAL_BAD;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_SIGNAL_FIX;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_SIGNAL_LOST;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class GpsStatusTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
private final static Location badFix = new Location("gps");
|
||||
private final static Location ok = new Location("gps");
|
||||
|
||||
static {
|
||||
badFix.setAccuracy(50);
|
||||
|
||||
ok.setAccuracy(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartDisabledEnabledStop() {
|
||||
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||
|
||||
// given
|
||||
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler());
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
assertEquals(List.of(GPS_ENABLED), statusList);
|
||||
|
||||
subject.onGpsDisabled();
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_DISABLED), statusList);
|
||||
|
||||
subject.onGpsEnabled();
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_DISABLED, GPS_ENABLED), statusList);
|
||||
|
||||
subject.stop();
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_DISABLED, GPS_ENABLED, GPS_NONE), statusList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBadfixOk() {
|
||||
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||
|
||||
// given
|
||||
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler());
|
||||
subject.onRecordingDistanceChanged(Distance.of(10));
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
subject.onNewTrackPoint(new TrackPoint(badFix, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(badFix, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX, GPS_SIGNAL_BAD), statusList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartSignalLost() {
|
||||
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||
|
||||
// given
|
||||
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler());
|
||||
subject.onRecordingDistanceChanged(Distance.of(10));
|
||||
subject.onMinRecordingIntervalChanged(GpsStatusManager.SIGNAL_LOST_THRESHOLD.multipliedBy(-1));
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now().minusMillis(1000)));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
subject.determineGpsStatusByTime(Instant.now());
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_FIX), statusList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartSignalLostByTimer() throws InterruptedException {
|
||||
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||
|
||||
final HandlerThread handlerThread = new HandlerThread("solution!");
|
||||
handlerThread.start();
|
||||
|
||||
// given
|
||||
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler(handlerThread.getLooper()));
|
||||
subject.onRecordingDistanceChanged(Distance.of(10));
|
||||
subject.onMinRecordingIntervalChanged(GpsStatusManager.SIGNAL_LOST_THRESHOLD.multipliedBy(-1).plus(Duration.ofMillis(10)));
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
Thread.sleep(100);
|
||||
assertEquals(List.of(GPS_ENABLED), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
Thread.sleep(100);
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(badFix, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_BAD), statusList);
|
||||
|
||||
Thread.sleep(100);
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_BAD, GPS_SIGNAL_LOST), statusList);
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TrackPointCreatorTest {
|
||||
|
||||
@Mock
|
||||
private Context context;
|
||||
|
||||
@Mock
|
||||
private TrackPointCreator.Callback server;
|
||||
|
||||
@Mock
|
||||
private GPSManager locationHandler;
|
||||
|
||||
private TrackPointCreator subject;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
subject = new TrackPointCreator(locationHandler, server);
|
||||
subject.start(context, null);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
subject.stop();
|
||||
}
|
||||
|
||||
@Ignore("ServiceExecutor disabled for #822")
|
||||
@Test
|
||||
public void sendTrackPoint() throws InterruptedException {
|
||||
// given
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, null);
|
||||
Distance horizontalAccuracyThreshold = Distance.of(50);
|
||||
|
||||
// when
|
||||
subject.onNewTrackPoint(trackPoint);
|
||||
|
||||
// then
|
||||
Thread.sleep(10); // Wait for executor service
|
||||
verify(server).newTrackPoint(trackPoint, horizontalAccuracyThreshold);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public class DescriptionGeneratorTest {
|
||||
stats.setTotalAltitudeLoss(6000f);
|
||||
stats.setStartTime(START_TIME);
|
||||
track.setTrackStatistics(stats);
|
||||
track.setCategory("hiking");
|
||||
track.setActivityTypeLocalized("hiking");
|
||||
String expected = //"Created by"
|
||||
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
||||
+ "Name: -<br>"
|
||||
@@ -118,7 +118,7 @@ public class DescriptionGeneratorTest {
|
||||
stats.setTotalAltitudeLoss(6000f);
|
||||
stats.setStartTime(START_TIME);
|
||||
track.setTrackStatistics(stats);
|
||||
track.setCategory("hiking");
|
||||
track.setActivityTypeLocalized("hiking");
|
||||
String expected = //"Created by"
|
||||
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
||||
+ "Name: -<br>"
|
||||
|
||||
+6
-26
@@ -5,7 +5,6 @@ import static org.junit.Assert.assertNull;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -78,13 +77,13 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
// when
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
data.trackPoints.forEach(subject::addTrackPoint);
|
||||
data.trackPoints().forEach(subject::addTrackPoint);
|
||||
|
||||
// then
|
||||
TrackStatistics statistics = subject.getTrackStatistics();
|
||||
assertEquals(128.03, statistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(13), statistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(9), statistics.getMovingTime());
|
||||
assertEquals(142.26, statistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(14), statistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(10), statistics.getMovingTime());
|
||||
|
||||
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
|
||||
assertEquals(32.5, statistics.getMaxAltitude(), 0.01);
|
||||
@@ -93,8 +92,8 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
assertEquals(14.226, statistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(14.226, statistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(9.84, statistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(106.85f, statistics.getAverageHeartRate().getBPM(), 0.01);
|
||||
assertEquals(10.16, statistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(106.64f, statistics.getAverageHeartRate().getBPM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -226,25 +225,6 @@ public class TrackStatisticsUpdaterTest {
|
||||
assertEquals(59.18, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_not_moving() {
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_moving() {
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_not_moving_and_sensor_speed() {
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_moving_and_sensor_speed() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_maxSpeed_ignore_above_acceleration() {
|
||||
|
||||
+6
-6
@@ -16,18 +16,18 @@ import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class AggregatedStatisticsTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private static Track createTrack(Context context, Distance totalDistance, Duration totalTime, String category) {
|
||||
private static Track createTrack(Context context, Distance totalDistance, Duration totalTime, String activityTypeLocalized) {
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
statistics.setStartTime(Instant.ofEpochMilli(1000L)); // Resulting start time
|
||||
statistics.setStopTime(statistics.getStartTime().plus(totalTime));
|
||||
@@ -40,8 +40,8 @@ public class AggregatedStatisticsTest {
|
||||
statistics.setMinAltitude(1200.0); // Resulting min altitude
|
||||
|
||||
Track track = new Track();
|
||||
track.setIcon(TrackIconUtils.getIconValue(context, category));
|
||||
track.setCategory(category);
|
||||
track.setActivityType(ActivityType.findByLocalizedString(context, activityTypeLocalized));
|
||||
track.setActivityTypeLocalized(activityTypeLocalized);
|
||||
track.setTrackStatistics(statistics);
|
||||
return track;
|
||||
}
|
||||
@@ -243,8 +243,8 @@ public class AggregatedStatisticsTest {
|
||||
// Check order
|
||||
|
||||
{
|
||||
assertEquals(biking, aggregatedStatistics.getItem(0).getCategory());
|
||||
assertEquals(driving, aggregatedStatistics.getItem(3).getCategory());
|
||||
assertEquals(biking, aggregatedStatistics.getItem(0).getActivityTypeLocalized());
|
||||
assertEquals(driving, aggregatedStatistics.getItem(3).getActivityTypeLocalized());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -51,7 +51,7 @@ public class EspressoAggregatedFilterTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
Pair<Track, List<TrackPoint>> pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 20);
|
||||
pair.first.setCategory(CATEGORY);
|
||||
pair.first.setActivityTypeLocalized(CATEGORY);
|
||||
TestDataUtil.insertTrackWithLocations(new ContentProviderUtils(ApplicationProvider.getApplicationContext()), pair.first, pair.second);
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ public class EspressoAggregatedFilterTest {
|
||||
public void espressoAggregatedFilterTest() {
|
||||
// open AggregatedStatisticsActivity through toolbar's menu item
|
||||
onView(ViewMatchers.withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
||||
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.track_list_aggregated_stats))));
|
||||
onView(withId(R.id.track_list_aggregated_stats)).perform(click());
|
||||
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.aggregated_stats_button))));
|
||||
onView(withId(R.id.aggregated_stats_button)).perform(click());
|
||||
|
||||
// open FilterDialogFragment through toolbar's menu item
|
||||
onView(withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
||||
|
||||
@@ -79,7 +79,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(-14.8980, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(-14.8980, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,7 +94,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,7 +109,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,7 +124,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -139,7 +139,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(-17.2260, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(-17.2260, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,7 +154,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(-39.4865, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(-39.4865, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -174,7 +174,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint1.getLocation());
|
||||
|
||||
// then
|
||||
assertNotEquals(altitude_egm2008.correctAltitude(trackPoint1.getLocation()), altitude_egm2008.correctAltitude(trackPoint2.getLocation()), 0.0001);
|
||||
assertNotEquals(altitude_egm2008.correctAltitude(trackPoint1.getLocation()).toM(), altitude_egm2008.correctAltitude(trackPoint2.getLocation()).toM(), 0.0001);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -190,7 +190,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(22.99, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(22.99, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -205,7 +205,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(-85.824, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(-85.824, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,11 +17,14 @@ package de.dennisguse.opentracks.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -37,7 +40,7 @@ public class FileUtilsTest {
|
||||
*/
|
||||
@Test
|
||||
public void testBuildUniqueFileName_new() {
|
||||
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
|
||||
String filename = FileUtils.buildUniqueFileName(FileUtils.getPhotoDir(ApplicationProvider.getApplicationContext()), "Filename", "ext");
|
||||
assertEquals("Filename.ext", filename);
|
||||
}
|
||||
|
||||
@@ -45,9 +48,14 @@ public class FileUtilsTest {
|
||||
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file exists already.
|
||||
*/
|
||||
@Test
|
||||
public void testBuildUniqueFileName_exist() {
|
||||
// Expect "/default.prop" to exist on the phone/emulator
|
||||
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
|
||||
public void testBuildUniqueFileName_exist() throws IOException {
|
||||
// given
|
||||
new File(FileUtils.getPhotoDir(ApplicationProvider.getApplicationContext()), "default.prop").createNewFile();
|
||||
|
||||
//when
|
||||
String filename = FileUtils.buildUniqueFileName(FileUtils.getPhotoDir(ApplicationProvider.getApplicationContext()), "default", "prop");
|
||||
|
||||
// then
|
||||
assertEquals("default(1).prop", filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<shortcut
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_statistics_24dp"
|
||||
android:shortcutId="stats"
|
||||
android:shortcutLongLabel="@string/menu_aggregated_statistics"
|
||||
android:shortcutShortLabel="@string/menu_aggregated_statistics">
|
||||
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetClass="de.dennisguse.opentracks.ui.aggregatedStatistics.AggregatedStatisticsActivity"
|
||||
android:targetPackage="de.dennisguse.opentracks.debug" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
@@ -41,6 +41,9 @@ limitations under the License.
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
@@ -89,6 +92,7 @@ limitations under the License.
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- NOTE: the name of the MAIN/LAUNCHER is used by icon packs; if this is changed, icon packs break.-->
|
||||
<activity
|
||||
android:name=".introduction.IntroductionActivity"
|
||||
android:exported="true"
|
||||
@@ -98,6 +102,11 @@ limitations under the License.
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".AboutActivity" />
|
||||
@@ -348,6 +357,7 @@ limitations under the License.
|
||||
<activity android:name=".ui.aggregatedStatistics.AggregatedStatisticsActivity" />
|
||||
|
||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserActivity" />
|
||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserOneFileActivity" />
|
||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$DefaultTrackExportDirectoryChooserActivity" />
|
||||
|
||||
<activity android:name=".io.file.exporter.ExportActivity" />
|
||||
@@ -400,10 +410,8 @@ limitations under the License.
|
||||
|
||||
<service
|
||||
android:name=".services.TrackDeleteService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:icon="@drawable/ic_logo_color_24dp"
|
||||
android:label="@string/delete_service" />
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"/>
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -24,7 +25,6 @@ import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.services.TrackDeleteService;
|
||||
import de.dennisguse.opentracks.services.TrackDeleteServiceConnection;
|
||||
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment;
|
||||
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
|
||||
|
||||
@@ -37,31 +37,8 @@ import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragm
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
//TODO Check if this class is still such a good idea; inheritance might limit maintainability
|
||||
public abstract class AbstractTrackDeleteActivity extends AbstractActivity implements ConfirmDeleteCaller, TrackDeleteServiceConnection.Listener {
|
||||
public abstract class AbstractTrackDeleteActivity extends AbstractActivity implements ConfirmDeleteCaller, TrackDeleteService.TrackDeleteResultReceiver.Receiver {
|
||||
|
||||
private TrackDeleteServiceConnection trackDeleteServiceConnection;
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
trackDeleteServiceConnection = new TrackDeleteServiceConnection(this);
|
||||
trackDeleteServiceConnection.bind(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (trackDeleteServiceConnection != null) {
|
||||
trackDeleteServiceConnection.unbind(this);
|
||||
trackDeleteServiceConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete tracks.
|
||||
*
|
||||
* @param trackIds the track ids
|
||||
*/
|
||||
protected void deleteTracks(Track.Id... trackIds) {
|
||||
ConfirmDeleteDialogFragment.showDialog(getSupportFragmentManager(), trackIds);
|
||||
}
|
||||
@@ -77,29 +54,10 @@ public abstract class AbstractTrackDeleteActivity extends AbstractActivity imple
|
||||
Toast.makeText(this, getString(R.string.track_delete_not_recording), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
trackDeleteServiceConnection = new TrackDeleteServiceConnection(this);
|
||||
trackDeleteServiceConnection.startAndBind(this, trackIdList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every time a track is deleted.
|
||||
*/
|
||||
protected void onTrackDeleteStatus(TrackDeleteService.DeletionFinishedStatus deletionFinishedStatus) {
|
||||
if (trackDeleteServiceConnection != null) {
|
||||
trackDeleteServiceConnection.unbind(this);
|
||||
trackDeleteServiceConnection = null;
|
||||
onDeleteFinished();
|
||||
}
|
||||
TrackDeleteService.enqueue(this, new TrackDeleteService.TrackDeleteResultReceiver(new Handler(), this), trackIdList);
|
||||
}
|
||||
|
||||
protected abstract void onDeleteConfirmed();
|
||||
|
||||
protected abstract void onDeleteFinished();
|
||||
|
||||
protected abstract Track.Id getRecordingTrackId();
|
||||
|
||||
@Override
|
||||
public void connected(TrackDeleteService service) {
|
||||
service.getDeletingStatusObservable().observe(AbstractTrackDeleteActivity.this, this::onTrackDeleteStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public class ShowErrorActivity extends AbstractActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
viewBinding.textViewError.setText(getIntent().getStringExtra(EXTRA_ERROR_TEXT));
|
||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(createErrorTitle());
|
||||
viewBinding.showErrorToolbar.setTitle(createErrorTitle());
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.databinding.TrackEditBinding;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
|
||||
/**
|
||||
@@ -48,7 +47,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
private Track track;
|
||||
private String iconValue;
|
||||
private ActivityType activityType;
|
||||
|
||||
private TrackEditBinding viewBinding;
|
||||
|
||||
@@ -74,28 +73,31 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
|
||||
viewBinding.fields.trackEditName.setText(track.getName());
|
||||
|
||||
viewBinding.fields.trackEditActivityType.setText(track.getCategory());
|
||||
viewBinding.fields.trackEditActivityType.setText(track.getActivityTypeLocalized());
|
||||
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(this));
|
||||
viewBinding.fields.trackEditActivityType.setAdapter(adapter);
|
||||
viewBinding.fields.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(this, (String) viewBinding.fields.trackEditActivityType.getAdapter().getItem(position))));
|
||||
viewBinding.fields.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> {
|
||||
String localizedActivityType = (String) viewBinding.fields.trackEditActivityType.getAdapter().getItem(position);
|
||||
setActivityTypeIcon(ActivityType.findByLocalizedString(this, localizedActivityType));
|
||||
});
|
||||
viewBinding.fields.trackEditActivityType.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
||||
TrackEditActivity.this, viewBinding.fields.trackEditActivityType.getText().toString()));
|
||||
String localizedActivityType = viewBinding.fields.trackEditActivityType.getText().toString();
|
||||
setActivityTypeIcon(ActivityType.findByLocalizedString(this, localizedActivityType));
|
||||
}
|
||||
});
|
||||
|
||||
iconValue = null;
|
||||
activityType = null;
|
||||
if (bundle != null) {
|
||||
iconValue = bundle.getString(ICON_VALUE_KEY);
|
||||
activityType = (ActivityType) bundle.getSerializable(ICON_VALUE_KEY);
|
||||
}
|
||||
if (iconValue == null) {
|
||||
iconValue = track.getIcon();
|
||||
if (activityType == null) {
|
||||
activityType = track.getActivityType();
|
||||
}
|
||||
|
||||
setActivityTypeIcon(iconValue);
|
||||
viewBinding.fields.trackEditActivityTypeIcon.setOnClickListener(v -> ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), viewBinding.fields.trackEditActivityType.getText().toString()));
|
||||
setActivityTypeIcon(activityType);
|
||||
viewBinding.fields.trackEditActivityTypeIcon.setOnClickListener(v -> ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), this, viewBinding.fields.trackEditActivityType.getText().toString()));
|
||||
|
||||
viewBinding.fields.trackEditDescription.setText(track.getDescription());
|
||||
|
||||
@@ -109,7 +111,6 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
viewBinding.trackEditCancel.setOnClickListener(v -> finish());
|
||||
viewBinding.trackEditCancel.setVisibility(View.VISIBLE);
|
||||
|
||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(getString(R.string.menu_edit));
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
}
|
||||
|
||||
@@ -128,7 +129,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(ICON_VALUE_KEY, iconValue);
|
||||
outState.putSerializable(ICON_VALUE_KEY, activityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,14 +138,14 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
private void setActivityTypeIcon(String iconValue) {
|
||||
this.iconValue = iconValue;
|
||||
viewBinding.fields.trackEditActivityTypeIcon.setImageResource(TrackIconUtils.getIconDrawable(iconValue));
|
||||
private void setActivityTypeIcon(ActivityType activityType) {
|
||||
this.activityType = activityType;
|
||||
viewBinding.fields.trackEditActivityTypeIcon.setImageResource(activityType.getIconDrawableId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String value) {
|
||||
setActivityTypeIcon(value);
|
||||
viewBinding.fields.trackEditActivityType.setText(getString(TrackIconUtils.getIconActivityType(value)));
|
||||
public void onChooseActivityTypeDone(ActivityType activityType) {
|
||||
setActivityTypeIcon(activityType);
|
||||
viewBinding.fields.trackEditActivityType.setText(getString(activityType.getLocalizedStringId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.database.Cursor;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
@@ -36,6 +35,7 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
||||
@@ -43,6 +43,8 @@ import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.CursorLoader;
|
||||
import androidx.loader.content.Loader;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
@@ -50,6 +52,7 @@ import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -72,7 +75,6 @@ import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PermissionRequester;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
|
||||
/**
|
||||
* An activity displaying a list of tracks.
|
||||
@@ -83,7 +85,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
private static final String TAG = TrackListActivity.class.getSimpleName();
|
||||
|
||||
// The following are setFrequency in onCreate
|
||||
// The following are set in onCreate
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private ResourceCursorAdapter resourceCursorAdapter;
|
||||
|
||||
@@ -137,7 +139,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
// Menu items
|
||||
private MenuItem searchMenuItem;
|
||||
private MenuItem startGpsMenuItem;
|
||||
|
||||
private final TrackRecordingServiceConnection.Callback bindChangedCallback = (service, unused) -> {
|
||||
service.getRecordingStatusObservable()
|
||||
@@ -165,6 +166,25 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||
|
||||
viewBinding.aggregatedStatsButton.setOnClickListener((view) -> startActivity(IntentUtils.newIntent(this, AggregatedStatisticsActivity.class)));
|
||||
viewBinding.sensorStartButton.setOnClickListener((view) -> {
|
||||
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
||||
if (locationManager != null && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
||||
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||
} else {
|
||||
// Invoke trackRecordingService
|
||||
if (!gpsStatusValue.isGpsStarted()) {
|
||||
trackRecordingServiceConnection.startAndBindWithCallback(this);
|
||||
} else {
|
||||
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (trackRecordingService != null) {
|
||||
trackRecordingService.stopSensorsAndShutdown(); //TODO Handle this in TrackRecordingServiceConnection
|
||||
}
|
||||
trackRecordingServiceConnection.unbindAndStop(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
||||
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackIdId) -> {
|
||||
Track.Id trackId = new Track.Id(trackIdId);
|
||||
@@ -194,14 +214,15 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
||||
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||
int startTimeOffsetIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
||||
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
||||
int activityTypeIndex = cursor.getColumnIndexOrThrow(TracksColumns.ACTIVITY_TYPE_LOCALIZED);
|
||||
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||
int markerCountIndex = cursor.getColumnIndexOrThrow(TracksColumns.MARKER_COUNT);
|
||||
|
||||
Track.Id trackId = new Track.Id(cursor.getLong(idIndex));
|
||||
boolean isRecording = trackId.equals(recordingStatus.getTrackId());
|
||||
String icon = cursor.getString(iconIndex);
|
||||
int iconId = TrackIconUtils.getIconDrawable(icon);
|
||||
int iconId = ActivityType.findBy(icon)
|
||||
.getIconDrawableId();
|
||||
String name = cursor.getString(nameIndex);
|
||||
String totalTime = StringUtils.formatElapsedTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
|
||||
String totalDistance = DistanceFormatter.Builder()
|
||||
@@ -210,13 +231,13 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
int markerCount = cursor.getInt(markerCountIndex);
|
||||
long startTime = cursor.getLong(startTimeIndex);
|
||||
int startTimeOffset = cursor.getInt(startTimeOffsetIndex);
|
||||
String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex);
|
||||
String activityType = icon != null && !icon.equals("") ? null : cursor.getString(activityTypeIndex);
|
||||
String description = cursor.getString(descriptionIndex);
|
||||
|
||||
ListItemUtils.setListItem(TrackListActivity.this, view, isRecording,
|
||||
iconId, R.string.image_track, name, totalTime, totalDistance, markerCount,
|
||||
OffsetDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneOffset.ofTotalSeconds(startTimeOffset)),
|
||||
category, description, false);
|
||||
activityType, description, false);
|
||||
}
|
||||
};
|
||||
viewBinding.trackList.setAdapter(resourceCursorAdapter);
|
||||
@@ -246,7 +267,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
}
|
||||
|
||||
// Recording -> Stop
|
||||
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(1000);
|
||||
ActivityUtils.vibrate(this, 1000);
|
||||
updateGpsMenuItem(false, false);
|
||||
trackRecordingServiceConnection.stopRecording(TrackListActivity.this);
|
||||
viewBinding.trackListFabAction.setImageResource(R.drawable.ic_baseline_record_24);
|
||||
@@ -254,7 +275,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
return true;
|
||||
});
|
||||
|
||||
setSupportActionBar(viewBinding.bottomAppBar);
|
||||
setSupportActionBar(viewBinding.trackListToolbar);
|
||||
|
||||
loadData(getIntent());
|
||||
}
|
||||
@@ -309,21 +330,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
getMenuInflater().inflate(R.menu.track_list, menu);
|
||||
|
||||
searchMenuItem = menu.findItem(R.id.track_list_search);
|
||||
SearchView searchView = ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
||||
|
||||
searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setPadding(0, 0, 48, 0);
|
||||
|
||||
SearchView.SearchAutoComplete searchAutoComplete = searchView.findViewById(androidx.appcompat.R.id.search_src_text);
|
||||
searchAutoComplete.setHintTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||
searchAutoComplete.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||
|
||||
searchView.setOnCloseListener(() -> {
|
||||
searchView.clearFocus();
|
||||
searchMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
return true;
|
||||
});
|
||||
|
||||
startGpsMenuItem = menu.findItem(R.id.track_list_start_gps);
|
||||
ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
@@ -340,34 +347,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.track_list_start_gps) {
|
||||
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
||||
if (locationManager != null && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
||||
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||
} else {
|
||||
// Invoke trackRecordingService
|
||||
if (!gpsStatusValue.isGpsStarted()) {
|
||||
trackRecordingServiceConnection.startAndBindWithCallback(this);
|
||||
} else {
|
||||
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (trackRecordingService != null) {
|
||||
trackRecordingService.stopSensorsAndShutdown(); //TODO Handle this in TrackRecordingServiceConnection
|
||||
}
|
||||
trackRecordingServiceConnection.unbindAndStop(this);
|
||||
}
|
||||
|
||||
// Update menu after starting or stopping gps
|
||||
this.invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.track_list_aggregated_stats) {
|
||||
startActivity(IntentUtils.newIntent(this, AggregatedStatisticsActivity.class));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.track_list_markers) {
|
||||
startActivity(IntentUtils.newIntent(this, MarkerListActivity.class));
|
||||
return true;
|
||||
@@ -382,6 +361,12 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
||||
searchView.setIconified(false);
|
||||
searchMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.track_list_help) {
|
||||
startActivity(IntentUtils.newIntent(this, HelpActivity.class));
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -437,7 +422,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeleteFinished() {
|
||||
public void onDeleteFinished() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@@ -455,14 +440,12 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
*/
|
||||
//TODO Check if if can be avoided to call this outside of onGpsStatusChanged()
|
||||
private void updateGpsMenuItem(boolean isGpsStarted, boolean isRecording) {
|
||||
if (startGpsMenuItem != null) {
|
||||
startGpsMenuItem.setVisible(!isRecording);
|
||||
if (!isRecording) {
|
||||
startGpsMenuItem.setTitle(isGpsStarted ? R.string.menu_stop_gps : R.string.menu_start_gps);
|
||||
startGpsMenuItem.setIcon(isGpsStarted ? gpsStatusValue.icon : R.drawable.ic_gps_off_24dp);
|
||||
if (startGpsMenuItem.getIcon() instanceof AnimatedVectorDrawable) {
|
||||
((AnimatedVectorDrawable) startGpsMenuItem.getIcon()).start();
|
||||
}
|
||||
MaterialButton startGpsMenuItem = viewBinding.sensorStartButton;
|
||||
startGpsMenuItem.setVisibility(!isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||
if (!isRecording) {
|
||||
startGpsMenuItem.setIcon(AppCompatResources.getDrawable(this, isGpsStarted ? gpsStatusValue.icon : R.drawable.ic_gps_off_24dp));
|
||||
if (startGpsMenuItem.getIcon() instanceof AnimatedVectorDrawable) {
|
||||
((AnimatedVectorDrawable) startGpsMenuItem.getIcon()).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -532,11 +515,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
public void setSearch(String searchQuery) {
|
||||
this.searchQuery = searchQuery;
|
||||
restart();
|
||||
if (searchQuery != null) {
|
||||
setTitle(searchQuery);
|
||||
} else {
|
||||
setTitle(R.string.app_name);
|
||||
}
|
||||
viewBinding.trackListToolbar.setTitle(searchQuery == null ? getString(R.string.app_name) : searchQuery);
|
||||
}
|
||||
|
||||
public void restart() {
|
||||
@@ -547,7 +526,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
|
||||
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME, TracksColumns.STARTTIME_OFFSET,
|
||||
TracksColumns.DESCRIPTION, TracksColumns.ACTIVITY_TYPE_LOCALIZED, TracksColumns.STARTTIME, TracksColumns.STARTTIME_OFFSET,
|
||||
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON, TracksColumns.MARKER_COUNT};
|
||||
|
||||
final String sortOrder = TracksColumns.STARTTIME + " DESC";
|
||||
@@ -557,7 +536,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
} else {
|
||||
final String SEARCH_QUERY = TracksColumns.NAME + " LIKE ? OR " +
|
||||
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
||||
TracksColumns.CATEGORY + " LIKE ?";
|
||||
TracksColumns.ACTIVITY_TYPE_LOCALIZED + " LIKE ?";
|
||||
final String[] selectionArgs = new String[]{"%" + searchQuery + "%", "%" + searchQuery + "%", "%" + searchQuery + "%"};
|
||||
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, SEARCH_QUERY, selectionArgs, sortOrder);
|
||||
}
|
||||
@@ -581,7 +560,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
private void setFloatButton() {
|
||||
viewBinding.trackListFabAction.setImageResource(recordingStatus.isRecording() ? R.drawable.ic_baseline_stop_24 : R.drawable.ic_baseline_record_24);
|
||||
viewBinding.trackListFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, recordingStatus.isRecording() ? R.color.opentracks_secondary_color : R.color.red_dark));
|
||||
viewBinding.trackListFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, recordingStatus.isRecording() ? R.color.opentracks : R.color.red_dark));
|
||||
}
|
||||
|
||||
private void onRecordingStatusChanged(RecordingStatus status) {
|
||||
|
||||
@@ -40,7 +40,6 @@ import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.databinding.TrackRecordedBinding;
|
||||
import de.dennisguse.opentracks.fragments.StatisticsRecordedFragment;
|
||||
import de.dennisguse.opentracks.services.RecordingStatus;
|
||||
import de.dennisguse.opentracks.services.TrackDeleteService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
@@ -103,7 +102,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(track != null ? track.getName() : "");
|
||||
viewBinding.bottomAppBarLayout.bottomAppBar.replaceMenu(R.menu.track_detail);
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
|
||||
@@ -231,22 +229,13 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
return recordingStatus.getTrackId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTrackDeleteStatus(TrackDeleteService.DeletionFinishedStatus deletionFinishedStatus) {
|
||||
super.onTrackDeleteStatus(deletionFinishedStatus);
|
||||
if (deletionFinishedStatus.isDeleted(trackId)) {
|
||||
runOnUiThread(this::finish);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeleteConfirmed() {
|
||||
runOnUiThread(this::finish);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeleteFinished() {
|
||||
// Do nothing
|
||||
public void onDeleteFinished() {
|
||||
runOnUiThread(this::finish);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -32,6 +30,7 @@ import de.dennisguse.opentracks.chart.ChartFragment;
|
||||
import de.dennisguse.opentracks.chart.TrackDataHubInterface;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackDataHub;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.databinding.TrackRecordingBinding;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
@@ -45,9 +44,9 @@ import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalsFragment;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerEditActivity;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerListActivity;
|
||||
import de.dennisguse.opentracks.ui.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
|
||||
/**
|
||||
@@ -134,10 +133,10 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
viewBinding.trackRecordingFabAction.setImageResource(R.drawable.ic_baseline_stop_24);
|
||||
viewBinding.trackRecordingFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.opentracks_secondary_color));
|
||||
viewBinding.trackRecordingFabAction.setBackgroundColor(ContextCompat.getColor(this, R.color.opentracks_secondary_color));
|
||||
viewBinding.trackRecordingFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.opentracks));
|
||||
viewBinding.trackRecordingFabAction.setBackgroundColor(ContextCompat.getColor(this, R.color.opentracks));
|
||||
viewBinding.trackRecordingFabAction.setOnLongClickListener((view) -> {
|
||||
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(1000);
|
||||
ActivityUtils.vibrate(this, 1000);
|
||||
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this);
|
||||
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackStoppedActivity.class)
|
||||
.putExtra(TrackStoppedActivity.EXTRA_TRACK_ID, trackId);
|
||||
@@ -318,10 +317,10 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
public void onChooseActivityTypeDone(ActivityType activityType) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
|
||||
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
||||
String activityTypeLocalized = getString(activityType.getLocalizedStringId());
|
||||
TrackUtils.updateTrack(this, track, null, activityTypeLocalized, null, contentProviderUtils);
|
||||
}
|
||||
|
||||
private class CustomFragmentPagerAdapter extends FragmentStateAdapter {
|
||||
|
||||
@@ -20,7 +20,6 @@ import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragm
|
||||
import de.dennisguse.opentracks.util.ExportUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
|
||||
public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
|
||||
@@ -50,22 +49,23 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
|
||||
viewBinding.trackEditName.setText(track.getName());
|
||||
|
||||
viewBinding.trackEditActivityType.setText(track.getCategory());
|
||||
viewBinding.trackEditActivityType.setText(track.getActivityTypeLocalized());
|
||||
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(this));
|
||||
viewBinding.trackEditActivityType.setAdapter(adapter);
|
||||
viewBinding.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(this, (String) viewBinding.trackEditActivityType.getAdapter().getItem(position))));
|
||||
viewBinding.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> {
|
||||
String localizedActivityType = (String) viewBinding.trackEditActivityType.getAdapter().getItem(position);
|
||||
setActivityTypeIcon(ActivityType.findByLocalizedString(this, localizedActivityType));
|
||||
});
|
||||
viewBinding.trackEditActivityType.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
||||
TrackStoppedActivity.this, viewBinding.trackEditActivityType.getText().toString()));
|
||||
String localizedActivityType = viewBinding.trackEditActivityType.getText().toString();
|
||||
setActivityTypeIcon(ActivityType.findByLocalizedString(this, localizedActivityType));
|
||||
}
|
||||
});
|
||||
|
||||
String iconValue = track.getIcon();
|
||||
|
||||
setActivityTypeIcon(iconValue);
|
||||
viewBinding.trackEditActivityTypeIcon.setOnClickListener(v -> ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), viewBinding.trackEditActivityType.getText().toString()));
|
||||
setActivityTypeIcon(track.getActivityType());
|
||||
viewBinding.trackEditActivityTypeIcon.setOnClickListener(v -> ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), this, viewBinding.trackEditActivityType.getText().toString()));
|
||||
|
||||
viewBinding.trackEditDescription.setText(track.getDescription());
|
||||
|
||||
@@ -74,7 +74,7 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
{
|
||||
Pair<String, String> parts = SpeedFormatter.Builder()
|
||||
.setUnit(PreferencesUtils.getUnitSystem())
|
||||
.setReportSpeedOrPace(PreferencesUtils.isReportSpeed(track.getCategory()))
|
||||
.setReportSpeedOrPace(PreferencesUtils.isReportSpeed(track))
|
||||
.build(this)
|
||||
.getSpeedParts(track.getTrackStatistics().getAverageMovingSpeed());
|
||||
viewBinding.speed.setText(parts.first);
|
||||
@@ -125,14 +125,14 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
private void setActivityTypeIcon(String iconValue) {
|
||||
viewBinding.trackEditActivityTypeIcon.setImageResource(TrackIconUtils.getIconDrawable(iconValue));
|
||||
private void setActivityTypeIcon(ActivityType activityType) {
|
||||
viewBinding.trackEditActivityTypeIcon.setImageResource(activityType.getIconDrawableId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
setActivityTypeIcon(iconValue);
|
||||
viewBinding.trackEditActivityType.setText(getString(TrackIconUtils.getIconActivityType(iconValue)));
|
||||
public void onChooseActivityTypeDone(ActivityType activityType) {
|
||||
setActivityTypeIcon(activityType);
|
||||
viewBinding.trackEditActivityType.setText(getString(activityType.getLocalizedStringId()));
|
||||
}
|
||||
|
||||
private void resumeTrackAndFinish() {
|
||||
@@ -158,7 +158,7 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeleteFinished() {
|
||||
public void onDeleteFinished() {
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
|
||||
|
||||
// Stats gathered from the received data
|
||||
private final List<ChartPoint> pendingPoints = new ArrayList<>();
|
||||
private String category = "";
|
||||
private String activityTypeLocalized = "";
|
||||
|
||||
// Modes of operation
|
||||
private boolean chartByDistance;
|
||||
@@ -87,7 +87,7 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
|
||||
}
|
||||
}
|
||||
if (PreferencesUtils.isKey(R.string.stats_rate_key, key)) {
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(category);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(activityTypeLocalized);
|
||||
if (reportSpeed != viewBinding.chartView.getReportSpeed()) {
|
||||
viewBinding.chartView.setReportSpeed(reportSpeed);
|
||||
viewBinding.chartView.applyReportSpeed();
|
||||
@@ -160,12 +160,12 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
|
||||
public void onTrackUpdated(Track track) {
|
||||
if (isResumed()) {
|
||||
if (track == null) {
|
||||
category = "";
|
||||
activityTypeLocalized = "";
|
||||
return;
|
||||
}
|
||||
|
||||
category = track.getCategory();
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(category);
|
||||
activityTypeLocalized = track.getActivityTypeLocalized();
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(activityTypeLocalized);
|
||||
if (reportSpeed != viewBinding.chartView.getReportSpeed()) {
|
||||
viewBinding.chartView.setReportSpeed(reportSpeed);
|
||||
viewBinding.chartView.applyReportSpeed();
|
||||
|
||||
@@ -212,7 +212,7 @@ abstract class ChartValueSeries {
|
||||
int getTitleId(UnitSystem unitSystem) {
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> metricTitleId;
|
||||
case IMPERIAL -> imperialTitleId;
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> imperialTitleId;
|
||||
case NAUTICAL_IMPERIAL -> nauticalTitleId;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -735,7 +735,7 @@ public class ChartView extends View {
|
||||
if (chartByDistance) {
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> context.getString(R.string.unit_kilometer);
|
||||
case IMPERIAL -> context.getString(R.string.unit_mile);
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> context.getString(R.string.unit_mile);
|
||||
case NAUTICAL_IMPERIAL -> context.getString(R.string.unit_nautical_mile);
|
||||
};
|
||||
} else {
|
||||
@@ -946,7 +946,7 @@ public class ChartView extends View {
|
||||
*/
|
||||
private void updateEffectiveDimensions() {
|
||||
effectiveWidth = Math.max(0, width - leftBorder - rightBorder);
|
||||
effectiveHeight = Math.max(0, height - topBorder - bottomBorder);
|
||||
effectiveHeight = Math.max(0, height - topBorder - bottomBorder - spacer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,6 +40,7 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
@@ -100,7 +101,7 @@ public class ContentProviderUtils {
|
||||
int uuidIndex = cursor.getColumnIndexOrThrow(TracksColumns.UUID);
|
||||
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
|
||||
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
||||
int activityTypeLocalizedIndex = cursor.getColumnIndexOrThrow(TracksColumns.ACTIVITY_TYPE_LOCALIZED);
|
||||
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||
int startTimeOffsetIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
||||
int stopTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STOPTIME);
|
||||
@@ -128,8 +129,8 @@ public class ContentProviderUtils {
|
||||
if (!cursor.isNull(descriptionIndex)) {
|
||||
track.setDescription(cursor.getString(descriptionIndex));
|
||||
}
|
||||
if (!cursor.isNull(categoryIndex)) {
|
||||
track.setCategory(cursor.getString(categoryIndex));
|
||||
if (!cursor.isNull(activityTypeLocalizedIndex)) {
|
||||
track.setActivityTypeLocalized(cursor.getString(activityTypeLocalizedIndex));
|
||||
}
|
||||
|
||||
if (!cursor.isNull(startTimeIndex)) {
|
||||
@@ -163,7 +164,7 @@ public class ContentProviderUtils {
|
||||
trackStatistics.setTotalAltitudeLoss(cursor.getFloat(altitudeLossIndex));
|
||||
}
|
||||
if (!cursor.isNull(iconIndex)) {
|
||||
track.setIcon(cursor.getString(iconIndex));
|
||||
track.setActivityType(ActivityType.findBy(cursor.getString(iconIndex)));
|
||||
}
|
||||
return track;
|
||||
}
|
||||
@@ -290,7 +291,7 @@ public class ContentProviderUtils {
|
||||
values.put(TracksColumns.UUID, UUIDUtils.toBytes(track.getUuid()));
|
||||
values.put(TracksColumns.NAME, track.getName());
|
||||
values.put(TracksColumns.DESCRIPTION, track.getDescription());
|
||||
values.put(TracksColumns.CATEGORY, track.getCategory());
|
||||
values.put(TracksColumns.ACTIVITY_TYPE_LOCALIZED, track.getActivityTypeLocalized());
|
||||
values.put(TracksColumns.STARTTIME_OFFSET, track.getZoneOffset().getTotalSeconds());
|
||||
if (trackStatistics.getStartTime() != null) {
|
||||
values.put(TracksColumns.STARTTIME, trackStatistics.getStartTime().toEpochMilli());
|
||||
@@ -308,7 +309,7 @@ public class ContentProviderUtils {
|
||||
values.put(TracksColumns.MAX_ALTITUDE, trackStatistics.getMaxAltitude());
|
||||
values.put(TracksColumns.ALTITUDE_GAIN, trackStatistics.getTotalAltitudeGain());
|
||||
values.put(TracksColumns.ALTITUDE_LOSS, trackStatistics.getTotalAltitudeLoss());
|
||||
values.put(TracksColumns.ICON, track.getIcon());
|
||||
values.put(TracksColumns.ICON, track.getActivityType() != null ? track.getActivityType().getIconId() : "");
|
||||
|
||||
return values;
|
||||
}
|
||||
@@ -403,7 +404,7 @@ public class ContentProviderUtils {
|
||||
public void deleteMarker(Context context, Marker.Id markerId) {
|
||||
final Marker marker = getMarker(markerId);
|
||||
deleteMarkerPhoto(context, marker);
|
||||
contentResolver.delete(MarkerColumns.CONTENT_URI, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.getId())});
|
||||
contentResolver.delete(MarkerColumns.CONTENT_URI, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.id())});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,7 +423,7 @@ public class ContentProviderUtils {
|
||||
}
|
||||
|
||||
public Marker getMarker(@NonNull Marker.Id markerId) {
|
||||
try (Cursor cursor = getMarkerCursor(null, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.getId())}, MarkerColumns._ID, 1)) {
|
||||
try (Cursor cursor = getMarkerCursor(null, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.id())}, MarkerColumns._ID, 1)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return createMarker(cursor);
|
||||
}
|
||||
@@ -442,7 +443,7 @@ public class ContentProviderUtils {
|
||||
String[] selectionArgs;
|
||||
if (minMarkerId != null) {
|
||||
selection = MarkerColumns.TRACKID + "=? AND " + MarkerColumns._ID + ">=?";
|
||||
selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(minMarkerId.getId())};
|
||||
selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(minMarkerId.id())};
|
||||
} else {
|
||||
selection = MarkerColumns.TRACKID + "=?";
|
||||
selectionArgs = new String[]{Long.toString(trackId.getId())};
|
||||
@@ -495,7 +496,7 @@ public class ContentProviderUtils {
|
||||
if (!updateMarker.hasPhoto()) {
|
||||
deleteMarkerPhoto(context, savedMarker);
|
||||
}
|
||||
int rows = contentResolver.update(MarkerColumns.CONTENT_URI, createContentValues(updateMarker), MarkerColumns._ID + "=?", new String[]{Long.toString(updateMarker.getId().getId())});
|
||||
int rows = contentResolver.update(MarkerColumns.CONTENT_URI, createContentValues(updateMarker), MarkerColumns._ID + "=?", new String[]{Long.toString(updateMarker.getId().id())});
|
||||
return rows == 1;
|
||||
}
|
||||
|
||||
@@ -503,7 +504,7 @@ public class ContentProviderUtils {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
if (marker.getId() != null) {
|
||||
values.put(MarkerColumns._ID, marker.getId().getId());
|
||||
values.put(MarkerColumns._ID, marker.getId().id());
|
||||
}
|
||||
values.put(MarkerColumns.NAME, marker.getName());
|
||||
values.put(MarkerColumns.DESCRIPTION, marker.getDescription());
|
||||
|
||||
@@ -213,7 +213,7 @@ public class ShareContentProvider extends CustomContentProvider {
|
||||
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
||||
// TODO handle failure (i.e., do not export an empty file)
|
||||
trackExporter.writeTrack(tracks.toArray(new Track[0]), fileOutputStream);
|
||||
trackExporter.writeTrack(tracks, fileOutputStream);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "there occurred an error while sharing a file: " + e);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||
import de.dennisguse.opentracks.services.RecordingStatus;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.handlers.EGM2008CorrectionManager;
|
||||
import de.dennisguse.opentracks.services.handlers.AltitudeCorrectionManager;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
|
||||
@@ -73,7 +73,7 @@ public class TrackDataHub {
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private final int targetNumPoints;
|
||||
|
||||
private final EGM2008CorrectionManager egm2008Correction = new EGM2008CorrectionManager();
|
||||
private final AltitudeCorrectionManager egm2008Correction = new AltitudeCorrectionManager();
|
||||
|
||||
//TODO Check if this is needed.
|
||||
private HandlerThread handlerThread;
|
||||
|
||||
@@ -30,9 +30,9 @@ public class TrackSelection implements ContentProviderUtils.ContentProviderSelec
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackSelection addCategory(String category) {
|
||||
if (!this.categories.contains(category)) {
|
||||
this.categories.add(category);
|
||||
public TrackSelection addActivityType(String activityType) {
|
||||
if (!this.categories.contains(activityType)) {
|
||||
this.categories.add(activityType);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public class TrackSelection implements ContentProviderUtils.ContentProviderSelec
|
||||
}
|
||||
if (!categories.isEmpty()) {
|
||||
selection += selection.isEmpty() ? "" : " AND ";
|
||||
selection += String.format(TracksColumns.CATEGORY + " IN (%s)", TextUtils.join(",", Collections.nCopies(categories.size(), "?")));
|
||||
selection += String.format(TracksColumns.ACTIVITY_TYPE_LOCALIZED + " IN (%s)", TextUtils.join(",", Collections.nCopies(categories.size(), "?")));
|
||||
}
|
||||
if (from != null && to != null) {
|
||||
selection += selection.isEmpty() ? "" : " AND ";
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
public enum ActivityIcon {
|
||||
AIRPLANE("AIRPLANE", R.drawable.ic_activity_flight_24dp),
|
||||
BIKE("BIKE", R.drawable.ic_activity_bike_24dp),
|
||||
MOUNTAIN_BIKE("MOUNTAIN_BIKE", R.drawable.ic_activity_mtb_24dp),
|
||||
MOTOR_BIKE("MOTOR_BIKE", R.drawable.ic_activity_motorbike_24dp),
|
||||
KAYAK("KAYAK", R.drawable.ic_activity_kayaking_24dp),
|
||||
BOAT("BOAT", R.drawable.ic_activity_boat_24dp),
|
||||
SAILING("SAILING", R.drawable.ic_activity_sailing_24dp),
|
||||
DRIVE("DRIVE", R.drawable.ic_activity_drive_24dp),
|
||||
RUN("RUN", R.drawable.ic_activity_run_24dp),
|
||||
SKI("SKI", R.drawable.ic_activity_skiing_24dp),
|
||||
SNOW_BOARDING("SNOW_BOARDING", R.drawable.ic_activity_snowboarding_24dp),
|
||||
UNKNOWN("UNKNOWN", R.drawable.ic_logo_24dp),
|
||||
WALK("WALK", R.drawable.ic_activity_walk_24dp),
|
||||
ESCOOTER("ESCOOTER", R.drawable.ic_activity_escooter_24dp),
|
||||
INLINE_SKATING("INLINES_SKATING", R.drawable.ic_activity_inline_skating_24dp),
|
||||
SKATE_BOARDING("SKATE_BOARDING", R.drawable.ic_activity_skateboarding_24dp),
|
||||
CLIMBING("CLIMBING", R.drawable.ic_activity_climbing_24dp),
|
||||
SWIMMING("SWIMMING", R.drawable.ic_activity_swimming_24dp),
|
||||
SWIMMING_OPEN("SWIMMING_OPEN", R.drawable.ic_activity_swimming_open_24dp),
|
||||
WORKOUT("WORKOUT", R.drawable.ic_activity_workout_24dp);
|
||||
|
||||
@Deprecated //TODO should be removed.
|
||||
final String iconId;
|
||||
final int iconDrawableId;
|
||||
|
||||
ActivityIcon(String iconId, int iconDrawableId) {
|
||||
this.iconId = iconId;
|
||||
this.iconDrawableId = iconDrawableId;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -10,70 +12,138 @@ import java.util.stream.Collectors;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
public enum ActivityType {
|
||||
AIRPLANE("AIRPLANE", R.drawable.ic_activity_flight_24dp, true, R.string.activity_type_airplane, R.string.activity_type_commercial_airplane, R.string.activity_type_rc_airplane),
|
||||
BIKE("BIKE", R.drawable.ic_activity_bike_24dp, true, R.string.activity_type_biking, R.string.activity_type_cycling, R.string.activity_type_dirt_bike, R.string.activity_type_road_biking, R.string.activity_type_track_cycling),
|
||||
MOUNTAIN_BIKE("MOUNTAIN_BIKE", R.drawable.ic_activity_mtb_24dp, true, R.string.activity_type_mountain_biking),
|
||||
MOTOR_BIKE("MOTOR_BIKE", R.drawable.ic_activity_motorbike_24dp, true, R.string.activity_type_motor_bike),
|
||||
KAYAK("KAYAK", R.drawable.ic_activity_kayaking_24dp, true, R.string.activity_type_kayaking),
|
||||
BOAT("BOAT", R.drawable.ic_activity_boat_24dp, true, R.string.activity_type_boat, R.string.activity_type_ferry, R.string.activity_type_motor_boating, R.string.activity_type_rc_boat),
|
||||
SAILING("SAILING", R.drawable.ic_activity_sailing_24dp, true, R.string.activity_type_sailing),
|
||||
DRIVE("DRIVE", R.drawable.ic_activity_drive_24dp, true, R.string.activity_type_atv, R.string.activity_type_driving, R.string.activity_type_driving_bus, R.string.activity_type_driving_car),
|
||||
RUN("RUN", R.drawable.ic_activity_run_24dp, false, R.string.activity_type_running, R.string.activity_type_street_running, R.string.activity_type_track_running, R.string.activity_type_trail_running),
|
||||
SKI("SKI", R.drawable.ic_activity_skiing_24dp, true, R.string.activity_type_cross_country_skiing, R.string.activity_type_skiing),
|
||||
SNOW_BOARDING("SNOW_BOARDING", R.drawable.ic_activity_snowboarding_24dp, true, R.string.activity_type_snow_boarding),
|
||||
UNKNOWN("UNKNOWN", R.drawable.ic_logo_24dp, true, R.string.activity_type_unknown),
|
||||
WALK("WALK", R.drawable.ic_activity_walk_24dp, false, R.string.activity_type_hiking, R.string.activity_type_off_trail_hiking, R.string.activity_type_speed_walking, R.string.activity_type_trail_hiking, R.string.activity_type_walking),
|
||||
ESCOOTER("ESCOOTER", R.drawable.ic_activity_escooter_24dp, true, R.string.activity_type_escooter),
|
||||
INLINE_SKATING("INLINES_SKATING", R.drawable.ic_activity_inline_skating_24dp, true, R.string.activity_type_inline_skating),
|
||||
SKATE_BOARDING("SKATE_BOARDING", R.drawable.ic_activity_skateboarding_24dp, true, R.string.activity_type_skate_boarding),
|
||||
CLIMBING("CLIMBING", R.drawable.ic_activity_climbing_24dp, false, R.string.activity_type_climbing),
|
||||
SWIMMING("SWIMMING", R.drawable.ic_activity_swimming_24dp, false, R.string.activity_type_swimming),
|
||||
SWIMMING_OPEN("SWIMMING_OPEN", R.drawable.ic_activity_swimming_open_24dp, false, R.string.activity_type_swimming_open),
|
||||
WORKOUT("WORKOUT", R.drawable.ic_activity_workout_24dp, false, R.string.activity_type_workout);
|
||||
|
||||
AIRPLANE("airplane", R.string.activity_type_airplane, ActivityIcon.AIRPLANE, true),
|
||||
ATV("ATV", R.string.activity_type_atv, ActivityIcon.DRIVE, true),
|
||||
BIKING("biking", R.string.activity_type_biking, ActivityIcon.BIKE, true),
|
||||
BLIMP("blimp", R.string.activity_type_blimp, ActivityIcon.UNKNOWN, true),
|
||||
BOAT("boat", R.string.activity_type_boat, ActivityIcon.BOAT, true),
|
||||
CLIMBING("climbing", R.string.activity_type_climbing, ActivityIcon.CLIMBING, false),
|
||||
COMMERCIAL_AIRPLANE("commercial airplane", R.string.activity_type_commercial_airplane, ActivityIcon.AIRPLANE, true),
|
||||
CROSS_COUNTRY_SKIING("cross-country skiing", R.string.activity_type_cross_country_skiing, ActivityIcon.SKI, true),
|
||||
CYCLING("cycling", R.string.activity_type_cycling, ActivityIcon.BIKE, true),
|
||||
DIRT_BIKE("dirt bike", R.string.activity_type_dirt_bike, ActivityIcon.MOTOR_BIKE, true),
|
||||
DONKEY_BACK_RIDING("donkey back riding", R.string.activity_type_donkey_back_riding, ActivityIcon.UNKNOWN, true),
|
||||
DRIVING("driving", R.string.activity_type_driving, ActivityIcon.DRIVE, true),
|
||||
DRIVING_BUS("driving bus", R.string.activity_type_driving_bus, ActivityIcon.DRIVE, true),
|
||||
DRIVING_CAR("driving car", R.string.activity_type_driving_car, ActivityIcon.DRIVE, true),
|
||||
ESCOOTER("escooter", R.string.activity_type_escooter, ActivityIcon.ESCOOTER, true),
|
||||
FERRY("ferry", R.string.activity_type_ferry, ActivityIcon.BOAT, true),
|
||||
FRISBEE("frisbee", R.string.activity_type_frisbee, ActivityIcon.UNKNOWN, true),
|
||||
GLIDING("gliding", R.string.activity_type_gliding, ActivityIcon.UNKNOWN, true),
|
||||
HANG_GLIDING("hang gliding", R.string.activity_type_hang_gliding, ActivityIcon.UNKNOWN, true),
|
||||
HELICOPTER("helicopter", R.string.activity_type_helicopter, ActivityIcon.UNKNOWN, true),
|
||||
HIKING("hiking", R.string.activity_type_hiking, ActivityIcon.WALK, false),
|
||||
HORSE_BACK_RIDING("horse back riding", R.string.activity_type_horse_back_riding, ActivityIcon.UNKNOWN, true),
|
||||
HOT_AIR_BALLOON("hot air balloon", R.string.activity_type_hot_air_balloon, ActivityIcon.UNKNOWN, true),
|
||||
ICE_SAILING("ice sailing", R.string.activity_type_ice_sailing, ActivityIcon.UNKNOWN, true),
|
||||
INLINE_SKATING("inline skating", R.string.activity_type_inline_skating, ActivityIcon.INLINE_SKATING, true),
|
||||
KAYAKING("kayaking", R.string.activity_type_kayaking, ActivityIcon.KAYAK, true),
|
||||
KITE_SURFING("kite surfing", R.string.activity_type_kite_surfing, ActivityIcon.UNKNOWN, true),
|
||||
LAND_SAILING("land sailing", R.string.activity_type_land_sailing, ActivityIcon.UNKNOWN, true),
|
||||
MIXED_TYPE("mixed type", R.string.activity_type_mixed_type, ActivityIcon.UNKNOWN, true),
|
||||
MOTOR_BIKE("motor bike", R.string.activity_type_motor_bike, ActivityIcon.MOTOR_BIKE, true),
|
||||
MOTOR_BOATING("motor boating", R.string.activity_type_motor_boating, ActivityIcon.BOAT, true),
|
||||
MOUNTAIN_BIKING("mountain biking", R.string.activity_type_mountain_biking, ActivityIcon.MOUNTAIN_BIKE, true),
|
||||
OFF_TRAIL_HIKING("off trail hiking", R.string.activity_type_hiking, ActivityIcon.WALK, false),
|
||||
OTHER("other", R.string.activity_type_other, ActivityIcon.UNKNOWN, true),
|
||||
PADDLING("paddling", R.string.activity_type_paddling, ActivityIcon.UNKNOWN, true),
|
||||
PARA_GLIDING("para gliding", R.string.activity_type_para_gliding, ActivityIcon.UNKNOWN, true),
|
||||
RC_AIRPLANE("RC airplane", R.string.activity_type_rc_airplane, ActivityIcon.AIRPLANE, true),
|
||||
RC_BOAT("RC boat", R.string.activity_type_rc_boat, ActivityIcon.BOAT, true),
|
||||
RC_HELICOPTER("RC helicopter", R.string.activity_type_rc_helicopter, ActivityIcon.UNKNOWN, true),
|
||||
RIDING("riding", R.string.activity_type_horse_back_riding, ActivityIcon.UNKNOWN, true),
|
||||
ROAD_BIKING("road biking", R.string.activity_type_road_biking, ActivityIcon.BIKE, true),
|
||||
ROLLER_SKIING("roller skiing", R.string.activity_type_roller_skiing, ActivityIcon.UNKNOWN, true),
|
||||
ROWING("rowing", R.string.activity_type_rowing, ActivityIcon.UNKNOWN, true),
|
||||
RUNNING("running", R.string.activity_type_running, ActivityIcon.RUN, false),
|
||||
SAILING("sailing", R.string.activity_type_sailing, ActivityIcon.SAILING, true),
|
||||
SEAPLANE("seaplane", R.string.activity_type_seaplane, ActivityIcon.AIRPLANE, true),
|
||||
SKATE_BOARDING("skate boarding", R.string.activity_type_skate_boarding, ActivityIcon.SKATE_BOARDING, true),
|
||||
SKATING("skating", R.string.activity_type_skating, ActivityIcon.UNKNOWN, true),
|
||||
SKIING("skiing", R.string.activity_type_skiing, ActivityIcon.SKI, true),
|
||||
SKY_JUMPING("sky jumping", R.string.activity_type_sky_jumping, ActivityIcon.UNKNOWN, true),
|
||||
SLED("sled", R.string.activity_type_sled, ActivityIcon.UNKNOWN, true),
|
||||
SNOW_BOARDING("snow boarding", R.string.activity_type_snow_boarding, ActivityIcon.SNOW_BOARDING, true),
|
||||
SNOW_SHOEING("snow shoeing", R.string.activity_type_snow_shoeing, ActivityIcon.UNKNOWN, true),
|
||||
SPEED_WALKING("speed walking", R.string.activity_type_speed_walking, ActivityIcon.WALK, false),
|
||||
STREET_RUNNING("street running", R.string.activity_type_street_running, ActivityIcon.RUN, false),
|
||||
SURFING("surfing", R.string.activity_type_surfing, ActivityIcon.UNKNOWN, true),
|
||||
TRACK_CYCLING("track cycling", R.string.activity_type_track_cycling, ActivityIcon.BIKE, true),
|
||||
TRACK_RUNNING("track running", R.string.activity_type_trail_running, ActivityIcon.RUN, false),
|
||||
TRAIL_HIKING("trail hiking", R.string.activity_type_trail_hiking, ActivityIcon.WALK, false),
|
||||
TRAIL_RUNNING("trail running", R.string.activity_type_trail_running, ActivityIcon.RUN, false),
|
||||
TRAIN("train", R.string.activity_type_train, ActivityIcon.UNKNOWN, true),
|
||||
ULTIMATE_FRISBEE("ultimate frisbee", R.string.activity_type_ultimate_frisbee, ActivityIcon.UNKNOWN, true),
|
||||
WAKEBOARDING("wakeboarding", R.string.activity_type_wakeboarding, ActivityIcon.UNKNOWN, true),
|
||||
WALKING("walking", R.string.activity_type_walking, ActivityIcon.WALK, false),
|
||||
WATER_SKIING("water skiing", R.string.activity_type_water_skiing, ActivityIcon.UNKNOWN, true),
|
||||
WIND_SURFING("wind surfing", R.string.activity_type_wind_surfing, ActivityIcon.UNKNOWN, true),
|
||||
SWIMMING("swimming", R.string.activity_type_swimming, ActivityIcon.SWIMMING, false),
|
||||
SWIMMING_OPEN("swimming in open water", R.string.activity_type_swimming_open, ActivityIcon.SWIMMING_OPEN, false),
|
||||
WORKOUT("workout", R.string.activity_type_workout, ActivityIcon.WORKOUT, false),
|
||||
UNKNOWN("unknown", R.string.activity_type_unknown, ActivityIcon.UNKNOWN, true);
|
||||
|
||||
final String id;
|
||||
final int iconId;
|
||||
|
||||
final ActivityIcon icon;
|
||||
final boolean showSpeedPreferred;
|
||||
final int[] localizedStringIds;
|
||||
final int localizedStringId;
|
||||
|
||||
//isSpeed?
|
||||
|
||||
ActivityType(String id, int iconId, boolean showSpeedPreferred, int... localizedStringIds) {
|
||||
ActivityType(String id, int localizedStringId, ActivityIcon icon, boolean showSpeedPreferred) {
|
||||
this.id = id;
|
||||
this.iconId = iconId;
|
||||
this.localizedStringId = localizedStringId;
|
||||
this.icon = icon;
|
||||
this.showSpeedPreferred = showSpeedPreferred;
|
||||
this.localizedStringIds = localizedStringIds;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getIconId() {
|
||||
return iconId;
|
||||
@Deprecated
|
||||
public String getIconId() {
|
||||
return icon.iconId;
|
||||
}
|
||||
|
||||
public int getIconDrawableId() {
|
||||
return icon.iconDrawableId;
|
||||
}
|
||||
|
||||
public int getLocalizedStringId() {
|
||||
return localizedStringId;
|
||||
}
|
||||
|
||||
public boolean isShowSpeedPreferred() {
|
||||
return showSpeedPreferred;
|
||||
}
|
||||
|
||||
public int[] getLocalizedStringIds() {
|
||||
return localizedStringIds;
|
||||
}
|
||||
|
||||
public int getFirstLocalizedStringId() {
|
||||
return localizedStringIds[0];
|
||||
}
|
||||
|
||||
public static List<String> getLocalizedStrings(Context context) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (ActivityType activityType : values()) {
|
||||
List<String> strings = Arrays.stream(activityType.localizedStringIds)
|
||||
.mapToObj(context::getString)
|
||||
.collect(Collectors.toList());
|
||||
result.addAll(strings);
|
||||
}
|
||||
return Arrays.stream(values())
|
||||
.map(ActivityType::getLocalizedStringId)
|
||||
.map(context::getString)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return result;
|
||||
@NonNull
|
||||
public static ActivityType findBy(String iconId) {
|
||||
if (ActivityIcon.UNKNOWN.iconId.equals(iconId)) {
|
||||
return ActivityType.UNKNOWN;
|
||||
}
|
||||
return Arrays.stream(ActivityType.values()).filter(
|
||||
it -> it.icon.iconId.equals(iconId)
|
||||
).findFirst()
|
||||
.orElse(ActivityType.UNKNOWN);
|
||||
}
|
||||
|
||||
public static ActivityType findByLocalizedString(Context context, String localizedActivityType) {
|
||||
return findByLocalizedString(context.getResources(), localizedActivityType);
|
||||
}
|
||||
|
||||
public static ActivityType findByLocalizedString(Resources resources, String localizedActivityType) {
|
||||
return Arrays.stream(ActivityType.values())
|
||||
.filter(it -> resources.getString(it.getLocalizedStringId()).equals(localizedActivityType))
|
||||
.findFirst()
|
||||
.orElse(ActivityType.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,7 @@ public abstract class Altitude {
|
||||
public double toM() {
|
||||
return altitude_m;
|
||||
}
|
||||
|
||||
public abstract Altitude replace(double altitude_m);
|
||||
|
||||
|
||||
public abstract int getLabelId();
|
||||
|
||||
public static class WGS84 extends Altitude {
|
||||
@@ -31,12 +29,7 @@ public abstract class Altitude {
|
||||
return R.string.wgs84;
|
||||
}
|
||||
|
||||
public static Altitude of(double altitude_m) {
|
||||
return new WGS84(altitude_m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Altitude replace(double altitude_m) {
|
||||
public static WGS84 of(double altitude_m) {
|
||||
return new WGS84(altitude_m);
|
||||
}
|
||||
}
|
||||
@@ -52,12 +45,7 @@ public abstract class Altitude {
|
||||
return R.string.egm2008;
|
||||
}
|
||||
|
||||
public static Altitude of(double altitude_m) {
|
||||
return new EGM2008(altitude_m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Altitude replace(double altitude_m) {
|
||||
public static EGM2008 of(double altitude_m) {
|
||||
return new EGM2008(altitude_m);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
public record AtmosphericPressure(float value) {
|
||||
|
||||
public static AtmosphericPressure ofPA(float value_Pa) {
|
||||
return new AtmosphericPressure(value_Pa * 100);
|
||||
}
|
||||
|
||||
public static AtmosphericPressure ofHPA(float value_hPa) {
|
||||
return new AtmosphericPressure(value_hPa);
|
||||
}
|
||||
|
||||
public float getPA() {
|
||||
return value * 100;
|
||||
}
|
||||
|
||||
public float getHPA() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
public record BatteryLevel(int percentage) {
|
||||
|
||||
public static BatteryLevel of(int percentage) {
|
||||
return new BatteryLevel(percentage);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Cadence {
|
||||
public record Cadence(float value_rpm) {
|
||||
|
||||
public static Cadence of(float value, Duration duration) {
|
||||
if (duration.isZero()) {
|
||||
@@ -23,34 +20,7 @@ public class Cadence {
|
||||
return of(0.0f);
|
||||
}
|
||||
|
||||
private final float value_rpm;
|
||||
|
||||
private Cadence(float value) {
|
||||
this.value_rpm = value;
|
||||
}
|
||||
|
||||
public float getRPM() {
|
||||
return value_rpm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Cadence cadence = (Cadence) o;
|
||||
return Float.compare(cadence.value_rpm, value_rpm) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value_rpm);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Cadence{" +
|
||||
"value=" + value_rpm + " rpm" +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,13 @@ package de.dennisguse.opentracks.data.models;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
|
||||
public class Distance {
|
||||
public record Distance(double distance_m) {
|
||||
|
||||
public static Distance of(double distance_m) {
|
||||
return new Distance(distance_m);
|
||||
}
|
||||
|
||||
public static Distance of(Double distance_m) {
|
||||
if (distance_m == null) {
|
||||
return Distance.of(Double.NaN);
|
||||
@@ -20,11 +17,9 @@ public class Distance {
|
||||
return Distance.of((double) distance_m);
|
||||
}
|
||||
}
|
||||
|
||||
public static Distance of(String distance_m) {
|
||||
return of(Float.parseFloat(distance_m));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Distance ofOrNull(Double distance_m) {
|
||||
if (distance_m == null) {
|
||||
@@ -60,17 +55,11 @@ public class Distance {
|
||||
public static Distance one(UnitSystem unitSystem) {
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> Distance.ofKilometer(1);
|
||||
case IMPERIAL -> Distance.ofMile(1);
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> Distance.ofMile(1);
|
||||
case NAUTICAL_IMPERIAL -> Distance.ofNauticalMile(1);
|
||||
};
|
||||
}
|
||||
|
||||
private final double distance_m;
|
||||
|
||||
private Distance(double distance_m) {
|
||||
this.distance_m = distance_m;
|
||||
}
|
||||
|
||||
public Distance plus(@NonNull Distance distance) {
|
||||
return new Distance(distance_m + distance.distance_m);
|
||||
}
|
||||
@@ -130,39 +119,18 @@ public class Distance {
|
||||
public double toKM_Miles(UnitSystem unitSystem) {
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> toKM();
|
||||
case IMPERIAL -> toMI();
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> toMI();
|
||||
case NAUTICAL_IMPERIAL -> toNauticalMiles();
|
||||
};
|
||||
}
|
||||
|
||||
public double toM_FT(UnitSystem unitSystem) {
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> toM();
|
||||
case NAUTICAL_IMPERIAL, IMPERIAL -> toFT();
|
||||
case METRIC, IMPERIAL_METER -> toM();
|
||||
case NAUTICAL_IMPERIAL, IMPERIAL_FEET -> toFT();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Distance distance = (Distance) o;
|
||||
return Double.compare(distance.distance_m, distance_m) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(distance_m);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Distance{" +
|
||||
"distance_m=" + distance_m +
|
||||
'}';
|
||||
}
|
||||
|
||||
// multiplication factors for conversion
|
||||
private static final double KM_TO_M = 1000.0;
|
||||
private static final double M_TO_KM = 1 / KM_TO_M;
|
||||
|
||||
@@ -47,8 +47,9 @@ public class DistanceFormatter {
|
||||
if (distance.isInvalid()) {
|
||||
String valueUnknown = resources.getString(R.string.value_unknown);
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
||||
case IMPERIAL, NAUTICAL_IMPERIAL ->
|
||||
case METRIC, IMPERIAL_METER ->
|
||||
new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
||||
case IMPERIAL_FEET, NAUTICAL_IMPERIAL ->
|
||||
new Pair<>(valueUnknown, resources.getString(R.string.unit_feet));
|
||||
};
|
||||
}
|
||||
@@ -61,13 +62,20 @@ public class DistanceFormatter {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
|
||||
}
|
||||
}
|
||||
case IMPERIAL -> {
|
||||
case IMPERIAL_FEET -> {
|
||||
if (distance.greaterThan(Distance.ofMile(threshold))) {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
|
||||
} else {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
||||
}
|
||||
}
|
||||
case IMPERIAL_METER -> {
|
||||
if (distance.greaterThan(Distance.ofMile(threshold))) {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
|
||||
} else {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
|
||||
}
|
||||
}
|
||||
case NAUTICAL_IMPERIAL -> {
|
||||
if (distance.greaterThan(Distance.ofNauticalMile(threshold))) {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toNauticalMiles(), decimalCount), resources.getString(R.string.unit_nautical_mile));
|
||||
|
||||
@@ -1,43 +1,12 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class HeartRate {
|
||||
public record HeartRate(float value) {
|
||||
|
||||
public static HeartRate of(float value) {
|
||||
return new HeartRate(value);
|
||||
}
|
||||
|
||||
private final float value;
|
||||
|
||||
private HeartRate(float value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public float getBPM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
HeartRate heartRate = (HeartRate) o;
|
||||
return Float.compare(heartRate.value, value) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HeartRate{" +
|
||||
"value=" + value + " bpm" +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
@@ -267,31 +266,7 @@ public final class Marker {
|
||||
return photoUrl != null && !"".equals(photoUrl);
|
||||
}
|
||||
|
||||
public static class Id implements Parcelable {
|
||||
|
||||
private final long id;
|
||||
|
||||
public Id(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
//TOOD Limit visibility to TrackRecordingService / ContentProvider
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Id id1 = (Id) o;
|
||||
return id == id1.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
public record Id(long id) implements Parcelable {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@@ -309,7 +284,7 @@ public final class Marker {
|
||||
parcel.writeLong(id);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Id> CREATOR = new Parcelable.Creator<>() {
|
||||
public static final Creator<Id> CREATOR = new Creator<>() {
|
||||
public Id createFromParcel(Parcel in) {
|
||||
return new Id(in.readLong());
|
||||
}
|
||||
|
||||
@@ -1,43 +1,12 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Power {
|
||||
public record Power(float value) {
|
||||
|
||||
public static Power of(float value) {
|
||||
return new Power(value);
|
||||
}
|
||||
|
||||
private final float value;
|
||||
|
||||
private Power(float value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public float getW() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Power power = (Power) o;
|
||||
return Float.compare(power.value, value) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Power{" +
|
||||
"value=" + value + " W" +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
|
||||
public class Speed {
|
||||
public record Speed(double speed_mps) {
|
||||
|
||||
public static Speed of(Distance distance, Duration duration) {
|
||||
if (duration.isZero()) {
|
||||
@@ -47,12 +44,6 @@ public class Speed {
|
||||
return Speed.of(Math.abs(speed1.speed_mps - speed2.speed_mps));
|
||||
}
|
||||
|
||||
private final double speed_mps;
|
||||
|
||||
private Speed(double speed_mps) {
|
||||
this.speed_mps = speed_mps;
|
||||
}
|
||||
|
||||
public Speed mul(double factor) {
|
||||
return new Speed(factor * speed_mps);
|
||||
}
|
||||
@@ -117,29 +108,8 @@ public class Speed {
|
||||
public double to(UnitSystem unitSystem) {
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> toKMH();
|
||||
case IMPERIAL -> toMPH();
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> toMPH();
|
||||
case NAUTICAL_IMPERIAL -> toKnots();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Speed speed = (Speed) o;
|
||||
return Double.compare(speed.speed_mps, speed_mps) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(speed_mps);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Speed{" +
|
||||
"speed_mps=" + speed_mps +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class SpeedFormatter {
|
||||
int unitId = switch (unitSystem) {
|
||||
case METRIC ->
|
||||
reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
||||
case IMPERIAL ->
|
||||
case IMPERIAL_FEET, IMPERIAL_METER ->
|
||||
reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
||||
case NAUTICAL_IMPERIAL ->
|
||||
reportSpeedOrPace ? R.string.unit_knots : R.string.unit_minute_per_nautical_mile;
|
||||
|
||||
@@ -44,9 +44,9 @@ public class Track {
|
||||
|
||||
private String name = "";
|
||||
private String description = "";
|
||||
private String category = "";
|
||||
private String activityTypeLocalized = "";
|
||||
|
||||
private String icon = "";
|
||||
private ActivityType activityType;
|
||||
|
||||
private final ZoneOffset zoneOffset;
|
||||
|
||||
@@ -54,7 +54,7 @@ public class Track {
|
||||
|
||||
@VisibleForTesting
|
||||
public Track() {
|
||||
this.zoneOffset = ZoneOffset.UTC;
|
||||
this(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
public Track(@NonNull ZoneOffset zoneOffset) {
|
||||
@@ -97,20 +97,20 @@ public class Track {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
public String getActivityTypeLocalized() {
|
||||
return activityTypeLocalized;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
public void setActivityTypeLocalized(String activityType) {
|
||||
this.activityTypeLocalized = activityType;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
public ActivityType getActivityType() {
|
||||
return activityType;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
public void setActivityType(ActivityType activityType) {
|
||||
this.activityType = activityType;
|
||||
}
|
||||
|
||||
public ZoneOffset getZoneOffset() {
|
||||
|
||||
@@ -130,11 +130,11 @@ public class TrackPoint {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isSegmentStart() {
|
||||
return type == Type.SEGMENT_START_AUTOMATIC || type == Type.SEGMENT_START_MANUAL;
|
||||
public boolean isSegmentManualStart() {
|
||||
return type == Type.SEGMENT_START_MANUAL;
|
||||
}
|
||||
|
||||
public boolean isSegmentEnd() {
|
||||
public boolean isSegmentManualEnd() {
|
||||
return type == Type.SEGMENT_END_MANUAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public interface TracksColumns extends BaseColumns {
|
||||
String UUID = "uuid"; // identifier to make tracks globally unique (prevent re-import)
|
||||
String NAME = "name"; // track name
|
||||
String DESCRIPTION = "description"; // track description
|
||||
String CATEGORY = "category"; // track activity type
|
||||
String ACTIVITY_TYPE_LOCALIZED = "category"; // track activity type
|
||||
String STARTTIME = "starttime"; // track start time
|
||||
String STARTTIME_OFFSET = "starttime_offset"; // in plus/minus in seconds
|
||||
String STOPTIME = "stoptime"; // track stop time
|
||||
@@ -57,13 +57,13 @@ public interface TracksColumns extends BaseColumns {
|
||||
String MAX_ALTITUDE = "maxelevation"; // maximum altitude //TODO RENAME column
|
||||
String ALTITUDE_GAIN = "elevationgain"; // altitude gain //TODO RENAME column
|
||||
String ALTITUDE_LOSS = "elevationloss"; // altitude loss //TODO RENAME column
|
||||
String ICON = "icon"; // track activity type icon
|
||||
String ICON = "icon"; // track activity type icon //TODO DEPRECATED
|
||||
|
||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ NAME + " TEXT, "
|
||||
+ DESCRIPTION + " TEXT, "
|
||||
+ CATEGORY + " TEXT, "
|
||||
+ ACTIVITY_TYPE_LOCALIZED + " TEXT, "
|
||||
+ STARTTIME + " INTEGER, "
|
||||
+ STOPTIME + " INTEGER, "
|
||||
+ NUMPOINTS + " INTEGER, "
|
||||
|
||||
+45
-20
@@ -16,37 +16,62 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.databinding.ChooseActivityTypeBinding;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
|
||||
/**
|
||||
* A DialogFragment to choose an activity type.
|
||||
*/
|
||||
public class ChooseActivityTypeDialogFragment extends DialogFragment implements AdapterView.OnItemClickListener {
|
||||
|
||||
private static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType";
|
||||
|
||||
public static void showDialog(FragmentManager fragmentManager, String preselectedCategory) {
|
||||
new ChooseActivityTypeDialogFragment(preselectedCategory).show(fragmentManager, ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
@Deprecated
|
||||
public static void showDialog(FragmentManager fragmentManager, Context context, String activityTypeLocalized) {
|
||||
ActivityType activityType = ActivityType.findByLocalizedString(context, activityTypeLocalized);
|
||||
showDialog(fragmentManager, activityType);
|
||||
}
|
||||
|
||||
private static int getPosition(Context context, String category) {
|
||||
if (category == null) {
|
||||
public static void showDialog(FragmentManager fragmentManager, ActivityType activityType) {
|
||||
new ChooseActivityTypeDialogFragment(activityType).show(fragmentManager, ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
}
|
||||
|
||||
private static final List<ActivityType> activityTypes = List.of(
|
||||
ActivityType.UNKNOWN,
|
||||
ActivityType.AIRPLANE,
|
||||
ActivityType.BIKING,
|
||||
ActivityType.MOUNTAIN_BIKING,
|
||||
ActivityType.MOTOR_BIKE,
|
||||
ActivityType.KAYAKING,
|
||||
ActivityType.BOAT,
|
||||
ActivityType.SAILING,
|
||||
ActivityType.DRIVING,
|
||||
ActivityType.RUNNING,
|
||||
ActivityType.SNOW_BOARDING,
|
||||
ActivityType.SKIING,
|
||||
ActivityType.WALKING,
|
||||
ActivityType.ESCOOTER,
|
||||
ActivityType.INLINE_SKATING,
|
||||
ActivityType.SKATE_BOARDING,
|
||||
ActivityType.CLIMBING,
|
||||
ActivityType.SWIMMING,
|
||||
ActivityType.SWIMMING_OPEN,
|
||||
ActivityType.WORKOUT
|
||||
);
|
||||
|
||||
private static int getPosition(Context context, ActivityType activityType) {
|
||||
if (activityType == null) {
|
||||
return -1;
|
||||
}
|
||||
String iconValue = TrackIconUtils.getIconValue(context, category);
|
||||
|
||||
return TrackIconUtils.getAllIconValues().indexOf(iconValue);
|
||||
return activityTypes.indexOf(activityType);
|
||||
}
|
||||
|
||||
private ChooseActivityTypeBinding viewBinding;
|
||||
|
||||
private final String preselectedCategory;
|
||||
private final ActivityType preselectedActivityType;
|
||||
|
||||
private ChooseActivityTypeCaller chooseActivityTypeCaller;
|
||||
|
||||
private ChooseActivityTypeDialogFragment(String preselectedCategory) {
|
||||
this.preselectedCategory = preselectedCategory;
|
||||
private ChooseActivityTypeDialogFragment(ActivityType activityType) {
|
||||
this.preselectedActivityType = activityType;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -61,13 +86,13 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment implements
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
viewBinding = ChooseActivityTypeBinding.inflate(inflater, container, false);
|
||||
|
||||
List<Integer> imageIds = new ArrayList<>();
|
||||
for (String iconValue : TrackIconUtils.getAllIconValues()) {
|
||||
imageIds.add(TrackIconUtils.getIconDrawable(iconValue));
|
||||
List<Integer> iconDrawableIds = new ArrayList<>();
|
||||
for (ActivityType activityType : activityTypes) {
|
||||
iconDrawableIds.add(activityType.getIconDrawableId());
|
||||
}
|
||||
|
||||
final ChooseActivityTypeImageAdapter imageAdapter = new ChooseActivityTypeImageAdapter(imageIds);
|
||||
int position = getPosition(getContext(), preselectedCategory);
|
||||
final ChooseActivityTypeImageAdapter imageAdapter = new ChooseActivityTypeImageAdapter(iconDrawableIds);
|
||||
int position = getPosition(getContext(), preselectedActivityType);
|
||||
if (position != -1) {
|
||||
imageAdapter.setSelected(position);
|
||||
}
|
||||
@@ -94,7 +119,7 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment implements
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||
chooseActivityTypeCaller.onChooseActivityTypeDone(TrackIconUtils.getAllIconValues().get(position));
|
||||
chooseActivityTypeCaller.onChooseActivityTypeDone(activityTypes.get(position));
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@@ -103,6 +128,6 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment implements
|
||||
*/
|
||||
public interface ChooseActivityTypeCaller {
|
||||
|
||||
void onChooseActivityTypeDone(String iconValue);
|
||||
void onChooseActivityTypeDone(ActivityType activityType);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||
}
|
||||
|
||||
if (position == selected) {
|
||||
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.opentracks_secondary_color));
|
||||
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.opentracks));
|
||||
} else {
|
||||
imageView.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package de.dennisguse.opentracks.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
@@ -28,12 +29,11 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -43,7 +43,6 @@ import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
|
||||
/**
|
||||
* A fragment to display track statistics to the user for a recorded {@link Track}.
|
||||
@@ -89,7 +88,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
if (PreferencesUtils.isKey(R.string.stats_rate_key, key) && track != null) {
|
||||
updateUInecessary = true;
|
||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(track.getCategory());
|
||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(track);
|
||||
}
|
||||
|
||||
if (key != null && updateUInecessary && isResumed()) {
|
||||
@@ -113,10 +112,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
viewBinding = StatisticsRecordedBinding.inflate(inflater, container, false);
|
||||
|
||||
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
||||
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
||||
// sensorsRecyclerView.setAdapter(sensorsAdapter);
|
||||
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@@ -155,7 +150,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||
|
||||
boolean prefsChanged = this.track == null || (!this.track.getCategory().equals(track.getCategory()));
|
||||
boolean prefsChanged = this.track == null || (!this.track.getActivityTypeLocalized().equals(track.getActivityTypeLocalized()));
|
||||
this.track = track;
|
||||
if (prefsChanged) {
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
||||
@@ -191,8 +186,12 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
// Set activity type
|
||||
{
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), track.getCategory());
|
||||
viewBinding.statsActivityTypeIcon.setImageDrawable(ContextCompat.getDrawable(getContext(), TrackIconUtils.getIconDrawable(trackIconValue)));
|
||||
Context context = getContext();
|
||||
String localizedActivityType = track.getActivityTypeLocalized();
|
||||
String trackIconValue = ActivityType.findByLocalizedString(context, localizedActivityType)
|
||||
.getIconId();
|
||||
viewBinding.statsActivityTypeIcon.setImageDrawable(ContextCompat.getDrawable(getContext(), ActivityType.findBy(trackIconValue)
|
||||
.getIconDrawableId()));
|
||||
}
|
||||
|
||||
// Set time and start datetime
|
||||
@@ -253,6 +252,28 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
if (sensorStatistics == null) {
|
||||
return;
|
||||
}
|
||||
//TODO
|
||||
|
||||
if (sensorStatistics.hasHeartRate()) {
|
||||
String maxBPM = String.valueOf(Math.round(sensorStatistics.getMaxHeartRate().getBPM()));
|
||||
String avgBPM = String.valueOf(Math.round(sensorStatistics.getAvgHeartRate().getBPM()));
|
||||
|
||||
viewBinding.statsHeartRateGroup.setVisibility(View.VISIBLE);
|
||||
viewBinding.statsMaxHeartRateValue.setText(maxBPM);
|
||||
viewBinding.statsAvgHeartRateValue.setText(avgBPM);
|
||||
}
|
||||
if (sensorStatistics.hasCadence()) {
|
||||
String maxRPM = String.valueOf(Math.round(sensorStatistics.getMaxCadence().getRPM()));
|
||||
String avgRPM = String.valueOf(Math.round(sensorStatistics.getAvgCadence().getRPM()));
|
||||
|
||||
viewBinding.statsCadenceGroup.setVisibility(View.VISIBLE);
|
||||
viewBinding.statsMaxCadenceValue.setText(maxRPM);
|
||||
viewBinding.statsAvgCadenceValue.setText(avgRPM);
|
||||
}
|
||||
if (sensorStatistics.hasPower()) {
|
||||
String W = String.valueOf(Math.round(sensorStatistics.getAvgPower().getW()));
|
||||
|
||||
viewBinding.statsPowerGroup.setVisibility(View.VISIBLE);
|
||||
viewBinding.statsPowerValue.setText(W);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,9 +122,8 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
}
|
||||
recordingLayout = newRecordingLayout;
|
||||
|
||||
viewBinding.statsLayout.setColumnCount(recordingLayout.getColumnsPerRow());
|
||||
|
||||
viewBinding.statsLayout.removeAllViews(); //Let's start from scratch
|
||||
viewBinding.statsLayout.setColumnCount(recordingLayout.getColumnsPerRow());
|
||||
viewHolders.clear();
|
||||
|
||||
Map<String, Callable<StatisticViewHolder<?>>> m = Mapping.create(getContext());
|
||||
|
||||
@@ -20,15 +20,19 @@ public class TrackFilenameGenerator {
|
||||
|
||||
public static final String UUID_KEY = "{uuid}";
|
||||
public static final String TRACKNAME_KEY = "{name}";
|
||||
public static final String CATEGORY_KEY = "{category}";
|
||||
public static final String ACTIVITY_TYPE_KEY = "{category}";
|
||||
public static final String STARTTIME_TIME_KEY = "{time}";
|
||||
public static final String STARTTIME_DATE_KEY = "{date}";
|
||||
|
||||
public static String getAllOptions() {
|
||||
return Stream.of(UUID_KEY, TRACKNAME_KEY, CATEGORY_KEY, STARTTIME_TIME_KEY, STARTTIME_DATE_KEY)
|
||||
return Stream.of(UUID_KEY, TRACKNAME_KEY, ACTIVITY_TYPE_KEY, STARTTIME_TIME_KEY, STARTTIME_DATE_KEY)
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
public static String format(@NonNull String name, @NonNull TrackFileFormat trackFileFormat) {
|
||||
return FileUtils.sanitizeFileName(name + "." + trackFileFormat.getExtension());
|
||||
}
|
||||
|
||||
private final String template;
|
||||
|
||||
public TrackFilenameGenerator(@NonNull String template) {
|
||||
@@ -40,11 +44,11 @@ public class TrackFilenameGenerator {
|
||||
|
||||
values.put(UUID_KEY, track.getUuid().toString().substring(0, 8));
|
||||
values.put(TRACKNAME_KEY, track.getName());
|
||||
values.put(CATEGORY_KEY, track.getCategory());
|
||||
values.put(ACTIVITY_TYPE_KEY, track.getActivityTypeLocalized());
|
||||
values.put(STARTTIME_TIME_KEY, track.getStartTime().toLocalTime().toString());
|
||||
values.put(STARTTIME_DATE_KEY, track.getStartTime().toLocalDate().toString());
|
||||
|
||||
return FileUtils.sanitizeFileName(format(template, values)) + "." + trackFileFormat.getExtension();
|
||||
return format(format(template, values), trackFileFormat);
|
||||
}
|
||||
|
||||
private static String format(String template, Map<String, String> values) {
|
||||
@@ -77,10 +81,6 @@ public class TrackFilenameGenerator {
|
||||
return String.format(templateCompiled, valueList.toArray());
|
||||
}
|
||||
|
||||
public String getTemplate() {
|
||||
return template;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
try {
|
||||
getExample();
|
||||
|
||||
@@ -23,8 +23,6 @@ import androidx.annotation.NonNull;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
@@ -68,6 +66,9 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
SPEED_FORMAT.setMaximumFractionDigits(2);
|
||||
SPEED_FORMAT.setGroupingUsed(false);
|
||||
|
||||
DISTANCE_FORMAT.setMaximumFractionDigits(0);
|
||||
DISTANCE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
HEARTRATE_FORMAT.setMaximumFractionDigits(0);
|
||||
HEARTRATE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
@@ -87,15 +88,23 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
||||
return writeTrack(new Track[]{track}, outputStream);
|
||||
}
|
||||
public boolean writeTrack(@NonNull List<Track> tracks, @NonNull OutputStream outputStream) {
|
||||
List<Column> columns = List.of(
|
||||
new Column("time", null),
|
||||
new Column("trackpoint_type", t -> quote(t.getType().name())),
|
||||
new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLatitude()) : ""),
|
||||
new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLongitude()) : ""),
|
||||
new Column("altitude", t -> t.hasAltitude() ? COORDINATE_FORMAT.format(t.getAltitude().toM()) : ""),
|
||||
new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""),
|
||||
new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
||||
|
||||
@Override
|
||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
||||
List<Column> columns = Collections.unmodifiableList(Arrays.asList(new Column("time", null), new Column("trackpoint_type", t -> quote(t.getType().name())), new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLatitude()) : ""), new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLongitude()) : ""), new Column("altitude", t -> t.hasAltitude() ? COORDINATE_FORMAT.format(t.getAltitude().toM()) : ""), new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""), new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
||||
|
||||
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""), new Column("altitude_gain", t -> t.hasAltitudeGain() ? DISTANCE_FORMAT.format(t.getAltitudeGain()) : ""), new Column("altitude_loss", t -> t.hasAltitudeLoss() ? DISTANCE_FORMAT.format(t.getAltitudeLoss()) : ""), new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""), new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""), new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""), new Column("power", t -> t.hasPower() ? ALTITUDE_FORMAT.format(t.getPower().getW()) : "")));
|
||||
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""),
|
||||
new Column("altitude_gain", t -> t.hasAltitudeGain() ? DISTANCE_FORMAT.format(t.getAltitudeGain()) : ""),
|
||||
new Column("altitude_loss", t -> t.hasAltitudeLoss() ? DISTANCE_FORMAT.format(t.getAltitudeLoss()) : ""),
|
||||
new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""),
|
||||
new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""),
|
||||
new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""),
|
||||
new Column("power", t -> t.hasPower() ? ALTITUDE_FORMAT.format(t.getPower().getW()) : ""));
|
||||
|
||||
try {
|
||||
prepare(outputStream);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -24,6 +23,7 @@ import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
@@ -31,14 +31,15 @@ import androidx.fragment.app.FragmentActivity;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||
import de.dennisguse.opentracks.databinding.ExportActivityBinding;
|
||||
import de.dennisguse.opentracks.io.file.ErrorListDialog;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.ExportUtils;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
@@ -47,12 +48,20 @@ import de.dennisguse.opentracks.util.FileUtils;
|
||||
* An activity for saving tracks to the external storage.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
* TODO: This class needs some refactoring.
|
||||
* * It pushes each export job (usually one Track) for export; although export could be run in parallel.
|
||||
* Also this requires that the ExportActivity stays in foreground, so the user has to activily wait.
|
||||
* It would be better to let the ExportService handle this and let it report progress / conflicts to ExportActivity
|
||||
* * File name conflicts are checked in this class instead of the ExportService.
|
||||
* So, for this check actually a different file name might be used than in the ExportService.
|
||||
* * Saved state as an object instead of individual values.
|
||||
*/
|
||||
public class ExportActivity extends FragmentActivity implements ExportServiceResultReceiver.Receiver {
|
||||
public class ExportActivity extends FragmentActivity implements ExportService.ExportServiceResultReceiver.Receiver {
|
||||
|
||||
private static final String TAG = ExportActivity.class.getSimpleName();
|
||||
|
||||
public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
|
||||
public static final String EXTRA_ONE_FILE_KEY = "one_file";
|
||||
public static final String EXTRA_TRACKFILEFORMAT_KEY = "trackfileformat";
|
||||
|
||||
private static final String BUNDLE_AUTO_CONFLICT = "auto_conflict";
|
||||
@@ -64,14 +73,18 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
private static final String BUNDLE_DIRECTORY_FILES = "track_directory_files";
|
||||
private static final String BUNDLE_TRACK_ERRORS = "track_errors";
|
||||
|
||||
private static final int CONFLICT_NONE = 0;
|
||||
private static final int CONFLICT_OVERWRITE = 1;
|
||||
private static final int CONFLICT_SKIP = 2;
|
||||
private static final String BUNDLE_EXPORT_TASKS = "export_tasks";
|
||||
|
||||
private enum ConflictResolutionStrategy {
|
||||
CONFLICT_NONE,
|
||||
CONFLICT_OVERWRITE,
|
||||
CONFLICT_SKIP
|
||||
}
|
||||
|
||||
private TrackFileFormat trackFileFormat;
|
||||
private Uri directoryUri;
|
||||
|
||||
private ExportServiceResultReceiver resultReceiver;
|
||||
private ExportService.ExportServiceResultReceiver resultReceiver;
|
||||
|
||||
private List<String> directoryFiles;
|
||||
|
||||
@@ -87,12 +100,12 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
|
||||
private ArrayList<String> trackErrors = new ArrayList<>();
|
||||
|
||||
private int autoConflict;
|
||||
private ConflictResolutionStrategy autoConflict;
|
||||
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
// List of tracks to be exported.
|
||||
private final ArrayList<Track> tracks = new ArrayList<>();
|
||||
private ArrayList<ExportTask> exportTasks;
|
||||
|
||||
private final LinkedBlockingQueue<PendingConflict> conflictsQueue = new LinkedBlockingQueue<>();
|
||||
private final Handler conflictsHandler = new Handler();
|
||||
@@ -100,34 +113,36 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
private final Runnable conflictsRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (conflictsQueue.size() > 0) {
|
||||
PendingConflict conflict = conflictsQueue.peek();
|
||||
if (conflict.resolve()) {
|
||||
conflictsQueue.remove(conflict);
|
||||
if (!conflictsQueue.isEmpty()) {
|
||||
conflictsHandler.post(conflictsRunnable);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
viewBinding.exportProgressLeftButton.setOnClickListener((view) -> {
|
||||
setConflictVisibility(View.GONE);
|
||||
conflict.skip();
|
||||
conflictsQueue.remove(conflict);
|
||||
if (!conflictsQueue.isEmpty()) {
|
||||
conflictsHandler.post(conflictsRunnable);
|
||||
}
|
||||
});
|
||||
|
||||
viewBinding.exportProgressRightButton.setOnClickListener((view) -> {
|
||||
setConflictVisibility(View.GONE);
|
||||
conflict.overwrite();
|
||||
conflictsQueue.remove(conflict);
|
||||
if (!conflictsQueue.isEmpty()) {
|
||||
conflictsHandler.post(conflictsRunnable);
|
||||
}
|
||||
});
|
||||
if (conflictsQueue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PendingConflict conflict = conflictsQueue.peek();
|
||||
if (conflict.resolve()) {
|
||||
conflictsQueue.remove(conflict);
|
||||
if (!conflictsQueue.isEmpty()) {
|
||||
conflictsHandler.post(conflictsRunnable);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
viewBinding.exportProgressLeftButton.setOnClickListener((view) -> {
|
||||
setConflictVisibility(View.GONE);
|
||||
conflict.skip();
|
||||
conflictsQueue.remove(conflict);
|
||||
if (!conflictsQueue.isEmpty()) {
|
||||
conflictsHandler.post(conflictsRunnable);
|
||||
}
|
||||
});
|
||||
|
||||
viewBinding.exportProgressRightButton.setOnClickListener((view) -> {
|
||||
setConflictVisibility(View.GONE);
|
||||
conflict.overwrite();
|
||||
conflictsQueue.remove(conflict);
|
||||
if (!conflictsQueue.isEmpty()) {
|
||||
conflictsHandler.post(conflictsRunnable);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -139,23 +154,27 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
|
||||
directoryUri = getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY);
|
||||
trackFileFormat = (TrackFileFormat) getIntent().getSerializableExtra(EXTRA_TRACKFILEFORMAT_KEY);
|
||||
boolean allInOneFile = getIntent().getBooleanExtra(EXTRA_ONE_FILE_KEY, false);
|
||||
|
||||
contentProviderUtils = new ContentProviderUtils(this);
|
||||
|
||||
DocumentFile documentFile = DocumentFile.fromTreeUri(this, directoryUri);
|
||||
String directoryDisplayName = FileUtils.getPath(documentFile);
|
||||
|
||||
resultReceiver = new ExportServiceResultReceiver(new Handler(), this);
|
||||
resultReceiver = new ExportService.ExportServiceResultReceiver(new Handler(), this);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
autoConflict = CONFLICT_NONE;
|
||||
autoConflict = ConflictResolutionStrategy.CONFLICT_NONE;
|
||||
setProgress();
|
||||
new Thread(() -> {
|
||||
directoryFiles = ExportUtils.getAllFiles(ExportActivity.this, documentFile.getUri());
|
||||
runOnUiThread(() -> initExport(0));
|
||||
runOnUiThread(() -> {
|
||||
createExportTasks(allInOneFile);
|
||||
nextExport(null);
|
||||
});
|
||||
}).start();
|
||||
} else {
|
||||
autoConflict = savedInstanceState.getInt(BUNDLE_AUTO_CONFLICT);
|
||||
autoConflict = ConflictResolutionStrategy.valueOf(savedInstanceState.getString(BUNDLE_AUTO_CONFLICT));
|
||||
trackExportSuccessCount = savedInstanceState.getInt(BUNDLE_SUCCESS_COUNT);
|
||||
trackExportErrorCount = savedInstanceState.getInt(BUNDLE_ERROR_COUNT);
|
||||
trackExportOverwrittenCount = savedInstanceState.getInt(BUNDLE_OVERWRITTEN_COUNT);
|
||||
@@ -163,19 +182,19 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
trackExportTotalCount = savedInstanceState.getInt(BUNDLE_TOTAL_COUNT);
|
||||
directoryFiles = savedInstanceState.getStringArrayList(BUNDLE_DIRECTORY_FILES);
|
||||
trackErrors = savedInstanceState.getStringArrayList(BUNDLE_TRACK_ERRORS);
|
||||
|
||||
exportTasks = new ArrayList<>(savedInstanceState.getParcelableArrayList(BUNDLE_EXPORT_TASKS));
|
||||
setProgress();
|
||||
initExport(getTotalDone());
|
||||
nextExport(null);
|
||||
}
|
||||
|
||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(getString(R.string.export_progress_message, directoryDisplayName));
|
||||
viewBinding.exportActivityToolbar.setTitle(getString(R.string.export_progress_message, directoryDisplayName));
|
||||
viewBinding.bottomAppBarLayout.bottomAppBar.setNavigationIcon(R.drawable.ic_logo_color_24dp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt(BUNDLE_AUTO_CONFLICT, autoConflict);
|
||||
outState.putString(BUNDLE_AUTO_CONFLICT, autoConflict.name());
|
||||
outState.putInt(BUNDLE_SUCCESS_COUNT, trackExportSuccessCount);
|
||||
outState.putInt(BUNDLE_ERROR_COUNT, trackExportErrorCount);
|
||||
outState.putInt(BUNDLE_OVERWRITTEN_COUNT, trackExportOverwrittenCount);
|
||||
@@ -183,13 +202,14 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
outState.putInt(BUNDLE_TOTAL_COUNT, trackExportTotalCount);
|
||||
outState.putStringArrayList(BUNDLE_DIRECTORY_FILES, (ArrayList<String>) directoryFiles);
|
||||
outState.putStringArrayList(BUNDLE_TRACK_ERRORS, trackErrors);
|
||||
outState.putParcelableArrayList(BUNDLE_EXPORT_TASKS, exportTasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
conflictsQueue.clear();
|
||||
tracks.clear();
|
||||
exportTasks.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,57 +222,50 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
this.doubleBackToCancel = true;
|
||||
Toast.makeText(this, getString(R.string.generic_click_twice_cancel), Toast.LENGTH_SHORT).show();
|
||||
|
||||
new Handler().postDelayed(() -> doubleBackToCancel=false, 2000);
|
||||
new Handler().postDelayed(() -> doubleBackToCancel = false, 2000);
|
||||
}
|
||||
|
||||
private void initExport(int from) {
|
||||
try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
|
||||
if (cursor == null) {
|
||||
onExportEnded();
|
||||
return;
|
||||
}
|
||||
|
||||
trackExportTotalCount = cursor.getCount();
|
||||
viewBinding.exportProgressTotal.setText("" + trackExportTotalCount);
|
||||
for (int i = from; i < trackExportTotalCount; i++) {
|
||||
cursor.moveToPosition(i);
|
||||
Track track = ContentProviderUtils.createTrack(cursor);
|
||||
tracks.add(track);
|
||||
}
|
||||
|
||||
if (!tracks.isEmpty()) {
|
||||
export(tracks.get(0));
|
||||
} else {
|
||||
onExportEnded();
|
||||
}
|
||||
private void createExportTasks(boolean allInOneFile) {
|
||||
List<Track> tracks = contentProviderUtils.getTracks();
|
||||
exportTasks = new ArrayList<>();
|
||||
if (allInOneFile) {
|
||||
String filename = "OpenTracks-Backup";
|
||||
exportTasks.add(new ExportTask(filename, trackFileFormat, tracks.stream().map(Track::getId).collect(Collectors.toList())));
|
||||
} else {
|
||||
exportTasks.addAll(tracks.stream().map(it -> new ExportTask(null, trackFileFormat, List.of(it.getId()))).collect(Collectors.toList()));
|
||||
}
|
||||
trackExportTotalCount = exportTasks.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue track identified by UUID to be exported if not exported already or there is a conflict resolution.
|
||||
*
|
||||
* @param track Track object.
|
||||
* @param conflictResolution conflict resolution to be applied if needed.
|
||||
*/
|
||||
private void export(Track track, int conflictResolution) {
|
||||
boolean fileExists = exportFileExists(track);
|
||||
private void export(ExportTask exportTask, ConflictResolutionStrategy conflictResolution) {
|
||||
boolean fileExists = exportFileExists(exportTask);
|
||||
|
||||
if (fileExists && conflictResolution == CONFLICT_NONE) {
|
||||
conflict(track);
|
||||
} else if (fileExists && conflictResolution == CONFLICT_SKIP) {
|
||||
if (fileExists && conflictResolution == ConflictResolutionStrategy.CONFLICT_NONE) {
|
||||
conflict(exportTask);
|
||||
} else if (fileExists && conflictResolution == ConflictResolutionStrategy.CONFLICT_SKIP) {
|
||||
trackExportSkippedCount++;
|
||||
onExportCompleted(track);
|
||||
nextExport(exportTask);
|
||||
} else {
|
||||
ExportService.enqueue(this, resultReceiver, track.getId(), trackFileFormat, directoryUri);
|
||||
ExportService.enqueue(this, resultReceiver, exportTask, directoryUri);
|
||||
}
|
||||
}
|
||||
|
||||
private void export(Track track) {
|
||||
export(track, autoConflict);
|
||||
private void export(ExportTask exportTask) {
|
||||
export(exportTask, autoConflict);
|
||||
}
|
||||
|
||||
private boolean exportFileExists(Track track) {
|
||||
String filename = PreferencesUtils.getTrackFileformatGenerator().format(track, trackFileFormat);
|
||||
@Deprecated //TODO Check should be done in ExportService
|
||||
private boolean exportFileExists(ExportTask exportTask) {
|
||||
String filename;
|
||||
if (exportTask.isMultiExport()) {
|
||||
filename = TrackFilenameGenerator.format(exportTask.getFilename(), exportTask.getTrackFileFormat());
|
||||
} else {
|
||||
Track track = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0));
|
||||
filename = PreferencesUtils.getTrackFileformatGenerator().format(track, trackFileFormat);
|
||||
}
|
||||
return directoryFiles.stream().anyMatch(filename::equals);
|
||||
}
|
||||
|
||||
@@ -269,31 +282,29 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
}
|
||||
|
||||
private void setProgress() {
|
||||
int done = getTotalDone();
|
||||
|
||||
viewBinding.exportProgressDone.setText("" + done);
|
||||
viewBinding.exportProgressDone.setText("" + getTotalDone());
|
||||
viewBinding.exportProgressTotal.setText("" + trackExportTotalCount);
|
||||
|
||||
viewBinding.exportProgressBar.setProgress((int) ((float) done / (float) trackExportTotalCount * 100f));
|
||||
viewBinding.exportProgressSummaryNew.setText(String.valueOf(trackExportSuccessCount));
|
||||
viewBinding.exportProgressSummaryOverwrite.setText(String.valueOf(trackExportOverwrittenCount));
|
||||
viewBinding.exportProgressSummarySkip.setText(String.valueOf(trackExportSkippedCount));
|
||||
viewBinding.exportProgressSummaryErrors.setText(String.valueOf(trackExportErrorCount));
|
||||
viewBinding.exportProgressBar.setProgress((int) ((float) getTotalDone() / (float) trackExportTotalCount * 100f));
|
||||
viewBinding.exportProgressSummaryNew.setText("" + trackExportSuccessCount);
|
||||
viewBinding.exportProgressSummaryOverwrite.setText("" + trackExportOverwrittenCount);
|
||||
viewBinding.exportProgressSummarySkip.setText("" + trackExportSkippedCount);
|
||||
viewBinding.exportProgressSummaryErrors.setText("" + trackExportErrorCount);
|
||||
viewBinding.exportProgressSummaryNewGroup.setVisibility(trackExportSuccessCount > 0 ? View.VISIBLE : View.GONE);
|
||||
viewBinding.exportProgressSummaryOverwriteGroup.setVisibility(trackExportOverwrittenCount > 0 ? View.VISIBLE : View.GONE);
|
||||
viewBinding.exportProgressSummarySkipGroup.setVisibility(trackExportSkippedCount > 0 ? View.VISIBLE : View.GONE);
|
||||
viewBinding.exportProgressSummaryErrorsGroup.setVisibility(trackExportErrorCount > 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void onExportCompleted(Track track) {
|
||||
tracks.remove(track);
|
||||
private void nextExport(@Nullable ExportTask exportTask) {
|
||||
exportTasks.remove(exportTask);
|
||||
|
||||
setProgress();
|
||||
if (tracks.isEmpty()) {
|
||||
if (exportTasks.isEmpty()) {
|
||||
onExportEnded();
|
||||
return;
|
||||
}
|
||||
export(tracks.get(0));
|
||||
export(exportTasks.get(0));
|
||||
}
|
||||
|
||||
private void onExportEnded() {
|
||||
@@ -318,30 +329,32 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExportSuccess(Track.Id trackId) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
if (exportFileExists(track)) {
|
||||
public void onExportSuccess(ExportTask exportTask) {
|
||||
if (exportFileExists(exportTask)) {
|
||||
trackExportOverwrittenCount++;
|
||||
} else {
|
||||
trackExportSuccessCount++;
|
||||
}
|
||||
|
||||
onExportCompleted(track);
|
||||
nextExport(exportTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExportError(Track.Id trackId) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
public void onExportError(ExportTask exportTask) {
|
||||
trackExportErrorCount++;
|
||||
trackErrors.add(track.getName());
|
||||
String name;
|
||||
if (exportTask.isMultiExport()) {
|
||||
name = exportTask.getFilename();
|
||||
} else {
|
||||
name = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0)).getName();
|
||||
}
|
||||
trackErrors.add(name);
|
||||
|
||||
onExportCompleted(track);
|
||||
nextExport(exportTask);
|
||||
}
|
||||
|
||||
private void conflict(Track track) {
|
||||
PendingConflict newConflict = new PendingConflict(track);
|
||||
private void conflict(ExportTask exportTask) {
|
||||
PendingConflict newConflict = new PendingConflict(exportTask);
|
||||
conflictsQueue.add(newConflict);
|
||||
|
||||
if (conflictsQueue.size() == 1) {
|
||||
@@ -353,10 +366,10 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
* Handle conflicts (exporting file already exists).
|
||||
*/
|
||||
private class PendingConflict {
|
||||
private final Track track;
|
||||
private final ExportTask exportTask;
|
||||
|
||||
public PendingConflict(Track track) {
|
||||
this.track = track;
|
||||
public PendingConflict(ExportTask exportTask) {
|
||||
this.exportTask = exportTask;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -366,14 +379,20 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
* @return true if it could resolve the conflict or false otherwise.
|
||||
*/
|
||||
public boolean resolve() {
|
||||
if (autoConflict == CONFLICT_NONE) {
|
||||
if (autoConflict == ConflictResolutionStrategy.CONFLICT_NONE) {
|
||||
viewBinding.exportProgressAlertIcon.setImageDrawable(ContextCompat.getDrawable(ExportActivity.this, R.drawable.ic_report_problem_24));
|
||||
viewBinding.exportProgressAlertMsg.setText(getString(R.string.export_track_already_exists_msg, track.getName()));
|
||||
String name;
|
||||
if (exportTask.isMultiExport()) {
|
||||
name = exportTask.getFilename();
|
||||
} else {
|
||||
name = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0)).getName();
|
||||
}
|
||||
viewBinding.exportProgressAlertMsg.setText(getString(R.string.export_track_already_exists_msg, name));
|
||||
setConflictVisibility(View.VISIBLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
export(track);
|
||||
export(exportTask);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -381,10 +400,10 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
* Overwrite the export file and set the autoConflict if user set the "do it for all" switch button.
|
||||
*/
|
||||
public void overwrite() {
|
||||
export(track, CONFLICT_OVERWRITE);
|
||||
export(exportTask, ConflictResolutionStrategy.CONFLICT_OVERWRITE);
|
||||
|
||||
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
||||
autoConflict = CONFLICT_OVERWRITE;
|
||||
autoConflict = ConflictResolutionStrategy.CONFLICT_OVERWRITE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,10 +411,10 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
* Skip the export file and set the autoConflict if user set the "do it for all" switch button.
|
||||
*/
|
||||
public void skip() {
|
||||
export(track, CONFLICT_SKIP);
|
||||
export(exportTask, ConflictResolutionStrategy.CONFLICT_SKIP);
|
||||
|
||||
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
||||
autoConflict = CONFLICT_SKIP;
|
||||
autoConflict = ConflictResolutionStrategy.CONFLICT_SKIP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.ResultReceiver;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -12,9 +13,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.core.app.JobIntentService;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.ExportUtils;
|
||||
|
||||
public class ExportService extends JobIntentService {
|
||||
@@ -22,16 +20,14 @@ public class ExportService extends JobIntentService {
|
||||
private static final int JOB_ID = 1;
|
||||
|
||||
private static final String EXTRA_RECEIVER = "extra_receiver";
|
||||
private static final String EXTRA_TRACK_ID = "extra_track_id";
|
||||
private static final String EXTRA_TRACK_FILE_FORMAT = "extra_track_file_format";
|
||||
private static final String EXTRA_EXPORT_TASK = "export_task";
|
||||
private static final String EXTRA_DIRECTORY_URI = "extra_directory_uri";
|
||||
private static final String TAG = ExportService.class.getSimpleName();
|
||||
|
||||
public static void enqueue(Context context, ExportServiceResultReceiver receiver, Track.Id trackId, TrackFileFormat trackFileFormat, Uri directoryUri) {
|
||||
public static void enqueue(Context context, ExportServiceResultReceiver receiver, ExportTask exportTask, Uri directoryUri) {
|
||||
Intent intent = new Intent(context, JobService.class);
|
||||
intent.putExtra(EXTRA_RECEIVER, receiver);
|
||||
intent.putExtra(EXTRA_TRACK_ID, trackId);
|
||||
intent.putExtra(EXTRA_TRACK_FILE_FORMAT, trackFileFormat);
|
||||
intent.putExtra(EXTRA_EXPORT_TASK, exportTask);
|
||||
intent.putExtra(EXTRA_DIRECTORY_URI, directoryUri);
|
||||
enqueueWork(context, ExportService.class, JOB_ID, intent);
|
||||
}
|
||||
@@ -40,13 +36,12 @@ public class ExportService extends JobIntentService {
|
||||
protected void onHandleWork(@NonNull Intent intent) {
|
||||
// Get all data.
|
||||
ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RECEIVER);
|
||||
Track.Id trackId = intent.getParcelableExtra(EXTRA_TRACK_ID);
|
||||
TrackFileFormat trackFileFormat = (TrackFileFormat) intent.getSerializableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||
ExportTask exportTask = intent.getParcelableExtra(EXTRA_EXPORT_TASK);
|
||||
Uri directoryUri = intent.getParcelableExtra(EXTRA_DIRECTORY_URI);
|
||||
|
||||
// Prepare resultCode and bundle to send to the receiver.
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID, trackId);
|
||||
bundle.putParcelable(ExportServiceResultReceiver.RESULT_EXTRA_EXPORT_TASK, exportTask);
|
||||
|
||||
// Build directory file.
|
||||
DocumentFile directoryFile = DocumentFile.fromTreeUri(this, directoryUri);
|
||||
@@ -57,12 +52,43 @@ public class ExportService extends JobIntentService {
|
||||
}
|
||||
|
||||
// Export.
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
boolean success = ExportUtils.exportTrack(this, trackFileFormat, directoryFile, track);
|
||||
boolean success = ExportUtils.exportTrack(this, directoryFile, exportTask);
|
||||
|
||||
// Send result to the receiver.
|
||||
int resultCode = success ? ExportServiceResultReceiver.RESULT_CODE_SUCCESS : ExportServiceResultReceiver.RESULT_CODE_ERROR;
|
||||
resultReceiver.send(resultCode, bundle);
|
||||
}
|
||||
|
||||
public static class ExportServiceResultReceiver extends ResultReceiver {
|
||||
|
||||
public static final int RESULT_CODE_SUCCESS = 1;
|
||||
public static final int RESULT_CODE_ERROR = 0;
|
||||
|
||||
public static final String RESULT_EXTRA_EXPORT_TASK = "result_extra_export_task";
|
||||
|
||||
private final Receiver receiver;
|
||||
|
||||
public ExportServiceResultReceiver(Handler handler, @NonNull Receiver receiver) {
|
||||
super(handler);
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||
ExportTask exportTask = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_EXPORT_TASK);
|
||||
switch (resultCode) {
|
||||
case RESULT_CODE_SUCCESS -> receiver.onExportSuccess(exportTask);
|
||||
case RESULT_CODE_ERROR -> receiver.onExportError(exportTask);
|
||||
default -> throw new RuntimeException("Unknown resultCode.");
|
||||
}
|
||||
}
|
||||
|
||||
public interface Receiver {
|
||||
default void onExportSuccess(ExportTask exportTask) {
|
||||
}
|
||||
|
||||
default void onExportError(ExportTask exportTask) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.ResultReceiver;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
|
||||
/**
|
||||
* Create a new ResultReceive to receive results.
|
||||
* Your {@link #onReceiveResult} method will be called from the thread running <var>handler</var> if given, or from an arbitrary thread if null.
|
||||
*/
|
||||
public class ExportServiceResultReceiver extends ResultReceiver {
|
||||
|
||||
public static final int RESULT_CODE_SUCCESS = 1;
|
||||
public static final int RESULT_CODE_ERROR = 0;
|
||||
|
||||
public static final String RESULT_EXTRA_TRACK_ID = "result_extra_track_id";
|
||||
|
||||
private final Receiver receiver;
|
||||
|
||||
public ExportServiceResultReceiver(Handler handler, @NonNull Receiver receiver) {
|
||||
super(handler);
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||
Track.Id trackId = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
||||
switch (resultCode) {
|
||||
case RESULT_CODE_SUCCESS -> receiver.onExportSuccess(trackId);
|
||||
case RESULT_CODE_ERROR -> receiver.onExportError(trackId);
|
||||
default -> throw new RuntimeException("Unknown resultCode.");
|
||||
}
|
||||
}
|
||||
|
||||
public interface Receiver {
|
||||
default void onExportSuccess(Track.Id trackId) {
|
||||
}
|
||||
|
||||
default void onExportError(Track.Id trackId) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
|
||||
public class ExportTask implements Parcelable {
|
||||
|
||||
private final String filename; //TODO Name will only be used when exporting multiple tracks
|
||||
|
||||
private final TrackFileFormat trackFileFormat;
|
||||
|
||||
private final List<Track.Id> trackIds;
|
||||
|
||||
public ExportTask(@Nullable String filename, @NonNull TrackFileFormat trackFileFormat, @NonNull List<Track.Id> trackIds) {
|
||||
this.filename = filename;
|
||||
this.trackFileFormat = trackFileFormat;
|
||||
this.trackIds = trackIds;
|
||||
}
|
||||
|
||||
protected ExportTask(Parcel in) {
|
||||
filename = in.readString();
|
||||
trackFileFormat = TrackFileFormat.valueOf(in.readString());
|
||||
trackIds = in.createTypedArrayList(Track.Id.CREATOR);
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public TrackFileFormat getTrackFileFormat() {
|
||||
return trackFileFormat;
|
||||
}
|
||||
|
||||
public List<Track.Id> getTrackIds() {
|
||||
return trackIds;
|
||||
}
|
||||
|
||||
public boolean isMultiExport() {
|
||||
return trackIds.size() > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ExportTask that = (ExportTask) o;
|
||||
return Objects.equals(filename, that.filename) && trackFileFormat == that.trackFileFormat && Objects.equals(trackIds, that.trackIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(filename, trackFileFormat, trackIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(filename);
|
||||
dest.writeString(trackFileFormat.name());
|
||||
dest.writeTypedList(trackIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final Creator<ExportTask> CREATOR = new Creator<>() {
|
||||
@Override
|
||||
public ExportTask createFromParcel(Parcel in) {
|
||||
return new ExportTask(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExportTask[] newArray(int size) {
|
||||
return new ExportTask[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -98,12 +98,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
||||
return writeTrack(new Track[]{track}, outputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
||||
public boolean writeTrack(List<Track> tracks, @NonNull OutputStream outputStream) {
|
||||
try {
|
||||
prepare(outputStream);
|
||||
writeHeader();
|
||||
@@ -156,8 +151,12 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
if (wroteSegment) writeCloseSegment();
|
||||
writeOpenSegment();
|
||||
wroteSegment = true;
|
||||
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||
sensorPoints.clear();
|
||||
if (trackPoint.hasLocation()) {
|
||||
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||
sensorPoints.clear();
|
||||
} else {
|
||||
sensorPoints.add(trackPoint);
|
||||
}
|
||||
}
|
||||
case SENSORPOINT -> sensorPoints.add(trackPoint);
|
||||
case TRACKPOINT -> {
|
||||
@@ -273,7 +272,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
printWriter.println("<trk>");
|
||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||
printWriter.println("<type>" + StringUtils.formatCData(track.getCategory()) + "</type>");
|
||||
printWriter.println("<type>" + StringUtils.formatCData(track.getActivityTypeLocalized()) + "</type>");
|
||||
|
||||
printWriter.println("<extensions>");
|
||||
printWriter.println("<topografix:color>c0c0c0</topografix:color>");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user