diff --git a/.classpath b/.classpath
index 8981741b..d57ec025 100644
--- a/.classpath
+++ b/.classpath
@@ -1,8 +1,9 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000..023543f9
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,291 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=1
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=false
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=100
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 00000000..23c2f966
--- /dev/null
+++ b/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Android
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=android;com;dalvik;gov;junit;libcore;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ea022b99..b9cb3d14 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3,6 +3,7 @@
Copyright (C) 2010, 2011, 2012, 2013 Herbert von Broeuschmeul
Copyright (C) 2010, 2011, 2012, 2013 BluetoothGPS4Droid Project
Copyright (C) 2011, 2012, 2013 UsbGPS4Droid Project
+ Copyright (C) 2013 Alexey Illarionov
This file is part of UsbGPS4Droid.
@@ -24,17 +25,20 @@
+ android:versionName="1.0 beta2">
-
+
-
-
+
@@ -46,28 +50,17 @@
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/project.properties b/project.properties
index 895c9ce2..73fc6610 100644
--- a/project.properties
+++ b/project.properties
@@ -8,4 +8,4 @@
# project structure.
# Project target.
-target=android-16
+target=android-18
diff --git a/res/layout/about.xml b/res/layout/about.xml
index 197d5e47..6cfdec0c 100644
--- a/res/layout/about.xml
+++ b/res/layout/about.xml
@@ -28,7 +28,7 @@
>
- beta 1
-
startGps
gpsLocationProviderKey
replaceStdtGps
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 557a5221..bed003d9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -39,7 +39,9 @@
Mock location provider name
Usb GPS name: %s
Connection retry number
- Number of attempts before exiting: %s
+
+ - Number of attempts before exiting: %s
+
Enable/Disable NMEA log
Track recording is off
Track recording is on
diff --git a/res/xml/device_filter.xml b/res/xml/device_filter.xml
index 4ef291b9..4437b1c6 100644
--- a/res/xml/device_filter.xml
+++ b/res/xml/device_filter.xml
@@ -1,4 +1,11 @@
-
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/pref.xml b/res/xml/pref.xml
index f3674afd..f5ef2d6f 100644
--- a/res/xml/pref.xml
+++ b/res/xml/pref.xml
@@ -79,8 +79,7 @@
android:key="@string/pref_connection_retries_key"
android:title="@string/pref_connection_retries_title"
android:defaultValue="@string/defaultConnectionRetries"
- android:inputType="numberDecimal"
- android:summary="@string/pref_connection_retries_summary" />
+ android:inputType="numberDecimal" />
.
- */
-
-package org.broeuschmeul.android.gps.bluetooth.provider;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.broeuschmeul.android.gps.usb.provider.R;
-
-import android.app.AlertDialog;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-//import android.bluetooth.BluetoothAdapter;
-//import android.bluetooth.BluetoothDevice;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbDeviceConnection;
-import android.hardware.usb.UsbEndpoint;
-import android.hardware.usb.UsbInterface;
-import android.hardware.usb.UsbManager;
-import android.hardware.usb.UsbRequest;
-import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.EditTextPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceActivity;
-import android.preference.PreferenceManager;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.text.method.LinkMovementMethod;
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * A PreferenceActivity Class used to configure, start and stop the NMEA tracker service.
- *
- * @author Herbert von Broeuschmeul
- *
- */
-public class BluetoothGpsActivity extends PreferenceActivity implements OnPreferenceChangeListener, OnSharedPreferenceChangeListener {
-
- /**
- * Tag used for log messages
- */
- private static final String LOG_TAG = "UsbGPS";
-
- private SharedPreferences sharedPref ;
-// private BluetoothAdapter bluetoothAdapter = null;
- private UsbManager usbManager = null;
- private String deviceName = "";
-
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- addPreferencesFromResource(R.xml.pref);
- sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
- sharedPref.registerOnSharedPreferenceChangeListener(this);
- usbManager = (UsbManager) getSystemService(this.USB_SERVICE);
- Preference pref = findPreference(BluetoothGpsProviderService.PREF_ABOUT);
- pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- BluetoothGpsActivity.this.displayAboutDialog();
- return true;
- }
- });
- }
-
- /* (non-Javadoc)
- * @see android.app.Activity#onResume()
- */
- @Override
- protected void onResume() {
- this.updateDevicePreferenceList();
- super.onResume();
- }
-
- private void updateDevicePreferenceSummary(){
- // update bluetooth device summary
- String defaultDeviceName = "";
- ListPreference prefDevices = (ListPreference)findPreference(BluetoothGpsProviderService.PREF_GPS_DEVICE);
- if (! usbManager.getDeviceList().isEmpty()){
- defaultDeviceName = usbManager.getDeviceList().keySet().iterator().next();
- }
- deviceName = sharedPref.getString(BluetoothGpsProviderService.PREF_GPS_DEVICE, defaultDeviceName);
- String deviceDisplayedName = "";
- if (! usbManager.getDeviceList().isEmpty() && usbManager.getDeviceList().get(deviceName) != null){
- deviceDisplayedName = usbManager.getDeviceList().get(deviceName).getDeviceName();
- } else if ((usbManager.getDeviceList().size() == 1) && (usbManager.getDeviceList().get(defaultDeviceName) != null)){
- deviceDisplayedName = usbManager.getDeviceList().get(defaultDeviceName).getDeviceName();
- deviceName = defaultDeviceName;
- prefDevices.setValue(defaultDeviceName);
- }
- prefDevices.setSummary(getString(R.string.pref_gps_device_summary, deviceDisplayedName));
- ListPreference prefDeviceSpeed = (ListPreference)findPreference(BluetoothGpsProviderService.PREF_GPS_DEVICE_SPEED);
- prefDeviceSpeed.setSummary(getString(R.string.pref_gps_device_speed_summary, sharedPref.getString(BluetoothGpsProviderService.PREF_GPS_DEVICE_SPEED, getString(R.string.defaultGpsDeviceSpeed))));
- }
-
- private void updateDevicePreferenceList(){
- // update bluetooth device summary
- updateDevicePreferenceSummary();
- // update bluetooth device list
- ListPreference prefDevices = (ListPreference)findPreference(BluetoothGpsProviderService.PREF_GPS_DEVICE);
- HashMap connectedUsbDevices = usbManager.getDeviceList();
- String[] entryValues = new String[connectedUsbDevices.size()];
- String[] entries = new String[connectedUsbDevices.size()];
- int i = 0;
- // Loop through usb devices
- for (String name : connectedUsbDevices.keySet()) {
- // Add the name and address to the ListPreference enties and entyValues
- UsbDevice device = connectedUsbDevices.get(name);
- Log.v(LOG_TAG, "device: "+name + " -- " + device.getDeviceName()+ " -- "+device);
- Log.v(LOG_TAG, "device prot: "+device.getDeviceProtocol() + " class: " + device.getDeviceClass()+ " sub class: " + device.getDeviceSubclass());
- Log.v(LOG_TAG, "device dev id: "+device.getDeviceId() + " prod id: " + device.getProductId()+ " sub vend id: " + device.getVendorId());
- Log.v(LOG_TAG, "device int nb: "+device.getInterfaceCount());
- for (int k=0; k < device.getInterfaceCount(); k++){
- UsbInterface usbIntf = device.getInterface(k);
- Log.v(LOG_TAG, "intf id: : "+ usbIntf.getId() + " -- "+usbIntf);
- Log.v(LOG_TAG, "intf prot: "+usbIntf.getInterfaceProtocol() + " class: " + usbIntf.getInterfaceClass()+ " sub class: " +usbIntf.getInterfaceSubclass());
- Log.v(LOG_TAG, "intf int nb: "+usbIntf.getEndpointCount());
- for (int j=0; j < usbIntf.getEndpointCount(); j++){
- UsbEndpoint endPt = usbIntf.getEndpoint(j);
- Log.v(LOG_TAG, "endPt: : "+endPt + " type: "+endPt.getType()+ " dir: "+endPt.getDirection() );
- }
- }
- entryValues[i] = device.getDeviceName();
- entries[i] = name;
- i++;
- }
- prefDevices.setEntryValues(entryValues);
- prefDevices.setEntries(entries);
- Preference pref = (Preference)findPreference(BluetoothGpsProviderService.PREF_TRACK_RECORDING);
- pref.setEnabled(sharedPref.getBoolean(BluetoothGpsProviderService.PREF_START_GPS_PROVIDER, false));
- pref = (Preference)findPreference(BluetoothGpsProviderService.PREF_MOCK_GPS_NAME);
- String mockProvider = sharedPref.getString(BluetoothGpsProviderService.PREF_MOCK_GPS_NAME, getString(R.string.defaultMockGpsName));
- pref.setSummary(getString(R.string.pref_mock_gps_name_summary,mockProvider));
- pref = (Preference)findPreference(BluetoothGpsProviderService.PREF_CONNECTION_RETRIES);
- String maxConnRetries = sharedPref.getString(BluetoothGpsProviderService.PREF_CONNECTION_RETRIES, getString(R.string.defaultConnectionRetries));
- pref.setSummary(getString(R.string.pref_connection_retries_summary,maxConnRetries));
- pref = (Preference)findPreference(BluetoothGpsProviderService.PREF_GPS_LOCATION_PROVIDER);
- if (sharedPref.getBoolean(BluetoothGpsProviderService.PREF_REPLACE_STD_GPS, true)){
- String s = getString(R.string.pref_gps_location_provider_summary);
- pref.setSummary(s);
- Log.v(LOG_TAG, "loc. provider: "+s);
- Log.v(LOG_TAG, "loc. provider: "+pref.getSummary());
- } else {
- String s = getString(R.string.pref_mock_gps_name_summary, mockProvider);
- pref.setSummary(s);
- Log.v(LOG_TAG, "loc. provider: "+s);
- Log.v(LOG_TAG, "loc. provider: "+pref.getSummary());
- }
- this.onContentChanged();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- sharedPref.unregisterOnSharedPreferenceChangeListener(this);
- }
-
- private void displayAboutDialog(){
- View messageView = getLayoutInflater().inflate(R.layout.about, null, false);
- // we need this to enable html links
- TextView textView = (TextView) messageView.findViewById(R.id.about_license);
- textView.setMovementMethod(LinkMovementMethod.getInstance());
- // When linking text, force to always use default color. This works
- // around a pressed color state bug.
- int defaultColor = textView.getTextColors().getDefaultColor();
- textView.setTextColor(defaultColor);
- textView = (TextView) messageView.findViewById(R.id.about_sources);
- textView.setTextColor(defaultColor);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.about_title);
- builder.setIcon(R.drawable.gplv3_icon);
- builder.setView(messageView);
- builder.show();
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if (BluetoothGpsProviderService.PREF_START_GPS_PROVIDER.equals(key)){
- boolean val = sharedPreferences.getBoolean(key, false);
- CheckBoxPreference pref = (CheckBoxPreference)findPreference(key);
- if (pref.isChecked() != val){
- pref.setChecked(val);
- } else if (val){
- String device = sharedPreferences.getString(BluetoothGpsProviderService.PREF_GPS_DEVICE, "");
- if (! device.equals(deviceName) && deviceName != null && deviceName.length() > 0){
- sharedPreferences.edit().putString(BluetoothGpsProviderService.PREF_GPS_DEVICE,deviceName).commit();
- }
- startService(new Intent(BluetoothGpsProviderService.ACTION_START_GPS_PROVIDER));
- } else {
- startService(new Intent(BluetoothGpsProviderService.ACTION_STOP_GPS_PROVIDER));
- }
- } else if (BluetoothGpsProviderService.PREF_TRACK_RECORDING.equals(key)){
- boolean val = sharedPreferences.getBoolean(key, false);
- CheckBoxPreference pref = (CheckBoxPreference)findPreference(key);
- if (pref.isChecked() != val){
- pref.setChecked(val);
- } else if (val){
- startService(new Intent(BluetoothGpsProviderService.ACTION_START_TRACK_RECORDING));
- } else {
- startService(new Intent(BluetoothGpsProviderService.ACTION_STOP_TRACK_RECORDING));
- }
- } else if (BluetoothGpsProviderService.PREF_GPS_DEVICE.equals(key)){
- updateDevicePreferenceSummary();
- } else if (BluetoothGpsProviderService.PREF_GPS_DEVICE_SPEED.equals(key)){
- updateDevicePreferenceSummary();
- } else if (BluetoothGpsProviderService.PREF_SIRF_ENABLE_GLL.equals(key)
- || BluetoothGpsProviderService.PREF_SIRF_ENABLE_GGA.equals(key)
- || BluetoothGpsProviderService.PREF_SIRF_ENABLE_RMC.equals(key)
- || BluetoothGpsProviderService.PREF_SIRF_ENABLE_VTG.equals(key)
- || BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSA.equals(key)
- || BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSV.equals(key)
- || BluetoothGpsProviderService.PREF_SIRF_ENABLE_ZDA.equals(key)
- || BluetoothGpsProviderService.PREF_SIRF_ENABLE_SBAS.equals(key)
- || BluetoothGpsProviderService.PREF_SIRF_ENABLE_NMEA.equals(key)
- || BluetoothGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION.equals(key)
- ){
- enableSirfFeature(key);
- }
- this.updateDevicePreferenceList();
- }
- private void enableSirfFeature(String key){
- CheckBoxPreference pref = (CheckBoxPreference)(findPreference(key));
- if (pref.isChecked() != sharedPref.getBoolean(key, false)){
- pref.setChecked(sharedPref.getBoolean(key, false));
- } else {
- Intent configIntent = new Intent(BluetoothGpsProviderService.ACTION_CONFIGURE_SIRF_GPS);
- configIntent.putExtra(key, pref.isChecked());
- startService(configIntent);
- }
- }
-}
diff --git a/src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsProviderService.java b/src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsProviderService.java
deleted file mode 100644
index 852b318b..00000000
--- a/src/org/broeuschmeul/android/gps/bluetooth/provider/BluetoothGpsProviderService.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2010, 2011, 2012 Herbert von Broeuschmeul
- * Copyright (C) 2010, 2011, 2012 BluetoothGPS4Droid Project
- * Copyright (C) 2011, 2012 UsbGPS4Droid Project
- *
- * This file is part of UsbGPS4Droid.
- *
- * UsbGPS4Droid is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * UsbGPS4Droid is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with UsbGPS4Droid. If not, see .
- */
-
-/**
- *
- */
-package org.broeuschmeul.android.gps.bluetooth.provider;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.broeuschmeul.android.gps.usb.provider.R;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Service;
-//import android.bluetooth.BluetoothAdapter;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.location.GpsStatus.NmeaListener;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.preference.PreferenceManager;
-import android.util.Config;
-import android.util.Log;
-import android.widget.Toast;
-
-/**
- * A Service used to replace Android internal GPS with a bluetooth GPS and/or write GPS NMEA data in a File.
- *
- * @author Herbert von Broeuschmeul
- *
- */
-public class BluetoothGpsProviderService extends Service implements NmeaListener, LocationListener {
-
- public static final String ACTION_START_TRACK_RECORDING = "org.broeuschmeul.android.gps.usb.tracker.nmea.intent.action.START_TRACK_RECORDING";
- public static final String ACTION_STOP_TRACK_RECORDING = "org.broeuschmeul.android.gps.usb.tracker.nmea.intent.action.STOP_TRACK_RECORDING";
- public static final String ACTION_START_GPS_PROVIDER = "org.broeuschmeul.android.gps.usb.provider.nmea.intent.action.START_GPS_PROVIDER";
- public static final String ACTION_STOP_GPS_PROVIDER = "org.broeuschmeul.android.gps.usb.provider.nmea.intent.action.STOP_GPS_PROVIDER";
- public static final String ACTION_CONFIGURE_SIRF_GPS = "org.broeuschmeul.android.gps.usb.provider.nmea.intent.action.CONFIGURE_SIRF_GPS";
- public static final String PREF_START_GPS_PROVIDER = "startGps";
- public static final String PREF_GPS_LOCATION_PROVIDER = "gpsLocationProviderKey";
- public static final String PREF_REPLACE_STD_GPS = "replaceStdtGps";
- public static final String PREF_FORCE_ENABLE_PROVIDER = "forceEnableProvider";
- public static final String PREF_MOCK_GPS_NAME = "mockGpsName";
- public static final String PREF_CONNECTION_RETRIES = "connectionRetries";
- public static final String PREF_TRACK_RECORDING = "trackRecording";
- public static final String PREF_TRACK_FILE_DIR = "trackFileDirectory";
- public static final String PREF_TRACK_FILE_PREFIX = "trackFilePrefix";
- public static final String PREF_GPS_DEVICE = "usbDevice";
- public static final String PREF_GPS_DEVICE_SPEED = "gpsDeviceSpeed";
- public static final String PREF_ABOUT = "about";
-
- /**
- * Tag used for log messages
- */
- private static final String LOG_TAG = "UsbGPS";
-
- public static final String PREF_SIRF_GPS = "sirfGps";
- public static final String PREF_SIRF_ENABLE_GGA = "enableGGA";
- public static final String PREF_SIRF_ENABLE_RMC = "enableRMC";
- public static final String PREF_SIRF_ENABLE_GLL = "enableGLL";
- public static final String PREF_SIRF_ENABLE_VTG = "enableVTG";
- public static final String PREF_SIRF_ENABLE_GSA = "enableGSA";
- public static final String PREF_SIRF_ENABLE_GSV = "enableGSV";
- public static final String PREF_SIRF_ENABLE_ZDA = "enableZDA";
- public static final String PREF_SIRF_ENABLE_SBAS = "enableSBAS";
- public static final String PREF_SIRF_ENABLE_NMEA = "enableNMEA";
- public static final String PREF_SIRF_ENABLE_STATIC_NAVIGATION = "enableStaticNavigation";
-
- private BlueetoothGpsManager gpsManager = null;
- private PrintWriter writer;
- private File trackFile;
- private boolean preludeWritten = false;
- private Toast toast ;
-
- @Override
- public void onCreate() {
- super.onCreate();
- toast = Toast.makeText(getApplicationContext(), "NMEA track recording... on", Toast.LENGTH_SHORT);
- }
-
- @Override
- public void onStart(Intent intent, int startId) {
-// super.onStart(intent, startId);
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- SharedPreferences.Editor edit = sharedPreferences.edit();
- String deviceAddress = sharedPreferences.getString(PREF_GPS_DEVICE, "");
- int maxConRetries = Integer.parseInt(sharedPreferences.getString(PREF_CONNECTION_RETRIES, this.getString(R.string.defaultConnectionRetries)));
- if (Config.LOGD){
- Log.d(LOG_TAG, "prefs device addr: "+deviceAddress);
- }
- if (ACTION_START_GPS_PROVIDER.equals(intent.getAction())){
- if (gpsManager == null){
- if (true /*|| BluetoothAdapter.checkBluetoothAddress(deviceAddress)*/){
- String mockProvider = LocationManager.GPS_PROVIDER;
- if (! sharedPreferences.getBoolean(PREF_REPLACE_STD_GPS, true)){
- mockProvider = sharedPreferences.getString(PREF_MOCK_GPS_NAME, getString(R.string.defaultMockGpsName));
- }
- gpsManager = new BlueetoothGpsManager(this, deviceAddress, maxConRetries);
- boolean enabled = gpsManager.enable();
-// Bundle extras = intent.getExtras();
- if (sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, false) != enabled){
- edit.putBoolean(PREF_START_GPS_PROVIDER,enabled);
- edit.commit();
- }
- if (enabled) {
- gpsManager.enableMockLocationProvider(mockProvider);
- Notification notification = new Notification(R.drawable.ic_stat_notify, this.getString(R.string.foreground_gps_provider_started_notification), System.currentTimeMillis());
- Intent myIntent = new Intent(this, BluetoothGpsActivity.class);
- PendingIntent myPendingIntent = PendingIntent.getActivity(this, 0, myIntent, PendingIntent.FLAG_CANCEL_CURRENT);
- notification.setLatestEventInfo(getApplicationContext(), this.getString(R.string.foreground_service_started_notification_title), this.getString(R.string.foreground_gps_provider_started_notification), myPendingIntent);
- startForeground(R.string.foreground_gps_provider_started_notification, notification);
-// if (sharedPreferences.getBoolean(PREF_SIRF_GPS, false)){
-// gpsManager.enableSirfConfig(sharedPreferences);
-// }
- toast.setText(this.getString(R.string.msg_gps_provider_started));
- toast.show();
- } else {
- stopSelf();
- }
- } else {
- stopSelf();
- }
- } else {
- toast.setText(this.getString(R.string.msg_gps_provider_already_started));
- toast.show();
- }
- } else if (ACTION_START_TRACK_RECORDING.equals(intent.getAction())){
- if (trackFile == null){
- if (gpsManager != null){
- beginTrack();
- gpsManager.addNmeaListener(this);
- if (! sharedPreferences.getBoolean(PREF_TRACK_RECORDING, false)){
- edit.putBoolean(PREF_TRACK_RECORDING,true);
- edit.commit();
- }
- toast.setText(this.getString(R.string.msg_nmea_recording_started));
- toast.show();
- } else {
- endTrack();
- if ( sharedPreferences.getBoolean(PREF_TRACK_RECORDING, true)){
- edit.putBoolean(PREF_TRACK_RECORDING,false);
- edit.commit();
- }
- }
- } else {
- toast.setText(this.getString(R.string.msg_nmea_recording_already_started));
- toast.show();
- }
- } else if (ACTION_STOP_TRACK_RECORDING.equals(intent.getAction())){
- if (gpsManager != null){
- gpsManager.removeNmeaListener(this);
- endTrack();
- toast.setText(this.getString(R.string.msg_nmea_recording_stopped));
- toast.show();
- }
- if (sharedPreferences.getBoolean(PREF_TRACK_RECORDING, true)){
- edit.putBoolean(PREF_TRACK_RECORDING,false);
- edit.commit();
- }
- } else if (ACTION_STOP_GPS_PROVIDER.equals(intent.getAction())){
- if (sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, true)){
- edit.putBoolean(PREF_START_GPS_PROVIDER,false);
- edit.commit();
- }
- stopSelf();
- } else if (ACTION_CONFIGURE_SIRF_GPS.equals(intent.getAction())){
- if (gpsManager != null){
- Bundle extras = intent.getExtras();
- gpsManager.enableSirfConfig(extras);
- }
- }
- }
-
- /* (non-Javadoc)
- * @see android.app.Service#onStartCommand(android.content.Intent, int, int)
- */
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- onStart(intent, startId);
- return Service.START_NOT_STICKY;
- }
-
- @Override
- public void onDestroy() {
- BlueetoothGpsManager manager = gpsManager;
- gpsManager = null;
- if (manager != null){
- if (manager.getDisableReason() != 0){
- toast.setText(getString(R.string.msg_gps_provider_stopped_by_problem, getString(manager.getDisableReason())));
- toast.show();
- } else {
- toast.setText(R.string.msg_gps_provider_stopped);
- toast.show();
- }
- manager.removeNmeaListener(this);
- manager.disableMockLocationProvider();
- manager.disable();
- }
- endTrack();
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- SharedPreferences.Editor edit = sharedPreferences.edit();
- if (sharedPreferences.getBoolean(PREF_TRACK_RECORDING, true)){
- edit.putBoolean(PREF_TRACK_RECORDING,false);
- edit.commit();
- }
- if (sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, true)){
- edit.putBoolean(PREF_START_GPS_PROVIDER,false);
- edit.commit();
- }
- super.onDestroy();
- }
-
- private void beginTrack(){
- SimpleDateFormat fmt = new SimpleDateFormat("_yyyy-MM-dd_HH-mm-ss'.nmea'");
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- String trackDirName = sharedPreferences.getString(PREF_TRACK_FILE_DIR, this.getString(R.string.defaultTrackFileDirectory));
- String trackFilePrefix = sharedPreferences.getString(PREF_TRACK_FILE_PREFIX, this.getString(R.string.defaultTrackFilePrefix));
- trackFile = new File(trackDirName,trackFilePrefix+fmt.format(new Date()));
- Log.d(LOG_TAG, "Writing the prelude of the NMEA file: "+trackFile.getAbsolutePath());
- File trackDir = trackFile.getParentFile();
- try {
- if ((! trackDir.mkdirs()) && (! trackDir.isDirectory())){
- Log.e(LOG_TAG, "Error while creating parent dir of NMEA file: "+trackDir.getAbsolutePath());
- }
- writer = new PrintWriter(new BufferedWriter(new FileWriter(trackFile)));
- preludeWritten = true;
- } catch (IOException e) {
- Log.e(LOG_TAG, "Error while writing the prelude of the NMEA file: "+trackFile.getAbsolutePath(), e);
- // there was an error while writing the prelude of the NMEA file, stopping the service...
- stopSelf();
- }
- }
- private void endTrack(){
- if (trackFile != null && writer != null){
- Log.d(LOG_TAG, "Ending the NMEA file: "+trackFile.getAbsolutePath());
- preludeWritten = false;
- writer.close();
- trackFile = null;
- }
- }
- private void addNMEAString(String data){
- if (! preludeWritten){
- beginTrack();
- }
- Log.v(LOG_TAG, "Adding data in the NMEA file: "+ data);
- if (trackFile != null && writer != null){
- writer.print(data);
- }
- }
- /* (non-Javadoc)
- * @see android.app.Service#onBind(android.content.Intent)
- */
- @Override
- public IBinder onBind(Intent intent) {
- if (Config.LOGD){
- Log.d(LOG_TAG, "trying access IBinder");
- }
- return null;
- }
-
- @Override
- public void onLocationChanged(Location location) {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void onProviderDisabled(String provider) {
- Log.i(LOG_TAG, "The GPS has been disabled.....stopping the NMEA tracker service.");
- stopSelf();
- }
-
- @Override
- public void onProviderEnabled(String provider) {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void onNmeaReceived(long timestamp, String data) {
- addNMEAString(data);
- }
-}
diff --git a/src/org/broeuschmeul/android/gps/nmea/util/NmeaParser.java b/src/org/broeuschmeul/android/gps/nmea/util/NmeaParser.java
index 2c5db10c..2dcad537 100644
--- a/src/org/broeuschmeul/android/gps/nmea/util/NmeaParser.java
+++ b/src/org/broeuschmeul/android/gps/nmea/util/NmeaParser.java
@@ -2,32 +2,25 @@
* Copyright (C) 2010, 2011, 2012 Herbert von Broeuschmeul
* Copyright (C) 2010, 2011, 2012 BluetoothGPS4Droid Project
* Copyright (C) 2011, 2012 UsbGPS4Droid Project
- *
+ *
* This file is part of UsbGPS4Droid.
*
* UsbGPS4Droid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* UsbGPS4Droid is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with UsbGPS4Droid. If not, see .
*/
package org.broeuschmeul.android.gps.nmea.util;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
@@ -37,11 +30,18 @@
import android.text.TextUtils.SimpleStringSplitter;
import android.util.Log;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* This class is used to parse NMEA sentences an generate the Android Locations when there is a new GPS FIX.
* It manage also the Mock Location Provider (enable/disable/fix & status notification)
* and can compute the the checksum of a NMEA sentence.
- *
+ *
* @author Herbert von Broeuschmeul
*
*/
@@ -51,6 +51,8 @@ public class NmeaParser {
*/
private static final String LOG_TAG = "UsbGPS";
+ private static final Pattern sNmeaSentencePattern = Pattern.compile("\\$([^*$]*)(?:\\*([0-9A-F][0-9A-F]))?\r\n");
+
private String fixTime = null;
private long fixTimestamp;
@@ -97,8 +99,8 @@ public void enableMockLocationProvider(String gpsName, boolean force){
}
prov = lm.getProvider(mockLocationProvider);
lm.addTestProvider(mockLocationProvider, false, true,false, false, true, true, true, Criteria.POWER_MEDIUM, Criteria.ACCURACY_FINE);
- if ( force
- || (prov == null)
+ if ( force
+ || (prov == null)
// || (! LocationManager.GPS_PROVIDER.equals(mockLocationProvider))
){
Log.d(LOG_TAG, "enabling Mock provider: "+mockLocationProvider);
@@ -129,7 +131,7 @@ public void disableMockLocationProvider(){
Log.v(LOG_TAG, "Mock provider: "+prov.getName()+" "+prov.getPowerRequirement()+" "+prov.getAccuracy()+" "+lm.isProviderEnabled(mockLocationProvider));
}
mockGpsEnabled = false;
- if ( mockGpsAutoEnabled ) {
+ if ( mockGpsAutoEnabled ) {
Log.d(LOG_TAG, "disabling Mock provider: "+mockLocationProvider);
lm.setTestProviderEnabled(mockLocationProvider, false);
}
@@ -150,7 +152,7 @@ public void disableMockLocationProvider(){
}
Log.d(LOG_TAG, "removed mock GPS");
} else {
- Log.d(LOG_TAG, "Mock provider already disabled: "+mockLocationProvider);
+ Log.d(LOG_TAG, "Mock provider already disabled: "+mockLocationProvider);
}
} catch (SecurityException e){
Log.e(LOG_TAG, "Error while enabling Mock Mocations Provider", e);
@@ -168,7 +170,7 @@ public void disableMockLocationProvider(){
public boolean isMockGpsEnabled() {
return mockGpsEnabled;
}
-
+
public void setMockLocationProviderOutOfService(){
notifyStatusChanged(LocationProvider.OUT_OF_SERVICE, null, System.currentTimeMillis());
}
@@ -179,7 +181,7 @@ public void setMockLocationProviderOutOfService(){
public String getMockLocationProvider() {
return mockLocationProvider;
}
-
+
private void notifyFix(Location fix) throws SecurityException {
fixTime = null;
hasGGA = false;
@@ -193,7 +195,7 @@ private void notifyFix(Location fix) throws SecurityException {
this.fix = null;
}
}
-
+
private void notifyStatusChanged(int status, Bundle extras, long updateTime){
fixTime = null;
hasGGA = false;
@@ -210,24 +212,28 @@ private void notifyStatusChanged(int status, Bundle extras, long updateTime){
this.mockStatus = status;
}
}
-
- // parse NMEA Sentence
+
+ // parse NMEA Sentence
public String parseNmeaSentence(String gpsSentence) throws SecurityException {
String nmeaSentence = null;
- Log.v(LOG_TAG, "data: "+System.currentTimeMillis()+" "+gpsSentence);
- Pattern xx = Pattern.compile("\\$([^*$]*)(?:\\*([0-9A-F][0-9A-F]))?\r\n");
- Matcher m = xx.matcher(gpsSentence);
- if (m.matches()){
- nmeaSentence = m.group(0);
- String sentence = m.group(1);
- String checkSum = m.group(2);
- Log.v(LOG_TAG, "data: "+System.currentTimeMillis()+" "+sentence+" cheksum: "+checkSum +" control: "+String.format("%02X",computeChecksum(sentence)));
- SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
- splitter.setString(sentence);
- String command = splitter.next();
- if (command.equals("GPGGA")){
- /* $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
-
+
+ final Matcher m = sNmeaSentencePattern.matcher(gpsSentence);
+
+ if (!m.matches()) {
+ Log.v(LOG_TAG, "note NMEA sentence: "+System.currentTimeMillis()+" "+gpsSentence);
+ return null;
+ }
+
+ nmeaSentence = m.group(0);
+ String sentence = m.group(1);
+ String checkSum = m.group(2);
+ Log.v(LOG_TAG, "data: "+System.currentTimeMillis()+" "+sentence+" cheksum: "+checkSum +" control: "+String.format("%02X",computeChecksum(sentence)));
+ SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(sentence);
+ String command = splitter.next();
+ if (command.equals("GPGGA")){
+ /* $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
+
Where:
GGA Global Positioning System Fix Data
123519 Fix taken at 12:35:19 UTC
@@ -249,19 +255,19 @@ public String parseNmeaSentence(String gpsSentence) throws SecurityException {
ellipsoid
(empty field) time in seconds since last DGPS update
(empty field) DGPS station ID number
- *47 the checksum data, always begins with *
- */
- // UTC time of fix HHmmss.S
- String time = splitter.next();
- // latitude ddmm.M
- String lat = splitter.next();
- // direction (N/S)
- String latDir = splitter.next();
- // longitude dddmm.M
- String lon = splitter.next();
- // direction (E/W)
- String lonDir = splitter.next();
- /* fix quality:
+ *47 the checksum data, always begins with *
+ */
+ // UTC time of fix HHmmss.S
+ String time = splitter.next();
+ // latitude ddmm.M
+ String lat = splitter.next();
+ // direction (N/S)
+ String latDir = splitter.next();
+ // longitude dddmm.M
+ String lon = splitter.next();
+ // direction (E/W)
+ String lonDir = splitter.next();
+ /* fix quality:
0= invalid
1 = GPS fix (SPS)
2 = DGPS fix
@@ -271,62 +277,62 @@ public String parseNmeaSentence(String gpsSentence) throws SecurityException {
6 = estimated (dead reckoning) (2.3 feature)
7 = Manual input mode
8 = Simulation mode
- */
- String quality = splitter.next();
- // Number of satellites being tracked
- String nbSat = splitter.next();
- // Horizontal dilution of position (float)
- String hdop = splitter.next();
- // Altitude, Meters, above mean sea level
- String alt = splitter.next();
- // Height of geoid (mean sea level) above WGS84 ellipsoid
- String geoAlt = splitter.next();
- // time in seconds since last DGPS update
- // DGPS station ID number
- if (quality != null && !quality.equals("") && !quality.equals("0") ){
- if (this.mockStatus != LocationProvider.AVAILABLE){
- long updateTime = parseNmeaTime(time);
- notifyStatusChanged(LocationProvider.AVAILABLE, null, updateTime);
- }
- if (! time.equals(fixTime)){
- notifyFix(fix);
- fix = new Location(mockLocationProvider);
- fixTime = time;
- fixTimestamp = parseNmeaTime(time);
- fix.setTime(fixTimestamp);
- Log.v(LOG_TAG, "Fix: "+fix);
- }
- if (lat != null && !lat.equals("")){
- fix.setLatitude(parseNmeaLatitude(lat,latDir));
- }
- if (lon != null && !lon.equals("")){
- fix.setLongitude(parseNmeaLongitude(lon,lonDir));
- }
- if (hdop != null && !hdop.equals("")){
- fix.setAccuracy(Float.parseFloat(hdop)*precision);
- }
- if (alt != null && !alt.equals("")){
- fix.setAltitude(Double.parseDouble(alt));
- }
- if (nbSat != null && !nbSat.equals("")){
- Bundle extras = new Bundle();
- extras.putInt("satellites", Integer.parseInt(nbSat));
- fix.setExtras(extras);
- }
- Log.v(LOG_TAG, "Fix: "+System.currentTimeMillis()+" "+fix);
- hasGGA = true;
- if (hasGGA && hasRMC){
- notifyFix(fix);
- }
- } else if(quality.equals("0")){
- if (this.mockStatus != LocationProvider.TEMPORARILY_UNAVAILABLE){
- long updateTime = parseNmeaTime(time);
- notifyStatusChanged(LocationProvider.TEMPORARILY_UNAVAILABLE, null, updateTime);
- }
- }
- } else if (command.equals("GPRMC")){
- /* $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
-
+ */
+ String quality = splitter.next();
+ // Number of satellites being tracked
+ String nbSat = splitter.next();
+ // Horizontal dilution of position (float)
+ String hdop = splitter.next();
+ // Altitude, Meters, above mean sea level
+ String alt = splitter.next();
+ // Height of geoid (mean sea level) above WGS84 ellipsoid
+ String geoAlt = splitter.next();
+ // time in seconds since last DGPS update
+ // DGPS station ID number
+ if (quality != null && !quality.equals("") && !quality.equals("0") ){
+ if (this.mockStatus != LocationProvider.AVAILABLE){
+ long updateTime = parseNmeaTime(time);
+ notifyStatusChanged(LocationProvider.AVAILABLE, null, updateTime);
+ }
+ if (! time.equals(fixTime)){
+ notifyFix(fix);
+ fix = new Location(mockLocationProvider);
+ fixTime = time;
+ fixTimestamp = parseNmeaTime(time);
+ fix.setTime(fixTimestamp);
+ Log.v(LOG_TAG, "Fix: "+fix);
+ }
+ if (lat != null && !lat.equals("")){
+ fix.setLatitude(parseNmeaLatitude(lat,latDir));
+ }
+ if (lon != null && !lon.equals("")){
+ fix.setLongitude(parseNmeaLongitude(lon,lonDir));
+ }
+ if (hdop != null && !hdop.equals("")){
+ fix.setAccuracy(Float.parseFloat(hdop)*precision);
+ }
+ if (alt != null && !alt.equals("")){
+ fix.setAltitude(Double.parseDouble(alt));
+ }
+ if (nbSat != null && !nbSat.equals("")){
+ Bundle extras = new Bundle();
+ extras.putInt("satellites", Integer.parseInt(nbSat));
+ fix.setExtras(extras);
+ }
+ Log.v(LOG_TAG, "Fix: "+System.currentTimeMillis()+" "+fix);
+ hasGGA = true;
+ if (hasGGA && hasRMC){
+ notifyFix(fix);
+ }
+ } else if(quality.equals("0")){
+ if (this.mockStatus != LocationProvider.TEMPORARILY_UNAVAILABLE){
+ long updateTime = parseNmeaTime(time);
+ notifyStatusChanged(LocationProvider.TEMPORARILY_UNAVAILABLE, null, updateTime);
+ }
+ }
+ } else if (command.equals("GPRMC")){
+ /* $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
+
Where:
RMC Recommended Minimum sentence C
123519 Fix taken at 12:35:19 UTC
@@ -337,152 +343,151 @@ public String parseNmeaSentence(String gpsSentence) throws SecurityException {
084.4 Track angle in degrees True
230394 Date - 23rd of March 1994
003.1,W Magnetic Variation
- *6A The checksum data, always begins with *
- */
- // UTC time of fix HHmmss.S
- String time = splitter.next();
- // fix status (A/V)
- String status = splitter.next();
- // latitude ddmm.M
- String lat = splitter.next();
- // direction (N/S)
- String latDir = splitter.next();
- // longitude dddmm.M
- String lon = splitter.next();
- // direction (E/W)
- String lonDir = splitter.next();
- // Speed over the ground in knots
- String speed = splitter.next();
- // Track angle in degrees True
- String bearing = splitter.next();
- // UTC date of fix DDMMYY
- String date = splitter.next();
- // Magnetic Variation ddd.D
- String magn = splitter.next();
- // Magnetic variation direction (E/W)
- String magnDir = splitter.next();
- // for NMEA 0183 version 3.00 active the Mode indicator field is added
- // Mode indicator, (A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator )
- if (status != null && !status.equals("") && status.equals("A") ){
- if (this.mockStatus != LocationProvider.AVAILABLE){
- long updateTime = parseNmeaTime(time);
- notifyStatusChanged(LocationProvider.AVAILABLE, null, updateTime);
- }
- if (! time.equals(fixTime)){
- notifyFix(fix);
- fix = new Location(mockLocationProvider);
- fixTime = time;
- fixTimestamp = parseNmeaTime(time);
- fix.setTime(fixTimestamp);
- Log.v(LOG_TAG, "Fix: "+fix);
- }
- if (lat != null && !lat.equals("")){
- fix.setLatitude(parseNmeaLatitude(lat,latDir));
- }
- if (lon != null && !lon.equals("")){
- fix.setLongitude(parseNmeaLongitude(lon,lonDir));
- }
- if (speed != null && !speed.equals("")){
- fix.setSpeed(parseNmeaSpeed(speed, "N"));
- }
- if (bearing != null && !bearing.equals("")){
- fix.setBearing(Float.parseFloat(bearing));
- }
- Log.v(LOG_TAG, "Fix: "+System.currentTimeMillis()+" "+fix);
- hasRMC = true;
- if (hasGGA && hasRMC){
- notifyFix(fix);
- }
- } else if(status.equals("V")){
- if (this.mockStatus != LocationProvider.TEMPORARILY_UNAVAILABLE){
- long updateTime = parseNmeaTime(time);
- notifyStatusChanged(LocationProvider.TEMPORARILY_UNAVAILABLE, null, updateTime);
- }
- }
- } else if (command.equals("GPGSA")){
- /* $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39
-
+ *6A The checksum data, always begins with *
+ */
+ // UTC time of fix HHmmss.S
+ String time = splitter.next();
+ // fix status (A/V)
+ String status = splitter.next();
+ // latitude ddmm.M
+ String lat = splitter.next();
+ // direction (N/S)
+ String latDir = splitter.next();
+ // longitude dddmm.M
+ String lon = splitter.next();
+ // direction (E/W)
+ String lonDir = splitter.next();
+ // Speed over the ground in knots
+ String speed = splitter.next();
+ // Track angle in degrees True
+ String bearing = splitter.next();
+ // UTC date of fix DDMMYY
+ String date = splitter.next();
+ // Magnetic Variation ddd.D
+ String magn = splitter.next();
+ // Magnetic variation direction (E/W)
+ String magnDir = splitter.next();
+ // for NMEA 0183 version 3.00 active the Mode indicator field is added
+ // Mode indicator, (A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator )
+ if (status != null && !status.equals("") && status.equals("A") ){
+ if (this.mockStatus != LocationProvider.AVAILABLE){
+ long updateTime = parseNmeaTime(time);
+ notifyStatusChanged(LocationProvider.AVAILABLE, null, updateTime);
+ }
+ if (! time.equals(fixTime)){
+ notifyFix(fix);
+ fix = new Location(mockLocationProvider);
+ fixTime = time;
+ fixTimestamp = parseNmeaTime(time);
+ fix.setTime(fixTimestamp);
+ Log.v(LOG_TAG, "Fix: "+fix);
+ }
+ if (lat != null && !lat.equals("")){
+ fix.setLatitude(parseNmeaLatitude(lat,latDir));
+ }
+ if (lon != null && !lon.equals("")){
+ fix.setLongitude(parseNmeaLongitude(lon,lonDir));
+ }
+ if (speed != null && !speed.equals("")){
+ fix.setSpeed(parseNmeaSpeed(speed, "N"));
+ }
+ if (bearing != null && !bearing.equals("")){
+ fix.setBearing(Float.parseFloat(bearing));
+ }
+ Log.v(LOG_TAG, "Fix: "+System.currentTimeMillis()+" "+fix);
+ hasRMC = true;
+ if (hasGGA && hasRMC){
+ notifyFix(fix);
+ }
+ } else if(status.equals("V")){
+ if (this.mockStatus != LocationProvider.TEMPORARILY_UNAVAILABLE){
+ long updateTime = parseNmeaTime(time);
+ notifyStatusChanged(LocationProvider.TEMPORARILY_UNAVAILABLE, null, updateTime);
+ }
+ }
+ } else if (command.equals("GPGSA")){
+ /* $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39
+
Where:
GSA Satellite status
- A Auto selection of 2D or 3D fix (M = manual)
+ A Auto selection of 2D or 3D fix (M = manual)
3 3D fix - values include: 1 = no fix
2 = 2D fix
3 = 3D fix
- 04,05... PRNs of satellites used for fix (space for 12)
- 2.5 PDOP (Position dilution of precision)
- 1.3 Horizontal dilution of precision (HDOP)
+ 04,05... PRNs of satellites used for fix (space for 12)
+ 2.5 PDOP (Position dilution of precision)
+ 1.3 Horizontal dilution of precision (HDOP)
2.1 Vertical dilution of precision (VDOP)
- *39 the checksum data, always begins with *
- */
- // mode : A Auto selection of 2D or 3D fix / M = manual
- String mode = splitter.next();
- // fix type : 1 - no fix / 2 - 2D / 3 - 3D
- String fixType = splitter.next();
- // discard PRNs of satellites used for fix (space for 12)
- for (int i=0 ; ((i<12)&&(! "1".equals(fixType))) ; i++){
- splitter.next();
- }
- // Position dilution of precision (float)
- String pdop = splitter.next();
- // Horizontal dilution of precision (float)
- String hdop = splitter.next();
- // Vertical dilution of precision (float)
- String vdop = splitter.next();
- } else if (command.equals("GPVTG")){
- /* $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48
-
+ *39 the checksum data, always begins with *
+ */
+ // mode : A Auto selection of 2D or 3D fix / M = manual
+ String mode = splitter.next();
+ // fix type : 1 - no fix / 2 - 2D / 3 - 3D
+ String fixType = splitter.next();
+ // discard PRNs of satellites used for fix (space for 12)
+ for (int i=0 ; ((i<12)&&(! "1".equals(fixType))) ; i++){
+ splitter.next();
+ }
+ // Position dilution of precision (float)
+ String pdop = splitter.next();
+ // Horizontal dilution of precision (float)
+ String hdop = splitter.next();
+ // Vertical dilution of precision (float)
+ String vdop = splitter.next();
+ } else if (command.equals("GPVTG")){
+ /* $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48
+
where:
VTG Track made good and ground speed
054.7,T True track made good (degrees)
034.4,M Magnetic track made good
005.5,N Ground speed, knots
010.2,K Ground speed, Kilometers per hour
- *48 Checksum
- */
- // Track angle in degrees True
- String bearing = splitter.next();
- // T
- splitter.next();
- // Magnetic track made good
- String magn = splitter.next();
- // M
- splitter.next();
- // Speed over the ground in knots
- String speedKnots = splitter.next();
- // N
- splitter.next();
- // Speed over the ground in Kilometers per hour
- String speedKm = splitter.next();
- // K
- splitter.next();
- // for NMEA 0183 version 3.00 active the Mode indicator field is added
- // Mode indicator, (A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator )
- } else if (command.equals("GPGLL")){
- /* $GPGLL,4916.45,N,12311.12,W,225444,A,*1D
-
+ *48 Checksum
+ */
+ // Track angle in degrees True
+ String bearing = splitter.next();
+ // T
+ splitter.next();
+ // Magnetic track made good
+ String magn = splitter.next();
+ // M
+ splitter.next();
+ // Speed over the ground in knots
+ String speedKnots = splitter.next();
+ // N
+ splitter.next();
+ // Speed over the ground in Kilometers per hour
+ String speedKm = splitter.next();
+ // K
+ splitter.next();
+ // for NMEA 0183 version 3.00 active the Mode indicator field is added
+ // Mode indicator, (A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator )
+ } else if (command.equals("GPGLL")){
+ /* $GPGLL,4916.45,N,12311.12,W,225444,A,*1D
+
Where:
GLL Geographic position, Latitude and Longitude
4916.46,N Latitude 49 deg. 16.45 min. North
12311.12,W Longitude 123 deg. 11.12 min. West
225444 Fix taken at 22:54:44 UTC
A Data Active or V (void)
- *iD checksum data
- */
- // latitude ddmm.M
- String lat = splitter.next();
- // direction (N/S)
- String latDir = splitter.next();
- // longitude dddmm.M
- String lon = splitter.next();
- // direction (E/W)
- String lonDir = splitter.next();
- // UTC time of fix HHmmss.S
- String time = splitter.next();
- // fix status (A/V)
- String status = splitter.next();
- // for NMEA 0183 version 3.00 active the Mode indicator field is added
- // Mode indicator, (A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator )
- }
+ *iD checksum data
+ */
+ // latitude ddmm.M
+ String lat = splitter.next();
+ // direction (N/S)
+ String latDir = splitter.next();
+ // longitude dddmm.M
+ String lon = splitter.next();
+ // direction (E/W)
+ String lonDir = splitter.next();
+ // UTC time of fix HHmmss.S
+ String time = splitter.next();
+ // fix status (A/V)
+ String status = splitter.next();
+ // for NMEA 0183 version 3.00 active the Mode indicator field is added
+ // Mode indicator, (A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator )
}
return nmeaSentence;
}
@@ -491,7 +496,7 @@ public double parseNmeaLatitude(String lat,String orientation){
double latitude = 0.0;
if (lat != null && orientation != null && !lat.equals("") && !orientation.equals("")){
double temp1 = Double.parseDouble(lat);
- double temp2 = Math.floor(temp1/100);
+ double temp2 = Math.floor(temp1/100);
double temp3 = (temp1/100 - temp2)/0.6;
if (orientation.equals("S")){
latitude = -(temp2+temp3);
@@ -505,7 +510,7 @@ public double parseNmeaLongitude(String lon,String orientation){
double longitude = 0.0;
if (lon != null && orientation != null && !lon.equals("") && !orientation.equals("")){
double temp1 = Double.parseDouble(lon);
- double temp2 = Math.floor(temp1/100);
+ double temp2 = Math.floor(temp1/100);
double temp3 = (temp1/100 - temp2)/0.6;
if (orientation.equals("W")){
longitude = -(temp2+temp3);
@@ -536,7 +541,7 @@ public long parseNmeaTime(String time){
long now = System.currentTimeMillis();
long today = now - (now %86400000L);
long temp1;
- // sometime we don't have millisecond in the time string, so we have to reformat it
+ // sometime we don't have millisecond in the time string, so we have to reformat it
temp1 = fmt.parse(String.format((Locale)null,"%010.3f", Double.parseDouble(time))).getTime();
long temp2 = today+temp1;
// if we're around midnight we could have a problem...
@@ -556,7 +561,7 @@ public long parseNmeaTime(String time){
public byte computeChecksum(String s){
byte checksum = 0;
for (char c : s.toCharArray()){
- checksum ^= (byte)c;
+ checksum ^= (byte)c;
}
return checksum;
}
diff --git a/src/org/broeuschmeul/android/gps/usb/UsbAcmController.java b/src/org/broeuschmeul/android/gps/usb/UsbAcmController.java
new file mode 100644
index 00000000..617e32bf
--- /dev/null
+++ b/src/org/broeuschmeul/android/gps/usb/UsbAcmController.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2013 Alexey Illarionov
+ *
+ * This file is part of UsbGPS4Droid.
+ *
+ * UsbGPS4Droid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * UsbGPS4Droid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with UsbGPS4Droid. If not, see .
+ */
+
+package org.broeuschmeul.android.gps.usb;
+
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbManager;
+import android.util.Log;
+
+import org.broeuschmeul.android.gps.usb.provider.BuildConfig;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/* USB CDC ACM (Communication Device Class Abstract Control Model) */
+public class UsbAcmController extends UsbSerialController {
+
+ // Debugging
+ private static final String TAG = UsbAcmController.class.getSimpleName();
+ private static final boolean D = BuildConfig.DEBUG & true;
+
+ private UsbDeviceConnection mUsbConnection;
+
+ private UsbSerialInputStream inputStream = null;
+ private UsbSerialOutputStream outputStream = null;
+ private UsbSerialInterruptListener interruptListener = null;
+
+ private int mBaudrate;
+
+ private final AcmConfig mAcmConfig;
+
+ private static class AcmConfig {
+
+ android.hardware.usb.UsbInterface mCommunicationInterface = null;
+ android.hardware.usb.UsbInterface mDataInterface = null;
+
+ UsbEndpoint mInterruptEndpoint = null;
+ UsbEndpoint mBulkInEndpoint = null;
+ UsbEndpoint mBulkOutEndpoint = null;
+
+ public AcmConfig(UsbDevice usbDevice) throws UsbControllerException {
+
+ int ifaceIdx, ifaceCount;
+ ifaceCount = usbDevice.getInterfaceCount();
+ foreachIface: for (ifaceIdx=0; ifaceIdx>> 8) & 0xff);
+ req[2] = (byte)((baudrate >>> 16) & 0xff);
+ req[3] = (byte)((baudrate >>> 24) & 0xff);
+
+ /* bCharFormat */
+ if (stopBits != 1 && (stopBits != 2)) throw new IllegalArgumentException("Wrong stop bits");
+ req[4] = stopBits == 1 ? (byte)0 : (byte)2;
+
+ /* bParityType */
+ switch (parity) {
+ case 'N': req[5] = 0; break; /* None */
+ case 'O': req[5] = 1; break; /* Odd */
+ case 'E': req[5] = 2; break; /* Even */
+ case 'M': req[5] = 3; break; /* Mark */
+ case 'S': req[5] = 4; break; /* Space */
+ default: throw new IllegalArgumentException("Wrong parity");
+ }
+
+ /* bDataBits */
+ switch (dataBits) {
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 16:
+ req[6] = (byte)dataBits;
+ break;
+ default: throw new IllegalArgumentException("Wrong data bits");
+ }
+
+ Log.d(TAG, "SetLineCoding rate=" + baudrate + " " +
+ Integer.toString(dataBits) +
+ Character.toString(parity) +
+ stopBits);
+
+ if (mUsbConnection.controlTransfer(
+ 0x21,
+ 0x20, /* SET_LINE_CODING */
+ 0,
+ 0, /* bulk data interface number */
+ req,
+ req.length,
+ 1000
+ ) < 0)
+ return false;
+
+ return true;
+ }
+}
diff --git a/src/org/broeuschmeul/android/gps/usb/UsbGpsManager.java b/src/org/broeuschmeul/android/gps/usb/UsbGpsManager.java
new file mode 100644
index 00000000..0277f463
--- /dev/null
+++ b/src/org/broeuschmeul/android/gps/usb/UsbGpsManager.java
@@ -0,0 +1,399 @@
+package org.broeuschmeul.android.gps.usb;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.ConditionVariable;
+import android.util.Log;
+
+import org.broeuschmeul.android.gps.usb.UsbSerialController.UsbControllerException;
+import org.broeuschmeul.android.gps.usb.provider.BuildConfig;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class UsbGpsManager {
+
+ private static final boolean DBG = BuildConfig.DEBUG & true;
+ static final String TAG = UsbGpsManager.class.getSimpleName();
+
+ // Constants that indicate the current connection state
+ public static final int STATE_IDLE = 0;
+ public static final int STATE_CONNECTING = 1;
+ public static final int STATE_CONNECTED = 2;
+ public static final int STATE_WAITING = 3;
+ public static final int STATE_RECONNECTING = 4;
+
+ public static final String ACTION_USB_DEVICE_ATTACHED = "ru0xdc.rtkgps.UsbToRtklib.ACTION_USB_DEVICE_ATTACHED";
+
+ final UsbReceiver mUsbReceiver;
+
+ private Callbacks mCallbacks;
+
+ public static final int RECONNECT_TIMEOUT_MS = 2000;
+
+ private static final Callbacks sDummyCallbacks = new Callbacks() {
+ @Override
+ public void onConnected() {}
+ @Override
+ public void onStopped() {}
+ @Override
+ public void onConnectionLost() {}
+ };
+
+
+ public interface Callbacks {
+
+ public void onConnected();
+
+ public void onStopped();
+
+ public void onConnectionLost();
+
+ }
+
+ public UsbGpsManager(Context serviceContext) {
+ mUsbReceiver = new UsbReceiver(serviceContext);
+ mCallbacks = sDummyCallbacks;
+ }
+
+ public void start() {
+ mUsbReceiver.start();
+ }
+
+ public void stop() {
+ mUsbReceiver.stop();
+ }
+
+ public void setBaudRate(int baudrate) {
+ mUsbReceiver.setBaudRate(baudrate);
+ }
+
+ public int getBaudRate() {
+ return mUsbReceiver.getBaudRate();
+ }
+
+ public void setCallbacks(Callbacks callbacks) {
+ if (callbacks == null) throw new IllegalStateException();
+ mCallbacks = callbacks;
+ }
+
+ private class UsbReceiver {
+
+ final String ACTION_USB_PERMISSION = UsbReceiver.class.getName() + ".USB_PERMISSION";
+
+ private int mBaudrate = UsbSerialController.DEFAULT_BAUDRATE;
+
+ private Context mContext;
+
+ private UsbManager mUsbManager;
+
+ final ConditionVariable mIsUsbDeviceReadyCondvar;
+
+ private UsbServiceThread mServiceThread;
+
+ public UsbReceiver(Context pContext) {
+
+ this.mContext = pContext;
+ this.mUsbManager = (UsbManager) pContext.getSystemService(Context.USB_SERVICE);
+ mIsUsbDeviceReadyCondvar = new ConditionVariable(false);
+
+ if (mUsbManager == null) throw new IllegalStateException("USB not available");
+ }
+
+ public synchronized void start() {
+ final IntentFilter f;
+ f = new IntentFilter();
+ f.addAction(ACTION_USB_DEVICE_ATTACHED);
+ f.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ f.addAction(ACTION_USB_PERMISSION);
+
+ mContext.registerReceiver(mUsbStateListener, f);
+
+ mServiceThread = new UsbServiceThread();
+ mServiceThread.start();
+
+ try {
+ final UsbDevice d = UsbUtils.findSupportedDevices(mUsbManager).get(0);
+ requestPermission(d);
+ }catch (IndexOutOfBoundsException ignore) { }
+
+ }
+
+ public synchronized void setBaudRate(int baudrate) {
+ this.mBaudrate = baudrate;
+ }
+
+ public synchronized int getBaudRate() {
+ return this.mBaudrate;
+ }
+
+
+ public boolean isDeviceReady() {
+ return mIsUsbDeviceReadyCondvar.block(1);
+ }
+
+ public void waitDevice() {
+ mIsUsbDeviceReadyCondvar.block();
+ }
+
+ public synchronized void write(byte[] buffer, int offset, int count) throws IOException {
+ if (mServiceThread == null) throw new IOException("not connected");
+ mServiceThread.write(buffer, offset, count);
+ }
+
+ public synchronized void stop() {
+ mContext.unregisterReceiver(mUsbStateListener);
+ mServiceThread.cancel();
+ mServiceThread = null;
+ mIsUsbDeviceReadyCondvar.open();
+ }
+
+
+ private void requestPermission(UsbDevice d) {
+ if (DBG) Log.d(TAG, "requestPermission() device=" + d.toString());
+ final PendingIntent premissionIntent = PendingIntent.getBroadcast(mContext,
+ 0, new Intent(ACTION_USB_PERMISSION), 0);
+ mUsbManager.requestPermission(d, premissionIntent);
+ }
+
+ void onUsbDeviceAttached(UsbDevice device) {
+ if (DBG) Log.d(TAG, "onUsbDeviceAttached() device=" + device.toString());
+
+ if (UsbUtils.probeDevice(mUsbManager, device) != null) {
+ requestPermission(device);
+ }
+ }
+
+ synchronized void onUsbDeviceDetached(UsbDevice device) {
+
+ if (DBG) Log.d(TAG, "onUsbDeviceDetached() device=" + device.toString());
+
+ UsbSerialController controller = mServiceThread.getController();
+
+ if (controller == null) return;
+ if (!device.equals(controller.getDevice())) return;
+
+ mServiceThread.setController(null);
+ }
+
+ synchronized void onUsbPermissionGranted(UsbDevice device) {
+ if (DBG) Log.d(TAG, "onUsbPermissionGranted() device=" + device.toString());
+ UsbSerialController controller = mServiceThread.getController();
+
+ if (controller != null) return;
+
+ controller = UsbUtils.probeDevice(mUsbManager, device);
+ if (controller == null) return;
+
+ controller.setBaudRate(mBaudrate);
+
+ mServiceThread.setController(controller);
+
+ }
+
+ private final BroadcastReceiver mUsbStateListener = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ UsbDevice device;
+ String action = intent.getAction();
+ Log.v(TAG, "Received intent " + action);
+
+ if (action.equals(ACTION_USB_DEVICE_ATTACHED)) {
+ device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ onUsbDeviceAttached(device);
+ }else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
+ device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ onUsbDeviceDetached(device);
+ }else if (action.equals(ACTION_USB_PERMISSION)) {
+ boolean granted;
+ device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
+ if (granted) {
+ onUsbPermissionGranted(device);
+ }
+ }else {
+ Log.e(TAG, "Unknown action " + action);
+ }
+ }
+ };
+
+ private class UsbServiceThread extends Thread {
+
+ private InputStream mInputStream;
+ private OutputStream mOutputStream;
+
+ private int mConnectionState;
+ private volatile boolean cancelRequested;
+
+ private UsbSerialController mUsbController;
+
+ private final ConditionVariable serialControllerSet;
+
+ public UsbServiceThread() {
+ mInputStream = UsbUtils.DummyInputStream.instance;
+ mOutputStream = UsbUtils.DummyOutputStream.instance;
+ mConnectionState = STATE_IDLE;
+ cancelRequested = false;
+ mUsbController = null;
+ serialControllerSet = new ConditionVariable(false);
+ }
+
+ public synchronized void setController(UsbSerialController controller) {
+ if (mUsbController != null) {
+ serialControllerSet.close();
+ mUsbController.detach();
+ }
+ mUsbController = controller;
+ if (controller != null) serialControllerSet.open();
+ }
+
+ public synchronized UsbSerialController getController() {
+ return mUsbController;
+ }
+
+ private synchronized void setState(int state) {
+ int oldState = mConnectionState;
+ mConnectionState = state;
+ if (DBG) Log.d(TAG, "setState() " + oldState + " -> " + state);
+
+ if (mConnectionState == STATE_CONNECTED)
+ mIsUsbDeviceReadyCondvar.open();
+ else {
+ mIsUsbDeviceReadyCondvar.close();
+ //mLocalSocketThread.disconnect();
+ }
+ }
+
+ public synchronized void cancel() {
+ cancelRequested = true;
+ mCallbacks.onStopped();
+ if (mUsbController != null) {
+ mUsbController.detach();
+ mUsbController=null;
+ }
+ }
+
+ /**
+ * Write to the connected OutStream.
+ * @param buffer The bytes to write
+ */
+ public void write(byte[] buffer, int offset, int count) throws IOException {
+ OutputStream os;
+ synchronized(this) {
+ if (mConnectionState != STATE_CONNECTED) {
+ Log.e(TAG, "write() error: not connected");
+ return;
+ }
+ os = mOutputStream;
+ }
+ os.write(buffer, offset, count);
+ }
+
+ private synchronized void throwIfCancelRequested() throws CancelRequestedException {
+ if (cancelRequested) throw new CancelRequestedException();
+ }
+
+ private void connect() throws UsbControllerException, CancelRequestedException {
+
+ serialControllerSet.block();
+
+ synchronized(UsbReceiver.this) {
+ synchronized (this) {
+ throwIfCancelRequested();
+ if (DBG) Log.v(TAG, "attach(). baudrate: "+ mUsbController.getBaudRate());
+ mUsbController.attach();
+ mInputStream = mUsbController.getInputStream();
+ mOutputStream = mUsbController.getOutputStream();
+ }
+ }
+ return;
+ }
+
+ private void connectLoop() throws CancelRequestedException {
+
+ if (DBG) Log.v(TAG, "connectLoop()");
+
+ while(true) {
+ try {
+ connect();
+ return;
+ }catch (UsbControllerException e) {
+ synchronized(this) {
+ throwIfCancelRequested();
+ setState(STATE_RECONNECTING);
+ try {
+ wait(RECONNECT_TIMEOUT_MS);
+ } catch(InterruptedException ie) {
+ throwIfCancelRequested();
+ }
+ }
+ }
+ }
+ }
+
+
+ private void transferDataLoop() throws CancelRequestedException {
+ int rcvd;
+ final byte buf[] = new byte[4096];
+
+ try {
+ while(true) {
+ rcvd = mInputStream.read(buf, 0, buf.length);
+ if (rcvd >= 0) {
+ /*
+ try {
+ //mLocalSocketThread.write(buf, 0, rcvd);
+ }catch (IOException e) {
+ // TODO
+ e.printStackTrace();
+ }
+ */
+ }
+ if (rcvd < 0)
+ throw new IOException("EOF");
+ }
+ }catch (IOException e) {
+ synchronized(this) {
+ if (mUsbController!=null) mUsbController.detach();
+ //mInputStream = RtklibLocalSocketThread.DummyInputStream.instance;
+ //mOutputStream = RtklibLocalSocketThread.DummyOutputStream.instance;
+ throwIfCancelRequested();
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ Log.i(TAG, "BEGIN UsbToLocalSocket-USB");
+ setName("UsbToLocalSocket-USB");
+ try {
+ setState(STATE_CONNECTING);
+ while (true) {
+ throwIfCancelRequested();
+ connectLoop();
+
+ setState(STATE_CONNECTED);
+ transferDataLoop();
+
+ setState(STATE_RECONNECTING);
+ mCallbacks.onConnectionLost();
+ }
+ }catch(CancelRequestedException cre) {}
+ }
+ }
+
+ private class CancelRequestedException extends Exception {
+ private static final long serialVersionUID = 1L;
+ }
+ }
+
+
+
+}
diff --git a/src/org/broeuschmeul/android/gps/usb/UsbPl2303Controller.java b/src/org/broeuschmeul/android/gps/usb/UsbPl2303Controller.java
new file mode 100644
index 00000000..5c53df5e
--- /dev/null
+++ b/src/org/broeuschmeul/android/gps/usb/UsbPl2303Controller.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2013 Alexey Illarionov
+ *
+ * This file is part of UsbGPS4Droid.
+ *
+ * UsbGPS4Droid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * UsbGPS4Droid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with UsbGPS4Droid. If not, see .
+ */
+
+package org.broeuschmeul.android.gps.usb;
+
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbManager;
+import android.util.Log;
+
+import org.broeuschmeul.android.gps.usb.provider.BuildConfig;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+
+public class UsbPl2303Controller extends UsbSerialController {
+
+ // Debugging
+ private static final String TAG = UsbPl2303Controller.class.getSimpleName();
+ private static final boolean D = BuildConfig.DEBUG & true;
+
+ private static final int PL2303_INIT_TIMEOUT_MS = 2000;
+
+ private static final int[] PL2303_BAUD_RATES= new int[] {
+ 75, 150, 300, 600, 1200,
+ 1800, 2400, 3600, 4800, 7200,
+ 9600, 14400, 19200, 28800, 38400,
+ 57600, 115200, 230400, 460800, 614400,
+ 921600, 1228800, 2457600, 3000000, 6000000, 12000000
+ };
+
+
+ private UsbDeviceConnection mUsbConnection;
+ private android.hardware.usb.UsbInterface mUsbInterfaces[];
+ private UsbEndpoint mInterruptEndpoint = null;
+ private UsbEndpoint mBulkInEndpoint = null;
+ private UsbEndpoint mBulkOutEndpoint = null;
+ private int mBaudrate;
+ private boolean isPl2303Hx;
+
+ private UsbSerialInputStream inputStream = null;
+ private UsbSerialOutputStream outputStream = null;
+ private UsbSerialInterruptListener interruptListener = null;
+
+
+ public UsbPl2303Controller(UsbManager usbManager, UsbDevice usbDevice)
+ throws UsbControllerException {
+ super(usbManager, usbDevice);
+
+ int endpointCount;
+
+ if (!UsbPl2303Controller.probe(usbDevice)) {
+ throw new UsbControllerException("probe() failed");
+ }
+
+ if (usbDevice.getInterfaceCount() != 1) {
+ throw new UsbControllerException("getInterfaceCount() != 1");
+ }
+
+ mUsbInterfaces = new android.hardware.usb.UsbInterface[1];
+ mUsbInterfaces[0] = usbDevice.getInterface(0);
+ endpointCount = mUsbInterfaces[0].getEndpointCount();
+ if (endpointCount != 3) {
+ throw new UsbControllerException("getEndpointCount() != 3 (" + endpointCount + ")");
+ }
+
+ for (int i=0; i < endpointCount; i++) {
+ UsbEndpoint e = mUsbInterfaces[0].getEndpoint(i);
+ switch (e.getType()) {
+ case UsbConstants.USB_ENDPOINT_XFER_INT:
+ mInterruptEndpoint = e;
+ break;
+ case UsbConstants.USB_ENDPOINT_XFER_BULK:
+ if (e.getDirection() == UsbConstants.USB_DIR_IN)
+ mBulkInEndpoint = e;
+ else
+ mBulkOutEndpoint = e;
+ break;
+ default:
+ throw new UsbControllerException("Unexpected endpoint " + i + "type = " + e.getType());
+ }
+ }
+
+ if (mInterruptEndpoint == null) {
+ throw new UsbControllerException("Interrupt input endpoint not found");
+ }else if (mBulkInEndpoint == null) {
+ throw new UsbControllerException("Bulk data input endpoint not found");
+ }else if (mBulkOutEndpoint == null) {
+ throw new UsbControllerException("Bulk data output endpoint not found");
+ }
+
+ isPl2303Hx = (usbDevice.getDeviceClass() != 0x02)
+ && (mBulkOutEndpoint.getMaxPacketSize() == 0x40);
+ mBaudrate = DEFAULT_BAUDRATE;
+ }
+
+ public static boolean probe(UsbDevice d) {
+
+ int vid, pid;
+ boolean passed = false;
+
+ vid = d.getVendorId();
+ pid = d.getProductId();
+
+ /* Keep in sync usb_device_filter.xml */
+ switch (vid) {
+ case 0x067b: /* Prolific */
+ switch (pid) {
+ case 0x2303: /* 0x067b 0x2303 PL2303 Serial */
+ case 0x1234: /* 0x067b 0x1234 DCU-11 Phone Cable */
+ passed = true;
+ break;
+ }
+ break;
+ case 0x5372: /* Prolific2 */
+ if (pid == 0x2303) passed = true; /* 0x5372 0x2303 Prolific2 PL2303 */
+ break;
+ }
+
+ if (D) Log.v(TAG, "Probe for " + vid + ":" + pid + " " + (passed ? "passed" : "failed"));
+
+ return passed;
+ }
+
+ public synchronized boolean isAttached() {
+ return inputStream != null;
+ }
+
+ @Override
+ public synchronized void attach() throws UsbControllerException {
+
+ if (isAttached()) return;
+
+ if (!mUsbManager.hasPermission(mUsbDevice)) {
+ throw new UsbControllerException("no permission");
+ }
+
+ mUsbConnection = mUsbManager.openDevice(mUsbDevice);
+ if (mUsbConnection == null) {
+ throw new UsbControllerException("openDevice() failed");
+ }
+
+ for (int i=0; i< mUsbInterfaces.length; ++i) {
+ if (mUsbConnection.claimInterface(mUsbInterfaces[i], true) == false) {
+ for (int j=0; j= 0;
+ }
+
+ private boolean pl2303Init() {
+ byte buf[] = new byte[4];
+ final int read = UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_VENDOR;
+ final int write = UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR;
+ final int tmout = PL2303_INIT_TIMEOUT_MS;
+
+ if ((mUsbConnection.controlTransfer(read, 0x01, 0x8484, 0, buf, 1, tmout) < 0)
+ || (mUsbConnection.controlTransfer(write, 0x01, 0x0404, 0, null, 0, tmout) < 0)
+ || (mUsbConnection.controlTransfer(read, 0x01, 0x8484, 0, buf, 1, tmout) < 0)
+ || (mUsbConnection.controlTransfer(read, 0x01, 0x8383, 0, buf, 1, tmout) < 0)
+ || (mUsbConnection.controlTransfer(read, 0x01, 0x8484, 0, buf, 1, tmout) < 0)
+ || (mUsbConnection.controlTransfer(write, 0x01, 0x0404, 1, null, 0, tmout) < 0)
+ || (mUsbConnection.controlTransfer(read, 0x01, 0x8484, 0, buf, 1, tmout) < 0)
+ || (mUsbConnection.controlTransfer(read, 0x01, 0x8383, 0, buf, 1, tmout) < 0)
+ || (mUsbConnection.controlTransfer(write, 0x01, 0, 1, null, 0, tmout) < 0)
+ || (mUsbConnection.controlTransfer(write, 0x01, 1, 0, null, 0, tmout) < 0)
+ ) return false;
+ if (isPl2303Hx) {
+ if (mUsbConnection.controlTransfer(write, 0x01, 2, 0x44, null, 0, tmout) < 0) return false;
+ }else {
+ if (mUsbConnection.controlTransfer(write, 0x01, 2, 0x24, null, 0, tmout) < 0) return false;
+ }
+
+ if ((mUsbConnection.controlTransfer(write, 0x01, 8, 0, null, 0, tmout) < 0)
+ || (mUsbConnection.controlTransfer(write, 0x01, 9, 0, null, 0, tmout) < 0)
+ ) return false;
+
+ return true;
+ }
+
+ private boolean pl2303SetLineCoding() {
+ return pl2303SetLineCoding(this.mBaudrate);
+ }
+
+ private boolean pl2303SetLineCoding(int baudrate) {
+ return pl2303SetLineCoding(baudrate, 8, 'N', 1);
+ }
+
+ private boolean pl2303SetLineCoding(int baudrate, int dataBits, char parity, int stopBits) {
+ int idx;
+ byte req[] = new byte[7];
+
+ /* dwDTERate */
+ idx = Arrays.binarySearch(PL2303_BAUD_RATES, baudrate);
+ if (idx < 0) {
+ baudrate = PL2303_BAUD_RATES[-idx == PL2303_BAUD_RATES.length ? -idx-1 : -idx];
+ }
+ req[0] = (byte)(baudrate & 0xff);
+ req[1] = (byte)((baudrate >>> 8) & 0xff);
+ req[2] = (byte)((baudrate >>> 16) & 0xff);
+ req[3] = (byte)((baudrate >>> 24) & 0xff);
+
+ /* bCharFormat */
+ if (stopBits != 1 && (stopBits != 2)) throw new IllegalArgumentException("Wrong stop bits");
+ req[4] = stopBits == 1 ? (byte)0 : (byte)2;
+
+ /* bParityType */
+ switch (parity) {
+ case 'N': req[5] = 0; break; /* None */
+ case 'O': req[5] = 1; break; /* Odd */
+ case 'E': req[5] = 2; break; /* Even */
+ case 'M': req[5] = 3; break; /* Mark */
+ case 'S': req[5] = 4; break; /* Space */
+ default: throw new IllegalArgumentException("Wrong parity");
+ }
+
+ /* bDataBits */
+ switch (dataBits) {
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ req[6] = (byte)dataBits;
+ break;
+ default: throw new IllegalArgumentException("Wrong data bits");
+ }
+
+ Log.d(TAG, "SetLineCoding rate=" + baudrate + " " +
+ Integer.toString(dataBits) +
+ Character.toString(parity) +
+ stopBits);
+
+ if (mUsbConnection.controlTransfer(
+ 0x21,
+ 0x20, /* SET_LINE_CODING */
+ 0,
+ 0, /* bulk data interface number */
+ req,
+ req.length,
+ 1000
+ ) < 0)
+ return false;
+
+ /* CRTSCTS=off */
+ if (mUsbConnection.controlTransfer(
+ UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
+ 0x01, 0, 0, null, 0, 1000) < 0)
+ return false;
+
+ return true;
+ }
+
+}
diff --git a/src/org/broeuschmeul/android/gps/usb/UsbSerialController.java b/src/org/broeuschmeul/android/gps/usb/UsbSerialController.java
new file mode 100644
index 00000000..b791d0cd
--- /dev/null
+++ b/src/org/broeuschmeul/android/gps/usb/UsbSerialController.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2013 Alexey Illarionov
+ *
+ * This file is part of UsbGPS4Droid.
+ *
+ * UsbGPS4Droid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * UsbGPS4Droid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with UsbGPS4Droid. If not, see .
+ */
+
+package org.broeuschmeul.android.gps.usb;
+
+import android.app.PendingIntent;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbRequest;
+import android.util.Log;
+
+import org.broeuschmeul.android.gps.usb.provider.BuildConfig;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+public abstract class UsbSerialController {
+
+ // Debugging
+ private static final String TAG = UsbSerialController.class.getSimpleName();
+ private static final boolean D = BuildConfig.DEBUG & true;
+
+ public static final int DEFAULT_BAUDRATE = 4800;
+
+ protected UsbManager mUsbManager;
+ protected UsbDevice mUsbDevice;
+
+ public UsbSerialController(UsbManager usbManager,
+ UsbDevice usbDevice) throws UsbControllerException {
+ this.mUsbDevice = usbDevice;
+ this.mUsbManager = usbManager;
+ }
+
+ public abstract void attach() throws UsbControllerException;
+ public abstract void detach();
+
+ public abstract void setBaudRate(int baudRate);
+ public abstract int getBaudRate();
+
+ public abstract InputStream getInputStream();
+ public abstract OutputStream getOutputStream();
+
+ public boolean hasPermission() {
+ return this.mUsbManager.hasPermission(this.mUsbDevice);
+ }
+
+ public void requestPermission(PendingIntent pi) {
+ this.mUsbManager.requestPermission(mUsbDevice, pi);
+ }
+
+ public static class UsbControllerException extends Exception {
+ private static final long serialVersionUID = 1L;
+ public UsbControllerException(String msg) { super(msg); }
+ }
+
+ public UsbDevice getDevice() {
+ return mUsbDevice;
+ }
+
+ protected static class UsbSerialInterruptListener extends Thread {
+
+ private boolean cancelRequested = false;
+ private UsbDeviceConnection mUsbConnection;
+ private ByteBuffer buffer;
+ private UsbRequest request;
+
+ public UsbSerialInterruptListener(UsbDeviceConnection connection, UsbEndpoint endpoint) {
+ this.mUsbConnection = connection;
+ this.setName("PL2303InterruptListener");
+ buffer = ByteBuffer.allocate(endpoint.getMaxPacketSize());
+ request = new UsbRequest();
+ request.initialize(connection, endpoint);
+ }
+
+ @Override
+ public void run() {
+ mainloop: while(!cancelRequested()) {
+ request.queue(buffer, buffer.capacity());
+ if (mUsbConnection.requestWait() == request) {
+ if (D) Log.v(TAG, "Interrupt received: " + buffer.toString() +
+ Arrays.toString(buffer.array()));
+ synchronized(this) {
+ try {
+ this.wait(100);
+ } catch (InterruptedException e) {
+ break mainloop;
+ }
+ if (cancelRequested) break mainloop;
+ }
+ }else {
+ Log.e(TAG, "requestWait failed, exiting");
+ break mainloop;
+ }
+ }
+ Log.d(TAG, "Pl2303InterruptListener thread stopped");
+ }
+
+ public synchronized void cancel() {
+ cancelRequested = true;
+ this.notify();
+ }
+
+ private synchronized boolean cancelRequested() {
+ return this.cancelRequested;
+ }
+
+ }
+
+ protected class UsbSerialInputStream extends InputStream {
+
+ private static final int DEFAULT_READ_TIMEOUT_MS = 30000;
+ private int mTimeout = DEFAULT_READ_TIMEOUT_MS;
+
+ private UsbDeviceConnection mUsbConnection;
+ private UsbEndpoint mUsbEndpoint;
+ private byte rcvPkt[] = null;
+
+ public UsbSerialInputStream(UsbDeviceConnection connection,
+ UsbEndpoint bulkInEndpoint,
+ int writeTmoutMs
+ ) {
+ mUsbConnection = connection;
+ mUsbEndpoint = bulkInEndpoint;
+ mTimeout = writeTmoutMs;
+ rcvPkt = new byte[mUsbEndpoint.getMaxPacketSize()];
+ }
+
+ public UsbSerialInputStream(UsbDeviceConnection connection,
+ UsbEndpoint bulkOutEndpoint) {
+ this(connection, bulkOutEndpoint, DEFAULT_READ_TIMEOUT_MS);
+ }
+
+ @Override
+ public int read() throws IOException {
+ synchronized(this) {
+ int rcvd = read(rcvPkt, 0, 1);
+ if (rcvd == 0) throw new IOException("timeout");
+ return rcvPkt[0] & 0xff;
+ }
+ }
+
+ @Override
+ public int read(byte[] buffer, int offset, int count) throws IOException {
+ int rcvd;
+
+ synchronized(this) {
+ if (offset == 0) {
+ rcvd = mUsbConnection.bulkTransfer(mUsbEndpoint, buffer,
+ count, mTimeout);
+ if (rcvd < 0) throw new IOException("bulkTransfer() error");
+ //if (D) Log.d(TAG, "Received " + rcvd + " bytes aligned");
+ return rcvd;
+ }else {
+ rcvd = mUsbConnection.bulkTransfer(mUsbEndpoint,
+ rcvPkt,
+ Math.min(count, rcvPkt.length),
+ mTimeout);
+ if (rcvd < 0) throw new IOException("bulkTransfer() error");
+ else if (rcvd > 0) {
+ System.arraycopy(rcvPkt, 0, buffer, offset, rcvd);
+ }
+ if (D) Log.d(TAG, "Received " + rcvd + " bytes");
+ return rcvd;
+ }
+ }
+ }
+ }
+
+ protected class UsbSerialOutputStream extends OutputStream {
+
+ private static final int DEFAULT_WRITE_TIMEOUT_MS = 2000;
+ private int mTimeout = DEFAULT_WRITE_TIMEOUT_MS;
+
+ private UsbDeviceConnection mUsbConnection;
+ private UsbEndpoint mUsbEndpoint;
+ private byte sndPkt[] = null;
+
+ public UsbSerialOutputStream(UsbDeviceConnection connection,
+ UsbEndpoint bulkOutEndpoint,
+ int writeTmoutMs
+ ) {
+ mUsbConnection = connection;
+ mUsbEndpoint = bulkOutEndpoint;
+ mTimeout = writeTmoutMs;
+ sndPkt = new byte[mUsbEndpoint.getMaxPacketSize()];
+ }
+
+ public UsbSerialOutputStream(UsbDeviceConnection connection,
+ UsbEndpoint bulkOutEndpoint) {
+ this(connection, bulkOutEndpoint, DEFAULT_WRITE_TIMEOUT_MS);
+ }
+
+ @Override
+ public void write(int arg0) throws IOException {
+ write(new byte[] { (byte) arg0 } );
+ }
+
+ @Override
+ public void write(byte[] buffer, int offset, int count) throws IOException {
+ synchronized(this) {
+ while(count>0) {
+ /* XXX: timeout */
+ int length = count > sndPkt.length ? sndPkt.length : count;
+ System.arraycopy(buffer, offset, sndPkt, 0, length);
+ int snd = mUsbConnection.bulkTransfer(mUsbEndpoint, sndPkt, length, mTimeout);
+ if (snd<0) throw new IOException("bulkTransfer() failed");
+ count -= snd;
+ offset += snd;
+ }
+ }
+ }
+ }
+
+}
+
diff --git a/src/org/broeuschmeul/android/gps/usb/UsbUtils.java b/src/org/broeuschmeul/android/gps/usb/UsbUtils.java
new file mode 100644
index 00000000..1459af43
--- /dev/null
+++ b/src/org/broeuschmeul/android/gps/usb/UsbUtils.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2013 Alexey Illarionov
+ *
+ * This file is part of UsbGPS4Droid.
+ *
+ * UsbGPS4Droid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * UsbGPS4Droid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with UsbGPS4Droid. If not, see .
+ */
+
+package org.broeuschmeul.android.gps.usb;
+
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.util.Log;
+
+import org.broeuschmeul.android.gps.usb.UsbSerialController.UsbControllerException;
+import org.broeuschmeul.android.gps.usb.provider.BuildConfig;
+import org.broeuschmeul.android.gps.usb.provider.R;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class UsbUtils {
+
+ // Debugging
+ private static final String TAG = UsbUtils.class.getSimpleName();
+ private static final boolean DBG = BuildConfig.DEBUG & true;
+
+
+ public static boolean isInUsbDeviceFilter(UsbDevice d, Resources appResources) {
+ int type;
+ XmlResourceParser parser;
+ boolean match = false;
+
+ parser = appResources.getXml(R.xml.device_filter);
+ try {
+ for (type=parser.getEventType();
+ !match && (type != XmlResourceParser.END_DOCUMENT);
+ type = parser.next()) {
+ int count;
+ int vendorId = -1;
+ int productId = -1;
+ int deviceClass = -1;
+ int deviceSubclass = -1;
+ int deviceProtocol = -1;
+
+
+ if (type != XmlResourceParser.START_TAG) continue;
+ if ("usb-device".equals(parser.getName())) continue;
+
+ count = parser.getAttributeCount();
+ for(int i=0; i findSupportedDevices(UsbManager usbManager) {
+ final ArrayList supportedList;
+ final HashMap deviceList;
+
+ deviceList = usbManager.getDeviceList();
+ supportedList = new ArrayList(deviceList.size());
+
+ for (UsbDevice d: deviceList.values()) {
+ if (probeDevice(usbManager, d) != null) {
+ supportedList.add(d);
+ }
+ }
+ return supportedList;
+ }
+
+ public static UsbSerialController probeDevice(UsbManager usbManager, UsbDevice d) {
+ if (DBG) Log.d(TAG, "probeDevice() device=" + d.toString());
+ try {
+ final UsbPl2303Controller c = new UsbPl2303Controller(usbManager, d);
+ return c;
+ }catch(UsbControllerException ignore) { }
+
+ try {
+ final UsbAcmController c = new UsbAcmController(usbManager, d);
+ return c;
+ }catch (UsbControllerException ignore) {}
+
+ return null;
+ }
+
+ static class DummyInputStream extends InputStream {
+
+ public static final DummyInputStream instance = new DummyInputStream();
+
+ private DummyInputStream() {}
+
+ @Override
+ public int read() throws IOException {
+ return -1;
+ }
+
+ @Override
+ public int read(byte[] buffer, int offset, int length) throws IOException {
+ return -1;
+ }
+ }
+
+ static class DummyOutputStream extends OutputStream {
+
+ public static final DummyOutputStream instance = new DummyOutputStream();
+
+ private DummyOutputStream() {}
+
+ @Override
+ public void write(int arg0) throws IOException {
+ }
+
+ @Override
+ public void write(byte[] buffer, int offset, int count)
+ throws IOException {
+ }
+ }
+
+
+}
diff --git a/src/org/broeuschmeul/android/gps/bluetooth/provider/BlueetoothGpsManager.java b/src/org/broeuschmeul/android/gps/usb/provider/BlueetoothGpsManager.java
similarity index 67%
rename from src/org/broeuschmeul/android/gps/bluetooth/provider/BlueetoothGpsManager.java
rename to src/org/broeuschmeul/android/gps/usb/provider/BlueetoothGpsManager.java
index 51231840..a405fe63 100644
--- a/src/org/broeuschmeul/android/gps/bluetooth/provider/BlueetoothGpsManager.java
+++ b/src/org/broeuschmeul/android/gps/usb/provider/BlueetoothGpsManager.java
@@ -2,84 +2,72 @@
* Copyright (C) 2010, 2011, 2012 Herbert von Broeuschmeul
* Copyright (C) 2010, 2011, 2012 BluetoothGPS4Droid Project
* Copyright (C) 2011, 2012 UsbGPS4Droid Project
- *
+ *
* This file is part of UsbGPS4Droid.
*
* UsbGPS4Droid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* UsbGPS4Droid is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with UsbGPS4Droid. If not, see .
*/
-package org.broeuschmeul.android.gps.bluetooth.provider;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.UUID;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import org.broeuschmeul.android.gps.usb.provider.BuildConfig;
-import org.broeuschmeul.android.gps.usb.provider.R;
-import org.broeuschmeul.android.gps.nmea.util.NmeaParser;
-import org.broeuschmeul.android.gps.sirf.util.SirfUtils;
+package org.broeuschmeul.android.gps.usb.provider;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
-//import android.bluetooth.BluetoothAdapter;
-//import android.bluetooth.BluetoothDevice;
-//import android.bluetooth.BluetoothSocket;
import android.content.Context;
+import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
-import android.content.Intent;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
-import android.hardware.usb.UsbRequest;
-import android.location.LocationManager;
import android.location.GpsStatus.NmeaListener;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
+import android.location.LocationManager;
import android.os.Bundle;
import android.os.SystemClock;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
import android.util.Log;
+import org.broeuschmeul.android.gps.nmea.util.NmeaParser;
+import org.broeuschmeul.android.gps.sirf.util.SirfUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
/**
* This class is used to establish and manage the connection with the bluetooth GPS.
- *
+ *
* @author Herbert von Broeuschmeul
*
*/
@@ -95,7 +83,8 @@ public class BlueetoothGpsManager {
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
+ @Override
+ public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
@@ -112,11 +101,6 @@ public void onReceive(Context context, Intent intent) {
Log.v(LOG_TAG, "will use default device speed: " + defaultDeviceSpeed);
deviceSpeed = defaultDeviceSpeed;
}
-// // reset eventual disabling cause
-// // setDisableReason(0);
-// // connection obtained so reset the number of connection try
-// nbRetriesRemaining = 1+maxConnectionRetries ;
-// notificationManager.cancel(R.string.connection_problem_notification_title);
Log.v(LOG_TAG, "starting usb reading task");
connectedGps = new ConnectedGps(device, deviceSpeed);
connectionAndReadingPool.execute(connectedGps);
@@ -134,47 +118,40 @@ public void onReceive(Context context, Intent intent) {
/**
* A utility class used to manage the communication with the bluetooth GPS whn the connection has been established.
* It is used to read NMEA data from the GPS or to send SIRF III binary commands or SIRF III NMEA commands to the GPS.
- * You should run the main read loop in one thread and send the commands in a separate one.
- *
+ * You should run the main read loop in one thread and send the commands in a separate one.
+ *
* @author Herbert von Broeuschmeul
*
*/
private class ConnectedGps extends Thread {
- /**
- * GPS bluetooth socket used for communication.
- */
- private final File gpsDev ;
- private final UsbDevice gpsUsbDev ;
private final UsbInterface intf;
private UsbEndpoint endpointIn = null;
private UsbEndpoint endpointOut = null;
private final UsbDeviceConnection connection;
private boolean closed = false;
/**
- * GPS InputStream from which we read data.
+ * GPS InputStream from which we read data.
*/
private final InputStream in;
/**
- * GPS output stream to which we send data (SIRF III binary commands).
+ * GPS output stream to which we send data (SIRF III binary commands).
*/
private final OutputStream out;
/**
- * GPS output stream to which we send data (SIRF III NMEA commands).
+ * GPS output stream to which we send data (SIRF III NMEA commands).
*/
private final PrintStream out2;
/**
- * A boolean which indicates if the GPS is ready to receive data.
+ * A boolean which indicates if the GPS is ready to receive data.
* In fact we consider that the GPS is ready when it begins to sends data...
*/
private boolean ready = false;
public ConnectedGps(UsbDevice device) {
- this(device, defaultDeviceSpeed);
+ this(device, defaultDeviceSpeed);
}
public ConnectedGps(UsbDevice device, String deviceSpeed) {
- this.gpsDev = null;
- this.gpsUsbDev = device;
intf = device.getInterface(0);
int i = intf.getEndpointCount();
endpointIn = null;
@@ -198,11 +175,11 @@ public ConnectedGps(UsbDevice device, String deviceSpeed) {
Log.d(LOG_TAG, "claiming interface");
resclaim = connection.claimInterface(intf, true);
Log.d(LOG_TAG, "data claim"+ resclaim);
-
+
InputStream tmpIn = null;
OutputStream tmpOut = null;
PrintStream tmpOut2 = null;
-
+
tmpIn = new InputStream() {
private byte[] buffer = new byte[128];
private byte[] usbBuffer = new byte[64];
@@ -210,8 +187,8 @@ public ConnectedGps(UsbDevice device, String deviceSpeed) {
private ByteBuffer bufferWrite = ByteBuffer.wrap(buffer);
private ByteBuffer bufferRead = (ByteBuffer)ByteBuffer.wrap(buffer).limit(0);
private boolean closed = false;
-
- @Override
+
+ @Override
public int read() throws IOException {
int b = 0;
if (BuildConfig.DEBUG || debug) Log.d(LOG_TAG, "trying to read data");
@@ -222,7 +199,7 @@ public int read() throws IOException {
if (nb > 0){
b = oneByteBuffer[0];
} else {
- // TODO : if nb = 0 then we have a pb
+ // TODO : if nb = 0 then we have a pb
b = -1;
Log.e(LOG_TAG, "data read() error code: " + nb );
}
@@ -280,7 +257,7 @@ public int read(byte[] buffer, int offset, int length)
if (n > bufferWrite.remaining()){
bufferRead.rewind();
bufferWrite.clear();
- }
+ }
bufferWrite.put(usbBuffer, 0, n);
bufferRead.limit(bufferWrite.position());
// if (BuildConfig.DEBUG || debug) Log.d(LOG_TAG, "data read: nb: " + n + " current: " + bufferRead.position() + " limit: " + bufferRead.limit() + " " + Arrays.toString(bufferRead.array()));
@@ -338,7 +315,7 @@ public void close() throws IOException {
closed = true;
}
};
-
+
tmpOut = new OutputStream() {
private byte[] buffer = new byte[128];
private byte[] usbBuffer = new byte[64];
@@ -346,7 +323,7 @@ public void close() throws IOException {
private ByteBuffer bufferWrite = ByteBuffer.wrap(buffer);
private ByteBuffer bufferRead = (ByteBuffer)ByteBuffer.wrap(buffer).limit(0);
private boolean closed = false;
-
+
@Override
public void write(int oneByte) throws IOException {
if (BuildConfig.DEBUG || debug) Log.d(LOG_TAG, "trying to write data (one byte): " + oneByte + " char: " + (char)oneByte);
@@ -367,7 +344,7 @@ public void write(byte[] buffer, int offset, int count)
if (BuildConfig.DEBUG || debug) Log.d(LOG_TAG, "trying to write data : " + Arrays.toString(this.buffer));
int n = 0;
if (! closed){
- n = connection.bulkTransfer(endpointOut, this.buffer, count, TIMEOUT);
+ n = connection.bulkTransfer(endpointOut, this.buffer, count, TIMEOUT);
} else {
Log.e(LOG_TAG, "error while trying to write data: outputStream closed");
}
@@ -405,9 +382,9 @@ public void write(byte[] buffer) throws IOException {
// TODO Auto-generated method stub
super.write(buffer);
}
-
+
};
-
+
try {
if (tmpOut != null){
tmpOut2 = new PrintStream(tmpOut, false, "US-ASCII");
@@ -452,7 +429,7 @@ public void run() {
// final byte[] datax = new byte[7];
// final ByteBuffer connectionSpeedInfoBuffer = ByteBuffer.wrap(datax,0,7).order(java.nio.ByteOrder.LITTLE_ENDIAN);
try {
- int res0 = connection.controlTransfer(0xA1, 33, 0, 0, datax, 7, 0);
+ int res0 = connection.controlTransfer(0xA1, 33, 0, 0, datax, 7, 0);
BlueetoothGpsManager.this.deviceSpeed = Integer.toString(connectionSpeedInfoBuffer.getInt(0));
Log.e(LOG_TAG, "info connection: " + Arrays.toString(datax));
Log.e(LOG_TAG, "info connection speed: " + BlueetoothGpsManager.this.deviceSpeed);
@@ -464,7 +441,7 @@ public void run() {
Log.e(LOG_TAG, "trying to use speed " + speed);
Log.d(LOG_TAG, "initializing connection: " + speed + " baud and 8N1 (0 bits no parity 1 stop bit");
connectionSpeedBuffer.putInt(0, speed);
- int res2 = connection.controlTransfer(0x21, 32, 0, 0, data, 7, 0);
+ int res2 = connection.controlTransfer(0x21, 32, 0, 0, data, 7, 0);
if (sirfGps){
Log.e(LOG_TAG, "trying to switch from SiRF binaray to NMEA");
connection.bulkTransfer(endpointOut, sirfBin2Nmea, sirfBin2Nmea.length, 0);
@@ -473,7 +450,7 @@ public void run() {
Thread.sleep(4000);
}
}
- res0 = connection.controlTransfer(0xA1, 33, 0, 0, datax, 7, 0);
+ res0 = connection.controlTransfer(0xA1, 33, 0, 0, datax, 7, 0);
Log.e(LOG_TAG, "info connection: " + Arrays.toString(datax));
Log.e(LOG_TAG, "info connection speed: " + connectionSpeedInfoBuffer.getInt(0));
if (! closed) {
@@ -496,13 +473,14 @@ public void run() {
ready = false;
autoConf.start();
}
-
+
public boolean isReady(){
return ready;
}
-
+
private long lastRead = 0;
- public void run() {
+ @Override
+ public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(in,"US-ASCII"),128);
// InputStreamReader reader = new InputStreamReader(in,"US-ASCII");
@@ -583,7 +561,7 @@ public void write(String buffer) {
Log.e(LOG_TAG, "Exception during write", e);
}
}
-
+
public void close(){
ready = false;
closed = true;
@@ -629,7 +607,7 @@ public void close(){
private boolean enabled = false;
private ExecutorService notificationPool;
private ScheduledExecutorService connectionAndReadingPool;
- private List nmeaListeners = Collections.synchronizedList(new LinkedList());
+ private List nmeaListeners = Collections.synchronizedList(new LinkedList());
private LocationManager locationManager;
private SharedPreferences sharedPreferences;
private ConnectedGps connectedGps;
@@ -659,27 +637,27 @@ public BlueetoothGpsManager(Service callingService, String deviceAddress, int ma
this.appContext = callingService.getApplicationContext();
locationManager = (LocationManager)callingService.getSystemService(Context.LOCATION_SERVICE);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(callingService);
- deviceSpeed = sharedPreferences.getString(BluetoothGpsProviderService.PREF_GPS_DEVICE_SPEED, callingService.getString(R.string.defaultGpsDeviceSpeed));
+ deviceSpeed = sharedPreferences.getString(UsbGpsProviderService.PREF_GPS_DEVICE_SPEED, callingService.getString(R.string.defaultGpsDeviceSpeed));
defaultDeviceSpeed = callingService.getString(R.string.defaultGpsDeviceSpeed);
setDeviceSpeed = !deviceSpeed.equals(callingService.getString(R.string.autoGpsDeviceSpeed));
- sirfGps = sharedPreferences.getBoolean(BluetoothGpsProviderService.PREF_SIRF_GPS, false);
+ sirfGps = sharedPreferences.getBoolean(UsbGpsProviderService.PREF_SIRF_GPS, false);
notificationManager = (NotificationManager)callingService.getSystemService(Context.NOTIFICATION_SERVICE);
- parser.setLocationManager(locationManager);
-
+ parser.setLocationManager(locationManager);
+
connectionProblemNotification = new Notification();
connectionProblemNotification.icon = R.drawable.ic_stat_notify;
- Intent stopIntent = new Intent(BluetoothGpsProviderService.ACTION_STOP_GPS_PROVIDER);
+ Intent stopIntent = new Intent(UsbGpsProviderService.ACTION_STOP_GPS_PROVIDER);
// PendingIntent stopPendingIntent = PendingIntent.getService(appContext, 0, stopIntent, PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent stopPendingIntent = PendingIntent.getService(appContext, 0, stopIntent, PendingIntent.FLAG_CANCEL_CURRENT);
connectionProblemNotification.contentIntent = stopPendingIntent;
serviceStoppedNotification = new Notification();
serviceStoppedNotification.icon=R.drawable.ic_stat_notify;
- Intent restartIntent = new Intent(BluetoothGpsProviderService.ACTION_START_GPS_PROVIDER);
+ Intent restartIntent = new Intent(UsbGpsProviderService.ACTION_START_GPS_PROVIDER);
PendingIntent restartPendingIntent = PendingIntent.getService(appContext, 0, restartIntent, PendingIntent.FLAG_CANCEL_CURRENT);
- serviceStoppedNotification.setLatestEventInfo(appContext,
- appContext.getString(R.string.service_closed_because_connection_problem_notification_title),
- appContext.getString(R.string.service_closed_because_connection_problem_notification),
+ serviceStoppedNotification.setLatestEventInfo(appContext,
+ appContext.getString(R.string.service_closed_because_connection_problem_notification_title),
+ appContext.getString(R.string.service_closed_because_connection_problem_notification),
restartPendingIntent);
usbManager = (UsbManager) callingService.getSystemService(callingService.USB_SERVICE);
@@ -689,14 +667,14 @@ public BlueetoothGpsManager(Service callingService, String deviceAddress, int ma
private void setDisableReason(int reasonId){
disableReason = reasonId;
}
-
+
/**
* @return
*/
public int getDisableReason(){
return disableReason;
}
-
+
/**
* @return true if the bluetooth GPS is enabled
*/
@@ -713,237 +691,63 @@ public synchronized boolean enable() {
notificationManager.cancel(R.string.service_closed_because_connection_problem_notification_title);
if (! enabled){
Log.d(LOG_TAG, "enabling Bluetooth GPS manager");
-// final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-// if (bluetoothAdapter == null) {
-// // Device does not support Bluetooth
-// Log.e(LOG_TAG, "Device does not support Bluetooth");
-// disable(R.string.msg_bluetooth_unsupported);
-// } else if (!bluetoothAdapter.isEnabled()) {
-// // Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
-// // startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
-// Log.e(LOG_TAG, "Bluetooth is not enabled");
-// disable(R.string.msg_bluetooth_disabled);
-// } else
if (Settings.Secure.getInt(callingService.getContentResolver(),Settings.Secure.ALLOW_MOCK_LOCATION, 0)==0){
Log.e(LOG_TAG, "Mock location provider OFF");
disable(R.string.msg_mock_location_disabled);
-// } else if ( (! locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
-// && (sharedPreferences.getBoolean(BluetoothGpsProviderService.PREF_REPLACE_STD_GPS, true))
-// ) {
-// Log.e(LOG_TAG, "GPS location provider OFF");
-// disable(R.string.msg_gps_provider_disabled);
} else {
-// final BluetoothDevice gpsDevice = bluetoothAdapter.getRemoteDevice(gpsDeviceAddress);
final String gpsDevice = gpsDeviceAddress;
if (gpsDevice == null || "".equals(gpsDevice)){
- Log.e(LOG_TAG, "GPS device not found");
+ Log.e(LOG_TAG, "GPS device not found");
disable(R.string.msg_gps_unavaible);
} else {
Log.e(LOG_TAG, "current device: "+gpsDevice );
-// try {
-// gpsSocket = gpsDevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
-// gpsDev = new File(gpsDeviceAddress);
gpsDev = usbManager.getDeviceList().get(gpsDevice);
-// } catch (IOException e) {
-// Log.e(LOG_TAG, "Error during connection", e);
-// gpsDev = null;
-// }
if (gpsDev == null){
Log.e(LOG_TAG, "Error while establishing connection: no device");
disable(R.string.msg_gps_unavaible);
} else {
- Runnable connectThread = new Runnable() {
- @Override
- public void run() {
- try {
- connected = false;
- Log.v(LOG_TAG, "current device: "+gpsDevice);
- if (/*(bluetoothAdapter.isEnabled()) && */(nbRetriesRemaining > 0 )){
-// try {
- if (connectedGps != null){
- connectedGps.close();
- }
- PendingIntent permissionIntent = PendingIntent.getBroadcast(callingService, 0, new Intent(ACTION_USB_PERMISSION), 0);
-// HashMap connectedUsbDevices = usbManager.getDeviceList();
-// UsbDevice device = connectedUsbDevices.values().iterator().next();
- gpsDev = usbManager.getDeviceList().get(gpsDevice);
- UsbDevice device = gpsDev;
- if (device != null && usbManager.hasPermission(device)){
- Log.d(LOG_TAG, "We have permession, good!");
- connected = true;
- if (setDeviceSpeed){
- Log.v(LOG_TAG, "will set devive speed: " + deviceSpeed);
- } else {
- Log.v(LOG_TAG, "will use default device speed: " + defaultDeviceSpeed);
- deviceSpeed = defaultDeviceSpeed;
- }
-// // reset eventual disabling cause
-// // setDisableReason(0);
-// // connection obtained so reset the number of connection try
-// nbRetriesRemaining = 1+maxConnectionRetries ;
-// notificationManager.cancel(R.string.connection_problem_notification_title);
- Log.v(LOG_TAG, "starting usb reading task");
- connectedGps = new ConnectedGps(device, deviceSpeed);
- connectionAndReadingPool.execute(connectedGps);
- Log.v(LOG_TAG, "usb reading thread started");
- } else if (device != null) {
- Log.d(LOG_TAG, "We don't have permession, so resquesting...");
- usbManager.requestPermission(device, permissionIntent);
- } else {
- Log.e(LOG_TAG, "Error while establishing connection: no device - " + gpsDevice);
- disable(R.string.msg_gps_unavaible);
- }
-
-// if ((gpsDev != null) && ((connectedGps == null) || (connectedGps.gpsDev != gpsDev))){
-// Log.d(LOG_TAG, "trying to close old socket");
-// gpsSocket.close();
-// }
-// } catch (IOException e) {
-// Log.e(LOG_TAG, "Error during disconnection", e);
-// }
-//// try {
-//// gpsSocket = gpsDevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
-// gpsDev = new File(gpsDeviceAddress);
-// // verify if we have enough rights..
-// Log.v(LOG_TAG, "Will verify if device exists and is a file: "+gpsDev.getAbsolutePath());
-// if (gpsDev.exists()){
-// Log.v(LOG_TAG, "Device exists and is a file: "+gpsDev.getAbsolutePath());
-// if (! gpsDev.canRead()){
-// Log.v(LOG_TAG, "Device is not readable, will try chmod 666 "+gpsDev.getAbsolutePath());
-// try {
-// // Try to get root privileges
-// Process p = Runtime.getRuntime().exec("su");
-// // change device rights
-// PrintStream os = new PrintStream(p.getOutputStream(),true);
-// os.println("chmod 666 "+ gpsDev.getAbsolutePath());
-// // exit
-// os.println("exit");
-// try {
-// p.waitFor();
-//// if (p.exitValue() != 255) {
-//// }
-//// else {
-//// }
-// } catch (InterruptedException e) {
-// // TODO update message
-// Log.e(LOG_TAG, "Error while establishing connection: ", e);
-// } finally {
-// p.destroy();
-// }
-// } catch (IOException e) {
-// // TODO update message
-// Log.e(LOG_TAG, "Error while establishing connection: ", e);
-// gpsDev = null;
-// } catch (SecurityException e) {
-// // TODO update message
-// Log.e(LOG_TAG, "Error while establishing connection: ", e);
-// gpsDev = null;
-// }
-// } else {
-// Log.v(LOG_TAG, "Device is readable: "+gpsDev.getAbsolutePath());
-// }
-// if (setDeviceSpeed){
-// Log.v(LOG_TAG, "will set devive spped: " + deviceSpeed);
-// try {
-// // Try to get root privileges
-// Process p = Runtime.getRuntime().exec("su");
-// // change device speed
-// PrintStream os = new PrintStream(p.getOutputStream(),true);
-//// os.println("stty -F "+ gpsDev.getAbsolutePath() + " ispeed "+deviceSpeed);
-// os.println("busybox stty -F "+ gpsDev.getAbsolutePath() + " ispeed "+deviceSpeed);
-// // exit
-// os.println("exit");
-// try {
-// p.waitFor();
-//// if (p.exitValue() != 255) {
-//// }
-//// else {
-//// }
-// } catch (InterruptedException e) {
-// // TODO update message
-// Log.e(LOG_TAG, "Error while changing device speed: ", e);
-// } finally {
-// p.destroy();
-// }
-// } catch (IOException e) {
-// // TODO update message
-// Log.e(LOG_TAG, "Error while while changing device speed: ", e);
-// // gpsDev = null;
-// } catch (SecurityException e) {
-// // TODO update message
-// Log.e(LOG_TAG, "Error while changing device speed: ", e);
-// // gpsDev = null;
-// }
-// } else {
-// Log.v(LOG_TAG, "Device speed: "+deviceSpeed);
-// }
-// } else {
-// Log.e(LOG_TAG, "Device doesn't exist: "+gpsDev.getAbsolutePath());
-// gpsDev = null;
-// }
-//// } catch (IOException e) {
-//// Log.e(LOG_TAG, "Error during connection", e);
-//// gpsDev = null;
-//// }
-// if (gpsDev == null){
-// Log.e(LOG_TAG, "Error while establishing connection: no device");
-// disable(R.string.msg_gps_unavaible);
-// } else {
-// // start GPS device
-//// try {
-//// File gpsControl = new File("/sys/devices/platform/gps_control/enable");
-//// if (gpsControl.isFile()){
-//// if (gpsControl.canWrite()){
-//// OutputStream os = new FileOutputStream(gpsControl);
-//// os.write('1');
-//// os.flush();
-//// os.close();
-//// }
-//// }
-//// } catch (FileNotFoundException e) {
-//// // TODO update message
-//// Log.e(LOG_TAG, "Error while starting GPS: ", e);
-//// } catch (IOException e) {
-//// // TODO update message
-//// Log.e(LOG_TAG, "Error while starting GPS: ", e);
-//// } catch (SecurityException e) {
-//// // TODO update message
-//// Log.e(LOG_TAG, "Error while starting GPS: ", e);
-//// }
-// // Cancel discovery because it will slow down the connection
-//// bluetoothAdapter.cancelDiscovery();
-// // we increment the number of connection tries
-// // Connect the device through the socket. This will block
-// // until it succeeds or throws an exception
-// Log.v(LOG_TAG, "connecting to socket");
-//// gpsDev.connect();
-// Log.d(LOG_TAG, "connected to socket");
-// connected = true;
-// // reset eventual disabling cause
-//// setDisableReason(0);
-// // connection obtained so reset the number of connection try
-// nbRetriesRemaining = 1+maxConnectionRetries ;
-// notificationManager.cancel(R.string.connection_problem_notification_title);
-// Log.v(LOG_TAG, "starting socket reading task");
-// connectedGps = new ConnectedGps(gpsDev);
-// connectionAndReadingPool.execute(connectedGps);
-// Log.v(LOG_TAG, "socket reading thread started");
-// }
-//// } else if (! bluetoothAdapter.isEnabled()) {
-// setDisableReason(R.string.msg_bluetooth_disabled);
- }
-// } catch (IOException connectException) {
-// // Unable to connect
-// Log.e(LOG_TAG, "error while connecting to socket", connectException);
-// // disable(R.string.msg_bluetooth_gps_unavaible);
- } finally {
- nbRetriesRemaining--;
- if (! connected) {
- disableIfNeeded();
- }
- }
- }
- };
+ Runnable connectThread = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ connected = false;
+ Log.v(LOG_TAG, "current device: "+gpsDevice);
+ if ((nbRetriesRemaining > 0 )){
+ if (connectedGps != null){
+ connectedGps.close();
+ }
+ PendingIntent permissionIntent = PendingIntent.getBroadcast(callingService, 0, new Intent(ACTION_USB_PERMISSION), 0);
+ gpsDev = usbManager.getDeviceList().get(gpsDevice);
+ UsbDevice device = gpsDev;
+ if (device != null && usbManager.hasPermission(device)){
+ Log.d(LOG_TAG, "We have permession, good!");
+ connected = true;
+ if (setDeviceSpeed){
+ Log.v(LOG_TAG, "will set devive speed: " + deviceSpeed);
+ } else {
+ Log.v(LOG_TAG, "will use default device speed: " + defaultDeviceSpeed);
+ deviceSpeed = defaultDeviceSpeed;
+ }
+ Log.v(LOG_TAG, "starting usb reading task");
+ connectedGps = new ConnectedGps(device, deviceSpeed);
+ connectionAndReadingPool.execute(connectedGps);
+ Log.v(LOG_TAG, "usb reading thread started");
+ } else if (device != null) {
+ Log.d(LOG_TAG, "We don't have permession, so resquesting...");
+ usbManager.requestPermission(device, permissionIntent);
+ } else {
+ Log.e(LOG_TAG, "Error while establishing connection: no device - " + gpsDevice);
+ disable(R.string.msg_gps_unavaible);
+ }
+ }
+ } finally {
+ nbRetriesRemaining--;
+ if (! connected) {
+ disableIfNeeded();
+ }
+ }
+ }
+ };
this.enabled = true;
callingService.registerReceiver(mUsbReceiver, filter);
Log.d(LOG_TAG, "Bluetooth GPS manager enabled");
@@ -962,11 +766,11 @@ public void run() {
}
return this.enabled;
}
-
+
/**
* Disables the bluetooth GPS Provider if the maximal number of connection retries is exceeded.
- * This is used when there are possibly non fatal connection problems.
- * In these cases the provider will try to reconnect with the bluetooth device
+ * This is used when there are possibly non fatal connection problems.
+ * In these cases the provider will try to reconnect with the bluetooth device
* and only after a given retries number will give up and shutdown the service.
*/
private synchronized void disableIfNeeded(){
@@ -976,35 +780,32 @@ private synchronized void disableIfNeeded(){
Log.e(LOG_TAG, "Unable to establish connection");
connectionProblemNotification.when = System.currentTimeMillis();
String pbMessage = appContext.getResources().getQuantityString(R.plurals.connection_problem_notification, nbRetriesRemaining, nbRetriesRemaining);
- connectionProblemNotification.setLatestEventInfo(appContext,
- appContext.getString(R.string.connection_problem_notification_title),
- pbMessage,
+ connectionProblemNotification.setLatestEventInfo(appContext,
+ appContext.getString(R.string.connection_problem_notification_title),
+ pbMessage,
connectionProblemNotification.contentIntent);
connectionProblemNotification.number = 1 + maxConnectionRetries - nbRetriesRemaining;
notificationManager.notify(R.string.connection_problem_notification_title, connectionProblemNotification);
} else {
-// notificationManager.cancel(R.string.connection_problem_notification_title);
-// serviceStoppedNotification.when = System.currentTimeMillis();
-// notificationManager.notify(R.string.service_closed_because_connection_problem_notification_title, serviceStoppedNotification);
disable(R.string.msg_two_many_connection_problems);
}
}
}
-
+
/**
* Disables the bluetooth GPS provider.
- *
- * It will:
+ *
+ * It will:
*
* - close the connection with the bluetooth device
* - disable the Mock Location Provider used for the bluetooth GPS
* - stop the BlueGPS4Droid service
*
- * The reasonId parameter indicates the reason to close the bluetooth provider.
+ * The reasonId parameter indicates the reason to close the bluetooth provider.
* If its value is zero, it's a normal shutdown (normally, initiated by the user).
- * If it's non-zero this value should correspond a valid localized string id (res/values..../...)
+ * If it's non-zero this value should correspond a valid localized string id (res/values..../...)
* which will be used to display a notification.
- *
+ *
* @param reasonId the reason to close the bluetooth provider.
*/
public synchronized void disable(int reasonId) {
@@ -1012,11 +813,11 @@ public synchronized void disable(int reasonId) {
setDisableReason(reasonId);
disable();
}
-
+
/**
* Disables the bluetooth GPS provider.
- *
- * It will:
+ *
+ * It will:
*
* - close the connection with the bluetooth device
* - disable the Mock Location Provider used for the bluetooth GPS
@@ -1028,8 +829,8 @@ public synchronized void disable() {
notificationManager.cancel(R.string.connection_problem_notification_title);
if (getDisableReason() != 0){
serviceStoppedNotification.when = System.currentTimeMillis();
- serviceStoppedNotification.setLatestEventInfo(appContext,
- appContext.getString(R.string.service_closed_because_connection_problem_notification_title),
+ serviceStoppedNotification.setLatestEventInfo(appContext,
+ appContext.getString(R.string.service_closed_because_connection_problem_notification_title),
appContext.getString(R.string.service_closed_because_connection_problem_notification, appContext.getString(getDisableReason())),
serviceStoppedNotification.contentIntent);
notificationManager.notify(R.string.service_closed_because_connection_problem_notification_title, serviceStoppedNotification);
@@ -1040,27 +841,7 @@ public synchronized void disable() {
enabled = false;
connectionAndReadingPool.shutdown();
// stop GPS device
-// try {
-// File gpsControl = new File("/sys/devices/platform/gps_control/enable");
-// if (gpsControl.isFile()){
-// if (gpsControl.canWrite()){
-// OutputStream os = new FileOutputStream(gpsControl);
-// os.write('0');
-// os.flush();
-// os.close();
-// }
-// }
-// } catch (FileNotFoundException e) {
-// // TODO update message
-// Log.e(LOG_TAG, "Error while stoping GPS: ", e);
-// } catch (IOException e) {
-// // TODO update message
-// Log.e(LOG_TAG, "Error while stoping GPS: ", e);
-// } catch (SecurityException e) {
-// // TODO update message
-// Log.e(LOG_TAG, "Error while stoping GPS: ", e);
-// }
- Runnable closeAndShutdown = new Runnable() {
+ Runnable closeAndShutdown = new Runnable() {
@Override
public void run(){
try {
@@ -1073,14 +854,6 @@ public void run(){
if (connectedGps != null){
connectedGps.close();
}
-// if ((gpsDev != null) && ((connectedGps == null) || (connectedGps.gpsDev != gpsDev))){
-// try {
-// Log.d(LOG_TAG, "closing Bluetooth GPS socket");
-// gpsDev.close();
-// } catch (IOException closeException) {
-// Log.e(LOG_TAG, "error while closing socket", closeException);
-// }
-// }
}
}
};
@@ -1088,7 +861,7 @@ public void run(){
nmeaListeners.clear();
disableMockLocationProvider();
notificationPool.shutdown();
-// connectionAndReadingPool.shutdown();
+
callingService.stopSelf();
Log.d(LOG_TAG, "Bluetooth GPS manager disabled");
}
@@ -1096,8 +869,8 @@ public void run(){
/**
* Enables the Mock GPS Location Provider used for the bluetooth GPS.
- * In fact, it delegates to the NMEA parser.
- *
+ * In fact, it delegates to the NMEA parser.
+ *
* @see NmeaParser#enableMockLocationProvider(java.lang.String)
* @param gpsName the name of the Location Provider to use for the bluetooth GPS
@@ -1112,8 +885,8 @@ public void enableMockLocationProvider(String gpsName, boolean force){
/**
* Enables the Mock GPS Location Provider used for the bluetooth GPS.
- * In fact, it delegates to the NMEA parser.
- *
+ * In fact, it delegates to the NMEA parser.
+ *
* @see NmeaParser#enableMockLocationProvider(java.lang.String)
* @param gpsName the name of the Location Provider to use for the bluetooth GPS
@@ -1121,15 +894,15 @@ public void enableMockLocationProvider(String gpsName, boolean force){
public void enableMockLocationProvider(String gpsName){
if (parser != null){
Log.d(LOG_TAG, "enabling mock locations provider: "+gpsName);
- boolean force = sharedPreferences.getBoolean(BluetoothGpsProviderService.PREF_FORCE_ENABLE_PROVIDER, true);
+ boolean force = sharedPreferences.getBoolean(UsbGpsProviderService.PREF_FORCE_ENABLE_PROVIDER, true);
parser.enableMockLocationProvider(gpsName, force);
}
}
/**
* Disables the current Mock GPS Location Provider used for the bluetooth GPS.
- * In fact, it delegates to the NMEA parser.
- *
+ * In fact, it delegates to the NMEA parser.
+ *
* @see NmeaParser#disableMockLocationProvider()
*/
public void disableMockLocationProvider(){
@@ -1141,10 +914,10 @@ public void disableMockLocationProvider(){
/**
* Getter use to know if the Mock GPS Listener used for the bluetooth GPS is enabled or not.
- * In fact, it delegates to the NMEA parser.
- *
+ * In fact, it delegates to the NMEA parser.
+ *
* @see NmeaParser#isMockGpsEnabled()
- *
+ *
* @return true if the Mock GPS Listener used for the bluetooth GPS is enabled.
*/
public boolean isMockGpsEnabled() {
@@ -1156,10 +929,10 @@ public boolean isMockGpsEnabled() {
}
/**
* Getter for the name of the current Mock Location Provider in use.
- * In fact, it delegates to the NMEA parser.
- *
+ * In fact, it delegates to the NMEA parser.
+ *
* @see NmeaParser#getMockLocationProvider()
- *
+ *
* @return the Mock Location Provider name used for the bluetooth GPS
*/
public String getMockLocationProvider() {
@@ -1172,8 +945,8 @@ public String getMockLocationProvider() {
/**
* Indicates that the bluetooth GPS Provider is out of service.
- * In fact, it delegates to the NMEA parser.
- *
+ * In fact, it delegates to the NMEA parser.
+ *
* @see NmeaParser#setMockLocationProviderOutOfService()
*/
private void setMockLocationProviderOutOfService(){
@@ -1184,8 +957,8 @@ private void setMockLocationProviderOutOfService(){
/**
* Adds an NMEA listener.
- * In fact, it delegates to the NMEA parser.
- *
+ * In fact, it delegates to the NMEA parser.
+ *
* @see NmeaParser#addNmeaListener(NmeaListener)
* @param listener a {@link NmeaListener} object to register
* @return true if the listener was successfully added
@@ -1200,10 +973,10 @@ public boolean addNmeaListener(NmeaListener listener){
/**
* Removes an NMEA listener.
- * In fact, it delegates to the NMEA parser.
- *
+ * In fact, it delegates to the NMEA parser.
+ *
* @see NmeaParser#removeNmeaListener(NmeaListener)
- * @param listener a {@link NmeaListener} object to remove
+ * @param listener a {@link NmeaListener} object to remove
*/
public void removeNmeaListener(NmeaListener listener){
Log.d(LOG_TAG, "removing NMEA listener");
@@ -1212,7 +985,7 @@ public void removeNmeaListener(NmeaListener listener){
/**
* Notifies the reception of a NMEA sentence from the bluetooth GPS to registered NMEA listeners.
- *
+ *
* @param nmeaSentence the complete NMEA sentence received from the bluetooth GPS (i.e. $....*XY where XY is the checksum)
* @return true if the input string is a valid NMEA sentence, false otherwise.
*/
@@ -1240,18 +1013,18 @@ private boolean notifyNmeaSentence(final String nmeaSentence){
@Override
public void run() {
listener.onNmeaReceived(timestamp, recognizedSentence);
- }
+ }
});
}
}
}
}
return res;
- }
+ }
/**
* Sends a NMEA sentence to the bluetooth GPS.
- *
+ *
* @param command the complete NMEA sentence (i.e. $....*XY where XY is the checksum).
*/
public void sendPackagedNmeaCommand(final String command){
@@ -1262,8 +1035,8 @@ public void sendPackagedNmeaCommand(final String command){
/**
* Sends a SIRF III binary command to the bluetooth GPS.
- *
- * @param commandHexa an hexadecimal string representing a complete binary command
+ *
+ * @param commandHexa an hexadecimal string representing a complete binary command
* (i.e. with the Start Sequence, Payload Length, Payload, Message Checksum and End Sequence).
*/
public void sendPackagedSirfCommand(final String commandHexa){
@@ -1275,7 +1048,7 @@ public void sendPackagedSirfCommand(final String commandHexa){
/**
* Sends a NMEA sentence to the bluetooth GPS.
- *
+ *
* @param sentence the NMEA sentence without the first "$", the last "*" and the checksum.
*/
public void sendNmeaCommand(String sentence){
@@ -1285,7 +1058,7 @@ public void sendNmeaCommand(String sentence){
/**
* Sends a SIRF III binary command to the bluetooth GPS.
- *
+ *
* @param payload an hexadecimal string representing the payload of the binary command
* (i.e. without Start Sequence, Payload Length, Message Checksum and End Sequence).
*/
@@ -1389,7 +1162,7 @@ private void enableSBAS(boolean enable){
public void enableSirfConfig(final Bundle extra){
Log.e(LOG_TAG, "spooling SiRF config: "+ extra);
if (isEnabled()){
- notificationPool.execute( new Runnable() {
+ notificationPool.execute( new Runnable() {
@Override
public void run() {
while ((enabled) && ((!connected) || (connectedGps == null) || (!connectedGps.isReady()))){
@@ -1398,34 +1171,34 @@ public void run() {
}
if (isEnabled() && (connected) && (connectedGps != null) && (connectedGps.isReady())){
Log.e(LOG_TAG, "init SiRF config: "+extra);
- if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GGA)){
- enableNmeaGGA(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GGA, true));
+ if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_GGA)){
+ enableNmeaGGA(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_GGA, true));
}
- if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_RMC)){
- enableNmeaRMC(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_RMC, true));
+ if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_RMC)){
+ enableNmeaRMC(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_RMC, true));
}
- if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GLL)){
- enableNmeaGLL(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GLL, false));
+ if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_GLL)){
+ enableNmeaGLL(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_GLL, false));
}
- if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_VTG)){
- enableNmeaVTG(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_VTG, false));
+ if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_VTG)){
+ enableNmeaVTG(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_VTG, false));
}
- if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSA)){
- enableNmeaGSA(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSA, false));
+ if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_GSA)){
+ enableNmeaGSA(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_GSA, false));
}
- if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSV)){
- enableNmeaGSV(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSV, false));
+ if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_GSV)){
+ enableNmeaGSV(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_GSV, false));
}
- if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_ZDA)){
- enableNmeaZDA(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_ZDA, false));
+ if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_ZDA)){
+ enableNmeaZDA(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_ZDA, false));
}
- if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION)){
- enableStaticNavigation(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION, false));
- } else if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_NMEA)){
- enableNMEA(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_NMEA, true));
+ if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION)){
+ enableStaticNavigation(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION, false));
+ } else if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_NMEA)){
+ enableNMEA(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_NMEA, true));
}
- if (extra.containsKey(BluetoothGpsProviderService.PREF_SIRF_ENABLE_SBAS)){
- enableSBAS(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_SBAS, true));
+ if (extra.containsKey(UsbGpsProviderService.PREF_SIRF_ENABLE_SBAS)){
+ enableSBAS(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_SBAS, true));
}
Log.e(LOG_TAG, "initialized SiRF config: "+extra);
}
@@ -1437,7 +1210,7 @@ public void run() {
public void enableSirfConfig(final SharedPreferences extra){
Log.e(LOG_TAG, "spooling SiRF config: "+ extra);
if (isEnabled()){
- notificationPool.execute( new Runnable() {
+ notificationPool.execute( new Runnable() {
@Override
public void run() {
while ((enabled) && ((!connected) || (connectedGps == null) || (!connectedGps.isReady()))){
@@ -1446,36 +1219,36 @@ public void run() {
}
if (isEnabled() && (connected) && (connectedGps != null) && (connectedGps.isReady())){
Log.e(LOG_TAG, "init SiRF config: "+extra);
- if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GLL)){
- enableNmeaGLL(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GLL, false));
+ if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_GLL)){
+ enableNmeaGLL(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_GLL, false));
}
- if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_VTG)){
- enableNmeaVTG(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_VTG, false));
+ if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_VTG)){
+ enableNmeaVTG(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_VTG, false));
}
- if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSA)){
- enableNmeaGSA(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSA, false));
+ if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_GSA)){
+ enableNmeaGSA(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_GSA, false));
}
- if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSV)){
- enableNmeaGSV(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GSV, false));
+ if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_GSV)){
+ enableNmeaGSV(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_GSV, false));
}
- if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_ZDA)){
- enableNmeaZDA(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_ZDA, false));
+ if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_ZDA)){
+ enableNmeaZDA(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_ZDA, false));
}
- if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION)){
- enableStaticNavigation(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION, false));
- } else if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_NMEA)){
- enableNMEA(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_NMEA, true));
+ if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION)){
+ enableStaticNavigation(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION, false));
+ } else if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_NMEA)){
+ enableNMEA(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_NMEA, true));
}
- if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_SBAS)){
- enableSBAS(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_SBAS, true));
+ if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_SBAS)){
+ enableSBAS(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_SBAS, true));
}
sendNmeaCommand(callingService.getString(R.string.sirf_nmea_gga_on));
sendNmeaCommand(callingService.getString(R.string.sirf_nmea_rmc_on));
- if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GGA)){
- enableNmeaGGA(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_GGA, true));
+ if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_GGA)){
+ enableNmeaGGA(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_GGA, true));
}
- if (extra.contains(BluetoothGpsProviderService.PREF_SIRF_ENABLE_RMC)){
- enableNmeaRMC(extra.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_RMC, true));
+ if (extra.contains(UsbGpsProviderService.PREF_SIRF_ENABLE_RMC)){
+ enableNmeaRMC(extra.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_RMC, true));
}
}
}
@@ -1485,7 +1258,7 @@ public void run() {
private void enableStaticNavigation(boolean enable){
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(callingService);
- boolean isInNmeaMode = sharedPreferences.getBoolean(BluetoothGpsProviderService.PREF_SIRF_ENABLE_NMEA, true);
+ boolean isInNmeaMode = sharedPreferences.getBoolean(UsbGpsProviderService.PREF_SIRF_ENABLE_NMEA, true);
if (isInNmeaMode){
enableNMEA(false);
}
diff --git a/src/org/broeuschmeul/android/gps/usb/provider/SettingsFragment.java b/src/org/broeuschmeul/android/gps/usb/provider/SettingsFragment.java
new file mode 100644
index 00000000..9a298c75
--- /dev/null
+++ b/src/org/broeuschmeul/android/gps/usb/provider/SettingsFragment.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2010, 2011, 2012 Herbert von Broeuschmeul
+ * Copyright (C) 2010, 2011, 2012 BluetoothGPS4Droid Project
+ * Copyright (C) 2011, 2012 UsbGPS4Droid Project
+ * Copyright (C) 2013 Alexey Illarionov
+ *
+ * This file is part of UsbGPS4Droid.
+ *
+ * UsbGPS4Droid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * UsbGPS4Droid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with UsbGPS4Droid. If not, see .
+ */
+
+package org.broeuschmeul.android.gps.usb.provider;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * A SettingsFragment Class used to configure, start and stop the NMEA tracker service.
+ *
+ * @author Herbert von Broeuschmeul
+ *
+ */
+public class SettingsFragment extends PreferenceFragment {
+
+ private static final boolean DBG = BuildConfig.DEBUG & false;
+ private static final String TAG = SettingsFragment.class.getSimpleName();
+
+ public interface Callbacks {
+
+ void displayAboutDialog();
+
+ void startGpsProviderService();
+
+ void stopGpsProviderService();
+
+ void startTrackRecording();
+
+ void stopTrackRecording();
+
+ void setSirfFeature(String key, boolean enabled);
+ }
+
+ private static final Callbacks sDummyCallbacks = new Callbacks() {
+ @Override public void displayAboutDialog() {}
+ @Override public void startGpsProviderService() {}
+ @Override public void stopGpsProviderService() {}
+ @Override public void startTrackRecording() {} ;
+ @Override public void stopTrackRecording() {} ;
+ @Override public void setSirfFeature(String key, boolean enabled) {} ;
+ };
+
+ private Callbacks mCallbacks = sDummyCallbacks;
+
+ private String deviceName = "";
+
+ private UsbManager usbManager;
+
+ private ListPreference mGpsDevicePreference, mDeviceSpeedPreference;
+ private EditTextPreference mMockGpsNamePreference, mConnectionRetriesPreference;
+ private Preference mTrackRecordingPreference;
+ private PreferenceScreen mGpsLocationProviderPreference;
+
+ @Override
+ public void onAttach(Activity activity) {
+ // TODO Auto-generated method stub
+ super.onAttach(activity);
+
+ if (!(activity instanceof Callbacks)) {
+ throw new IllegalStateException("Activity must implement fragment's callbacks.");
+ }
+
+ mCallbacks = (Callbacks)activity;
+ usbManager = (UsbManager) activity.getSystemService(Context.USB_SERVICE);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.pref);
+
+ mGpsDevicePreference = (ListPreference)findPreference(UsbGpsProviderService.PREF_GPS_DEVICE);
+ mDeviceSpeedPreference = (ListPreference)findPreference(UsbGpsProviderService.PREF_GPS_DEVICE_SPEED);
+ mTrackRecordingPreference = findPreference(UsbGpsProviderService.PREF_TRACK_RECORDING);
+ mMockGpsNamePreference = (EditTextPreference)findPreference(UsbGpsProviderService.PREF_MOCK_GPS_NAME);
+ mConnectionRetriesPreference = (EditTextPreference)findPreference(UsbGpsProviderService.PREF_CONNECTION_RETRIES);
+ mGpsLocationProviderPreference = (PreferenceScreen)findPreference(UsbGpsProviderService.PREF_GPS_LOCATION_PROVIDER);
+
+ Preference pref = findPreference(UsbGpsProviderService.PREF_ABOUT);
+ pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ mCallbacks.displayAboutDialog();
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ getPreferenceManager()
+ .getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateDevicePreferenceList();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ getPreferenceManager()
+ .getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mGpsDevicePreference = null;
+ mGpsDevicePreference = null;
+ mDeviceSpeedPreference = null;
+ mTrackRecordingPreference = null;
+ mMockGpsNamePreference = null;
+ mConnectionRetriesPreference = null;
+ mGpsLocationProviderPreference = null;
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mCallbacks = sDummyCallbacks;
+ usbManager = null;
+ }
+
+ private void updateDevicePreferenceSummary(){
+ final SharedPreferences sharedPref = getPreferenceManager().getSharedPreferences();
+
+ // update USB device summary
+ String defaultDeviceName = "";
+ if (! usbManager.getDeviceList().isEmpty()){
+ defaultDeviceName = usbManager.getDeviceList().keySet().iterator().next();
+ }
+ deviceName = sharedPref.getString(UsbGpsProviderService.PREF_GPS_DEVICE, defaultDeviceName);
+ String deviceDisplayedName = "";
+ if (! usbManager.getDeviceList().isEmpty() && usbManager.getDeviceList().get(deviceName) != null){
+ deviceDisplayedName = usbManager.getDeviceList().get(deviceName).getDeviceName();
+ } else if ((usbManager.getDeviceList().size() == 1) && (usbManager.getDeviceList().get(defaultDeviceName) != null)){
+ deviceDisplayedName = usbManager.getDeviceList().get(defaultDeviceName).getDeviceName();
+ deviceName = defaultDeviceName;
+ mGpsDevicePreference.setValue(defaultDeviceName);
+ }
+ mGpsDevicePreference.setSummary(getString(R.string.pref_gps_device_summary, deviceDisplayedName));
+ mDeviceSpeedPreference.setSummary(getString(R.string.pref_gps_device_speed_summary, sharedPref.getString(UsbGpsProviderService.PREF_GPS_DEVICE_SPEED, getString(R.string.defaultGpsDeviceSpeed))));
+ }
+
+ private void updateDevicePreferenceList(){
+ final SharedPreferences sharedPref = getPreferenceManager().getSharedPreferences();
+ final Resources resources = getResources();
+ final String mockProviderName;
+
+ // update bluetooth device summary
+ updateDevicePreferenceSummary();
+ // update bluetooth device list
+ HashMap connectedUsbDevices = usbManager.getDeviceList();
+ String[] entryValues = new String[connectedUsbDevices.size()];
+ String[] entries = new String[connectedUsbDevices.size()];
+ int i = 0;
+ // Loop through usb devices
+ for (String name : connectedUsbDevices.keySet()) {
+ // Add the name and address to the ListPreference enties and entyValues
+ UsbDevice device = connectedUsbDevices.get(name);
+ if (DBG) Log.v(TAG, "device: " + device);
+ for (int k=0; k < device.getInterfaceCount(); k++){
+ UsbInterface usbIntf = device.getInterface(k);
+ for (int j=0; j < usbIntf.getEndpointCount(); j++){
+ UsbEndpoint endPt = usbIntf.getEndpoint(j);
+ if (DBG) Log.v(TAG, "endPt: : "+endPt + " type: "+endPt.getType()+ " dir: "+endPt.getDirection() );
+ }
+ }
+ entryValues[i] = device.getDeviceName();
+ entries[i] = name;
+ i++;
+ }
+ mGpsDevicePreference.setEntryValues(entryValues);
+ mGpsDevicePreference.setEntries(entries);
+
+ mTrackRecordingPreference.setEnabled(sharedPref.getBoolean(UsbGpsProviderService.PREF_START_GPS_PROVIDER, false));
+
+ mockProviderName = mMockGpsNamePreference.getText();
+ mMockGpsNamePreference.setSummary(mockProviderName);
+
+ int connRetries;
+
+ try {
+ connRetries = Integer.valueOf(mConnectionRetriesPreference.getText());
+ }catch (NumberFormatException nfe) {
+ nfe.printStackTrace();
+ connRetries = Integer.valueOf(getString(R.string.defaultConnectionRetries));
+ }
+
+ mConnectionRetriesPreference.setSummary(resources.getQuantityString(
+ R.plurals.pref_connection_retries_summary, connRetries, connRetries));
+
+ if (sharedPref.getBoolean(UsbGpsProviderService.PREF_REPLACE_STD_GPS, true)){
+ String s = getString(R.string.pref_gps_location_provider_summary);
+ mGpsLocationProviderPreference.setSummary(s);
+ } else {
+ String s = getString(R.string.pref_mock_gps_name_summary, mockProviderName);
+ mGpsLocationProviderPreference.setSummary(s);
+ }
+ }
+
+ private final OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (UsbGpsProviderService.PREF_START_GPS_PROVIDER.equals(key)){
+ boolean val = sharedPreferences.getBoolean(key, false);
+ if (val){
+ mCallbacks.startGpsProviderService();
+ } else {
+ mCallbacks.stopGpsProviderService();
+ }
+ } else if (UsbGpsProviderService.PREF_TRACK_RECORDING.equals(key)){
+ boolean val = sharedPreferences.getBoolean(key, false);
+ if (val){
+ mCallbacks.startTrackRecording();
+ } else {
+ mCallbacks.stopTrackRecording();
+ }
+ } else if (UsbGpsProviderService.PREF_GPS_DEVICE.equals(key)){
+ updateDevicePreferenceSummary();
+ } else if (UsbGpsProviderService.PREF_GPS_DEVICE_SPEED.equals(key)){
+ updateDevicePreferenceSummary();
+ } else if (UsbGpsProviderService.PREF_SIRF_ENABLE_GLL.equals(key)
+ || UsbGpsProviderService.PREF_SIRF_ENABLE_GGA.equals(key)
+ || UsbGpsProviderService.PREF_SIRF_ENABLE_RMC.equals(key)
+ || UsbGpsProviderService.PREF_SIRF_ENABLE_VTG.equals(key)
+ || UsbGpsProviderService.PREF_SIRF_ENABLE_GSA.equals(key)
+ || UsbGpsProviderService.PREF_SIRF_ENABLE_GSV.equals(key)
+ || UsbGpsProviderService.PREF_SIRF_ENABLE_ZDA.equals(key)
+ || UsbGpsProviderService.PREF_SIRF_ENABLE_SBAS.equals(key)
+ || UsbGpsProviderService.PREF_SIRF_ENABLE_NMEA.equals(key)
+ || UsbGpsProviderService.PREF_SIRF_ENABLE_STATIC_NAVIGATION.equals(key)
+ ){
+ final boolean enabled = sharedPreferences.getBoolean(key, false);
+ mCallbacks.setSirfFeature(key, enabled);
+ }
+ updateDevicePreferenceList();
+ }
+ };
+
+}
diff --git a/src/org/broeuschmeul/android/gps/usb/provider/UsbGpsActivity.java b/src/org/broeuschmeul/android/gps/usb/provider/UsbGpsActivity.java
new file mode 100644
index 00000000..a4e5b69d
--- /dev/null
+++ b/src/org/broeuschmeul/android/gps/usb/provider/UsbGpsActivity.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010, 2011, 2012 Herbert von Broeuschmeul
+ * Copyright (C) 2010, 2011, 2012 BluetoothGPS4Droid Project
+ * Copyright (C) 2011, 2012 UsbGPS4Droid Project
+ *
+ * This file is part of UsbGPS4Droid.
+ *
+ * UsbGPS4Droid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * UsbGPS4Droid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with UsbGPS4Droid. If not, see .
+ */
+
+package org.broeuschmeul.android.gps.usb.provider;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceManager;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * A PreferenceActivity Class used to configure, start and stop the NMEA tracker service.
+ *
+ * @author Herbert von Broeuschmeul
+ *
+ */
+public class UsbGpsActivity extends PreferenceActivity implements SettingsFragment.Callbacks {
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ PreferenceManager.setDefaultValues(this,
+ R.xml.pref, false);
+ getFragmentManager().beginTransaction().replace(android.R.id.content,
+ new SettingsFragment()).commit();
+ }
+ }
+
+ @Override
+ public void displayAboutDialog(){
+ (new AboutDialogFragment()).show(getFragmentManager(), null);
+ }
+
+ @Override
+ public void startGpsProviderService() {
+ final Intent intent = new Intent(this, UsbGpsProviderService.class);
+ intent.setAction(UsbGpsProviderService.ACTION_START_GPS_PROVIDER);
+ startService(intent);
+ }
+
+ @Override
+ public void stopGpsProviderService() {
+ final Intent intent = new Intent(this, UsbGpsProviderService.class);
+ intent.setAction(UsbGpsProviderService.ACTION_STOP_GPS_PROVIDER);
+ startService(intent);
+ }
+
+ @Override
+ public void startTrackRecording() {
+ final Intent intent = new Intent(this, UsbGpsProviderService.class);
+ intent.setAction(UsbGpsProviderService.ACTION_START_TRACK_RECORDING);
+ startService(intent);
+ }
+
+ @Override
+ public void stopTrackRecording() {
+ final Intent intent = new Intent(this, UsbGpsProviderService.class);
+ intent.setAction(UsbGpsProviderService.ACTION_STOP_TRACK_RECORDING);
+ startService(intent);
+ }
+
+ @Override
+ public void setSirfFeature(String key, boolean enabled) {
+ final Intent intent = new Intent(this, UsbGpsProviderService.class);
+ intent.setAction(UsbGpsProviderService.ACTION_CONFIGURE_SIRF_GPS);
+ intent.putExtra(key, enabled);
+ startService(intent);
+ }
+
+ public static class AboutDialogFragment extends DialogFragment {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder;
+ final LayoutInflater inflater;
+ final View messageView;
+ final Activity activity;
+ TextView textView;
+
+ activity = getActivity();
+ inflater = activity.getLayoutInflater();
+
+ messageView = inflater.inflate(R.layout.about, null, false);
+ // we need this to enable html links
+ textView = (TextView) messageView.findViewById(R.id.about_license);
+ textView.setMovementMethod(LinkMovementMethod.getInstance());
+ // When linking text, force to always use default color. This works
+ // around a pressed color state bug.
+ final int defaultColor = textView.getTextColors().getDefaultColor();
+ textView.setTextColor(defaultColor);
+ ((TextView) messageView.findViewById(R.id.about_sources)).setTextColor(defaultColor);
+
+ try {
+ final PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0);
+ ((TextView) messageView.findViewById(R.id.about_version)).setText(pi.versionName);
+ } catch (NameNotFoundException nnfe) {
+ nnfe.printStackTrace();
+ }
+
+ builder = new AlertDialog.Builder(activity);
+ builder
+ .setTitle(R.string.about_title)
+ .setIcon(R.drawable.gplv3_icon)
+ .setView(messageView);
+
+ return builder.create();
+ }
+
+ }
+
+}
diff --git a/src/org/broeuschmeul/android/gps/usb/provider/UsbGpsProviderService.java b/src/org/broeuschmeul/android/gps/usb/provider/UsbGpsProviderService.java
new file mode 100644
index 00000000..97805e44
--- /dev/null
+++ b/src/org/broeuschmeul/android/gps/usb/provider/UsbGpsProviderService.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2010, 2011, 2012 Herbert von Broeuschmeul
+ * Copyright (C) 2010, 2011, 2012 BluetoothGPS4Droid Project
+ * Copyright (C) 2011, 2012 UsbGPS4Droid Project
+ *
+ * This file is part of UsbGPS4Droid.
+ *
+ * UsbGPS4Droid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * UsbGPS4Droid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with UsbGPS4Droid. If not, see .
+ */
+
+/**
+ *
+ */
+package org.broeuschmeul.android.gps.usb.provider;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.location.GpsStatus.NmeaListener;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+
+/**
+ * A Service used to replace Android internal GPS with a bluetooth GPS and/or write GPS NMEA data in a File.
+ *
+ * @author Herbert von Broeuschmeul
+ *
+ */
+public class UsbGpsProviderService extends Service {
+
+ private static final boolean DBG = BuildConfig.DEBUG & true;
+ static final String TAG = UsbGpsProviderService.class.getSimpleName();
+
+ public static final String ACTION_START_TRACK_RECORDING = "org.broeuschmeul.android.gps.usb.tracker.nmea.intent.action.START_TRACK_RECORDING";
+ public static final String ACTION_STOP_TRACK_RECORDING = "org.broeuschmeul.android.gps.usb.tracker.nmea.intent.action.STOP_TRACK_RECORDING";
+ public static final String ACTION_START_GPS_PROVIDER = "org.broeuschmeul.android.gps.usb.provider.nmea.intent.action.START_GPS_PROVIDER";
+ public static final String ACTION_STOP_GPS_PROVIDER = "org.broeuschmeul.android.gps.usb.provider.nmea.intent.action.STOP_GPS_PROVIDER";
+ public static final String ACTION_CONFIGURE_SIRF_GPS = "org.broeuschmeul.android.gps.usb.provider.nmea.intent.action.CONFIGURE_SIRF_GPS";
+
+ public static final String PREF_START_GPS_PROVIDER = "startGps";
+ public static final String PREF_GPS_LOCATION_PROVIDER = "gpsLocationProviderKey";
+ public static final String PREF_REPLACE_STD_GPS = "replaceStdtGps";
+ public static final String PREF_FORCE_ENABLE_PROVIDER = "forceEnableProvider";
+ public static final String PREF_MOCK_GPS_NAME = "mockGpsName";
+ public static final String PREF_CONNECTION_RETRIES = "connectionRetries";
+ public static final String PREF_TRACK_RECORDING = "trackRecording";
+ public static final String PREF_TRACK_FILE_DIR = "trackFileDirectory";
+ public static final String PREF_TRACK_FILE_PREFIX = "trackFilePrefix";
+ public static final String PREF_GPS_DEVICE = "usbDevice";
+ public static final String PREF_GPS_DEVICE_SPEED = "gpsDeviceSpeed";
+ public static final String PREF_ABOUT = "about";
+
+ /**
+ * Tag used for log messages
+ */
+ private static final String LOG_TAG = "UsbGPS";
+
+ public static final String PREF_SIRF_GPS = "sirfGps";
+ public static final String PREF_SIRF_ENABLE_GGA = "enableGGA";
+ public static final String PREF_SIRF_ENABLE_RMC = "enableRMC";
+ public static final String PREF_SIRF_ENABLE_GLL = "enableGLL";
+ public static final String PREF_SIRF_ENABLE_VTG = "enableVTG";
+ public static final String PREF_SIRF_ENABLE_GSA = "enableGSA";
+ public static final String PREF_SIRF_ENABLE_GSV = "enableGSV";
+ public static final String PREF_SIRF_ENABLE_ZDA = "enableZDA";
+ public static final String PREF_SIRF_ENABLE_SBAS = "enableSBAS";
+ public static final String PREF_SIRF_ENABLE_NMEA = "enableNMEA";
+ public static final String PREF_SIRF_ENABLE_STATIC_NAVIGATION = "enableStaticNavigation";
+
+ private BlueetoothGpsManager mGpsManager = null;
+ private PrintWriter writer;
+ private File trackFile;
+ private boolean preludeWritten = false;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+
+ if (intent == null) {
+ Log.v(TAG, "UsbGpsProviderService restarted");
+ processStartGpsProvider();
+ }else {
+ final String action = intent.getAction();
+ if (action.equals(ACTION_START_GPS_PROVIDER)) processStartGpsProvider();
+ else if(action.equals(ACTION_STOP_GPS_PROVIDER)) processStopGpsProvider();
+ else if(action.equals(ACTION_START_TRACK_RECORDING)) processStartTrackRecording();
+ else if(action.equals(ACTION_STOP_TRACK_RECORDING)) processStopTrackRecording();
+ else if(action.equals(ACTION_CONFIGURE_SIRF_GPS)) processConfigureSirfGps(intent.getExtras());
+ else Log.e(TAG, "onStartCommand(): unknown action " + action);
+ }
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ stop();
+ }
+
+
+ public boolean isServiceStarted() {
+ return mGpsManager != null;
+ }
+
+
+ private void processStartGpsProvider() {
+
+ if (isServiceStarted()) return;
+
+ Notification notification = createForegroundNotification();
+ startForeground(R.string.foreground_gps_provider_started_notification, notification);
+
+
+// if (mGpsManager == null){
+// if (true /*|| BluetoothAdapter.checkBluetoothAddress(deviceAddress)*/){
+// String mockProvider = LocationManager.GPS_PROVIDER;
+// if (! sharedPreferences.getBoolean(PREF_REPLACE_STD_GPS, true)){
+// mockProvider = sharedPreferences.getString(PREF_MOCK_GPS_NAME, getString(R.string.defaultMockGpsName));
+// }
+// mGpsManager = new BlueetoothGpsManager(this, deviceAddress, maxConRetries);
+// boolean enabled = mGpsManager.enable();
+// if (sharedPreferences.getBoolean(PREF_START_GPS_PROVIDER, false) != enabled){
+// edit.putBoolean(PREF_START_GPS_PROVIDER,enabled);
+// edit.commit();
+// }
+// if (enabled) {
+// mGpsManager.enableMockLocationProvider(mockProvider);
+// Notification notification = new Notification(R.drawable.ic_stat_notify, this.getString(R.string.foreground_gps_provider_started_notification), System.currentTimeMillis());
+// Intent myIntent = new Intent(this, UsbGpsActivity.class);
+// PendingIntent myPendingIntent = PendingIntent.getActivity(this, 0, myIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+// notification.setLatestEventInfo(getApplicationContext(), this.getString(R.string.foreground_service_started_notification_title), this.getString(R.string.foreground_gps_provider_started_notification), myPendingIntent);
+// startForeground(R.string.foreground_gps_provider_started_notification, notification);
+// toast.setText(this.getString(R.string.msg_gps_provider_started));
+// toast.show();
+// } else {
+// stopSelf();
+// }
+// } else {
+// stopSelf();
+// }
+// } else {
+// toast.setText(this.getString(R.string.msg_gps_provider_already_started));
+// toast.show();
+// }
+ }
+
+ private void processStopGpsProvider() {
+ stop();
+ stopSelf();
+ }
+
+ private void processStartTrackRecording() {
+ if (trackFile == null){
+ if (mGpsManager != null){
+ beginTrack();
+ mGpsManager.addNmeaListener(mNmeaListener);
+ Toast.makeText(this, getText(R.string.msg_nmea_recording_started),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ endTrack();
+ }
+ } else {
+ Toast.makeText(this, getText(R.string.msg_nmea_recording_already_started),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void processStopTrackRecording() {
+ if (mGpsManager != null){
+ mGpsManager.removeNmeaListener(mNmeaListener);
+ endTrack();
+ Toast.makeText(this, getText(R.string.msg_nmea_recording_stopped),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void processConfigureSirfGps(Bundle extras) {
+ if (!isServiceStarted()) return;
+ mGpsManager.enableSirfConfig(extras);
+ }
+
+ private void stop() {
+ stopForeground(true);
+
+ if (isServiceStarted()) {
+ //mGpsManager.stop();
+
+ Toast.makeText(this, R.string.msg_gps_provider_stopped, Toast.LENGTH_SHORT)
+ .show();
+
+ }
+ }
+
+
+ @SuppressWarnings("deprecation")
+ private Notification createForegroundNotification() {
+ CharSequence text = getText(R.string.foreground_gps_provider_started_notification);
+
+ Notification notification = new Notification(R.drawable.ic_stat_notify,
+ text, System.currentTimeMillis());
+
+ PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
+ new Intent(this, UsbGpsActivity.class), 0);
+
+ notification.setLatestEventInfo(this,
+ getText(R.string.foreground_gps_provider_started_notification), text, contentIntent);
+
+ return notification;
+ }
+
+ private void beginTrack(){
+ SimpleDateFormat fmt = new SimpleDateFormat("_yyyy-MM-dd_HH-mm-ss'.nmea'", Locale.US);
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ String trackDirName = sharedPreferences.getString(PREF_TRACK_FILE_DIR, this.getString(R.string.defaultTrackFileDirectory));
+ String trackFilePrefix = sharedPreferences.getString(PREF_TRACK_FILE_PREFIX, this.getString(R.string.defaultTrackFilePrefix));
+ trackFile = new File(trackDirName,trackFilePrefix+fmt.format(new Date()));
+ Log.d(LOG_TAG, "Writing the prelude of the NMEA file: "+trackFile.getAbsolutePath());
+ File trackDir = trackFile.getParentFile();
+ try {
+ if ((! trackDir.mkdirs()) && (! trackDir.isDirectory())){
+ Log.e(LOG_TAG, "Error while creating parent dir of NMEA file: "+trackDir.getAbsolutePath());
+ }
+ writer = new PrintWriter(new BufferedWriter(new FileWriter(trackFile)));
+ preludeWritten = true;
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Error while writing the prelude of the NMEA file: "+trackFile.getAbsolutePath(), e);
+ }
+ }
+ private void endTrack(){
+ if (trackFile != null && writer != null){
+ Log.d(LOG_TAG, "Ending the NMEA file: "+trackFile.getAbsolutePath());
+ preludeWritten = false;
+ writer.close();
+ trackFile = null;
+ }
+ }
+
+ private void addNMEAString(String data){
+ if (! preludeWritten){
+ beginTrack();
+ }
+ Log.v(LOG_TAG, "Adding data in the NMEA file: "+ data);
+ if (trackFile != null && writer != null){
+ writer.print(data);
+ }
+ }
+
+ private final NmeaListener mNmeaListener = new NmeaListener() {
+ @Override
+ public void onNmeaReceived(long timestamp, String data) {
+ addNMEAString(data);
+ }
+ };
+
+
+}