MISC: enhance code step 4 (flatten packages & merge kotlin files)

This commit is contained in:
imkiva 2021-05-08 18:49:01 +08:00 committed by Kiva Oyama
parent 6780c29f94
commit 47d2999207
146 changed files with 2463 additions and 2678 deletions

568
.editorconfig Normal file
View File

@ -0,0 +1,568 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 120
tab_width = 2
ij_continuation_indent_size = 2
ij_formatter_off_tag = @fmt:off
ij_formatter_on_tag = @fmt:on
ij_formatter_tags_enabled = false
ij_smart_tabs = true
ij_visual_guides = none
ij_wrap_on_typing = false
[*.java]
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = false
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_extends_list = false
ij_java_align_multiline_for = true
ij_java_align_multiline_method_parentheses = false
ij_java_align_multiline_parameters = true
ij_java_align_multiline_parameters_in_calls = false
ij_java_align_multiline_parenthesized_expression = false
ij_java_align_multiline_records = true
ij_java_align_multiline_resources = true
ij_java_align_multiline_ternary_operation = false
ij_java_align_multiline_text_blocks = false
ij_java_align_multiline_throws_list = false
ij_java_align_subsequent_simple_methods = false
ij_java_align_throws_keyword = false
ij_java_annotation_parameter_wrap = off
ij_java_array_initializer_new_line_after_left_brace = false
ij_java_array_initializer_right_brace_on_new_line = false
ij_java_array_initializer_wrap = off
ij_java_assert_statement_colon_on_next_line = false
ij_java_assert_statement_wrap = off
ij_java_assignment_wrap = off
ij_java_binary_operation_sign_on_next_line = false
ij_java_binary_operation_wrap = off
ij_java_blank_lines_after_anonymous_class_header = 0
ij_java_blank_lines_after_class_header = 0
ij_java_blank_lines_after_imports = 1
ij_java_blank_lines_after_package = 1
ij_java_blank_lines_around_class = 1
ij_java_blank_lines_around_field = 0
ij_java_blank_lines_around_field_in_interface = 0
ij_java_blank_lines_around_initializer = 1
ij_java_blank_lines_around_method = 1
ij_java_blank_lines_around_method_in_interface = 0
ij_java_blank_lines_before_class_end = 0
ij_java_blank_lines_before_imports = 1
ij_java_blank_lines_before_method_body = 0
ij_java_blank_lines_before_package = 0
ij_java_block_brace_style = end_of_line
ij_java_block_comment_at_first_column = true
ij_java_call_parameters_new_line_after_left_paren = false
ij_java_call_parameters_right_paren_on_new_line = false
ij_java_call_parameters_wrap = off
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = false
ij_java_class_annotation_wrap = split_into_lines
ij_java_class_brace_style = end_of_line
ij_java_class_count_to_use_import_on_demand = 5
ij_java_class_names_in_javadoc = 1
ij_java_do_not_indent_top_level_class_members = false
ij_java_do_not_wrap_after_single_annotation = false
ij_java_do_while_brace_force = never
ij_java_doc_add_blank_line_after_description = true
ij_java_doc_add_blank_line_after_param_comments = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = true
ij_java_doc_align_exception_comments = true
ij_java_doc_align_param_comments = true
ij_java_doc_do_not_wrap_if_one_line = true
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = true
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = false
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = false
ij_java_enum_constants_wrap = off
ij_java_extends_keyword_wrap = off
ij_java_extends_list_wrap = off
ij_java_field_annotation_wrap = split_into_lines
ij_java_finally_on_new_line = false
ij_java_for_brace_force = never
ij_java_for_statement_new_line_after_left_paren = false
ij_java_for_statement_right_paren_on_new_line = false
ij_java_for_statement_wrap = off
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_if_brace_force = never
ij_java_imports_layout = *,|,javax.**,java.**,|,$*
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 2
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
ij_java_keep_blank_lines_in_code = 2
ij_java_keep_blank_lines_in_declarations = 2
ij_java_keep_control_statement_in_one_line = true
ij_java_keep_first_column_comment = true
ij_java_keep_indents_on_empty_lines = false
ij_java_keep_line_breaks = true
ij_java_keep_multiple_expressions_in_one_line = false
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = false
ij_java_keep_simple_lambdas_in_one_line = true
ij_java_keep_simple_methods_in_one_line = false
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = true
ij_java_line_comment_at_first_column = false
ij_java_method_annotation_wrap = normal
ij_java_method_brace_style = end_of_line
ij_java_method_call_chain_wrap = off
ij_java_method_parameters_new_line_after_left_paren = false
ij_java_method_parameters_right_paren_on_new_line = false
ij_java_method_parameters_wrap = off
ij_java_modifier_list_wrap = false
ij_java_names_count_to_use_import_on_demand = 3
ij_java_new_line_after_lparen_in_record_header = false
ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
ij_java_parameter_annotation_wrap = off
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = false
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_record_components_wrap = normal
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = false
ij_java_replace_null_check = true
ij_java_replace_sum_lambda_with_method_ref = true
ij_java_resource_list_new_line_after_left_paren = false
ij_java_resource_list_right_paren_on_new_line = false
ij_java_resource_list_wrap = off
ij_java_rparen_on_new_line_in_record_header = false
ij_java_space_after_closing_angle_bracket_in_type_argument = false
ij_java_space_after_colon = true
ij_java_space_after_comma = true
ij_java_space_after_comma_in_type_arguments = true
ij_java_space_after_for_semicolon = true
ij_java_space_after_quest = true
ij_java_space_after_type_cast = true
ij_java_space_before_annotation_array_initializer_left_brace = false
ij_java_space_before_annotation_parameter_list = false
ij_java_space_before_array_initializer_left_brace = false
ij_java_space_before_catch_keyword = true
ij_java_space_before_catch_left_brace = true
ij_java_space_before_catch_parentheses = true
ij_java_space_before_class_left_brace = true
ij_java_space_before_colon = true
ij_java_space_before_colon_in_foreach = true
ij_java_space_before_comma = false
ij_java_space_before_do_left_brace = true
ij_java_space_before_else_keyword = true
ij_java_space_before_else_left_brace = true
ij_java_space_before_finally_keyword = true
ij_java_space_before_finally_left_brace = true
ij_java_space_before_for_left_brace = true
ij_java_space_before_for_parentheses = true
ij_java_space_before_for_semicolon = false
ij_java_space_before_if_left_brace = true
ij_java_space_before_if_parentheses = true
ij_java_space_before_method_call_parentheses = false
ij_java_space_before_method_left_brace = true
ij_java_space_before_method_parentheses = false
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
ij_java_space_before_quest = true
ij_java_space_before_switch_left_brace = true
ij_java_space_before_switch_parentheses = true
ij_java_space_before_synchronized_left_brace = true
ij_java_space_before_synchronized_parentheses = true
ij_java_space_before_try_left_brace = true
ij_java_space_before_try_parentheses = true
ij_java_space_before_type_parameter_list = false
ij_java_space_before_while_keyword = true
ij_java_space_before_while_left_brace = true
ij_java_space_before_while_parentheses = true
ij_java_space_inside_one_line_enum_braces = false
ij_java_space_within_empty_array_initializer_braces = false
ij_java_space_within_empty_method_call_parentheses = false
ij_java_space_within_empty_method_parentheses = false
ij_java_spaces_around_additive_operators = true
ij_java_spaces_around_assignment_operators = true
ij_java_spaces_around_bitwise_operators = true
ij_java_spaces_around_equality_operators = true
ij_java_spaces_around_lambda_arrow = true
ij_java_spaces_around_logical_operators = true
ij_java_spaces_around_method_ref_dbl_colon = false
ij_java_spaces_around_multiplicative_operators = true
ij_java_spaces_around_relational_operators = true
ij_java_spaces_around_shift_operators = true
ij_java_spaces_around_type_bounds_in_type_parameters = true
ij_java_spaces_around_unary_operator = false
ij_java_spaces_within_angle_brackets = false
ij_java_spaces_within_annotation_parentheses = false
ij_java_spaces_within_array_initializer_braces = false
ij_java_spaces_within_braces = false
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = false
ij_java_spaces_within_for_parentheses = false
ij_java_spaces_within_if_parentheses = false
ij_java_spaces_within_method_call_parentheses = false
ij_java_spaces_within_method_parentheses = false
ij_java_spaces_within_parentheses = false
ij_java_spaces_within_record_header = false
ij_java_spaces_within_switch_parentheses = false
ij_java_spaces_within_synchronized_parentheses = false
ij_java_spaces_within_try_parentheses = false
ij_java_spaces_within_while_parentheses = false
ij_java_special_else_if_treatment = true
ij_java_subclass_name_suffix = Impl
ij_java_ternary_operation_signs_on_next_line = false
ij_java_ternary_operation_wrap = off
ij_java_test_name_suffix = Test
ij_java_throws_keyword_wrap = off
ij_java_throws_list_wrap = off
ij_java_use_external_annotations = false
ij_java_use_fq_class_names = false
ij_java_use_relative_indents = false
ij_java_use_single_class_imports = true
ij_java_variable_annotation_wrap = off
ij_java_visibility = public
ij_java_while_brace_force = never
ij_java_while_on_new_line = false
ij_java_wrap_comments = false
ij_java_wrap_first_method_in_call_chain = false
ij_java_wrap_long_lines = false
[*.properties]
ij_properties_align_group_field_declarations = false
ij_properties_keep_blank_lines = false
ij_properties_key_value_delimiter = equals
ij_properties_spaces_around_key_value_delimiter = false
[*.proto]
ij_proto_keep_indents_on_empty_lines = false
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
ij_xml_align_attributes = true
ij_xml_align_text = false
ij_xml_attribute_wrap = normal
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 1
ij_xml_keep_indents_on_empty_lines = false
ij_xml_keep_line_breaks = true
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = false
ij_xml_line_comment_at_first_column = true
ij_xml_space_after_tag_name = false
ij_xml_space_around_equals_in_attribute = false
ij_xml_space_inside_empty_tag = false
ij_xml_text_wrap = normal
[{*.gant,*.gradle,*.groovy,*.gy}]
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
ij_groovy_align_multiline_assignment = false
ij_groovy_align_multiline_binary_operation = false
ij_groovy_align_multiline_chained_methods = false
ij_groovy_align_multiline_extends_list = false
ij_groovy_align_multiline_for = true
ij_groovy_align_multiline_list_or_map = true
ij_groovy_align_multiline_method_parentheses = false
ij_groovy_align_multiline_parameters = true
ij_groovy_align_multiline_parameters_in_calls = false
ij_groovy_align_multiline_resources = true
ij_groovy_align_multiline_ternary_operation = false
ij_groovy_align_multiline_throws_list = false
ij_groovy_align_named_args_in_map = true
ij_groovy_align_throws_keyword = false
ij_groovy_array_initializer_new_line_after_left_brace = false
ij_groovy_array_initializer_right_brace_on_new_line = false
ij_groovy_array_initializer_wrap = off
ij_groovy_assert_statement_wrap = off
ij_groovy_assignment_wrap = off
ij_groovy_binary_operation_wrap = off
ij_groovy_blank_lines_after_class_header = 0
ij_groovy_blank_lines_after_imports = 1
ij_groovy_blank_lines_after_package = 1
ij_groovy_blank_lines_around_class = 1
ij_groovy_blank_lines_around_field = 0
ij_groovy_blank_lines_around_field_in_interface = 0
ij_groovy_blank_lines_around_method = 1
ij_groovy_blank_lines_around_method_in_interface = 1
ij_groovy_blank_lines_before_imports = 1
ij_groovy_blank_lines_before_method_body = 0
ij_groovy_blank_lines_before_package = 0
ij_groovy_block_brace_style = end_of_line
ij_groovy_block_comment_at_first_column = true
ij_groovy_call_parameters_new_line_after_left_paren = false
ij_groovy_call_parameters_right_paren_on_new_line = false
ij_groovy_call_parameters_wrap = off
ij_groovy_catch_on_new_line = false
ij_groovy_class_annotation_wrap = split_into_lines
ij_groovy_class_brace_style = end_of_line
ij_groovy_class_count_to_use_import_on_demand = 5
ij_groovy_do_while_brace_force = never
ij_groovy_else_on_new_line = false
ij_groovy_enum_constants_wrap = off
ij_groovy_extends_keyword_wrap = off
ij_groovy_extends_list_wrap = off
ij_groovy_field_annotation_wrap = off
ij_groovy_finally_on_new_line = false
ij_groovy_for_brace_force = never
ij_groovy_for_statement_new_line_after_left_paren = false
ij_groovy_for_statement_right_paren_on_new_line = false
ij_groovy_for_statement_wrap = off
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
ij_groovy_keep_blank_lines_before_right_brace = 2
ij_groovy_keep_blank_lines_in_code = 2
ij_groovy_keep_blank_lines_in_declarations = 2
ij_groovy_keep_control_statement_in_one_line = true
ij_groovy_keep_first_column_comment = true
ij_groovy_keep_indents_on_empty_lines = false
ij_groovy_keep_line_breaks = true
ij_groovy_keep_multiple_expressions_in_one_line = false
ij_groovy_keep_simple_blocks_in_one_line = false
ij_groovy_keep_simple_classes_in_one_line = true
ij_groovy_keep_simple_lambdas_in_one_line = true
ij_groovy_keep_simple_methods_in_one_line = true
ij_groovy_label_indent_absolute = false
ij_groovy_label_indent_size = 0
ij_groovy_lambda_brace_style = end_of_line
ij_groovy_layout_static_imports_separately = true
ij_groovy_line_comment_add_space = false
ij_groovy_line_comment_at_first_column = true
ij_groovy_method_annotation_wrap = off
ij_groovy_method_brace_style = end_of_line
ij_groovy_method_call_chain_wrap = off
ij_groovy_method_parameters_new_line_after_left_paren = false
ij_groovy_method_parameters_right_paren_on_new_line = false
ij_groovy_method_parameters_wrap = off
ij_groovy_modifier_list_wrap = false
ij_groovy_names_count_to_use_import_on_demand = 3
ij_groovy_parameter_annotation_wrap = off
ij_groovy_parentheses_expression_new_line_after_left_paren = false
ij_groovy_parentheses_expression_right_paren_on_new_line = false
ij_groovy_prefer_parameters_wrap = false
ij_groovy_resource_list_new_line_after_left_paren = false
ij_groovy_resource_list_right_paren_on_new_line = false
ij_groovy_resource_list_wrap = off
ij_groovy_space_after_assert_separator = true
ij_groovy_space_after_colon = true
ij_groovy_space_after_comma = true
ij_groovy_space_after_comma_in_type_arguments = true
ij_groovy_space_after_for_semicolon = true
ij_groovy_space_after_quest = true
ij_groovy_space_after_type_cast = true
ij_groovy_space_before_annotation_parameter_list = false
ij_groovy_space_before_array_initializer_left_brace = false
ij_groovy_space_before_assert_separator = false
ij_groovy_space_before_catch_keyword = true
ij_groovy_space_before_catch_left_brace = true
ij_groovy_space_before_catch_parentheses = true
ij_groovy_space_before_class_left_brace = true
ij_groovy_space_before_closure_left_brace = true
ij_groovy_space_before_colon = true
ij_groovy_space_before_comma = false
ij_groovy_space_before_do_left_brace = true
ij_groovy_space_before_else_keyword = true
ij_groovy_space_before_else_left_brace = true
ij_groovy_space_before_finally_keyword = true
ij_groovy_space_before_finally_left_brace = true
ij_groovy_space_before_for_left_brace = true
ij_groovy_space_before_for_parentheses = true
ij_groovy_space_before_for_semicolon = false
ij_groovy_space_before_if_left_brace = true
ij_groovy_space_before_if_parentheses = true
ij_groovy_space_before_method_call_parentheses = false
ij_groovy_space_before_method_left_brace = true
ij_groovy_space_before_method_parentheses = false
ij_groovy_space_before_quest = true
ij_groovy_space_before_switch_left_brace = true
ij_groovy_space_before_switch_parentheses = true
ij_groovy_space_before_synchronized_left_brace = true
ij_groovy_space_before_synchronized_parentheses = true
ij_groovy_space_before_try_left_brace = true
ij_groovy_space_before_try_parentheses = true
ij_groovy_space_before_while_keyword = true
ij_groovy_space_before_while_left_brace = true
ij_groovy_space_before_while_parentheses = true
ij_groovy_space_in_named_argument = true
ij_groovy_space_in_named_argument_before_colon = false
ij_groovy_space_within_empty_array_initializer_braces = false
ij_groovy_space_within_empty_method_call_parentheses = false
ij_groovy_spaces_around_additive_operators = true
ij_groovy_spaces_around_assignment_operators = true
ij_groovy_spaces_around_bitwise_operators = true
ij_groovy_spaces_around_equality_operators = true
ij_groovy_spaces_around_lambda_arrow = true
ij_groovy_spaces_around_logical_operators = true
ij_groovy_spaces_around_multiplicative_operators = true
ij_groovy_spaces_around_regex_operators = true
ij_groovy_spaces_around_relational_operators = true
ij_groovy_spaces_around_shift_operators = true
ij_groovy_spaces_within_annotation_parentheses = false
ij_groovy_spaces_within_array_initializer_braces = false
ij_groovy_spaces_within_braces = true
ij_groovy_spaces_within_brackets = false
ij_groovy_spaces_within_cast_parentheses = false
ij_groovy_spaces_within_catch_parentheses = false
ij_groovy_spaces_within_for_parentheses = false
ij_groovy_spaces_within_gstring_injection_braces = false
ij_groovy_spaces_within_if_parentheses = false
ij_groovy_spaces_within_list_or_map = false
ij_groovy_spaces_within_method_call_parentheses = false
ij_groovy_spaces_within_method_parentheses = false
ij_groovy_spaces_within_parentheses = false
ij_groovy_spaces_within_switch_parentheses = false
ij_groovy_spaces_within_synchronized_parentheses = false
ij_groovy_spaces_within_try_parentheses = false
ij_groovy_spaces_within_tuple_expression = false
ij_groovy_spaces_within_while_parentheses = false
ij_groovy_special_else_if_treatment = true
ij_groovy_ternary_operation_wrap = off
ij_groovy_throws_keyword_wrap = off
ij_groovy_throws_list_wrap = off
ij_groovy_use_flying_geese_braces = false
ij_groovy_use_fq_class_names = false
ij_groovy_use_fq_class_names_in_javadoc = true
ij_groovy_use_relative_indents = false
ij_groovy_use_single_class_imports = true
ij_groovy_variable_annotation_wrap = off
ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_long_lines = false
[{*.kt,*.kts}]
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ij_kotlin_assignment_wrap = off
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = false
ij_kotlin_call_parameters_right_paren_on_new_line = false
ij_kotlin_call_parameters_wrap = off
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = split_into_lines
ij_kotlin_continuation_indent_for_chained_calls = true
ij_kotlin_continuation_indent_for_expression_bodies = true
ij_kotlin_continuation_indent_in_argument_lists = true
ij_kotlin_continuation_indent_in_elvis = true
ij_kotlin_continuation_indent_in_if_conditions = true
ij_kotlin_continuation_indent_in_parameter_lists = true
ij_kotlin_continuation_indent_in_supertype_lists = true
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = off
ij_kotlin_field_annotation_wrap = on_every_item
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = false
ij_kotlin_import_nested_classes = false
ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
ij_kotlin_keep_blank_lines_in_declarations = 2
ij_kotlin_keep_first_column_comment = true
ij_kotlin_keep_indents_on_empty_lines = false
ij_kotlin_keep_line_breaks = true
ij_kotlin_lbrace_on_next_line = false
ij_kotlin_line_comment_add_space = false
ij_kotlin_line_comment_at_first_column = true
ij_kotlin_method_annotation_wrap = normal
ij_kotlin_method_call_chain_wrap = off
ij_kotlin_method_parameters_new_line_after_left_paren = false
ij_kotlin_method_parameters_right_paren_on_new_line = false
ij_kotlin_method_parameters_wrap = off
ij_kotlin_name_count_to_use_star_import = 5
ij_kotlin_name_count_to_use_star_import_for_members = 3
ij_kotlin_packages_to_use_import_on_demand = java.util.*,io.ktor.**,java.awt.*,javax.swing.*
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 0
ij_kotlin_wrap_first_method_in_call_chain = false
[{*.har,*.json}]
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = true
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{*.markdown,*.md}]
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_keep_indents_on_empty_lines = false
ij_markdown_max_lines_around_block_elements = 1
ij_markdown_max_lines_around_header = 1
ij_markdown_max_lines_between_paragraphs = 1
ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1
[{*.yaml,*.yml}]
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_space_before_colon = true
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true

View File

@ -125,22 +125,22 @@
</activity-alias>
<activity
android:name=".ui.support.AboutActivity"
android:name=".ui.other.AboutActivity"
android:exported="false"
android:label="@string/about"
android:theme="@style/AppTheme.NoActionBar.Dark"/>
<activity
android:name=".ui.crash.CrashActivity"
android:name=".ui.other.CrashActivity"
android:exported="false"
android:label="@string/error"
android:theme="@style/AppTheme.NoActionBar.Dark"/>
<activity
android:name=".ui.setup.SetupActivity"
android:name=".ui.other.SetupActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".ui.bonus.BonusActivity"
android:name=".ui.other.BonusActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:theme="@style/AppTheme.NoActionBar.Dark"/>
@ -159,12 +159,6 @@
android:exported="false"
android:label="@string/pref_customization_color_scheme"
android:theme="@style/AppTheme.NoActionBar.Dark"/>
<activity
android:name=".ui.support.HelpActivity"
android:exported="false"
android:label="@string/faq"
android:theme="@style/AppTheme.NoActionBar.Dark"/>
<activity
android:name=".ui.settings.SettingActivity"
android:exported="false"
@ -191,4 +185,4 @@
android:value="true"/>
</application>
</manifest>
</manifest>

View File

@ -9,8 +9,8 @@ import android.view.Gravity
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import io.neoterm.component.NeoInitializer
import io.neoterm.frontend.config.NeoPreference
import io.neoterm.ui.bonus.BonusActivity
import io.neoterm.component.config.NeoPreference
import io.neoterm.ui.other.BonusActivity
import io.neoterm.utils.CrashHandler
/**
@ -73,4 +73,4 @@ class App : Application() {
return app!!
}
}
}
}

View File

@ -10,10 +10,9 @@ import io.neoterm.component.font.FontComponent
import io.neoterm.component.pm.PackageComponent
import io.neoterm.component.profile.ProfileComponent
import io.neoterm.component.session.SessionComponent
import io.neoterm.component.session.ShellProfile
import io.neoterm.component.userscript.UserScriptComponent
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.session.shell.ShellProfile
import io.neoterm.utils.NLog
/**
* @author kiva
@ -39,4 +38,4 @@ object NeoInitializer {
val profileComp = ComponentManager.getComponent<ProfileComponent>()
profileComp.registerProfile(ShellProfile.PROFILE_META_NAME, ShellProfile::class.java)
}
}
}

View File

@ -1,6 +0,0 @@
package io.neoterm.component.codegen
/**
* @author kiva
*/
class CodeGenParameter

View File

@ -1,12 +1,8 @@
package io.neoterm.component.codegen
import io.neoterm.component.codegen.interfaces.CodeGenObject
import io.neoterm.component.codegen.interfaces.CodeGenerator
import io.neoterm.frontend.component.NeoComponent
import io.neoterm.component.NeoComponent
/**
* @author kiva
*/
class CodeGenComponent : NeoComponent {
override fun onServiceInit() {
}
@ -23,3 +19,4 @@ class CodeGenComponent : NeoComponent {
}
}
class CodeGenParameter

View File

@ -1,15 +1,9 @@
package io.neoterm.component.codegen.generators
package io.neoterm.component.codegen
import io.neoterm.component.codegen.CodeGenParameter
import io.neoterm.component.codegen.interfaces.CodeGenObject
import io.neoterm.component.codegen.interfaces.CodeGenerator
import io.neoterm.component.ComponentManager
import io.neoterm.component.colorscheme.NeoColorScheme
import io.neoterm.component.config.ConfigureComponent
import io.neoterm.frontend.component.ComponentManager
/**
* @author kiva
*/
class NeoColorGenerator(parameter: CodeGenParameter) : CodeGenerator(parameter) {
override fun getGeneratorName(): String {
return "NeoColorScheme-Generator"
@ -44,7 +38,7 @@ class NeoColorGenerator(parameter: CodeGenParameter) : CodeGenerator(parameter)
" ${NeoColorScheme.COLOR_META_VERSION}: ${
colorScheme.colorVersion
?: component.getLoaderVersion()
}\n"
}\n",
)
builder.append("\n")
}
@ -61,4 +55,14 @@ class NeoColorGenerator(parameter: CodeGenParameter) : CodeGenerator(parameter)
builder.append(" }\n")
}
}
}
class NeoProfileGenerator(parameter: CodeGenParameter) : CodeGenerator(parameter) {
override fun getGeneratorName(): String {
return "NeoProfile-Generator"
}
override fun generateCode(codeGenObject: CodeGenObject): String {
return ""
}
}

View File

@ -1,18 +0,0 @@
package io.neoterm.component.codegen.generators
import io.neoterm.component.codegen.CodeGenParameter
import io.neoterm.component.codegen.interfaces.CodeGenObject
import io.neoterm.component.codegen.interfaces.CodeGenerator
/**
* @author kiva
*/
class NeoProfileGenerator(parameter: CodeGenParameter) : CodeGenerator(parameter) {
override fun getGeneratorName(): String {
return "NeoProfile-Generator"
}
override fun generateCode(codeGenObject: CodeGenObject): String {
return ""
}
}

View File

@ -1,12 +1,10 @@
package io.neoterm.component.codegen.interfaces
package io.neoterm.component.codegen
import io.neoterm.component.codegen.CodeGenParameter
/**
* @author kiva
*/
abstract class CodeGenerator(parameter: CodeGenParameter) {
abstract fun getGeneratorName(): String
abstract fun generateCode(codeGenObject: CodeGenObject): String
}
interface CodeGenObject {
fun getCodeGenerator(parameter: CodeGenParameter): CodeGenerator
}

View File

@ -1,10 +0,0 @@
package io.neoterm.component.codegen.interfaces
import io.neoterm.component.codegen.CodeGenParameter
/**
* @author kiva
*/
interface CodeGenObject {
fun getCodeGenerator(parameter: CodeGenParameter): CodeGenerator
}

View File

@ -1,15 +0,0 @@
package io.neoterm.component.colorscheme
/**
* @author kiva
*/
object DefaultColorScheme : NeoColorScheme() {
init {
/* NOTE: Keep in sync with assets/colors/Default.nl */
colorName = "Default"
foregroundColor = "#ffffff"
backgroundColor = "#14181c"
cursorColor = "#a9aaa9"
}
}

View File

@ -4,20 +4,17 @@ import android.content.Context
import io.neolang.visitor.ConfigVisitor
import io.neoterm.App
import io.neoterm.R
import io.neoterm.component.ComponentManager
import io.neoterm.component.ConfigFileBasedComponent
import io.neoterm.component.codegen.CodeGenComponent
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.component.helper.ConfigFileBasedComponent
import io.neoterm.frontend.config.NeoPreference
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.component.config.NeoPreference
import io.neoterm.component.config.NeoTermPath
import io.neoterm.frontend.session.view.TerminalView
import io.neoterm.frontend.session.view.extrakey.ExtraKeysView
import io.neoterm.utils.extractAssetsDir
import java.io.File
import java.nio.file.Files
/**
* @author kiva
*/
class ColorSchemeComponent : ConfigFileBasedComponent<NeoColorScheme>(NeoTermPath.COLORS_PATH) {
companion object {
fun colorFile(colorName: String): File {
@ -120,4 +117,5 @@ class ColorSchemeComponent : ConfigFileBasedComponent<NeoColorScheme>(NeoTermPat
throw RuntimeException("Failed to save file ${colorFile.absolutePath}")
}
}
}
}

View File

@ -3,23 +3,15 @@ package io.neoterm.component.colorscheme
import io.neolang.visitor.ConfigVisitor
import io.neoterm.backend.TerminalColorScheme
import io.neoterm.backend.TerminalColors
import io.neoterm.component.ConfigFileBasedObject
import io.neoterm.component.codegen.CodeGenObject
import io.neoterm.component.codegen.CodeGenParameter
import io.neoterm.component.codegen.generators.NeoColorGenerator
import io.neoterm.component.codegen.interfaces.CodeGenObject
import io.neoterm.component.codegen.interfaces.CodeGenerator
import io.neoterm.component.config.ConfigureComponent
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.component.helper.ConfigFileBasedObject
import io.neoterm.frontend.config.NeoConfigureFile
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import org.jetbrains.annotations.TestOnly
import java.io.File
import io.neoterm.component.codegen.CodeGenerator
import io.neoterm.component.codegen.NeoColorGenerator
import io.neoterm.frontend.session.view.TerminalView
import io.neoterm.frontend.session.view.extrakey.ExtraKeysView
import io.neoterm.utils.NLog
/**
* @author kiva
*/
open class NeoColorScheme : CodeGenObject, ConfigFileBasedObject {
companion object {
const val COLOR_PREFIX = "color"
@ -173,24 +165,15 @@ open class NeoColorScheme : CodeGenObject, ConfigFileBasedObject {
private fun getColorByVisitor(visitor: ConfigVisitor, colorName: String): String? {
return visitor.getStringValue(COLOR_PATH, colorName)
}
}
@TestOnly
fun testLoadConfigure(file: File): Boolean {
val loaderService = ComponentManager.getComponent<ConfigureComponent>()
object DefaultColorScheme : NeoColorScheme() {
init {
/* NOTE: Keep in sync with assets/colors/Default.nl */
colorName = "Default"
val configure: NeoConfigureFile?
try {
configure = loaderService.newLoader(file).loadConfigure()
if (configure == null) {
throw RuntimeException("Parse configuration failed.")
}
} catch (e: Exception) {
NLog.e("ExtraKey", "Failed to load extra key config: ${file.absolutePath}: ${e.localizedMessage}")
return false
}
val visitor = configure.getVisitor()
onConfigLoaded(visitor)
return true
foregroundColor = "#ffffff"
backgroundColor = "#14181c"
cursorColor = "#a9aaa9"
}
}
}

View File

@ -1,10 +1,13 @@
package io.neoterm.frontend.component
package io.neoterm.component
import java.util.concurrent.ConcurrentHashMap
/**
* @author kiva
*/
interface NeoComponent {
fun onServiceInit()
fun onServiceDestroy()
fun onServiceObtained()
}
object ComponentManager {
private val COMPONENTS = ConcurrentHashMap<Class<out NeoComponent>, NeoComponent>()
@ -43,3 +46,7 @@ object ComponentManager {
return componentInterface.newInstance()
}
}
class ComponentDuplicateException(serviceName: String) : RuntimeException("Service $serviceName duplicate")
class ComponentNotFoundException(serviceName: String) : RuntimeException("Component `$serviceName' not found")

View File

@ -1,22 +0,0 @@
package io.neoterm.component.completion
import io.neoterm.component.completion.provider.FileCompletionProvider
import io.neoterm.component.completion.provider.ProgramCompletionProvider
import io.neoterm.frontend.completion.CompletionManager
import io.neoterm.frontend.component.NeoComponent
/**
* @author kiva
*/
class CompletionComponent : NeoComponent {
override fun onServiceInit() {
CompletionManager.registerProvider(FileCompletionProvider())
CompletionManager.registerProvider(ProgramCompletionProvider())
}
override fun onServiceDestroy() {
}
override fun onServiceObtained() {
}
}

View File

@ -1,11 +1,20 @@
package io.neoterm.frontend.completion
package io.neoterm.component.completion
import io.neoterm.frontend.completion.model.CompletionResult
import io.neoterm.frontend.completion.provider.ICandidateProvider
import io.neoterm.component.NeoComponent
class CompletionComponent : NeoComponent {
override fun onServiceInit() {
CompletionManager.registerProvider(FileCompletionProvider())
CompletionManager.registerProvider(ProgramCompletionProvider())
}
override fun onServiceDestroy() {
}
override fun onServiceObtained() {
}
}
/**
* @author kiva
*/
object CompletionManager {
private val candidateProviders = mutableMapOf<String, ICandidateProvider>()
@ -37,4 +46,23 @@ object CompletionManager {
return ProviderDetector(candidateProviders.values
.takeWhile { it.canComplete(text) })
}
}
}
class ProviderDetector(val providers: List<ICandidateProvider>) : MarkScoreListener {
private var detectedProvider: ICandidateProvider? = null
override fun onMarkScore(score: Int) {
// TODO: Save provider score
}
fun detectBest(): ICandidateProvider? {
// TODO: detect best
detectedProvider = if (providers.isEmpty())
null
else
providers[0]
return detectedProvider
}
}

View File

@ -1,10 +1,10 @@
package io.neoterm.frontend.completion.model
package io.neoterm.component.completion
import io.neoterm.frontend.completion.listener.MarkScoreListener
class CompletionCandidate(val completeString: String) {
var displayName: String = completeString
var description: String? = null
}
/**
* @author kiva
*/
class CompletionResult(val candidates: List<CompletionCandidate>, var scoreMarker: MarkScoreListener) {
fun markScore(score: Int) {
scoreMarker.onMarkScore(score)
@ -13,4 +13,5 @@ class CompletionResult(val candidates: List<CompletionCandidate>, var scoreMarke
fun hasResult(): Boolean {
return candidates.isNotEmpty()
}
}
}

View File

@ -0,0 +1,16 @@
package io.neoterm.component.completion
interface MarkScoreListener {
fun onMarkScore(score: Int)
}
interface OnAutoCompleteListener {
fun onCompletionRequired(newText: String?)
fun onKeyCode(keyCode: Int, keyMod: Int)
fun onCleanUp()
fun onFinishCompletion(): Boolean
}
interface OnCandidateSelectedListener {
fun onCandidateSelected(candidate: CompletionCandidate)
}

View File

@ -1,16 +0,0 @@
package io.neoterm.component.completion.provider
import java.io.File
/**
* @author kiva
*/
class ProgramCompletionProvider : FileCompletionProvider() {
override val providerName: String
get() = "NeoTermProvider.ProgramCompletionProvider"
override fun generateDesc(file: File): String? {
return if (file.canExecute()) "<Program>" else super.generateDesc(file)
}
}

View File

@ -1,18 +1,18 @@
package io.neoterm.component.completion.provider
package io.neoterm.component.completion
import io.neoterm.frontend.completion.model.CompletionCandidate
import io.neoterm.frontend.completion.provider.ICandidateProvider
import java.io.File
/**
* @author kiva
*/
interface ICandidateProvider {
val providerName: String
fun provideCandidates(text: String): List<CompletionCandidate>
fun canComplete(text: String): Boolean
}
open class FileCompletionProvider : ICandidateProvider {
override val providerName: String
get() = "NeoTermProvider.FileCompletionProvider"
override fun provideCandidates(text: String): List<CompletionCandidate>? {
override fun provideCandidates(text: String): List<CompletionCandidate> {
var file = File(text)
var filter: ((File) -> Boolean)? = null
@ -33,20 +33,14 @@ open class FileCompletionProvider : ICandidateProvider {
return if (filter != null) path.listFiles(filter) else path.listFiles()
}
private fun generateCandidateList(file: File, filter: ((File) -> Boolean)?): List<CompletionCandidate>? {
if (file.canRead()) {
val candidates = mutableListOf<CompletionCandidate>()
listDirectory(file, filter)
.mapTo(candidates, {
val candidate = CompletionCandidate(it.name)
candidate.description = generateDesc(it)
candidate.displayName = generateDisplayName(it)
candidate
})
return candidates
}
return null
}
private fun generateCandidateList(file: File, filter: ((File) -> Boolean)?) =
if (file.canRead()) listDirectory(file, filter).map {
val candidate = CompletionCandidate(it.name)
candidate.description = generateDesc(it)
candidate.displayName = generateDisplayName(it)
candidate
}.toList()
else listOf()
open fun generateDisplayName(file: File): String {
return if (file.isDirectory) "${file.name}/" else file.name
@ -56,3 +50,13 @@ open class FileCompletionProvider : ICandidateProvider {
return null
}
}
class ProgramCompletionProvider : FileCompletionProvider() {
override val providerName: String
get() = "NeoTermProvider.ProgramCompletionProvider"
override fun generateDesc(file: File): String? {
return if (file.canExecute()) "<Program>" else super.generateDesc(file)
}
}

View File

@ -1,33 +0,0 @@
package io.neoterm.component.config
import io.neoterm.component.config.loaders.NeoLangConfigureLoader
import io.neoterm.component.config.loaders.OldConfigureLoader
import io.neoterm.frontend.component.NeoComponent
import java.io.File
/**
* @author kiva
*/
class ConfigureComponent : NeoComponent {
val CONFIG_LOADER_VERSION = 20
override fun onServiceInit() {
}
override fun onServiceDestroy() {
}
override fun onServiceObtained() {
}
fun getLoaderVersion(): Int {
return CONFIG_LOADER_VERSION
}
fun newLoader(configFile: File): IConfigureLoader {
return when (configFile.extension) {
"nl" -> NeoLangConfigureLoader(configFile)
else -> OldConfigureLoader(configFile)
}
}
}

View File

@ -1,10 +0,0 @@
package io.neoterm.component.config
import io.neoterm.frontend.config.NeoConfigureFile
/**
* @author kiva
*/
interface IConfigureLoader {
fun loadConfigure(): NeoConfigureFile?
}

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.config
package io.neoterm.component.config
import android.content.Context
import android.content.SharedPreferences
@ -6,18 +6,59 @@ import android.preference.PreferenceManager
import android.system.ErrnoException
import android.system.Os
import android.util.TypedValue
import io.neolang.parser.NeoLangParser
import io.neolang.visitor.ConfigVisitor
import io.neoterm.App
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.logging.NLog
import io.neoterm.component.NeoComponent
import io.neoterm.services.NeoTermService
import io.neoterm.utils.NLog
import java.io.File
import java.nio.file.Files
class ConfigureComponent : NeoComponent {
override fun onServiceInit() {
}
/**
* @author kiva
*/
override fun onServiceDestroy() {
}
override fun onServiceObtained() {
}
fun getLoaderVersion(): Int {
return CONFIG_LOADER_VERSION
}
fun newLoader(configFile: File): IConfigureLoader {
return when (configFile.extension) {
"nl" -> NeoLangConfigureLoader(configFile)
else -> OldConfigureLoader(configFile)
}
}
companion object {
private const val CONFIG_LOADER_VERSION = 20
}
}
open class NeoConfigureFile(val configureFile: File) {
private val configParser = NeoLangParser()
protected open var configVisitor: ConfigVisitor? = null
fun getVisitor() = configVisitor ?: throw IllegalStateException("Configure file not loaded or parse failed.")
open fun parseConfigure() = kotlin.runCatching {
val programCode = String(Files.readAllBytes(configureFile.toPath()))
configParser.setInputSource(programCode)
val ast = configParser.parse()
val astVisitor = ast.visit().getVisitor(ConfigVisitor::class.java) ?: return false
astVisitor.start()
configVisitor = astVisitor.getCallback()
}.isSuccess
}
object NeoPreference {
const val KEY_HAPPY_EGG = "neoterm_fun_happy"

View File

@ -1,10 +1,28 @@
package io.neoterm.frontend.config
package io.neoterm.component.config
import android.annotation.SuppressLint
/**
* @author kiva
*/
object DefaultValues {
const val fontSize = 30
const val enableBell = false
const val enableVibrate = false
const val enableExecveWrapper = true
const val enableAutoCompletion = false
const val enableFullScreen = false
const val enableAutoHideToolbar = false
const val enableSwitchNextTab = false
const val enableExtraKeys = true
const val enableExplicitExtraKeysWeight = false
const val enableBackButtonBeMappedToEscape = false
const val enableSpecialVolumeKeys = false
const val enableWordBasedIme = false
const val loginShell = "bash"
const val initialCommand = ""
const val defaultFont = "SourceCodePro"
}
object NeoTermPath {
@SuppressLint("SdCardPath")
const val ROOT_PATH = "/data/data/io.neoterm/files"
@ -32,4 +50,4 @@ object NeoTermPath {
init {
DEFAULT_MAIN_PACKAGE_SOURCE = SOURCE
}
}
}

View File

@ -0,0 +1,194 @@
package io.neoterm.component.config
import io.neolang.runtime.type.NeoLangValue
import io.neolang.visitor.ConfigVisitor
import io.neoterm.component.colorscheme.NeoColorScheme
import io.neoterm.component.extrakey.NeoExtraKey
import io.neoterm.frontend.session.view.extrakey.TextButton
import io.neoterm.utils.NLog
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.FileReader
import java.util.*
interface IConfigureLoader {
fun loadConfigure(): NeoConfigureFile?
}
class NeoLangConfigureLoader(private val configFile: File) : IConfigureLoader {
override fun loadConfigure(): NeoConfigureFile? {
val configureFile = NeoConfigureFile(configFile)
return if (configureFile.parseConfigure()) configureFile else null
}
}
class OldConfigureLoader(private val configFile: File) : IConfigureLoader {
override fun loadConfigure(): NeoConfigureFile? {
return when (configFile.extension) {
"eks" -> returnConfigure(OldExtraKeysConfigureFile(configFile))
"color" -> returnConfigure(OldColorSchemeConfigureFile(configFile))
else -> null
}
}
private fun returnConfigure(configureFile: NeoConfigureFile): NeoConfigureFile? {
return if (configureFile.parseConfigure()) configureFile else null
}
companion object {
class OldColorSchemeConfigureFile(configureFile: File) : NeoConfigureFile(configureFile) {
override var configVisitor: ConfigVisitor? = null
override fun parseConfigure(): Boolean {
try {
val visitor = ConfigVisitor()
visitor.onStart()
visitor.onEnterContext(NeoColorScheme.CONTEXT_META_NAME)
visitor.getCurrentContext()
.defineAttribute(NeoColorScheme.COLOR_META_NAME, NeoLangValue(configureFile.nameWithoutExtension))
.defineAttribute(NeoColorScheme.COLOR_META_VERSION, NeoLangValue("1.0"))
visitor.onEnterContext(NeoColorScheme.CONTEXT_COLOR_NAME)
return FileInputStream(configureFile).use {
val prop = Properties()
prop.load(it)
prop.forEach {
visitor.getCurrentContext().defineAttribute(it.key as String, NeoLangValue(it.value as String))
}
visitor.onFinish()
this.configVisitor = visitor
true
}
} catch (e: Exception) {
this.configVisitor = null
NLog.e("ConfigureLoader", "Error while loading old config", e)
return false
}
}
}
class OldExtraKeysConfigureFile(configureFile: File) : NeoConfigureFile(configureFile) {
override var configVisitor: ConfigVisitor? = null
override fun parseConfigure(): Boolean {
try {
val config = parseOldConfig(BufferedReader(FileReader(configureFile)))
return generateVisitor(config)
} catch (e: Exception) {
NLog.e("ConfigureLoader", "Failed to load old extra keys config: ${e.localizedMessage}")
return false
}
}
private fun generateVisitor(config: NeoExtraKey): Boolean {
configVisitor = ConfigVisitor()
val visitor = configVisitor!!
visitor.onStart()
visitor.onEnterContext(NeoExtraKey.EKS_META_CONTEXT_NAME)
visitor.getCurrentContext()
.defineAttribute(NeoExtraKey.EKS_META_VERSION, NeoLangValue(config.version))
.defineAttribute(NeoExtraKey.EKS_META_WITH_DEFAULT, NeoLangValue(config.withDefaultKeys))
// program
visitor.onEnterContext(NeoExtraKey.EKS_META_PROGRAM)
config.programNames.forEachIndexed { index, program ->
visitor.getCurrentContext().defineAttribute(index.toString(), NeoLangValue(program))
}
visitor.onExitContext()
// key
visitor.onEnterContext(NeoExtraKey.EKS_META_KEY)
config.shortcutKeys.forEachIndexed { index, button ->
if (button is TextButton) {
visitor.onEnterContext(index.toString())
visitor.getCurrentContext()
.defineAttribute(NeoExtraKey.EKS_META_WITH_ENTER, NeoLangValue(button.withEnter))
.defineAttribute(NeoExtraKey.EKS_META_DISPLAY, NeoLangValue(button.buttonKeys!!))
.defineAttribute(NeoExtraKey.EKS_META_CODE, NeoLangValue(button.buttonKeys!!))
visitor.onExitContext()
}
}
visitor.onExitContext()
visitor.onFinish()
return true
}
private fun parseOldConfig(source: BufferedReader): NeoExtraKey {
val config = NeoExtraKey()
var line: String? = source.readLine()
while (line != null) {
line = line.trim().trimEnd()
if (line.isEmpty() || line.startsWith("#")) {
line = source.readLine()
continue
}
if (line.startsWith(NeoExtraKey.EKS_META_VERSION)) {
parseHeader(line, config)
} else if (line.startsWith(NeoExtraKey.EKS_META_PROGRAM)) {
parseProgram(line, config)
} else if (line.startsWith("define")) {
parseKeyDefine(line, config)
} else if (line.startsWith(NeoExtraKey.EKS_META_WITH_DEFAULT)) {
parseWithDefault(line, config)
}
line = source.readLine()
}
if (config.version < 0) {
throw RuntimeException("Not a valid shortcut config file")
}
if (config.programNames.size == 0) {
throw RuntimeException("At least one program name should be given")
}
return config
}
private fun parseWithDefault(line: String, config: NeoExtraKey) {
val value = line.substring(NeoExtraKey.EKS_META_WITH_DEFAULT.length).trim().trimEnd()
config.withDefaultKeys = value == "true"
}
private fun parseKeyDefine(line: String, config: NeoExtraKey) {
val keyDefine = line.substring("define".length).trim().trimEnd()
val keyValues = keyDefine.split(" ")
if (keyValues.size < 2) {
throw RuntimeException("Bad define")
}
val buttonText = keyValues[0]
val withEnter = keyValues[1] == "true"
config.shortcutKeys.add(TextButton(buttonText, withEnter))
}
private fun parseProgram(line: String, config: NeoExtraKey) {
val programNames = line.substring(NeoExtraKey.EKS_META_PROGRAM.length).trim().trimEnd()
if (programNames.isEmpty()) {
return
}
for (name in programNames.split(" ")) {
config.programNames.add(name)
}
}
private fun parseHeader(line: String, config: NeoExtraKey) {
val version: Int
val versionString = line.substring(NeoExtraKey.EKS_META_VERSION.length).trim().trimEnd()
try {
version = Integer.parseInt(versionString)
} catch (e: NumberFormatException) {
throw RuntimeException("Bad version '$versionString'")
}
config.version = version
}
}
}
}

View File

@ -1,15 +0,0 @@
package io.neoterm.component.config.loaders
import io.neoterm.component.config.IConfigureLoader
import io.neoterm.frontend.config.NeoConfigureFile
import java.io.File
/**
* @author kiva
*/
class NeoLangConfigureLoader(private val configFile: File) : IConfigureLoader {
override fun loadConfigure(): NeoConfigureFile? {
val configureFile = NeoConfigureFile(configFile)
return if (configureFile.parseConfigure()) configureFile else null
}
}

View File

@ -1,47 +0,0 @@
package io.neoterm.component.config.loaders
import io.neolang.runtime.type.NeoLangValue
import io.neolang.visitor.ConfigVisitor
import io.neoterm.component.colorscheme.NeoColorScheme
import io.neoterm.frontend.config.NeoConfigureFile
import io.neoterm.frontend.logging.NLog
import java.io.File
import java.io.FileInputStream
import java.util.*
/**
* @author kiva
*/
class OldColorSchemeConfigureFile(configureFile: File) : NeoConfigureFile(configureFile) {
override var configVisitor: ConfigVisitor? = null
override fun parseConfigure(): Boolean {
try {
val visitor = ConfigVisitor()
visitor.onStart()
visitor.onEnterContext(NeoColorScheme.CONTEXT_META_NAME)
visitor.getCurrentContext()
.defineAttribute(NeoColorScheme.COLOR_META_NAME, NeoLangValue(configureFile.nameWithoutExtension))
.defineAttribute(NeoColorScheme.COLOR_META_VERSION, NeoLangValue("1.0"))
visitor.onEnterContext(NeoColorScheme.CONTEXT_COLOR_NAME)
return FileInputStream(configureFile).use {
val prop = Properties()
prop.load(it)
prop.forEach {
visitor.getCurrentContext().defineAttribute(it.key as String, NeoLangValue(it.value as String))
}
visitor.onFinish()
this.configVisitor = visitor
true
}
} catch (e: Exception) {
this.configVisitor = null
NLog.e("ConfigureLoader", "Error while loading old config", e)
return false
}
}
}

View File

@ -1,22 +0,0 @@
package io.neoterm.component.config.loaders
import io.neoterm.component.config.IConfigureLoader
import io.neoterm.frontend.config.NeoConfigureFile
import java.io.File
/**
* @author kiva
*/
class OldConfigureLoader(private val configFile: File) : IConfigureLoader {
override fun loadConfigure(): NeoConfigureFile? {
return when (configFile.extension) {
"eks" -> returnConfigure(OldExtraKeysConfigureFile(configFile))
"color" -> returnConfigure(OldColorSchemeConfigureFile(configFile))
else -> null
}
}
private fun returnConfigure(configureFile: NeoConfigureFile): NeoConfigureFile? {
return if (configureFile.parseConfigure()) configureFile else null
}
}

View File

@ -1,135 +0,0 @@
package io.neoterm.component.config.loaders
import io.neolang.runtime.type.NeoLangValue
import io.neolang.visitor.ConfigVisitor
import io.neoterm.component.extrakey.NeoExtraKey
import io.neoterm.frontend.config.NeoConfigureFile
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.terminal.extrakey.button.TextButton
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
/**
* @author kiva
*/
class OldExtraKeysConfigureFile(configureFile: File) : NeoConfigureFile(configureFile) {
override var configVisitor: ConfigVisitor? = null
override fun parseConfigure(): Boolean {
try {
val config = parseOldConfig(BufferedReader(FileReader(configureFile)))
return generateVisitor(config)
} catch (e: Exception) {
NLog.e("ConfigureLoader", "Failed to load old extra keys config: ${e.localizedMessage}")
return false
}
}
private fun generateVisitor(config: NeoExtraKey): Boolean {
configVisitor = ConfigVisitor()
val visitor = configVisitor!!
visitor.onStart()
visitor.onEnterContext(NeoExtraKey.EKS_META_CONTEXT_NAME)
visitor.getCurrentContext()
.defineAttribute(NeoExtraKey.EKS_META_VERSION, NeoLangValue(config.version))
.defineAttribute(NeoExtraKey.EKS_META_WITH_DEFAULT, NeoLangValue(config.withDefaultKeys))
// program
visitor.onEnterContext(NeoExtraKey.EKS_META_PROGRAM)
config.programNames.forEachIndexed { index, program ->
visitor.getCurrentContext().defineAttribute(index.toString(), NeoLangValue(program))
}
visitor.onExitContext()
// key
visitor.onEnterContext(NeoExtraKey.EKS_META_KEY)
config.shortcutKeys.forEachIndexed { index, button ->
if (button is TextButton) {
visitor.onEnterContext(index.toString())
visitor.getCurrentContext()
.defineAttribute(NeoExtraKey.EKS_META_WITH_ENTER, NeoLangValue(button.withEnter))
.defineAttribute(NeoExtraKey.EKS_META_DISPLAY, NeoLangValue(button.buttonKeys!!))
.defineAttribute(NeoExtraKey.EKS_META_CODE, NeoLangValue(button.buttonKeys!!))
visitor.onExitContext()
}
}
visitor.onExitContext()
visitor.onFinish()
return true
}
private fun parseOldConfig(source: BufferedReader): NeoExtraKey {
val config = NeoExtraKey()
var line: String? = source.readLine()
while (line != null) {
line = line.trim().trimEnd()
if (line.isEmpty() || line.startsWith("#")) {
line = source.readLine()
continue
}
if (line.startsWith(NeoExtraKey.EKS_META_VERSION)) {
parseHeader(line, config)
} else if (line.startsWith(NeoExtraKey.EKS_META_PROGRAM)) {
parseProgram(line, config)
} else if (line.startsWith("define")) {
parseKeyDefine(line, config)
} else if (line.startsWith(NeoExtraKey.EKS_META_WITH_DEFAULT)) {
parseWithDefault(line, config)
}
line = source.readLine()
}
if (config.version < 0) {
throw RuntimeException("Not a valid shortcut config file")
}
if (config.programNames.size == 0) {
throw RuntimeException("At least one program name should be given")
}
return config
}
private fun parseWithDefault(line: String, config: NeoExtraKey) {
val value = line.substring(NeoExtraKey.EKS_META_WITH_DEFAULT.length).trim().trimEnd()
config.withDefaultKeys = value == "true"
}
private fun parseKeyDefine(line: String, config: NeoExtraKey) {
val keyDefine = line.substring("define".length).trim().trimEnd()
val keyValues = keyDefine.split(" ")
if (keyValues.size < 2) {
throw RuntimeException("Bad define")
}
val buttonText = keyValues[0]
val withEnter = keyValues[1] == "true"
config.shortcutKeys.add(TextButton(buttonText, withEnter))
}
private fun parseProgram(line: String, config: NeoExtraKey) {
val programNames = line.substring(NeoExtraKey.EKS_META_PROGRAM.length).trim().trimEnd()
if (programNames.isEmpty()) {
return
}
for (name in programNames.split(" ")) {
config.programNames.add(name)
}
}
private fun parseHeader(line: String, config: NeoExtraKey) {
val version: Int
val versionString = line.substring(NeoExtraKey.EKS_META_VERSION.length).trim().trimEnd()
try {
version = Integer.parseInt(versionString)
} catch (e: NumberFormatException) {
throw RuntimeException("Bad version '$versionString'")
}
config.version = version
}
}

View File

@ -1,16 +1,16 @@
package io.neoterm.frontend.component.helper
package io.neoterm.component
import io.neolang.visitor.ConfigVisitor
import io.neoterm.component.config.ConfigureComponent
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.component.NeoComponent
import io.neoterm.frontend.logging.NLog
import io.neoterm.utils.NLog
import java.io.File
import java.io.FileFilter
/**
* @author kiva
*/
interface ConfigFileBasedObject {
@Throws(RuntimeException::class)
fun onConfigLoaded(configVisitor: ConfigVisitor)
}
abstract class ConfigFileBasedComponent<out T : ConfigFileBasedObject>(protected val baseDir: String) : NeoComponent {
companion object {
private val TAG = ConfigFileBasedComponent::class.java.simpleName
@ -60,4 +60,5 @@ abstract class ConfigFileBasedComponent<out T : ConfigFileBasedObject>(protected
abstract fun onCheckComponentFiles()
abstract fun onCreateComponentObject(configVisitor: ConfigVisitor): T
}
}

View File

@ -3,16 +3,13 @@ package io.neoterm.component.extrakey
import android.content.Context
import io.neolang.visitor.ConfigVisitor
import io.neoterm.App
import io.neoterm.frontend.component.helper.ConfigFileBasedComponent
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.component.ConfigFileBasedComponent
import io.neoterm.component.config.NeoTermPath
import io.neoterm.frontend.session.view.extrakey.ExtraKeysView
import io.neoterm.utils.NLog
import io.neoterm.utils.extractAssetsDir
import java.io.File
/**
* @author kiva
*/
class ExtraKeyComponent : ConfigFileBasedComponent<NeoExtraKey>(NeoTermPath.EKS_PATH) {
override val checkComponentFileWhenObtained
get() = true
@ -68,4 +65,4 @@ class ExtraKeyComponent : ConfigFileBasedComponent<NeoExtraKey>(NeoTermPath.EKS_
registerShortcutKeys(it)
}
}
}
}

View File

@ -1,16 +1,10 @@
package io.neoterm.component.extrakey
import io.neolang.visitor.ConfigVisitor
import io.neoterm.component.config.ConfigureComponent
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.component.helper.ConfigFileBasedObject
import io.neoterm.frontend.config.NeoConfigureFile
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.frontend.terminal.extrakey.button.IExtraButton
import io.neoterm.frontend.terminal.extrakey.button.TextButton
import org.jetbrains.annotations.TestOnly
import java.io.File
import io.neoterm.component.ConfigFileBasedObject
import io.neoterm.frontend.session.view.extrakey.ExtraKeysView
import io.neoterm.frontend.session.view.extrakey.IExtraButton
import io.neoterm.frontend.session.view.extrakey.TextButton
/**
* @author kiva
@ -86,24 +80,4 @@ class NeoExtraKey : ConfigFileBasedObject {
private fun getMetaByVisitor(visitor: ConfigVisitor, metaName: String): String? {
return visitor.getStringValue(EKS_META_CONTEXT_PATH, metaName)
}
@TestOnly
fun testLoadConfigure(file: File): Boolean {
val loaderService = ComponentManager.getComponent<ConfigureComponent>()
val configure: NeoConfigureFile?
try {
configure = loaderService.newLoader(file).loadConfigure()
if (configure == null) {
throw RuntimeException("Parse configuration failed.")
}
} catch (e: Exception) {
NLog.e("ExtraKey", "Failed to load extra key config: ${file.absolutePath}: ${e.localizedMessage}")
return false
}
val visitor = configure.getVisitor()
onConfigLoaded(visitor)
return true
}
}
}

View File

@ -4,18 +4,15 @@ import android.content.Context
import android.graphics.Typeface
import io.neoterm.App
import io.neoterm.R
import io.neoterm.frontend.component.NeoComponent
import io.neoterm.frontend.config.DefaultValues
import io.neoterm.frontend.config.NeoPreference
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.component.NeoComponent
import io.neoterm.component.config.DefaultValues
import io.neoterm.component.config.NeoPreference
import io.neoterm.component.config.NeoTermPath
import io.neoterm.frontend.session.view.TerminalView
import io.neoterm.frontend.session.view.extrakey.ExtraKeysView
import io.neoterm.utils.extractAssetsDir
import java.io.File
/**
* @author kiva
*/
class FontComponent : NeoComponent {
private lateinit var DEFAULT_FONT: NeoFont
private lateinit var fonts: MutableMap<String, NeoFont>
@ -126,4 +123,5 @@ class FontComponent : NeoComponent {
fonts.put(defaultFont, DEFAULT_FONT)
}
}
}
}

View File

@ -1,13 +1,10 @@
package io.neoterm.component.font
import android.graphics.Typeface
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.frontend.session.view.TerminalView
import io.neoterm.frontend.session.view.extrakey.ExtraKeysView
import java.io.File
/**
* @author kiva
*/
class NeoFont {
private var fontFile: File? = null
private var typeface: Typeface? = null
@ -36,4 +33,4 @@ class NeoFont {
}
return typeface
}
}
}

View File

@ -1,21 +0,0 @@
package io.neoterm.component.pm
/**
* @author kiva
*/
enum class Architecture {
ALL, ARM, AARCH64, X86, X86_64;
companion object {
fun parse(arch: String): Architecture {
return when (arch) {
"arm" -> ARM
"aarch64" -> AARCH64
"x86" -> X86
"x86_64" -> X86_64
else -> ALL
}
}
}
}

View File

@ -1,6 +1,6 @@
package io.neoterm.component.pm;
import io.neoterm.frontend.component.NeoComponent;
import io.neoterm.component.NeoComponent;
import java.io.File;
import java.io.FileInputStream;

View File

@ -1,54 +0,0 @@
package io.neoterm.component.pm
import io.neoterm.App
import io.neoterm.R
import io.neoterm.framework.NeoTermDatabase
import io.neoterm.frontend.config.NeoTermPath
/**
* @author kiva
*/
class SourceManager internal constructor() {
private val database = NeoTermDatabase.instance("sources")
init {
if (database.findAll<Source>(Source::class.java).isEmpty()) {
App.get().resources.getStringArray(R.array.pref_package_source_values)
.forEach {
database.saveBean(Source(it, "stable main", true))
}
}
}
fun addSource(sourceUrl: String, repo: String, enabled: Boolean) {
database.saveBean(Source(sourceUrl, repo, enabled))
}
fun removeSource(sourceUrl: String) {
database.deleteBeanByWhere(Source::class.java, "url == '$sourceUrl'")
}
fun updateAll(sources: List<Source>) {
database.dropAllTable()
database.saveBeans(sources)
}
fun getAllSources(): List<Source> {
return database.findAll<Source>(Source::class.java)
}
fun getEnabledSources(): List<Source> {
return getAllSources().filter { it.enabled }
}
fun getMainPackageSource(): String {
return getEnabledSources()
.map { it.repo }
.singleOrNull { it.trim() == "stable main" }
?: NeoTermPath.DEFAULT_MAIN_PACKAGE_SOURCE
}
fun applyChanges() {
database.vacuum()
}
}

View File

@ -1,8 +1,20 @@
package io.neoterm.component.pm
/**
* @author kiva
*/
enum class Architecture {
ALL, ARM, AARCH64, X86, X86_64;
companion object {
fun parse(arch: String): Architecture {
return when (arch) {
"arm" -> ARM
"aarch64" -> AARCH64
"x86" -> X86
"x86_64" -> X86_64
else -> ALL
}
}
}
}
class NeoPackageInfo {
var packageName: String? = null
@ -21,4 +33,3 @@ class NeoPackageInfo {
var homePage: String? = null
var description: String? = null
}

View File

@ -1,16 +1,16 @@
package io.neoterm.component.pm
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.logging.NLog
import io.neoterm.App
import io.neoterm.R
import io.neoterm.component.ComponentManager
import io.neoterm.component.config.NeoTermPath
import io.neoterm.framework.NeoTermDatabase
import io.neoterm.utils.NLog
import java.io.File
import java.net.URL
import java.nio.file.Files
import java.nio.file.Paths
/**
* @author kiva
*/
object SourceHelper {
fun syncSource() {
val sourceManager = ComponentManager.getComponent<PackageComponent>().sourceManager
@ -71,4 +71,50 @@ object SourceHelper {
return ""
}
}
}
}
class SourceManager internal constructor() {
private val database = NeoTermDatabase.instance("sources")
init {
if (database.findAll<Source>(Source::class.java).isEmpty()) {
App.get().resources.getStringArray(R.array.pref_package_source_values)
.forEach {
database.saveBean(Source(it, "stable main", true))
}
}
}
fun addSource(sourceUrl: String, repo: String, enabled: Boolean) {
database.saveBean(Source(sourceUrl, repo, enabled))
}
fun removeSource(sourceUrl: String) {
database.deleteBeanByWhere(Source::class.java, "url == '$sourceUrl'")
}
fun updateAll(sources: List<Source>) {
database.dropAllTable()
database.saveBeans(sources)
}
fun getAllSources(): List<Source> {
return database.findAll(Source::class.java)
}
fun getEnabledSources(): List<Source> {
return getAllSources().filter { it.enabled }
}
fun getMainPackageSource(): String {
return getEnabledSources()
.map { it.repo }
.singleOrNull { it.trim() == "stable main" }
?: NeoTermPath.DEFAULT_MAIN_PACKAGE_SOURCE
}
fun applyChanges() {
database.vacuum()
}
}

View File

@ -1,14 +1,11 @@
package io.neoterm.component.profile
import io.neolang.visitor.ConfigVisitor
import io.neoterm.frontend.component.helper.ConfigFileBasedComponent
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.logging.NLog
import io.neoterm.component.ConfigFileBasedComponent
import io.neoterm.component.config.NeoTermPath
import io.neoterm.utils.NLog
import java.io.File
/**
* @author kiva
*/
class ProfileComponent : ConfigFileBasedComponent<NeoProfile>(NeoTermPath.PROFILE_PATH) {
override val checkComponentFileWhenObtained
get() = true
@ -63,4 +60,4 @@ class ProfileComponent : ConfigFileBasedComponent<NeoProfile>(NeoTermPath.PROFIL
fun unregisterProfile(metaName: String) {
profileRegistry.remove(metaName)
}
}
}

View File

@ -1,21 +1,18 @@
package io.neoterm.component.profile
import io.neolang.visitor.ConfigVisitor
import io.neoterm.component.ComponentManager
import io.neoterm.component.ConfigFileBasedObject
import io.neoterm.component.codegen.CodeGenObject
import io.neoterm.component.codegen.CodeGenParameter
import io.neoterm.component.codegen.generators.NeoProfileGenerator
import io.neoterm.component.codegen.interfaces.CodeGenObject
import io.neoterm.component.codegen.interfaces.CodeGenerator
import io.neoterm.component.codegen.CodeGenerator
import io.neoterm.component.codegen.NeoProfileGenerator
import io.neoterm.component.config.ConfigureComponent
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.component.helper.ConfigFileBasedObject
import io.neoterm.frontend.config.NeoConfigureFile
import io.neoterm.frontend.logging.NLog
import io.neoterm.component.config.NeoConfigureFile
import io.neoterm.utils.NLog
import org.jetbrains.annotations.TestOnly
import java.io.File
/**
* @author kiva
*/
abstract class NeoProfile : CodeGenObject, ConfigFileBasedObject {
companion object {
private const val PROFILE_NAME = "name"
@ -70,4 +67,4 @@ abstract class NeoProfile : CodeGenObject, ConfigFileBasedObject {
protected fun ConfigVisitor.getProfileBoolean(key: String): Boolean? {
return this.getBooleanValue(profileMetaPath, key)
}
}
}

View File

@ -4,18 +4,10 @@ import android.annotation.SuppressLint
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import io.neoterm.Globals
import io.neoterm.frontend.component.NeoComponent
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.session.shell.ShellParameter
import io.neoterm.frontend.session.shell.ShellTermSession
import io.neoterm.frontend.session.xorg.XParameter
import io.neoterm.frontend.session.xorg.XSession
import io.neoterm.frontend.session.xorg.client.XSessionData
import io.neoterm.component.NeoComponent
import io.neoterm.component.config.NeoTermPath
import io.neoterm.utils.NLog
/**
* @author kiva
*/
class SessionComponent : NeoComponent {
companion object {
private var IS_LIBRARIES_LOADED = false
@ -50,7 +42,7 @@ class SessionComponent : NeoComponent {
} catch (error: UnsatisfiedLinkError) {
NLog.e(
"SessionComponent", "Error loading lib " + soPath
+ ", reason: " + error.localizedMessage
+ ", reason: " + error.localizedMessage
)
result = false
}
@ -109,4 +101,4 @@ class SessionComponent : NeoComponent {
.profile(parameter.shellProfile)
.create(context)
}
}
}

View File

@ -1,13 +1,163 @@
package io.neoterm.frontend.session.shell
package io.neoterm.component.session
import android.content.Context
import io.neolang.visitor.ConfigVisitor
import io.neoterm.App
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.session.shell.client.TermSessionCallback
import io.neoterm.bridge.SessionId
import io.neoterm.component.ComponentManager
import io.neoterm.component.colorscheme.ColorSchemeComponent
import io.neoterm.component.config.DefaultValues
import io.neoterm.component.config.NeoPreference
import io.neoterm.component.config.NeoTermPath
import io.neoterm.component.font.FontComponent
import io.neoterm.component.profile.NeoProfile
import io.neoterm.frontend.session.terminal.TermSessionCallback
import java.io.File
/**
* @author kiva
*/
class ShellParameter {
var sessionId: SessionId? = null
var executablePath: String? = null
var arguments: Array<String>? = null
var cwd: String? = null
var initialCommand: String? = null
var env: Array<Pair<String, String>>? = null
var sessionCallback: TerminalSession.SessionChangedCallback? = null
var systemShell: Boolean = false
var shellProfile: ShellProfile? = null
fun executablePath(executablePath: String?): ShellParameter {
this.executablePath = executablePath
return this
}
fun arguments(arguments: Array<String>?): ShellParameter {
this.arguments = arguments
return this
}
fun currentWorkingDirectory(cwd: String?): ShellParameter {
this.cwd = cwd
return this
}
fun initialCommand(initialCommand: String?): ShellParameter {
this.initialCommand = initialCommand
return this
}
fun environment(env: Array<Pair<String, String>>?): ShellParameter {
this.env = env
return this
}
fun callback(callback: TerminalSession.SessionChangedCallback?): ShellParameter {
this.sessionCallback = callback
return this
}
fun systemShell(systemShell: Boolean): ShellParameter {
this.systemShell = systemShell
return this
}
fun profile(shellProfile: ShellProfile): ShellParameter {
this.shellProfile = shellProfile
return this
}
fun session(sessionId: SessionId?): ShellParameter {
this.sessionId = sessionId
return this
}
fun willCreateNewSession(): Boolean {
return sessionId?.equals(SessionId.NEW_SESSION) ?: true
}
}
/**
* @author kiva
*/
class ShellProfile : NeoProfile() {
companion object {
const val PROFILE_META_NAME = "profile-shell"
private const val LOGIN_SHELL = "login-shell"
private const val INITIAL_COMMAND = "init-command"
private const val BELL = "bell"
private const val VIBRATE = "vibrate"
private const val EXECVE_WRAPPER = "execve-wrapper"
private const val SPECIAL_VOLUME_KEYS = "special-volume-keys"
private const val AUTO_COMPLETION = "auto-completion"
private const val BACK_KEY_TO_ESC = "back-key-esc"
private const val EXTRA_KEYS = "extra-keys"
private const val FONT = "font"
private const val COLOR_SCHEME = "color-scheme"
private const val WORD_BASED_IME = "word-based-ime"
fun create(): ShellProfile {
return ShellProfile()
}
}
override val profileMetaName = PROFILE_META_NAME
var loginShell = DefaultValues.loginShell
var initialCommand = DefaultValues.initialCommand
var enableBell = DefaultValues.enableBell
var enableVibrate = DefaultValues.enableVibrate
var enableExecveWrapper = DefaultValues.enableExecveWrapper
var enableSpecialVolumeKeys = DefaultValues.enableSpecialVolumeKeys
var enableAutoCompletion = DefaultValues.enableAutoCompletion
var enableBackKeyToEscape = DefaultValues.enableBackButtonBeMappedToEscape
var enableExtraKeys = DefaultValues.enableExtraKeys
var enableWordBasedIme = DefaultValues.enableWordBasedIme
var profileFont: String
var profileColorScheme: String
init {
val fontComp = ComponentManager.getComponent<FontComponent>()
val colorComp = ComponentManager.getComponent<ColorSchemeComponent>()
profileFont = fontComp.getCurrentFontName()
profileColorScheme = colorComp.getCurrentColorSchemeName()
loginShell = NeoPreference.getLoginShellPath()
initialCommand = NeoPreference.getInitialCommand()
enableBell = NeoPreference.isBellEnabled()
enableVibrate = NeoPreference.isVibrateEnabled()
enableExecveWrapper = NeoPreference.isExecveWrapperEnabled()
enableSpecialVolumeKeys = NeoPreference.isSpecialVolumeKeysEnabled()
enableAutoCompletion = NeoPreference.isAutoCompletionEnabled()
enableBackKeyToEscape = NeoPreference.isBackButtonBeMappedToEscapeEnabled()
enableExtraKeys = NeoPreference.isExtraKeysEnabled()
enableWordBasedIme = NeoPreference.isWordBasedImeEnabled()
}
override fun onConfigLoaded(configVisitor: ConfigVisitor) {
super.onConfigLoaded(configVisitor)
loginShell = configVisitor.getProfileString(LOGIN_SHELL, loginShell)
initialCommand = configVisitor.getProfileString(INITIAL_COMMAND, initialCommand)
enableBell = configVisitor.getProfileBoolean(BELL, enableBell)
enableVibrate = configVisitor.getProfileBoolean(VIBRATE, enableVibrate)
enableExecveWrapper = configVisitor.getProfileBoolean(EXECVE_WRAPPER, enableExecveWrapper)
enableSpecialVolumeKeys = configVisitor.getProfileBoolean(SPECIAL_VOLUME_KEYS, enableSpecialVolumeKeys)
enableAutoCompletion = configVisitor.getProfileBoolean(AUTO_COMPLETION, enableAutoCompletion)
enableBackKeyToEscape = configVisitor.getProfileBoolean(BACK_KEY_TO_ESC, enableBackKeyToEscape)
enableExtraKeys = configVisitor.getProfileBoolean(EXTRA_KEYS, enableExtraKeys)
enableWordBasedIme = configVisitor.getProfileBoolean(WORD_BASED_IME, enableWordBasedIme)
profileFont = configVisitor.getProfileString(FONT, profileFont)
profileColorScheme = configVisitor.getProfileString(COLOR_SCHEME, profileColorScheme)
}
}
/**
* @author kiva
*/
@ -244,4 +394,4 @@ open class ShellTermSession private constructor(
return "${NeoTermPath.USR_PATH}/bin:${NeoTermPath.USR_PATH}/bin/applets"
}
}
}
}

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.session.xorg
package io.neoterm.component.session
import android.app.UiModeManager
import android.content.Context
@ -7,6 +7,7 @@ import android.content.pm.PackageManager
import android.content.res.Configuration
import android.inputmethodservice.Keyboard
import android.inputmethodservice.KeyboardView
import android.os.Build
import android.os.SystemClock
import android.text.InputType
import android.view.*
@ -15,14 +16,11 @@ import android.widget.EditText
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import io.neoterm.*
import io.neoterm.frontend.session.xorg.client.XSessionData
import io.neoterm.xorg.NeoXorgViewClient
import io.neoterm.xorg.R
import java.util.*
/**
* @author kiva
*/
class XParameter
class XSession constructor(private val mActivity: AppCompatActivity, val mSessionData: XSessionData) :
NeoXorgViewClient {
@ -64,17 +62,11 @@ class XSession constructor(private val mActivity: AppCompatActivity, val mSessio
}
override fun getContext() = mActivity
override fun isKeyboardWithoutTextInputShown() = mSessionData.keyboardWithoutTextInputShown
override fun isPaused() = mSessionData.isPaused
override fun runOnUiThread(runnable: Runnable?) = mActivity.runOnUiThread(runnable)
override fun getGLView() = mSessionData.glView
override fun getWindow() = mActivity.window!!
override fun getWindowManager() = mActivity.windowManager!!
override fun showScreenKeyboardWithoutTextInputField(keyboard: Int) {
@ -244,7 +236,7 @@ class XSession constructor(private val mActivity: AppCompatActivity, val mSessio
val screenKeyboard = EditText(
mActivity, null,
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
android.R.style.TextAppearance_Material_Widget_EditText
else android.R.style.TextAppearance_Widget_EditText
)
@ -349,10 +341,10 @@ class XSession constructor(private val mActivity: AppCompatActivity, val mSessio
override fun setSystemMousePointerVisible(visible: Int) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
glView?.pointerIcon = android.view.PointerIcon.getSystemIcon(
glView?.pointerIcon = PointerIcon.getSystemIcon(
mActivity,
if (visible == 0) android.view.PointerIcon.TYPE_NULL
else android.view.PointerIcon.TYPE_DEFAULT
if (visible == 0) PointerIcon.TYPE_NULL
else PointerIcon.TYPE_DEFAULT
)
}
}
@ -417,5 +409,18 @@ class XSession constructor(private val mActivity: AppCompatActivity, val mSessio
}
}
}
}
class XSessionData {
var videoLayout: FrameLayout? = null
var audioThread: NeoAudioThread? = null
var screenKeyboard: View? = null
var glView: NeoGLView? = null
var isPaused = false
var client: NeoXorgViewClient? = null
var keyboardWithoutTextInputShown = false
var screenKeyboardHintMessage: String? = null
var textInput = LinkedList<Int>()
}

View File

@ -1,8 +0,0 @@
package io.neoterm.component.userscript
import java.io.File
/**
* @author kiva
*/
class UserScript(val scriptFile: File)

View File

@ -3,17 +3,16 @@ package io.neoterm.component.userscript
import android.content.Context
import android.system.Os
import io.neoterm.App
import io.neoterm.frontend.component.NeoComponent
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.logging.NLog
import io.neoterm.component.NeoComponent
import io.neoterm.component.config.NeoTermPath
import io.neoterm.utils.NLog
import io.neoterm.utils.extractAssetsDir
import java.io.File
/**
* @author kiva
*/
class UserScript(val scriptFile: File)
class UserScriptComponent : NeoComponent {
private var userScripts = listOf<UserScript>()
var userScripts = listOf<UserScript>()
private val scriptDir = File(NeoTermPath.USER_SCRIPT_PATH)
override fun onServiceInit() = checkForFiles()

View File

@ -9,7 +9,7 @@ import io.neoterm.App;
import io.neoterm.framework.database.*;
import io.neoterm.framework.database.bean.TableInfo;
import io.neoterm.framework.reflection.Reflect;
import io.neoterm.frontend.logging.NLog;
import io.neoterm.utils.NLog;
import java.io.File;
import java.io.IOException;

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.completion.view
package io.neoterm.frontend.completion
import android.content.Context
import android.view.Gravity
@ -11,11 +11,11 @@ import android.widget.PopupWindow
import android.widget.TextView
import io.neoterm.R
import io.neoterm.backend.TerminalColors
import io.neoterm.component.ComponentManager
import io.neoterm.component.colorscheme.ColorSchemeComponent
import io.neoterm.frontend.completion.listener.OnCandidateSelectedListener
import io.neoterm.frontend.completion.model.CompletionCandidate
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.component.completion.CompletionCandidate
import io.neoterm.component.completion.OnCandidateSelectedListener
import io.neoterm.frontend.session.view.TerminalView
/**
* @author kiva
@ -41,14 +41,13 @@ class CandidatePopupWindow(val context: Context) {
// Ensure that the popup window will not cover the IME.
val rootView = popWindow.contentView
if (rootView is MaxHeightView) {
val maxHeight = terminalView.height
rootView.setMaxHeight(maxHeight)
rootView.maxHeight = terminalView.height
}
popWindow.showAtLocation(
terminalView, Gravity.BOTTOM.and(Gravity.START),
terminalView.cursorAbsoluteX,
terminalView.cursorAbsoluteY
terminalView.cursorAbsoluteY,
)
}
}
@ -69,12 +68,12 @@ class CandidatePopupWindow(val context: Context) {
val listView = contentView.findViewById<ListView>(R.id.popup_complete_candidate_list)
candidateAdapter = CandidateAdapter(this)
listView.adapter = candidateAdapter
listView.setOnItemClickListener({ _, _, position, _ ->
listView.setOnItemClickListener { _, _, position, _ ->
val selectedItem = candidates?.get(position)
if (selectedItem != null) {
onCandidateSelectedListener?.onCandidateSelected(selectedItem)
}
})
}
candidateListView = listView
popupWindow.contentView = contentView
@ -143,4 +142,4 @@ class CandidatePopupWindow(val context: Context) {
description.setTextColor(textColor)
}
}
}
}

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.completion.view
package io.neoterm.frontend.completion
import android.content.Context
import android.util.AttributeSet
@ -6,18 +6,11 @@ import android.view.View
import android.widget.LinearLayout
class MaxHeightView : LinearLayout {
var maxHeight = -1
private var maxHeight = -1
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {}
fun setMaxHeight(maxHeight: Int) {
this.maxHeight = maxHeight
}
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var finalHeightMeasureSpec = heightMeasureSpec
@ -53,4 +46,4 @@ class MaxHeightView : LinearLayout {
super.onMeasure(widthMeasureSpec, finalHeightMeasureSpec)
}
}
}

View File

@ -1,25 +0,0 @@
package io.neoterm.frontend.completion
import io.neoterm.frontend.completion.listener.MarkScoreListener
import io.neoterm.frontend.completion.provider.ICandidateProvider
/**
* @author kiva
*/
class ProviderDetector(val providers: List<ICandidateProvider>) : MarkScoreListener {
private var detectedProvider: ICandidateProvider? = null
override fun onMarkScore(score: Int) {
// TODO: Save provider score
}
fun detectBest(): ICandidateProvider? {
// TODO: detect best
detectedProvider = if (providers.isEmpty())
null
else
providers[0]
return detectedProvider
}
}

View File

@ -1,9 +0,0 @@
package io.neoterm.frontend.completion.listener
/**
* @author kiva
*/
interface MarkScoreListener {
fun onMarkScore(score: Int)
}

View File

@ -1,16 +0,0 @@
package io.neoterm.frontend.completion.listener
/**
* @author Kiva
* *
* @version 1.0
*/
interface OnAutoCompleteListener {
fun onCompletionRequired(newText: String?)
fun onKeyCode(keyCode: Int, keyMod: Int)
fun onCleanUp()
fun onFinishCompletion(): Boolean
}

View File

@ -1,10 +0,0 @@
package io.neoterm.frontend.completion.listener
import io.neoterm.frontend.completion.model.CompletionCandidate
/**
* @author kiva
*/
interface OnCandidateSelectedListener {
fun onCandidateSelected(candidate: CompletionCandidate)
}

View File

@ -1,9 +0,0 @@
package io.neoterm.frontend.completion.model
/**
* @author kiva
*/
class CompletionCandidate(var completeString: String) {
var displayName: String = completeString
var description: String? = null
}

View File

@ -1,15 +0,0 @@
package io.neoterm.frontend.completion.provider
import io.neoterm.frontend.completion.model.CompletionCandidate
/**
* @author kiva
*/
interface ICandidateProvider {
val providerName: String
fun provideCandidates(text: String): List<CompletionCandidate>?
fun canComplete(text: String): Boolean
}

View File

@ -1,6 +0,0 @@
package io.neoterm.frontend.component
/**
* @author kiva
*/
class ComponentDuplicateException(serviceName: String) : RuntimeException("Service $serviceName duplicate")

View File

@ -1,7 +0,0 @@
package io.neoterm.frontend.component
/**
* @author kiva
*/
class ComponentNotFoundException(serviceName: String) : RuntimeException("Component `$serviceName' not found") {
}

View File

@ -1,10 +0,0 @@
package io.neoterm.frontend.component
/**
* @author kiva
*/
interface NeoComponent {
fun onServiceInit()
fun onServiceDestroy()
fun onServiceObtained()
}

View File

@ -1,11 +0,0 @@
package io.neoterm.frontend.component.helper
import io.neolang.visitor.ConfigVisitor
/**
* @author kiva
*/
interface ConfigFileBasedObject {
@Throws(RuntimeException::class)
fun onConfigLoaded(configVisitor: ConfigVisitor)
}

View File

@ -1,25 +0,0 @@
package io.neoterm.frontend.config
/**
* @author kiva
*/
object DefaultValues {
const val fontSize = 30
const val enableBell = false
const val enableVibrate = false
const val enableExecveWrapper = true
const val enableAutoCompletion = false
const val enableFullScreen = false
const val enableAutoHideToolbar = false
const val enableSwitchNextTab = false
const val enableExtraKeys = true
const val enableExplicitExtraKeysWeight = false
const val enableBackButtonBeMappedToEscape = false
const val enableSpecialVolumeKeys = false
const val enableWordBasedIme = false
const val loginShell = "bash"
const val initialCommand = ""
const val defaultFont = "SourceCodePro"
}

View File

@ -1,26 +0,0 @@
package io.neoterm.frontend.config
import io.neolang.parser.NeoLangParser
import io.neolang.visitor.ConfigVisitor
import java.io.File
import java.nio.file.Files
/**
* @author kiva
*/
open class NeoConfigureFile(val configureFile: File) {
private val configParser = NeoLangParser()
open protected var configVisitor: ConfigVisitor? = null
fun getVisitor() = configVisitor ?: throw IllegalStateException("Configure file not loaded or parse failed.")
open fun parseConfigure() = kotlin.runCatching {
val programCode = String(Files.readAllBytes(configureFile.toPath()))
configParser.setInputSource(programCode)
val ast = configParser.parse()
val astVisitor = ast.visit().getVisitor(ConfigVisitor::class.java) ?: return false
astVisitor.start()
configVisitor = astVisitor.getCallback()
}.isSuccess
}

View File

@ -1,39 +0,0 @@
package io.neoterm.frontend.floating
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.TerminalViewClient
import io.neoterm.utils.Terminals
/**
* @author kiva
*/
class WindowTermView(val context: Context) {
@SuppressLint("InflateParams")
var rootView: View = LayoutInflater.from(context).inflate(R.layout.ui_term_dialog, null, false)
private set
var terminalView: TerminalView = rootView.findViewById<TerminalView>(R.id.terminal_view_dialog)
private set
init {
Terminals.setupTerminalView(terminalView)
}
fun setTerminalViewClient(terminalViewClient: TerminalViewClient?) {
terminalView.setTerminalViewClient(terminalViewClient)
}
fun attachSession(terminalSession: TerminalSession?) {
terminalView.attachSession(terminalSession)
}
fun setInputMethodEnabled(enabled: Boolean) {
terminalView.isFocusable = enabled
terminalView.isFocusableInTouchMode = enabled
}
}

View File

@ -1,21 +1,23 @@
package io.neoterm.frontend.floating
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AlertDialog
import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.session.shell.ShellParameter
import io.neoterm.frontend.session.shell.ShellTermSession
import io.neoterm.frontend.session.shell.client.BasicSessionCallback
import io.neoterm.frontend.session.shell.client.BasicViewClient
import io.neoterm.component.session.ShellParameter
import io.neoterm.component.session.ShellTermSession
import io.neoterm.frontend.session.terminal.BasicSessionCallback
import io.neoterm.frontend.session.terminal.BasicViewClient
import io.neoterm.frontend.session.view.TerminalView
import io.neoterm.frontend.session.view.TerminalViewClient
import io.neoterm.utils.Terminals
typealias DialogSessionFinished = (TerminalDialog, TerminalSession?) -> Unit
/**
* @author kiva
*/
class TerminalDialog(val context: Context) {
private val termWindowView = WindowTermView(context)
private val terminalSessionCallback: BasicSessionCallback
@ -92,4 +94,29 @@ class TerminalDialog(val context: Context) {
}
return this
}
}
}
class WindowTermView(val context: Context) {
@SuppressLint("InflateParams")
var rootView: View = LayoutInflater.from(context).inflate(R.layout.ui_term_dialog, null, false)
private set
var terminalView: TerminalView = rootView.findViewById<TerminalView>(R.id.terminal_view_dialog)
private set
init {
Terminals.setupTerminalView(terminalView)
}
fun setTerminalViewClient(terminalViewClient: TerminalViewClient?) {
terminalView.setTerminalViewClient(terminalViewClient)
}
fun attachSession(terminalSession: TerminalSession?) {
terminalView.attachSession(terminalSession)
}
fun setInputMethodEnabled(enabled: Boolean) {
terminalView.isFocusable = enabled
terminalView.isFocusableInTouchMode = enabled
}
}

View File

@ -1,68 +0,0 @@
package io.neoterm.frontend.session.shell
import io.neoterm.backend.TerminalSession
import io.neoterm.bridge.SessionId
/**
* @author kiva
*/
class ShellParameter {
var sessionId: SessionId? = null
var executablePath: String? = null
var arguments: Array<String>? = null
var cwd: String? = null
var initialCommand: String? = null
var env: Array<Pair<String, String>>? = null
var sessionCallback: TerminalSession.SessionChangedCallback? = null
var systemShell: Boolean = false
var shellProfile: ShellProfile? = null
fun executablePath(executablePath: String?): ShellParameter {
this.executablePath = executablePath
return this
}
fun arguments(arguments: Array<String>?): ShellParameter {
this.arguments = arguments
return this
}
fun currentWorkingDirectory(cwd: String?): ShellParameter {
this.cwd = cwd
return this
}
fun initialCommand(initialCommand: String?): ShellParameter {
this.initialCommand = initialCommand
return this
}
fun environment(env: Array<Pair<String, String>>?): ShellParameter {
this.env = env
return this
}
fun callback(callback: TerminalSession.SessionChangedCallback?): ShellParameter {
this.sessionCallback = callback
return this
}
fun systemShell(systemShell: Boolean): ShellParameter {
this.systemShell = systemShell
return this
}
fun profile(shellProfile: ShellProfile): ShellParameter {
this.shellProfile = shellProfile
return this
}
fun session(sessionId: SessionId?): ShellParameter {
this.sessionId = sessionId
return this
}
fun willCreateNewSession(): Boolean {
return sessionId?.equals(SessionId.NEW_SESSION) ?: true
}
}

View File

@ -1,87 +0,0 @@
package io.neoterm.frontend.session.shell
import io.neolang.visitor.ConfigVisitor
import io.neoterm.component.colorscheme.ColorSchemeComponent
import io.neoterm.component.font.FontComponent
import io.neoterm.component.profile.NeoProfile
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.config.DefaultValues
import io.neoterm.frontend.config.NeoPreference
/**
* @author kiva
*/
class ShellProfile : NeoProfile() {
companion object {
const val PROFILE_META_NAME = "profile-shell"
private const val LOGIN_SHELL = "login-shell"
private const val INITIAL_COMMAND = "init-command"
private const val BELL = "bell"
private const val VIBRATE = "vibrate"
private const val EXECVE_WRAPPER = "execve-wrapper"
private const val SPECIAL_VOLUME_KEYS = "special-volume-keys"
private const val AUTO_COMPLETION = "auto-completion"
private const val BACK_KEY_TO_ESC = "back-key-esc"
private const val EXTRA_KEYS = "extra-keys"
private const val FONT = "font"
private const val COLOR_SCHEME = "color-scheme"
private const val WORD_BASED_IME = "word-based-ime"
fun create(): ShellProfile {
return ShellProfile()
}
}
override val profileMetaName = PROFILE_META_NAME
var loginShell = DefaultValues.loginShell
var initialCommand = DefaultValues.initialCommand
var enableBell = DefaultValues.enableBell
var enableVibrate = DefaultValues.enableVibrate
var enableExecveWrapper = DefaultValues.enableExecveWrapper
var enableSpecialVolumeKeys = DefaultValues.enableSpecialVolumeKeys
var enableAutoCompletion = DefaultValues.enableAutoCompletion
var enableBackKeyToEscape = DefaultValues.enableBackButtonBeMappedToEscape
var enableExtraKeys = DefaultValues.enableExtraKeys
var enableWordBasedIme = DefaultValues.enableWordBasedIme
var profileFont: String
var profileColorScheme: String
init {
val fontComp = ComponentManager.getComponent<FontComponent>()
val colorComp = ComponentManager.getComponent<ColorSchemeComponent>()
profileFont = fontComp.getCurrentFontName()
profileColorScheme = colorComp.getCurrentColorSchemeName()
loginShell = NeoPreference.getLoginShellPath()
initialCommand = NeoPreference.getInitialCommand()
enableBell = NeoPreference.isBellEnabled()
enableVibrate = NeoPreference.isVibrateEnabled()
enableExecveWrapper = NeoPreference.isExecveWrapperEnabled()
enableSpecialVolumeKeys = NeoPreference.isSpecialVolumeKeysEnabled()
enableAutoCompletion = NeoPreference.isAutoCompletionEnabled()
enableBackKeyToEscape = NeoPreference.isBackButtonBeMappedToEscapeEnabled()
enableExtraKeys = NeoPreference.isExtraKeysEnabled()
enableWordBasedIme = NeoPreference.isWordBasedImeEnabled()
}
override fun onConfigLoaded(configVisitor: ConfigVisitor) {
super.onConfigLoaded(configVisitor)
loginShell = configVisitor.getProfileString(LOGIN_SHELL, loginShell)
initialCommand = configVisitor.getProfileString(INITIAL_COMMAND, initialCommand)
enableBell = configVisitor.getProfileBoolean(BELL, enableBell)
enableVibrate = configVisitor.getProfileBoolean(VIBRATE, enableVibrate)
enableExecveWrapper = configVisitor.getProfileBoolean(EXECVE_WRAPPER, enableExecveWrapper)
enableSpecialVolumeKeys = configVisitor.getProfileBoolean(SPECIAL_VOLUME_KEYS, enableSpecialVolumeKeys)
enableAutoCompletion = configVisitor.getProfileBoolean(AUTO_COMPLETION, enableAutoCompletion)
enableBackKeyToEscape = configVisitor.getProfileBoolean(BACK_KEY_TO_ESC, enableBackKeyToEscape)
enableExtraKeys = configVisitor.getProfileBoolean(EXTRA_KEYS, enableExtraKeys)
enableWordBasedIme = configVisitor.getProfileBoolean(WORD_BASED_IME, enableWordBasedIme)
profileFont = configVisitor.getProfileString(FONT, profileFont)
profileColorScheme = configVisitor.getProfileString(COLOR_SCHEME, profileColorScheme)
}
}

View File

@ -1,33 +0,0 @@
package io.neoterm.frontend.session.shell.client
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.terminal.TerminalView
/**
* @author kiva
*/
open class BasicSessionCallback(var terminalView: TerminalView) : TerminalSession.SessionChangedCallback {
override fun onTextChanged(changedSession: TerminalSession?) {
if (changedSession != null) {
terminalView.onScreenUpdated()
}
}
override fun onTitleChanged(changedSession: TerminalSession?) {
}
override fun onSessionFinished(finishedSession: TerminalSession?) {
}
override fun onClipboardText(session: TerminalSession?, text: String?) {
}
override fun onBell(session: TerminalSession?) {
}
override fun onColorsChanged(session: TerminalSession?) {
if (session != null) {
terminalView.onScreenUpdated()
}
}
}

View File

@ -1,41 +0,0 @@
package io.neoterm.frontend.session.shell.client
import android.content.Context
import android.media.SoundPool
import android.os.Vibrator
import io.neoterm.R
import io.neoterm.frontend.session.shell.ShellTermSession
/**
* @author kiva
*/
class BellController constructor() {
companion object {
private val BELL_DELAY_MS = 100
}
private var bellId: Int = 0
private var soundPool: SoundPool? = null
private var lastBellTime = 0L
fun bellOrVibrate(context: Context, session: ShellTermSession) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastBellTime < BELL_DELAY_MS) {
return
}
lastBellTime = currentTime
if (session.shellProfile.enableBell) {
if (soundPool == null) {
soundPool = SoundPool.Builder().setMaxStreams(1).build()
bellId = soundPool!!.load(context, R.raw.bell, 1)
}
soundPool?.play(bellId, 1f, 1f, 0, 0, 1f)
}
if (session.shellProfile.enableVibrate) {
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
vibrator.vibrate(100)
}
}
}

View File

@ -1,165 +0,0 @@
package io.neoterm.frontend.session.shell.client
import android.util.Log
import android.view.KeyEvent
import io.neoterm.BuildConfig
import io.neoterm.frontend.completion.CompletionManager
import io.neoterm.frontend.completion.listener.OnAutoCompleteListener
import io.neoterm.frontend.completion.listener.OnCandidateSelectedListener
import io.neoterm.frontend.completion.model.CompletionCandidate
import io.neoterm.frontend.completion.model.CompletionResult
import io.neoterm.frontend.completion.view.CandidatePopupWindow
import io.neoterm.frontend.terminal.TerminalView
import java.util.*
/**
* @author kiva
*/
class TermCompleteListener(var terminalView: TerminalView?) : OnAutoCompleteListener, OnCandidateSelectedListener {
private val inputStack = Stack<Char>()
private var popupWindow: CandidatePopupWindow? = null
private var lastCompletedIndex = 0
override fun onKeyCode(keyCode: Int, keyMod: Int) {
when (keyCode) {
KeyEvent.KEYCODE_DEL -> {
popChar()
fixLastCompletedIndex()
triggerCompletion()
}
KeyEvent.KEYCODE_ENTER -> {
clearChars()
popupWindow?.dismiss()
}
}
}
private fun fixLastCompletedIndex() {
val currentText = getCurrentEditingText()
lastCompletedIndex = minOf(lastCompletedIndex, currentText.length - 1)
}
override fun onCompletionRequired(newText: String?) {
if (newText == null || newText.isEmpty()) {
return
}
pushString(newText)
triggerCompletion()
}
override fun onCleanUp() {
popupWindow?.dismiss()
popupWindow?.cleanup()
popupWindow = null
terminalView = null
}
override fun onFinishCompletion(): Boolean {
val popWindow = popupWindow ?: return false
if (popWindow.isShowing()) {
popWindow.dismiss()
return true
}
return false
}
override fun onCandidateSelected(candidate: CompletionCandidate) {
val session = terminalView?.currentSession ?: return
val textNeedCompletion = getCurrentEditingText().substring(lastCompletedIndex + 1)
val newText = candidate.completeString
val deleteLength = newText.indexOf(textNeedCompletion) + textNeedCompletion.length
if (deleteLength > 0) {
for (i in 0 until deleteLength) {
session.write("\b")
popChar()
}
}
if (BuildConfig.DEBUG) {
Log.e(
"NeoTerm-AC", "currentEditing: $textNeedCompletion, " +
"deleteLength: $deleteLength, completeString: $newText"
)
}
pushString(newText)
session.write(newText)
// Trigger next completion
lastCompletedIndex = inputStack.size
triggerCompletion()
}
private fun triggerCompletion() {
val text = getCurrentEditingText()
if (text.isEmpty()) {
return
}
val result = CompletionManager.tryCompleteFor(text)
if (!result.hasResult()) {
// A provider accepted the task
// But no candidates are provided
// Give it zero angrily!
result.markScore(0)
onFinishCompletion()
return
}
showAutoCompleteCandidates(result)
}
private fun showAutoCompleteCandidates(result: CompletionResult) {
val termView = terminalView
var popWindow = popupWindow
if (termView == null) {
return
}
if (popWindow == null) {
popWindow = CandidatePopupWindow(termView.context)
popWindow.onCandidateSelectedListener = this
this.popupWindow = popWindow
}
popWindow.candidates = result.candidates
popWindow.show(termView)
}
private fun getCurrentEditingText(): String {
val builder = StringBuilder()
val size = inputStack.size
var start = inputStack.lastIndexOf(' ')
if (start < 0) {
// Yes, it is -1, we will do `start + 1` below.
start = -1
}
IntRange(start + 1, size - 1)
.map { inputStack[it] }
.takeWhile { !(it == 0.toChar() || it == ' ') }
.forEach { builder.append(it) }
return builder.toString()
}
private fun clearChars() {
inputStack.clear()
lastCompletedIndex = 0
}
private fun popChar() {
if (inputStack.isNotEmpty()) {
inputStack.pop()
}
}
private fun pushString(string: String) {
string.toCharArray().forEach { pushChar(it) }
}
private fun pushChar(char: Char) {
inputStack.push(char)
}
}

View File

@ -1,56 +0,0 @@
package io.neoterm.frontend.session.shell.client
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.session.shell.ShellTermSession
/**
* @author kiva
*/
class TermSessionCallback : TerminalSession.SessionChangedCallback {
var termSessionData: TermSessionData? = null
var bellController: BellController? = null
override fun onTextChanged(changedSession: TerminalSession?) {
termSessionData?.termView?.onScreenUpdated()
}
override fun onTitleChanged(changedSession: TerminalSession?) {
if (changedSession?.title != null) {
termSessionData?.termUI?.requireUpdateTitle(changedSession.title)
}
}
override fun onSessionFinished(finishedSession: TerminalSession?) {
termSessionData?.termUI?.requireOnSessionFinished()
}
override fun onClipboardText(session: TerminalSession?, text: String?) {
val termView = termSessionData?.termView
if (termView != null) {
val clipboard = termView.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.primaryClip = ClipData.newPlainText("", text)
}
}
override fun onBell(session: TerminalSession?) {
val termView = termSessionData?.termView ?: return
val shellSession = session as ShellTermSession
if (bellController == null) {
bellController = BellController()
}
bellController?.bellOrVibrate(termView.context, shellSession)
}
override fun onColorsChanged(session: TerminalSession?) {
val termView = termSessionData?.termView
if (session != null && termView != null) {
termView.onScreenUpdated()
}
}
}

View File

@ -1,18 +0,0 @@
package io.neoterm.frontend.session.shell.client
/**
* @author kiva
*/
interface TermUiPresenter {
fun requireClose()
fun requireToggleFullScreen()
fun requirePaste()
fun requireUpdateTitle(title: String?)
fun requireOnSessionFinished()
fun requireHideIme()
fun requireFinishAutoCompletion(): Boolean
fun requireCreateNew()
fun requireSwitchToPrevious()
fun requireSwitchToNext()
fun requireSwitchTo(index: Int)
}

View File

@ -1,6 +0,0 @@
package io.neoterm.frontend.session.shell.client.event
/**
* @author kiva
*/
class CreateNewSessionEvent

View File

@ -1,6 +0,0 @@
package io.neoterm.frontend.session.shell.client.event
/**
* @author kiva
*/
class SwitchIndexedSessionEvent(val index: Int)

View File

@ -1,6 +0,0 @@
package io.neoterm.frontend.session.shell.client.event
/**
* @author kiva
*/
class SwitchSessionEvent(val toNext: Boolean)

View File

@ -1,9 +0,0 @@
package io.neoterm.frontend.session.shell.client.event
import io.neoterm.ui.term.tab.TermTab
/**
* @author kiva
*/
class TabCloseEvent(var termTab: TermTab)

View File

@ -1,6 +0,0 @@
package io.neoterm.frontend.session.shell.client.event
/**
* @author kiva
*/
class TitleChangedEvent(val title: String)

View File

@ -1,6 +0,0 @@
package io.neoterm.frontend.session.shell.client.event
/**
* @author kiva
*/
class ToggleFullScreenEvent()

View File

@ -1,6 +0,0 @@
package io.neoterm.frontend.session.shell.client.event
/**
* @author kiva
*/
class ToggleImeEvent

View File

@ -1,15 +1,12 @@
package io.neoterm.frontend.session.shell.client
package io.neoterm.frontend.session.terminal
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.completion.listener.OnAutoCompleteListener
import io.neoterm.frontend.session.shell.ShellProfile
import io.neoterm.frontend.session.shell.ShellTermSession
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.component.completion.OnAutoCompleteListener
import io.neoterm.component.session.ShellProfile
import io.neoterm.component.session.ShellTermSession
import io.neoterm.frontend.session.view.TerminalView
import io.neoterm.frontend.session.view.extrakey.ExtraKeysView
/**
* @author kiva
*/
class TermSessionData {
var termSession: TerminalSession? = null
var sessionCallback: TermSessionCallback? = null
@ -58,4 +55,18 @@ class TermSessionData {
this.termView = termView
this.extraKeysView = eks
}
}
}
interface TermUiPresenter {
fun requireClose()
fun requireToggleFullScreen()
fun requirePaste()
fun requireUpdateTitle(title: String?)
fun requireOnSessionFinished()
fun requireHideIme()
fun requireFinishAutoCompletion(): Boolean
fun requireCreateNew()
fun requireSwitchToPrevious()
fun requireSwitchToNext()
fun requireSwitchTo(index: Int)
}

View File

@ -0,0 +1,11 @@
package io.neoterm.frontend.session.terminal
import io.neoterm.ui.term.TermTab
class CreateNewSessionEvent
class SwitchIndexedSessionEvent(val index: Int)
class SwitchSessionEvent(val toNext: Boolean)
class TabCloseEvent(val termTab: TermTab)
class TitleChangedEvent(val title: String)
class ToggleFullScreenEvent
class ToggleImeEvent

View File

@ -1,17 +1,40 @@
package io.neoterm.frontend.session.shell.client
package io.neoterm.frontend.session.terminal
import android.content.Context
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.inputmethod.InputMethodManager
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.config.NeoPreference
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.TerminalViewClient
import io.neoterm.component.config.NeoPreference
import io.neoterm.frontend.session.view.TerminalView
import io.neoterm.frontend.session.view.TerminalViewClient
open class BasicSessionCallback(var terminalView: TerminalView) : TerminalSession.SessionChangedCallback {
override fun onTextChanged(changedSession: TerminalSession?) {
if (changedSession != null) {
terminalView.onScreenUpdated()
}
}
override fun onTitleChanged(changedSession: TerminalSession?) {
}
override fun onSessionFinished(finishedSession: TerminalSession?) {
}
override fun onClipboardText(session: TerminalSession?, text: String?) {
}
override fun onBell(session: TerminalSession?) {
}
override fun onColorsChanged(session: TerminalSession?) {
if (session != null) {
terminalView.onScreenUpdated()
}
}
}
/**
* @author kiva
*/
class BasicViewClient(val terminalView: TerminalView) : TerminalViewClient {
override fun onScale(scale: Float): Float {
if (scale < 0.9f || scale > 1.1f) {

View File

@ -1,20 +1,30 @@
package io.neoterm.frontend.session.shell.client
package io.neoterm.frontend.session.terminal
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.media.AudioManager
import android.media.SoundPool
import android.os.Vibrator
import android.util.Log
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import io.neoterm.BuildConfig
import io.neoterm.R
import io.neoterm.backend.KeyHandler
import io.neoterm.backend.TerminalSession
import io.neoterm.component.ComponentManager
import io.neoterm.component.completion.*
import io.neoterm.component.config.NeoPreference
import io.neoterm.component.extrakey.ExtraKeyComponent
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.config.NeoPreference
import io.neoterm.frontend.session.shell.ShellTermSession
import io.neoterm.frontend.terminal.TerminalViewClient
import io.neoterm.component.session.ShellTermSession
import io.neoterm.frontend.completion.CandidatePopupWindow
import io.neoterm.frontend.session.view.TerminalView
import io.neoterm.frontend.session.view.TerminalViewClient
import java.util.*
/**
* @author kiva
@ -267,4 +277,234 @@ class TermViewClient(val context: Context) : TerminalViewClient {
NeoPreference.store(NeoPreference.KEY_FONT_SIZE, fontSize)
}
}
}
}
/**
* @author kiva
*/
class TermSessionCallback : TerminalSession.SessionChangedCallback {
var termSessionData: TermSessionData? = null
var bellController: BellController? = null
override fun onTextChanged(changedSession: TerminalSession?) {
termSessionData?.termView?.onScreenUpdated()
}
override fun onTitleChanged(changedSession: TerminalSession?) {
if (changedSession?.title != null) {
termSessionData?.termUI?.requireUpdateTitle(changedSession.title)
}
}
override fun onSessionFinished(finishedSession: TerminalSession?) {
termSessionData?.termUI?.requireOnSessionFinished()
}
override fun onClipboardText(session: TerminalSession?, text: String?) {
val termView = termSessionData?.termView
if (termView != null) {
val clipboard = termView.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.primaryClip = ClipData.newPlainText("", text)
}
}
override fun onBell(session: TerminalSession?) {
val termView = termSessionData?.termView ?: return
val shellSession = session as ShellTermSession
if (bellController == null) {
bellController = BellController()
}
bellController?.bellOrVibrate(termView.context, shellSession)
}
override fun onColorsChanged(session: TerminalSession?) {
val termView = termSessionData?.termView
if (session != null && termView != null) {
termView.onScreenUpdated()
}
}
}
class BellController {
companion object {
private val BELL_DELAY_MS = 100
}
private var bellId: Int = 0
private var soundPool: SoundPool? = null
private var lastBellTime = 0L
fun bellOrVibrate(context: Context, session: ShellTermSession) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastBellTime < BELL_DELAY_MS) {
return
}
lastBellTime = currentTime
if (session.shellProfile.enableBell) {
if (soundPool == null) {
soundPool = SoundPool.Builder().setMaxStreams(1).build()
bellId = soundPool!!.load(context, R.raw.bell, 1)
}
soundPool?.play(bellId, 1f, 1f, 0, 0, 1f)
}
if (session.shellProfile.enableVibrate) {
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
vibrator.vibrate(100)
}
}
}
class TermCompleteListener(var terminalView: TerminalView?) : OnAutoCompleteListener, OnCandidateSelectedListener {
private val inputStack = Stack<Char>()
private var popupWindow: CandidatePopupWindow? = null
private var lastCompletedIndex = 0
override fun onKeyCode(keyCode: Int, keyMod: Int) {
when (keyCode) {
KeyEvent.KEYCODE_DEL -> {
popChar()
fixLastCompletedIndex()
triggerCompletion()
}
KeyEvent.KEYCODE_ENTER -> {
clearChars()
popupWindow?.dismiss()
}
}
}
private fun fixLastCompletedIndex() {
val currentText = getCurrentEditingText()
lastCompletedIndex = minOf(lastCompletedIndex, currentText.length - 1)
}
override fun onCompletionRequired(newText: String?) {
if (newText == null || newText.isEmpty()) {
return
}
pushString(newText)
triggerCompletion()
}
override fun onCleanUp() {
popupWindow?.dismiss()
popupWindow?.cleanup()
popupWindow = null
terminalView = null
}
override fun onFinishCompletion(): Boolean {
val popWindow = popupWindow ?: return false
if (popWindow.isShowing()) {
popWindow.dismiss()
return true
}
return false
}
override fun onCandidateSelected(candidate: CompletionCandidate) {
val session = terminalView?.currentSession ?: return
val textNeedCompletion = getCurrentEditingText().substring(lastCompletedIndex + 1)
val newText = candidate.completeString
val deleteLength = newText.indexOf(textNeedCompletion) + textNeedCompletion.length
if (deleteLength > 0) {
for (i in 0 until deleteLength) {
session.write("\b")
popChar()
}
}
if (BuildConfig.DEBUG) {
Log.e(
"NeoTerm-AC",
"currentEditing: $textNeedCompletion, " +
"deleteLength: $deleteLength, completeString: $newText"
)
}
pushString(newText)
session.write(newText)
// Trigger next completion
lastCompletedIndex = inputStack.size
triggerCompletion()
}
private fun triggerCompletion() {
val text = getCurrentEditingText()
if (text.isEmpty()) {
return
}
val result = CompletionManager.tryCompleteFor(text)
if (!result.hasResult()) {
// A provider accepted the task
// But no candidates are provided
// Give it zero angrily!
result.markScore(0)
onFinishCompletion()
return
}
showAutoCompleteCandidates(result)
}
private fun showAutoCompleteCandidates(result: CompletionResult) {
val termView = terminalView
var popWindow = popupWindow
if (termView == null) {
return
}
if (popWindow == null) {
popWindow = CandidatePopupWindow(termView.context)
popWindow.onCandidateSelectedListener = this
this.popupWindow = popWindow
}
popWindow.candidates = result.candidates
popWindow.show(termView)
}
private fun getCurrentEditingText(): String {
val builder = StringBuilder()
val size = inputStack.size
var start = inputStack.lastIndexOf(' ')
if (start < 0) {
// Yes, it is -1, we will do `start + 1` below.
start = -1
}
IntRange(start + 1, size - 1)
.map { inputStack[it] }
.takeWhile { !(it == 0.toChar() || it == ' ') }
.forEach { builder.append(it) }
return builder.toString()
}
private fun clearChars() {
inputStack.clear()
lastCompletedIndex = 0
}
private fun popChar() {
if (inputStack.isNotEmpty()) {
inputStack.pop()
}
}
private fun pushString(string: String) {
string.toCharArray().forEach { pushChar(it) }
}
private fun pushChar(char: Char) {
inputStack.push(char)
}
}

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.terminal
package io.neoterm.frontend.session.view
import android.content.Context
import android.view.GestureDetector

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.terminal;
package io.neoterm.frontend.session.view;
import android.graphics.Canvas;
import android.graphics.Paint;

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.terminal;
package io.neoterm.frontend.session.view;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@ -23,7 +23,7 @@ import android.view.inputmethod.InputConnection;
import android.widget.Scroller;
import io.neoterm.R;
import io.neoterm.backend.*;
import io.neoterm.frontend.completion.listener.OnAutoCompleteListener;
import io.neoterm.component.completion.OnAutoCompleteListener;
/**
* View displaying and interacting with a {@link TerminalSession}.
@ -1032,45 +1032,38 @@ public final class TerminalView extends View {
@Override
public void onDestroyActionMode(ActionMode mode) {
}
};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mActionMode = startActionMode(new ActionMode.Callback2() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return callback.onCreateActionMode(mode, menu);
}
mActionMode = startActionMode(new ActionMode.Callback2() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return callback.onCreateActionMode(mode, menu);
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return callback.onActionItemClicked(mode, item);
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// Ignore.
}
@Override
public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
int x1 = Math.round(mSelX1 * mRenderer.mFontWidth);
int x2 = Math.round(mSelX2 * mRenderer.mFontWidth);
int y1 = Math.round((mSelY1 - mTopRow) * mRenderer.mFontLineSpacing);
int y2 = Math.round((mSelY2 + 1 - mTopRow) * mRenderer.mFontLineSpacing);
outRect.set(Math.min(x1, x2), y1, Math.max(x1, x2), y2);
}
}, ActionMode.TYPE_FLOATING);
} else {
mActionMode = startActionMode(callback);
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return callback.onActionItemClicked(mode, item);
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// Ignore.
}
@Override
public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
int x1 = Math.round(mSelX1 * mRenderer.mFontWidth);
int x2 = Math.round(mSelX2 * mRenderer.mFontWidth);
int y1 = Math.round((mSelY1 - mTopRow) * mRenderer.mFontLineSpacing);
int y2 = Math.round((mSelY2 + 1 - mTopRow) * mRenderer.mFontLineSpacing);
outRect.set(Math.min(x1, x2), y1, Math.max(x1, x2), y2);
}
}, ActionMode.TYPE_FLOATING);
invalidate();
} else {
mActionMode.finish();

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.terminal;
package io.neoterm.frontend.session.view;
import android.view.KeyEvent;
import android.view.MotionEvent;
@ -11,7 +11,6 @@ import io.neoterm.backend.TerminalSession;
* <p/>
*/
public interface TerminalViewClient {
/**
* Callback function on scale events according to {@link ScaleGestureDetector#getScaleFactor()}.
*/

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.terminal.extrakey.combine
package io.neoterm.frontend.session.view.extrakey
/**
* <Ctrl> <Alt> <Delete>
@ -26,4 +26,4 @@ class CombinedSequence private constructor() {
return seq
}
}
}
}

View File

@ -1,4 +1,4 @@
package io.neoterm.frontend.terminal.extrakey
package io.neoterm.frontend.session.view.extrakey
import android.content.Context
import android.graphics.Typeface
@ -7,21 +7,15 @@ import android.view.*
import android.widget.GridLayout
import android.widget.LinearLayout
import io.neoterm.R
import io.neoterm.component.ComponentManager
import io.neoterm.component.config.NeoPreference
import io.neoterm.component.config.NeoTermPath
import io.neoterm.component.extrakey.ExtraKeyComponent
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.config.NeoPreference
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.session.shell.client.event.ToggleImeEvent
import io.neoterm.frontend.terminal.extrakey.button.ControlButton
import io.neoterm.frontend.terminal.extrakey.button.IExtraButton
import io.neoterm.frontend.terminal.extrakey.button.RepeatableButton
import io.neoterm.frontend.terminal.extrakey.button.StatedControlButton
import io.neoterm.frontend.terminal.extrakey.impl.ArrowButton
import io.neoterm.frontend.session.terminal.ToggleImeEvent
import org.greenrobot.eventbus.EventBus
import java.io.File
class ExtraKeysView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
companion object {
private val ESC = ControlButton(IExtraButton.KEY_ESC)
private val TAB = ControlButton(IExtraButton.KEY_TAB)
@ -68,7 +62,7 @@ class ExtraKeysView(context: Context, attrs: AttributeSet) : LinearLayout(contex
init {
alpha = DEFAULT_ALPHA
gravity = Gravity.TOP
orientation = LinearLayout.VERTICAL
orientation = VERTICAL
typeface = Typeface.createFromAsset(context.assets, "eks_font.ttf")
extraKeyComponent = ComponentManager.getComponent<ExtraKeyComponent>()

View File

@ -0,0 +1,222 @@
package io.neoterm.frontend.session.view.extrakey
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.widget.Button
import android.widget.ToggleButton
import androidx.appcompat.widget.AppCompatButton
import io.neoterm.R
import io.neoterm.frontend.session.view.TerminalView
/**
* @author kiva
*/
open class ControlButton(text: String) : TextButton(text, false)
/**
* @author kiva
*/
abstract class IExtraButton : View.OnClickListener {
var buttonKeys: CombinedSequence? = null
var displayText: String? = null
override fun toString(): String {
return "${this.javaClass.simpleName} { display: $displayText, code: ${buttonKeys?.keys} }"
}
abstract override fun onClick(view: View)
abstract fun makeButton(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): Button
companion object {
const val KEY_ESC = "Esc"
const val KEY_TAB = "Tab"
const val KEY_CTRL = "Ctrl"
const val KEY_ALT = "Alt"
const val KEY_PAGE_UP = "PgUp"
const val KEY_PAGE_DOWN = "PgDn"
const val KEY_HOME = "Home"
const val KEY_END = "End"
const val KEY_ARROW_UP_TEXT = "Up"
const val KEY_ARROW_DOWN_TEXT = "Down"
const val KEY_ARROW_LEFT_TEXT = "Left"
const val KEY_ARROW_RIGHT_TEXT = "Right"
const val KEY_SHOW_ALL_BUTTONS = "···"
const val KEY_TOGGLE_IME = "Im"
const val KEY_ARROW_UP = ""
const val KEY_ARROW_DOWN = ""
const val KEY_ARROW_LEFT = ""
const val KEY_ARROW_RIGHT = ""
var NORMAL_TEXT_COLOR = 0xFFFFFFFF.toInt()
var SELECTED_TEXT_COLOR = 0xFF80DEEA.toInt()
fun sendKey(view: View, keyName: String) {
var keyCode = 0
var chars = ""
when (keyName) {
KEY_ESC -> keyCode = KeyEvent.KEYCODE_ESCAPE
KEY_TAB -> keyCode = KeyEvent.KEYCODE_TAB
KEY_ARROW_UP -> keyCode = KeyEvent.KEYCODE_DPAD_UP
KEY_ARROW_LEFT -> keyCode = KeyEvent.KEYCODE_DPAD_LEFT
KEY_ARROW_RIGHT -> keyCode = KeyEvent.KEYCODE_DPAD_RIGHT
KEY_ARROW_DOWN -> keyCode = KeyEvent.KEYCODE_DPAD_DOWN
KEY_ARROW_UP_TEXT -> keyCode = KeyEvent.KEYCODE_DPAD_UP
KEY_ARROW_LEFT_TEXT -> keyCode = KeyEvent.KEYCODE_DPAD_LEFT
KEY_ARROW_RIGHT_TEXT -> keyCode = KeyEvent.KEYCODE_DPAD_RIGHT
KEY_ARROW_DOWN_TEXT -> keyCode = KeyEvent.KEYCODE_DPAD_DOWN
KEY_PAGE_UP -> keyCode = KeyEvent.KEYCODE_PAGE_UP
KEY_PAGE_DOWN -> keyCode = KeyEvent.KEYCODE_PAGE_DOWN
KEY_HOME -> keyCode = KeyEvent.KEYCODE_MOVE_HOME
KEY_END -> keyCode = KeyEvent.KEYCODE_MOVE_END
"" -> chars = "-"
else -> chars = keyName
}
if (keyCode > 0) {
view.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, keyCode))
view.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, keyCode))
} else if (chars.isNotEmpty()) {
val terminalView = view.findViewById<TerminalView>(R.id.terminal_view)
val session = terminalView.currentSession
session?.write(chars)
}
}
}
}
/**
* @author kiva
*/
open class RepeatableButton(buttonText: String) : ControlButton(buttonText) {
override fun makeButton(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): Button {
return RepeatableButtonWidget(context, attrs, defStyleAttr)
}
private class RepeatableButtonWidget(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
AppCompatButton(context!!, attrs, defStyleAttr) {
/**
* Milliseconds how long we trigger an action
* when long pressing
*/
private val LONG_CLICK_ACTION_INTERVAL = 100L
private var isMotionEventUp = true
var mHandler: Handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: android.os.Message) {
if (!isMotionEventUp && isEnabled) {
performClick()
this.sendEmptyMessageDelayed(0, LONG_CLICK_ACTION_INTERVAL)
}
}
}
init {
this.setOnLongClickListener {
isMotionEventUp = false
mHandler.sendEmptyMessage(0)
false
}
this.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_UP) {
isMotionEventUp = true
}
false
}
}
override fun performClick(): Boolean {
return super.performClick()
}
}
}
/**
* @author kiva
*/
open class StatedControlButton @JvmOverloads constructor(text: String, var initState: Boolean = false) :
ControlButton(text) {
var toggleButton: ToggleButton? = null
override fun onClick(view: View) {
setStatus(toggleButton?.isChecked)
}
override fun makeButton(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): Button {
val outerButton = ToggleButton(context, null, android.R.attr.buttonBarButtonStyle)
outerButton.isClickable = true
if (initState) {
outerButton.isChecked = true
outerButton.setTextColor(SELECTED_TEXT_COLOR)
}
this.toggleButton = outerButton
return outerButton
}
private fun setStatus(status: Boolean?) {
val button = toggleButton
if (button != null && status != null) {
button.isChecked = status
button.setTextColor(
if (status) SELECTED_TEXT_COLOR
else NORMAL_TEXT_COLOR
)
}
}
fun readState(): Boolean {
val button = toggleButton ?: return false
if (button.isPressed) return true
val result = button.isChecked
if (result) {
button.isChecked = false
button.setTextColor(NORMAL_TEXT_COLOR)
}
return result
}
}
/**
* @author kiva
*/
open class TextButton constructor(text: String, val withEnter: Boolean = false) : IExtraButton() {
init {
this.buttonKeys = CombinedSequence.solveString(text)
this.displayText = text
}
override fun onClick(view: View) {
buttonKeys!!.keys.forEach {
sendKey(view, it)
}
if (withEnter) {
sendKey(view, "\n")
}
}
override fun makeButton(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): Button {
return Button(context, attrs, defStyleAttr)
}
}
/**
* @author kiva
*/
class ArrowButton(arrowText: String) : RepeatableButton(arrowText)

View File

@ -1,8 +0,0 @@
package io.neoterm.frontend.session.xorg;
/**
* @author kiva
*/
public class XParameter {
}

View File

@ -1,25 +0,0 @@
package io.neoterm.frontend.session.xorg.client
import android.view.View
import android.widget.FrameLayout
import io.neoterm.NeoAudioThread
import io.neoterm.NeoGLView
import io.neoterm.xorg.NeoXorgViewClient
import java.util.*
/**
* @author kiva
*/
class XSessionData {
var videoLayout: FrameLayout? = null
var audioThread: NeoAudioThread? = null
var screenKeyboard: View? = null
var glView: NeoGLView? = null
var isPaused = false
var client: NeoXorgViewClient? = null
var keyboardWithoutTextInputShown = false
var screenKeyboardHintMessage: String? = null
var textInput = LinkedList<Int>()
}

View File

@ -1,7 +0,0 @@
package io.neoterm.frontend.terminal.extrakey.button
/**
* @author kiva
*/
open class ControlButton(text: String) : TextButton(text, false)

View File

@ -1,85 +0,0 @@
package io.neoterm.frontend.terminal.extrakey.button
import android.content.Context
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.View
import android.widget.Button
import io.neoterm.R
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.combine.CombinedSequence
/**
* @author kiva
*/
abstract class IExtraButton : View.OnClickListener {
var buttonKeys: CombinedSequence? = null
var displayText: String? = null
override fun toString(): String {
return "${this.javaClass.simpleName} { display: $displayText, code: ${buttonKeys?.keys} }"
}
abstract override fun onClick(view: View)
abstract fun makeButton(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): Button
companion object {
val KEY_ESC = "Esc"
val KEY_TAB = "Tab"
val KEY_CTRL = "Ctrl"
val KEY_ALT = "Alt"
val KEY_PAGE_UP = "PgUp"
val KEY_PAGE_DOWN = "PgDn"
val KEY_HOME = "Home"
val KEY_END = "End"
val KEY_ARROW_UP_TEXT = "Up"
val KEY_ARROW_DOWN_TEXT = "Down"
val KEY_ARROW_LEFT_TEXT = "Left"
val KEY_ARROW_RIGHT_TEXT = "Right"
val KEY_SHOW_ALL_BUTTONS = "···"
val KEY_TOGGLE_IME = "Im"
val KEY_ARROW_UP = ""
val KEY_ARROW_DOWN = ""
val KEY_ARROW_LEFT = ""
val KEY_ARROW_RIGHT = ""
var NORMAL_TEXT_COLOR = 0xFFFFFFFF.toInt()
var SELECTED_TEXT_COLOR = 0xFF80DEEA.toInt()
fun sendKey(view: View, keyName: String) {
var keyCode = 0
var chars = ""
when (keyName) {
KEY_ESC -> keyCode = KeyEvent.KEYCODE_ESCAPE
KEY_TAB -> keyCode = KeyEvent.KEYCODE_TAB
KEY_ARROW_UP -> keyCode = KeyEvent.KEYCODE_DPAD_UP
KEY_ARROW_LEFT -> keyCode = KeyEvent.KEYCODE_DPAD_LEFT
KEY_ARROW_RIGHT -> keyCode = KeyEvent.KEYCODE_DPAD_RIGHT
KEY_ARROW_DOWN -> keyCode = KeyEvent.KEYCODE_DPAD_DOWN
KEY_ARROW_UP_TEXT -> keyCode = KeyEvent.KEYCODE_DPAD_UP
KEY_ARROW_LEFT_TEXT -> keyCode = KeyEvent.KEYCODE_DPAD_LEFT
KEY_ARROW_RIGHT_TEXT -> keyCode = KeyEvent.KEYCODE_DPAD_RIGHT
KEY_ARROW_DOWN_TEXT -> keyCode = KeyEvent.KEYCODE_DPAD_DOWN
KEY_PAGE_UP -> keyCode = KeyEvent.KEYCODE_PAGE_UP
KEY_PAGE_DOWN -> keyCode = KeyEvent.KEYCODE_PAGE_DOWN
KEY_HOME -> keyCode = KeyEvent.KEYCODE_MOVE_HOME
KEY_END -> keyCode = KeyEvent.KEYCODE_MOVE_END
"" -> chars = "-"
else -> chars = keyName
}
if (keyCode > 0) {
view.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, keyCode))
view.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, keyCode))
} else if (chars.isNotEmpty()) {
val terminalView = view.findViewById<TerminalView>(R.id.terminal_view)
val session = terminalView.currentSession
session?.write(chars)
}
}
}
}

View File

@ -1,59 +0,0 @@
package io.neoterm.frontend.terminal.extrakey.button
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.Button
import androidx.appcompat.widget.AppCompatButton
/**
* @author kiva
*/
open class RepeatableButton(buttonText: String) : ControlButton(buttonText) {
override fun makeButton(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): Button {
return RepeatableButtonWidget(context, attrs, defStyleAttr)
}
private class RepeatableButtonWidget(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
AppCompatButton(context!!, attrs, defStyleAttr) {
/**
* Milliseconds how long we trigger an action
* when long pressing
*/
private val LONG_CLICK_ACTION_INTERVAL = 100L
private var isMotionEventUp = true
var mHandler: Handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: android.os.Message) {
if (!isMotionEventUp && isEnabled) {
performClick()
this.sendEmptyMessageDelayed(0, LONG_CLICK_ACTION_INTERVAL)
}
}
}
init {
this.setOnLongClickListener {
isMotionEventUp = false
mHandler.sendEmptyMessage(0)
false
}
this.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_UP) {
isMotionEventUp = true
}
false
}
}
override fun performClick(): Boolean {
return super.performClick()
}
}
}

View File

@ -1,56 +0,0 @@
package io.neoterm.frontend.terminal.extrakey.button
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.Button
import android.widget.ToggleButton
/**
* @author kiva
*/
open class StatedControlButton @JvmOverloads constructor(text: String, var initState: Boolean = false) :
ControlButton(text) {
var toggleButton: ToggleButton? = null
override fun onClick(view: View) {
setStatus(toggleButton?.isChecked)
}
override fun makeButton(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): Button {
val outerButton = ToggleButton(context, null, android.R.attr.buttonBarButtonStyle)
outerButton.isClickable = true
if (initState) {
outerButton.isChecked = true
outerButton.setTextColor(IExtraButton.SELECTED_TEXT_COLOR)
}
this.toggleButton = outerButton
return outerButton
}
private fun setStatus(status: Boolean?) {
val button = toggleButton
if (button != null && status != null) {
button.isChecked = status
button.setTextColor(
if (status) SELECTED_TEXT_COLOR
else NORMAL_TEXT_COLOR
)
}
}
fun readState(): Boolean {
val button = toggleButton ?: return false
if (button.isPressed) return true
val result = button.isChecked
if (result) {
button.isChecked = false
button.setTextColor(NORMAL_TEXT_COLOR)
}
return result
}
}

View File

@ -1,31 +0,0 @@
package io.neoterm.frontend.terminal.extrakey.button
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.Button
import io.neoterm.frontend.terminal.extrakey.combine.CombinedSequence
/**
* @author kiva
*/
open class TextButton constructor(text: String, val withEnter: Boolean = false) : IExtraButton() {
init {
this.buttonKeys = CombinedSequence.solveString(text)
this.displayText = text
}
override fun onClick(view: View) {
buttonKeys!!.keys.forEach {
sendKey(view, it)
}
if (withEnter) {
sendKey(view, "\n")
}
}
override fun makeButton(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): Button {
return Button(context, attrs, defStyleAttr)
}
}

View File

@ -1,8 +0,0 @@
package io.neoterm.frontend.terminal.extrakey.impl
import io.neoterm.frontend.terminal.extrakey.button.RepeatableButton
/**
* @author kiva
*/
class ArrowButton(arrowText: String) : RepeatableButton(arrowText)

View File

@ -14,11 +14,11 @@ import androidx.core.app.NotificationCompat
import io.neoterm.R
import io.neoterm.backend.EmulatorDebug
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.session.shell.ShellParameter
import io.neoterm.frontend.session.xorg.XParameter
import io.neoterm.frontend.session.xorg.XSession
import io.neoterm.component.session.ShellParameter
import io.neoterm.component.session.XParameter
import io.neoterm.component.session.XSession
import io.neoterm.ui.term.NeoTermActivity
import io.neoterm.utils.NLog
import io.neoterm.utils.Terminals

View File

@ -1,8 +0,0 @@
package io.neoterm.setup
/**
* @author kiva
*/
interface ResultListener {
fun onResult(error: Exception?)
}

View File

@ -5,8 +5,8 @@ import android.system.Os;
import android.util.Pair;
import androidx.appcompat.app.AppCompatActivity;
import io.neoterm.backend.EmulatorDebug;
import io.neoterm.frontend.config.NeoTermPath;
import io.neoterm.frontend.logging.NLog;
import io.neoterm.component.config.NeoTermPath;
import io.neoterm.utils.NLog;
import java.io.*;
import java.util.ArrayList;

View File

@ -8,8 +8,6 @@ import java.io.InputStream;
*/
public interface SourceConnection {
InputStream getInputStream() throws IOException;
int getSize();
void close();
}

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