Compare commits

..

10 Commits

Author SHA1 Message Date
5a826234d0 fix 2020-12-03 14:55:02 +08:00
9f5ffb084a 修复APP更新逻辑,以VersionName为准
修复App更新无法下载安装包问题
2020-11-23 18:38:01 +08:00
b5d4cbe718 更新启动弹窗隐私协议和用户协议内容 2020-11-20 14:20:49 +08:00
b39a47c0bd 光环马甲包4.3.8修改 2020-11-20 14:18:39 +08:00
5ea6647bf7 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into video_only
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/LoginActivity.java
#	app/src/main/java/com/gh/gamecenter/MainActivity.java
#	app/src/main/java/com/gh/gamecenter/SplashScreenActivity.java
#	app/src/main/java/com/gh/gamecenter/collection/CollectionWrapperFragment.java
#	app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.java
#	app/src/main/java/com/gh/gamecenter/history/HistoryWrapperFragment.kt
#	app/src/main/java/com/gh/gamecenter/personal/PersonalViewModel.kt
#	app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt
#	app/src/main/res/layout/fragment_home.xml
#	app/src/main/res/layout/fragment_login.xml
#	app/src/main/res/layout/fragment_personal.xml
2020-11-13 16:04:36 +08:00
798c4a2089 update 2020-04-29 10:59:39 +08:00
0686f70df6 更新到3.7.6 2020-04-29 10:51:28 +08:00
3c30564ab6 update 2020-04-10 19:10:11 +08:00
f04070e846 update 2020-04-02 18:39:01 +08:00
fc279839fa 光环助手马甲包——视频 https://gitlab.ghzs.com/pm/yunying/issues/1051 2020-04-02 15:07:11 +08:00
6151 changed files with 140667 additions and 252749 deletions

View File

@ -1,555 +0,0 @@
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 120
tab_width = 4
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_visual_guides = none
ij_wrap_on_typing = false
[*.java]
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = false
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_extends_list = false
ij_java_align_multiline_for = true
ij_java_align_multiline_method_parentheses = false
ij_java_align_multiline_parameters = true
ij_java_align_multiline_parameters_in_calls = false
ij_java_align_multiline_parenthesized_expression = false
ij_java_align_multiline_records = true
ij_java_align_multiline_resources = true
ij_java_align_multiline_ternary_operation = false
ij_java_align_multiline_text_blocks = false
ij_java_align_multiline_throws_list = false
ij_java_align_subsequent_simple_methods = false
ij_java_align_throws_keyword = false
ij_java_align_types_in_multi_catch = true
ij_java_annotation_parameter_wrap = off
ij_java_array_initializer_new_line_after_left_brace = false
ij_java_array_initializer_right_brace_on_new_line = false
ij_java_array_initializer_wrap = off
ij_java_assert_statement_colon_on_next_line = false
ij_java_assert_statement_wrap = off
ij_java_assignment_wrap = off
ij_java_binary_operation_sign_on_next_line = false
ij_java_binary_operation_wrap = off
ij_java_blank_lines_after_anonymous_class_header = 0
ij_java_blank_lines_after_class_header = 0
ij_java_blank_lines_after_imports = 1
ij_java_blank_lines_after_package = 1
ij_java_blank_lines_around_class = 1
ij_java_blank_lines_around_field = 0
ij_java_blank_lines_around_field_in_interface = 0
ij_java_blank_lines_around_initializer = 1
ij_java_blank_lines_around_method = 1
ij_java_blank_lines_around_method_in_interface = 1
ij_java_blank_lines_before_class_end = 0
ij_java_blank_lines_before_imports = 1
ij_java_blank_lines_before_method_body = 0
ij_java_blank_lines_before_package = 0
ij_java_block_brace_style = end_of_line
ij_java_block_comment_add_space = false
ij_java_block_comment_at_first_column = true
ij_java_builder_methods = none
ij_java_call_parameters_new_line_after_left_paren = false
ij_java_call_parameters_right_paren_on_new_line = false
ij_java_call_parameters_wrap = off
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = false
ij_java_class_annotation_wrap = split_into_lines
ij_java_class_brace_style = end_of_line
ij_java_class_count_to_use_import_on_demand = 99
ij_java_class_names_in_javadoc = 1
ij_java_do_not_indent_top_level_class_members = false
ij_java_do_not_wrap_after_single_annotation = false
ij_java_do_not_wrap_after_single_annotation_in_parameter = false
ij_java_do_while_brace_force = never
ij_java_doc_add_blank_line_after_description = true
ij_java_doc_add_blank_line_after_param_comments = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = true
ij_java_doc_align_exception_comments = true
ij_java_doc_align_param_comments = true
ij_java_doc_do_not_wrap_if_one_line = false
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = true
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = false
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = false
ij_java_enum_constants_wrap = off
ij_java_extends_keyword_wrap = off
ij_java_extends_list_wrap = off
ij_java_field_annotation_wrap = split_into_lines
ij_java_finally_on_new_line = false
ij_java_for_brace_force = never
ij_java_for_statement_new_line_after_left_paren = false
ij_java_for_statement_right_paren_on_new_line = false
ij_java_for_statement_wrap = off
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_if_brace_force = never
ij_java_imports_layout = $android.**,$androidx.**,$com.**,$junit.**,$net.**,$org.**,$java.**,$javax.**,$*,|,android.**,|,androidx.**,|,com.**,|,junit.**,|,net.**,|,org.**,|,java.**,|,javax.**,|,*,|
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 2
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
ij_java_keep_blank_lines_in_code = 2
ij_java_keep_blank_lines_in_declarations = 2
ij_java_keep_builder_methods_indents = false
ij_java_keep_control_statement_in_one_line = true
ij_java_keep_first_column_comment = true
ij_java_keep_indents_on_empty_lines = false
ij_java_keep_line_breaks = true
ij_java_keep_multiple_expressions_in_one_line = false
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = false
ij_java_keep_simple_lambdas_in_one_line = false
ij_java_keep_simple_methods_in_one_line = false
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = false
ij_java_line_comment_add_space_on_reformat = false
ij_java_line_comment_at_first_column = true
ij_java_method_annotation_wrap = split_into_lines
ij_java_method_brace_style = end_of_line
ij_java_method_call_chain_wrap = off
ij_java_method_parameters_new_line_after_left_paren = false
ij_java_method_parameters_right_paren_on_new_line = false
ij_java_method_parameters_wrap = off
ij_java_modifier_list_wrap = false
ij_java_multi_catch_types_wrap = normal
ij_java_names_count_to_use_import_on_demand = 99
ij_java_new_line_after_lparen_in_annotation = false
ij_java_new_line_after_lparen_in_record_header = false
ij_java_parameter_annotation_wrap = off
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = false
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_record_components_wrap = normal
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = false
ij_java_replace_null_check = true
ij_java_replace_sum_lambda_with_method_ref = true
ij_java_resource_list_new_line_after_left_paren = false
ij_java_resource_list_right_paren_on_new_line = false
ij_java_resource_list_wrap = off
ij_java_rparen_on_new_line_in_annotation = false
ij_java_rparen_on_new_line_in_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_annotation_eq = true
ij_java_spaces_around_assignment_operators = true
ij_java_spaces_around_bitwise_operators = true
ij_java_spaces_around_equality_operators = true
ij_java_spaces_around_lambda_arrow = true
ij_java_spaces_around_logical_operators = true
ij_java_spaces_around_method_ref_dbl_colon = false
ij_java_spaces_around_multiplicative_operators = true
ij_java_spaces_around_relational_operators = true
ij_java_spaces_around_shift_operators = true
ij_java_spaces_around_type_bounds_in_type_parameters = true
ij_java_spaces_around_unary_operator = false
ij_java_spaces_within_angle_brackets = false
ij_java_spaces_within_annotation_parentheses = false
ij_java_spaces_within_array_initializer_braces = false
ij_java_spaces_within_braces = false
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = false
ij_java_spaces_within_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
[.editorconfig]
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
ij_continuation_indent_size = 4
ij_xml_align_attributes = false
ij_xml_align_text = false
ij_xml_attribute_wrap = normal
ij_xml_block_comment_add_space = false
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 2
ij_xml_keep_indents_on_empty_lines = false
ij_xml_keep_line_breaks = false
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = false
ij_xml_line_comment_at_first_column = true
ij_xml_space_after_tag_name = false
ij_xml_space_around_equals_in_attribute = false
ij_xml_space_inside_empty_tag = true
ij_xml_text_wrap = normal
ij_xml_use_custom_settings = true
[{*.gant,*.groovy,*.gy}]
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
ij_groovy_align_multiline_assignment = false
ij_groovy_align_multiline_binary_operation = false
ij_groovy_align_multiline_chained_methods = false
ij_groovy_align_multiline_extends_list = false
ij_groovy_align_multiline_for = true
ij_groovy_align_multiline_list_or_map = true
ij_groovy_align_multiline_method_parentheses = false
ij_groovy_align_multiline_parameters = true
ij_groovy_align_multiline_parameters_in_calls = false
ij_groovy_align_multiline_resources = true
ij_groovy_align_multiline_ternary_operation = false
ij_groovy_align_multiline_throws_list = false
ij_groovy_align_named_args_in_map = true
ij_groovy_align_throws_keyword = false
ij_groovy_array_initializer_new_line_after_left_brace = false
ij_groovy_array_initializer_right_brace_on_new_line = false
ij_groovy_array_initializer_wrap = off
ij_groovy_assert_statement_wrap = off
ij_groovy_assignment_wrap = off
ij_groovy_binary_operation_wrap = off
ij_groovy_blank_lines_after_class_header = 0
ij_groovy_blank_lines_after_imports = 1
ij_groovy_blank_lines_after_package = 1
ij_groovy_blank_lines_around_class = 1
ij_groovy_blank_lines_around_field = 0
ij_groovy_blank_lines_around_field_in_interface = 0
ij_groovy_blank_lines_around_method = 1
ij_groovy_blank_lines_around_method_in_interface = 1
ij_groovy_blank_lines_before_imports = 1
ij_groovy_blank_lines_before_method_body = 0
ij_groovy_blank_lines_before_package = 0
ij_groovy_block_brace_style = end_of_line
ij_groovy_block_comment_add_space = false
ij_groovy_block_comment_at_first_column = true
ij_groovy_call_parameters_new_line_after_left_paren = false
ij_groovy_call_parameters_right_paren_on_new_line = false
ij_groovy_call_parameters_wrap = off
ij_groovy_catch_on_new_line = false
ij_groovy_class_annotation_wrap = split_into_lines
ij_groovy_class_brace_style = end_of_line
ij_groovy_class_count_to_use_import_on_demand = 5
ij_groovy_do_while_brace_force = never
ij_groovy_else_on_new_line = false
ij_groovy_enable_groovydoc_formatting = true
ij_groovy_enum_constants_wrap = off
ij_groovy_extends_keyword_wrap = off
ij_groovy_extends_list_wrap = off
ij_groovy_field_annotation_wrap = split_into_lines
ij_groovy_finally_on_new_line = false
ij_groovy_for_brace_force = never
ij_groovy_for_statement_new_line_after_left_paren = false
ij_groovy_for_statement_right_paren_on_new_line = false
ij_groovy_for_statement_wrap = off
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
ij_groovy_keep_blank_lines_before_right_brace = 2
ij_groovy_keep_blank_lines_in_code = 2
ij_groovy_keep_blank_lines_in_declarations = 2
ij_groovy_keep_control_statement_in_one_line = true
ij_groovy_keep_first_column_comment = true
ij_groovy_keep_indents_on_empty_lines = false
ij_groovy_keep_line_breaks = true
ij_groovy_keep_multiple_expressions_in_one_line = false
ij_groovy_keep_simple_blocks_in_one_line = false
ij_groovy_keep_simple_classes_in_one_line = true
ij_groovy_keep_simple_lambdas_in_one_line = true
ij_groovy_keep_simple_methods_in_one_line = true
ij_groovy_label_indent_absolute = false
ij_groovy_label_indent_size = 0
ij_groovy_lambda_brace_style = end_of_line
ij_groovy_layout_static_imports_separately = true
ij_groovy_line_comment_add_space = false
ij_groovy_line_comment_add_space_on_reformat = false
ij_groovy_line_comment_at_first_column = true
ij_groovy_method_annotation_wrap = split_into_lines
ij_groovy_method_brace_style = end_of_line
ij_groovy_method_call_chain_wrap = off
ij_groovy_method_parameters_new_line_after_left_paren = false
ij_groovy_method_parameters_right_paren_on_new_line = false
ij_groovy_method_parameters_wrap = off
ij_groovy_modifier_list_wrap = false
ij_groovy_names_count_to_use_import_on_demand = 3
ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
ij_groovy_parameter_annotation_wrap = off
ij_groovy_parentheses_expression_new_line_after_left_paren = false
ij_groovy_parentheses_expression_right_paren_on_new_line = false
ij_groovy_prefer_parameters_wrap = false
ij_groovy_resource_list_new_line_after_left_paren = false
ij_groovy_resource_list_right_paren_on_new_line = false
ij_groovy_resource_list_wrap = off
ij_groovy_space_after_assert_separator = true
ij_groovy_space_after_colon = true
ij_groovy_space_after_comma = true
ij_groovy_space_after_comma_in_type_arguments = true
ij_groovy_space_after_for_semicolon = true
ij_groovy_space_after_quest = true
ij_groovy_space_after_type_cast = true
ij_groovy_space_before_annotation_parameter_list = false
ij_groovy_space_before_array_initializer_left_brace = false
ij_groovy_space_before_assert_separator = false
ij_groovy_space_before_catch_keyword = true
ij_groovy_space_before_catch_left_brace = true
ij_groovy_space_before_catch_parentheses = true
ij_groovy_space_before_class_left_brace = true
ij_groovy_space_before_closure_left_brace = true
ij_groovy_space_before_colon = true
ij_groovy_space_before_comma = false
ij_groovy_space_before_do_left_brace = true
ij_groovy_space_before_else_keyword = true
ij_groovy_space_before_else_left_brace = true
ij_groovy_space_before_finally_keyword = true
ij_groovy_space_before_finally_left_brace = true
ij_groovy_space_before_for_left_brace = true
ij_groovy_space_before_for_parentheses = true
ij_groovy_space_before_for_semicolon = false
ij_groovy_space_before_if_left_brace = true
ij_groovy_space_before_if_parentheses = true
ij_groovy_space_before_method_call_parentheses = false
ij_groovy_space_before_method_left_brace = true
ij_groovy_space_before_method_parentheses = false
ij_groovy_space_before_quest = true
ij_groovy_space_before_record_parentheses = false
ij_groovy_space_before_switch_left_brace = true
ij_groovy_space_before_switch_parentheses = true
ij_groovy_space_before_synchronized_left_brace = true
ij_groovy_space_before_synchronized_parentheses = true
ij_groovy_space_before_try_left_brace = true
ij_groovy_space_before_try_parentheses = true
ij_groovy_space_before_while_keyword = true
ij_groovy_space_before_while_left_brace = true
ij_groovy_space_before_while_parentheses = true
ij_groovy_space_in_named_argument = true
ij_groovy_space_in_named_argument_before_colon = false
ij_groovy_space_within_empty_array_initializer_braces = false
ij_groovy_space_within_empty_method_call_parentheses = false
ij_groovy_spaces_around_additive_operators = true
ij_groovy_spaces_around_assignment_operators = true
ij_groovy_spaces_around_bitwise_operators = true
ij_groovy_spaces_around_equality_operators = true
ij_groovy_spaces_around_lambda_arrow = true
ij_groovy_spaces_around_logical_operators = true
ij_groovy_spaces_around_multiplicative_operators = true
ij_groovy_spaces_around_regex_operators = true
ij_groovy_spaces_around_relational_operators = true
ij_groovy_spaces_around_shift_operators = true
ij_groovy_spaces_within_annotation_parentheses = false
ij_groovy_spaces_within_array_initializer_braces = false
ij_groovy_spaces_within_braces = true
ij_groovy_spaces_within_brackets = false
ij_groovy_spaces_within_cast_parentheses = false
ij_groovy_spaces_within_catch_parentheses = false
ij_groovy_spaces_within_for_parentheses = false
ij_groovy_spaces_within_gstring_injection_braces = false
ij_groovy_spaces_within_if_parentheses = false
ij_groovy_spaces_within_list_or_map = false
ij_groovy_spaces_within_method_call_parentheses = false
ij_groovy_spaces_within_method_parentheses = false
ij_groovy_spaces_within_parentheses = false
ij_groovy_spaces_within_switch_parentheses = false
ij_groovy_spaces_within_synchronized_parentheses = false
ij_groovy_spaces_within_try_parentheses = false
ij_groovy_spaces_within_tuple_expression = false
ij_groovy_spaces_within_while_parentheses = false
ij_groovy_special_else_if_treatment = true
ij_groovy_ternary_operation_wrap = off
ij_groovy_throws_keyword_wrap = off
ij_groovy_throws_list_wrap = off
ij_groovy_use_flying_geese_braces = false
ij_groovy_use_fq_class_names = false
ij_groovy_use_fq_class_names_in_javadoc = true
ij_groovy_use_relative_indents = false
ij_groovy_use_single_class_imports = true
ij_groovy_variable_annotation_wrap = off
ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_chain_calls_after_dot = false
ij_groovy_wrap_long_lines = false
[{*.kt,*.kts,*.main.kts}]
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = false
ij_kotlin_allow_trailing_comma_on_call_site = false
ij_kotlin_assignment_wrap = normal
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_add_space = false
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = true
ij_kotlin_call_parameters_right_paren_on_new_line = true
ij_kotlin_call_parameters_wrap = on_every_item
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = split_into_lines
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_continuation_indent_for_chained_calls = false
ij_kotlin_continuation_indent_for_expression_bodies = false
ij_kotlin_continuation_indent_in_argument_lists = false
ij_kotlin_continuation_indent_in_elvis = false
ij_kotlin_continuation_indent_in_if_conditions = false
ij_kotlin_continuation_indent_in_parameter_lists = false
ij_kotlin_continuation_indent_in_supertype_lists = false
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = normal
ij_kotlin_field_annotation_wrap = split_into_lines
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = true
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_add_space_on_reformat = false
ij_kotlin_line_comment_at_first_column = true
ij_kotlin_method_annotation_wrap = split_into_lines
ij_kotlin_method_call_chain_wrap = normal
ij_kotlin_method_parameters_new_line_after_left_paren = true
ij_kotlin_method_parameters_right_paren_on_new_line = true
ij_kotlin_method_parameters_wrap = on_every_item
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.*,kotlinx.android.synthetic.**,io.ktor.**
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_use_custom_formatting_for_modifiers = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 1
ij_kotlin_wrap_first_method_in_call_chain = false

1
.gitignore vendored
View File

@ -7,6 +7,5 @@ local.properties
captures/
build/
release-app/
test-app/
scripts/apk-channel/
app/src/test/java/com/gh/gamecenter

View File

@ -1,85 +0,0 @@
stages:
- android-build && analysis
- docker-build && sendmail
- deploy-trigger
android_build:
tags:
- offline-test
stage: android-build && analysis
image: hub.shanqu.cc/devops/ci-android:jdk11
variables:
KUBERNETES_CPU_LIMIT: "16"
GIT_SUBMODULE_STRATEGY: recursive
Apk_Path: "app/build/outputs/apk/**/release/*.apk"
script:
- export GRADLE_USER_HOME=./.gradle
- chmod +x ./gradlew
- ./scripts/meta_build.sh --config_id 6400549c21c2c94ead074500 --sdk_platform toutiao --sdk_version 5.3.0 --channel BD-GHZS-ZP-KY --activate_reporting_ratio 60 --first_lanuch_jump e1wibGlua190eXBlXCI6XCJcIixcImxpbmtfaWRcIjpcIlwiLFwibGlua190ZXh0XCI6XCJcIixcImhvbWVfaW5kZXhcIjpcIlwiLFwiYm90dG9tX2luZGV4XCI6XCJcIn0= --unix_timestamp 1677657618 --output ./release/com.gh.gamecenter_5.17.4_694_BD-GHZS-ZP-KY_toutiao_5.3.0_1677657618.apk
- rm -rf ./.gradle/caches/build-cache-1
cache:
paths:
- .gradle
artifacts:
paths:
- Dockerfile
expire_in: 15 mins
only:
- feature-meta_build
# 构建推送docker镜像
docker-build:
tags:
- offline-test
stage: docker-build && sendmail
image: hub.shanqu.cc/library/docker:latest
variables:
GIT_STRATEGY: none
script:
- projectPath=`echo $CI_PROJECT_PATH | sed 's#/#-#g'`
- docker build -t registry.cn-shenzhen.aliyuncs.com/ghzs/$projectPath:latest .
- docker push registry.cn-shenzhen.aliyuncs.com/ghzs/$projectPath:latest
- docker run -e PROJECTKEY=$projectPath -e EMAIL=$GITLAB_USER_EMAIL -e BRANCH=$CI_COMMIT_REF_NAME --name send-email --rm hub.shanqu.cc/platform/send-sonar-report:latest
cache:
paths:
- .gradle
policy: pull
only:
- feature-meta_build
# 代码检查
sonarqube_analysis:
tags:
- offline-test
stage: android-build && analysis
image: sonarsource/sonar-scanner-cli:latest
dependencies: [] #禁止传递来的artifact
script:
## 获取项目的一级组和二级组和项目名作为projectKey例如projectKey=platform-backend-eci-monitor
- group=`echo $CI_PROJECT_PATH | sed 's#/#-#g'`
- sonar-scanner
-Dsonar.host.url=http://sonarqube-server.sonarqube:9000/
-Dsonar.login=be43de7264ce4c4766eb0c020373c3e74e6df257
-Dsonar.jacoco.reportPaths=target/jacoco.exec
-Dsonar.projectKey=$group
-Dsonar.projectName=$CI_PROJECT_PATH
-Dsonar.sourceEncoding=UTF-8
-Dsonar.exclusions=**/vendor/**,**/errcode/**
-Dsonar.gitlab.project_id=$CI_PROJECT_ID
-Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA
-Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME
-Dsonar.gitlab.ci_merge_request_iid=$CI_MERGE_REQUEST_IID
-Dsonar.gitlab.merge_request_discussion=true
-Dsonar.java.binaries=. # 如果不使用Maven或Gradle进行分析则必须手动提供测试二进制文件
only:
- feature-meta_build
## 触发多项目构建
trigger_job:
stage: deploy-trigger
variables:
BUILD_PROJECT_NAME: $CI_PROJECT_PATH
trigger:
project: devops/automation/build-eci
branch: dev

11
.gitmodules vendored
View File

@ -1,13 +1,4 @@
[submodule "libraries/LGLibrary"]
path = libraries/LGLibrary
url = ../../../android/common-library.git
url = git@gitlab.ghzs.com:android/common-library.git
branch = master
[submodule "vspace-bridge"]
path = vspace-bridge
url = ../../../cwzs/android/vspace-bridge.git
[submodule "module_common/src/debug/assets/assistant-android-mock"]
path = module_common/src/debug/assets/assistant-android-mock
url = ../../../halo/android/assistant-android-mock.git
[submodule "ndownload"]
path = ndownload
url = ../../../android/ndownload.git

View File

@ -1,15 +0,0 @@
FROM openjdk:11-jdk
WORKDIR /project
SHELL ["/bin/bash", "-c"]
#配置SDK环境变量
ENV ANDROID_SDK_ROOT /usr/lib/sdk
ENV ANDROID_HOME /usr/lib/sdk
ENV PATH $ANDROID_SDK_ROOT:$PATH
ENV PATH=$PATH:${ANDROID_HOME}/cmdline-tools/cmdline-tools/bin/
ENV GRADLE_USER_HOME /project/.gradle
RUN source ~/.bashrc
RUN sed -i "s@http://\(deb\|security\).debian.org@https://mirrors.aliyun.com@g" /etc/apt/sources.list \
&& apt-get --quiet update --yes \
&& apt-get --quiet install --yes lib32stdc++6 lib32z1 libncurses5 util-linux bash tzdata librdkafka-dev pkgconf \
&& rm -rf /var/lib/apt/lists/*
COPY .gradle /project/.gradle

121
README.md
View File

@ -1,66 +1,69 @@
# 光环助手Android客户端
### 概述
光环助手Android客户端目前使用 Kotlin 作为主要开发语言,以 MVVM 作为参考架构模式进行开发
### 约束
为编写易读易维护且较健壮的代码,可参考以下约束
1. 尽量将逻辑代码放置于 ViewModel 中View 中只执行 UI 操作
2. 尽量使 View 在被销毁之后仍能恢复状态,处理方式可参考 [保存界面状态](https://developer.android.com/topic/libraries/architecture/saving-states)
3. 尽量参考原有文件结构及命名规范,即以 大模块 - 小模块 的形式生成包关系
4. 遵循最小改动原则,在提交代码前务必先检查变动的代码,尽量以可控的变动规模来构成一个 commit ,以便日后追踪问题
5. Commit message 提交规范可参考 [Conventional Commits](https://www.conventionalcommits.org/zh-hans/v1.0.0/)
6. 代码规范可参考 [AOSP Java 风格](https://source.android.com/setup/contribute/code-style)
7. 尽量使用 Kotlin 来写新文件
8. 不要使用 DataBinding
9. Commit 前请确保不带入非项目必须文件,可手动修改 [.gitignore](https://stackoverflow.com/questions/8527597/how-do-i-ignore-files-in-a-directory-in-git) 文件忽略
10. 优先使用 ViewBinding 获取 View 对象
11. No AsyncTask!
### 公用部分
本项目使用 LiveData 实现了一个简单通用的基础列表分页功能,具体可见 `ListFragment`, `ListViewModel` 等类,理想情况下只需少量代码即可新建一个简单分页列表
### 首次拉取项目代码
`git clone -b dev git@git.shanqu.cc:halo/android/assistant-android.git --recursive`
### git 版本管理
本项目使用简化版的 git flow 来管理分支,细节请看 [光环安卓简单 git 规范](https://git.ghzs.com/halo/android/assistant-android/-/wikis/%E5%85%89%E7%8E%AF%E5%AE%89%E5%8D%93%E7%AE%80%E5%8D%95-git-%E8%A7%84%E8%8C%83)
### API 环境配置
本项目使用 Build Variants 来切换 API 环境
* internal 为测试环境
* publish 为正式环境
### 图片资源配置
* 新增图片资源时,默认只添加最高规格的 xxxhdpi 文件
* 新增图片资源时,需要将其转换为 .webp 格式 (包括含透明图层的图片默认质量为90%) (转换后体积变大的文件除外)
### 第三方appkey等配置
* 修改 `gradle.properties` 文件将各种key填入其中实现统一管理
* 通过 gradle 文件内的 resValue/buildConfigField/manifestPlaceHolder 方式实现编译期间修改,具体情况请参考 ``./build.gradle`` 和 ``./app/build.gradle`` 配置
### 混淆配置
* 本项目使用了微信的 [AndResGuard](https://github.com/shwenzhang/AndResGuard) 作为资源混淆压缩方案,新增需要使用 `getIdentifier` 获取的资源文件时需要添加至白名单
* 本项目默认使用 R8 作为混淆工具,往 proguard-rules.txt 添加 proguard 新配置项时请检查可用性(如语法等)
### APK打包配置
> 打内部测试包:`./scripts/test_build.sh`
> 打邮件测试包:`./scripts/jenkins_build.sh`
* 使用[ApkChannelPackage](https://github.com/ltlovezh/ApkChannelPackage)的方案
* 打包命令,视情况使用:
> 打包Tinker基准包`./scripts/tinker_release_base.sh`
> 以Tinker基准包打渠道包`./scripts/tinker_release_channel.sh`
> 以Tinker基准包打补丁包`./scripts/tinker_release_patch.sh`
### 混淆配置
* 配置文件Android默认配置+proguard-rules.txt等
* 参考libraries下每个项目独立的配置文件`proguard-project.txt`
### apk大小优化
* 限制resConfig资源集
* 开启ShrinkResources
* 开启混淆使用minifyEnabled(仅在release开启
* pngquant对png压缩、png/jpg->webp(未尝试)
### 第三方appkey等配置
* 修改`gradle.properties`文件将各种key填入其中实现统一管理
* 通过gradle文件内的resValue/buildConfigField/manifestPlaceHolder方式实现编译期间修改具体情况请参考``./build.gradle``和``./app/build.gradle``配置
### 拉取代码步骤
1. 拉取主项目代码: `git clone -b dev git@gitlab.ghzhushou.com:halo/assistant-android.git`
2. 初始化公用类库: `bash ./scripts/submodules_init.sh`
### submodule管理方式(只拉取master)
* 提交代码需要cd到submodule文件夹去做修改
* 更新远端代码,`bash ./scripts/submodules_update.sh`
### TODO
* 把原有 EventBus 的消息 Type 统一一个文件内
* 将实现细节从 View(Fragment、Activity) 剥离并以 MVVM 结构改造
* 重构 MainActivity
* GSON 序列化用统一一个, GsonUtil fromJson
* CleanApkAdapter 转化字符串size工具函数 比如SpeedUtils
* getString 解决 字符串hardcode问题
* ~~Adapter 里面clicklistener 用接口传参将点击操作委托给controller~~
* ~~Adapter ViewHolder的功能部分重写到ViewHolder类本身~~
* ~~activity 统一入口未完成(外部入口相关)去除多余activity使用统一toolbar~~
* ~~release / debug compile不同的类库不需要再做什么开关~~
* ~~Toolbar分离有图形按钮/没有图形按钮~~
### TODO Since 3.1
- 解决 Utils 工具类引发的内存泄漏问题
- 把原有 EventBus 的消息 Type 统一到一个文件内
- 将实现细节从 View(Fragment、Activity) 剥离并以 MVVM 结构改造
- ~~将 ListViewModel 所对应的 ListRepository 合并到 ListViewModel 中~~
- 依照光环助手界面功能以大模块 - 小模块的方式去修改包结构,包内文件建议以包名摘要作为前缀
- ~~使用 RxJava 的 Debounce 和 Map 操作优化搜索触发机制 参考资料:[1](https://proandroiddev.com/building-an-autocompleting-edittext-using-rxjava-f69c5c3f5a40),[2](https://medium.com/@kurtisnusbaum/rxandroid-basics-part-2-6e877af352)~~
- ~~把 ListViewModel 的数据结构类型转换方式换为抽象方法,让继承的类实现,避免出现无响应的问题~~
- ~~rxjava2 如果接口返回为空 会发生异常:java.lang.NullPointerException: Null is not a valid element (答案编辑) 解决方法->com.gh.gamecenter.retrofit.Response~~
- constraintLayout 1.1.2 导致布局出现异常(问题编辑标签选择弹窗)
- 搞清楚 GameManager 的用途,看能不能去掉
- 重构一下 MainActivity

View File

@ -1,20 +1,21 @@
// This comment exists for a reason, do not delete
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' // kotlin
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'AndResGuard'
import groovy.xml.XmlUtil
// apkChannelPackage
apply plugin: 'channel'
//apply from: 'tinker-support.gradle'
android {
String CONFIG_ID = ""
String FIRST_LAUNCH = ""
androidExtensions {
experimental = true
}
buildFeatures {
viewBinding true
dataBinding true
dataBinding {
enabled = true
}
compileOptions {
@ -29,31 +30,19 @@ android {
dexOptions {
// jumboMode = true
javaMaxHeapSize "4g"
preDexLibraries true
maxProcessCount 8
}
aaptOptions {
additionalParameters "--no-version-vectors"
}
kapt {
useBuildCache = true
javacOptions {
// 增加注解处理器的最大错误次数,默认为 100
option("-Xmaxerrs", 500)
}
}
defaultConfig {
vectorDrawables.useSupportLibrary = true
multiDexEnabled true
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'com.gh.EventBusIndex']
}
}
ndk {
// x86 本来是为了模拟器用户使用 RenderScript 用的,但是其实用到 RenderScript 的人本来就不多 (一天不到 100),用模拟器的人就更少了
// 加了 x86 反而会导致用户没法使用微博登录,因为微博没有提供 x86 的 SO ...
// 还会增大 APK 体积,所以还是去掉吧。数据可见 https://sentry.shanqu.cc/organizations/lightgame/issues/144232/?project=22
abiFilters "armeabi-v7a", "arm64-v8a"
abiFilters "armeabi-v7a"
}
renderscriptTargetApi 18
@ -70,20 +59,32 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-fresco.txt'
// 推广用的配置 id
buildConfigField "String", "CONFIG_ID", "\"${CONFIG_ID}\""
// 首次启动的跳转配置
buildConfigField "String", "FIRST_LAUNCH", "\"${FIRST_LAUNCH}\""
// All third-party appid/appkey
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_API_HOST", "\"${NEW_API_HOST}\""
buildConfigField "String", "VAPI_HOST", "\"${VAPI_HOST}\""
/**
* All third-party appid/appkey
*/
buildConfigField "String", "WECHAT_APPID", "\"${WECHAT_APPID}\""
buildConfigField "String", "WECHAT_SECRET", "\"${WECHAT_SECRET}\""
buildConfigField "String", "TENCENT_APPID", "\"${TENCENT_APPID}\""
buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\""
buildConfigField "String", "MTA_APPKEY", "\"${MTA_APPKEY}\""
buildConfigField "String", "TD_APPID", "\"${TD_APPID}\""
buildConfigField "String", "LETO_APPID", "\"${LETO_APPID}\""
buildConfigField "String", "TTAD_APPID", "\"${TTAD_APPID}\""
buildConfigField "String", "DOUYIN_CLIENTKEY", "\"${DOUYIN_CLIENTKEY}\""
buildConfigField "String", "DOUYIN_CLIENTSECRET", "\"${DOUYIN_CLIENTSECRET}\""
buildConfigField "String", "MIPUSH_APPID", "\"${MIPUSH_APPID}\""
buildConfigField "String", "MIPUSH_APPKEY", "\"${MIPUSH_APPKEY}\""
buildConfigField "String", "MEIZUPUSH_APPID", "\"${MEIZUPUSH_APPID}\""
buildConfigField "String", "MEIZUPUSH_APPKEY", "\"${MEIZUPUSH_APPKEY}\""
resValue "string", "huawei_push_appid", "appid=${HUAWEI_PUSH_APPID}"
/**
* Build Time 供区分 jenkins 打包时间用
*/
buildConfigField "long", "BUILD_TIME", "0"
}
// gradle 2.2以上默认同时启用v1和v2优先用于Android N
@ -98,12 +99,6 @@ android {
}
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.name)
}
}
buildTypes {
debug {
debuggable true
@ -130,93 +125,56 @@ android {
}
}
// Ignore useless variant
variantFilter { variant ->
def names = variant.flavors*.name
def isDebugType = variant.buildType.name == "debug"
if ((names.contains("tea") || name.contains("kuaishou") || name.contains("gdt")) && isDebugType) {
setIgnore(true)
}
}
flavorDimensions("env")
sourceSets {
publish {
java.srcDirs = ['src/main/java', "src/default/java"]
}
internal {
java.srcDirs = ['src/main/java', "src/default/java"]
}
tea {
java.srcDirs = ['src/main/java', 'src/tea/java']
}
kuaishou {
java.srcDirs = ['src/main/java', 'src/kuaishou/java']
}
gdt {
java.srcDirs = ['src/main/java', 'src/gdt/java']
}
}
flavorDimensions "nonsense"
/**
* 多渠道打包,渠道请参考"channel.txt"文件所有渠道值均通过java code设置
*/
productFlavors {
// internal test dev host
internal {
dimension "env"
versionNameSuffix "-debug"
buildConfigField "String", "DEV_API_HOST", "\"${DEV_API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_DEV_API_HOST}\""
buildConfigField "String", "DEV_VAPI_HOST", "\"${DEV_VAPI_HOST}\""
buildConfigField "String", "QUICK_LOGIN_APPID", "\"${DEV_QUICK_LOGIN_APPID}\""
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${DEV_QUICK_LOGIN_APPKEY}\""
}
// publish release host
publish {
dimension "env"
dimension "nonsense"
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""
buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\""
buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\""
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "SENSITIVE_API_HOST", "\"${SENSITIVE_API_HOST}\""
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
buildConfigField "String", "BUGLY_APPID", "\"${BUGLY_APPID}\""
}
// internal test dev host
internal {
dimension "nonsense"
versionNameSuffix "-debug"
tea {
dimension "env"
buildConfigField "String", "API_HOST", "\"${DEV_API_HOST}\""
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""
buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\""
buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\""
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "SENSITIVE_API_HOST", "\"${DEV_API_HOST}\""
manifestPlaceholders.put("APPLOG_SCHEME", "rangersapplog.byAx6uYt".toLowerCase())
}
kuaishou {
dimension "env"
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""
buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\""
buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\""
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
}
gdt {
dimension "env"
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""
buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\""
buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\""
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "UMENG_APPKEY", "\"${DEV_UMENG_APPKEY}\""
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${DEV_UMENG_MESSAGE_SECRET}\""
buildConfigField "String", "BUGLY_APPID", "\"${DEV_BUGLY_APPID}\""
}
}
}
// apkChannelPackage
channel {
//多渠道包的输出目录默认为new File(project.buildDir,"channel")
baseOutputDir = new File(project.buildDir, "channel")
//多渠道包的命名规则,默认为:${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}
apkNameFormat = '${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}'
}
rebuildChannel {
// baseDebugApk = 已有Debug APK
// baseReleaseApk = 已有Release APK
// //默认为new File(project.buildDir, "rebuildChannel/debug")
// debugOutputDir = Debug渠道包输出目录
// //默认为new File(project.buildDir, "rebuildChannel/release")
// releaseOutputDir = Release渠道包输出目录
}
repositories {
flatDir {
dirs 'libs', 'libs/aars'
@ -227,103 +185,135 @@ dependencies {
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
teaImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/tea/libs')
kuaishouImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/kuaishou/libs')
gdtImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/gdt/libs')
testImplementation 'junit:junit:4.12'
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakcanary}"
debugImplementation "com.squareup.leakcanary:leakcanary-android-process:${leakcanary}"
// debugImplementation "com.gu.android:toolargetool:${toolargetool}" // 需要使用调试时才启用
debugImplementation "com.github.nichbar:WhatTheStack:${whatTheStack}"
// debugImplementation "io.github.didi.dokit:dokitx:${dokit}"
debugImplementation "com.facebook.stetho:stetho:${stetho}"
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stetho}"
debugImplementation "com.squareup.okhttp3:logging-interceptor:${okHttp}"
debugImplementation "com.gu.android:toolargetool:${toolargetool}"
debugImplementation "com.github.nichbar:WhatTheStack:$whatTheStack"
implementation "androidx.core:core:${core}"
implementation "androidx.fragment:fragment:${fragment}"
implementation "androidx.multidex:multidex:${multiDex}"
implementation "androidx.fragment:fragment-ktx:${fragment}"
implementation "androidx.appcompat:appcompat:${appCompat}"
implementation "androidx.cardview:cardview:${cardView}"
implementation "androidx.annotation:annotation:${annotation}"
implementation "androidx.constraintlayout:constraintlayout:${constraintLayout}"
implementation "androidx.recyclerview:recyclerview:${recyclerView}"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifeCycle"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifeCycle"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifeCycle"
implementation "androidx.room:room-runtime:${room}"
implementation "androidx.room:room-rxjava2:${room}"
implementation "androidx.core:core-ktx:${ktx}"
implementation "androidx.viewpager2:viewpager2:${viewpager2}"
kapt "androidx.room:room-compiler:${room}"
implementation "com.google.android.material:material:${material}"
implementation "com.kyleduo.switchbutton:library:${switchButton}"
implementation "com.facebook.fresco:fresco:${fresco}"
implementation "com.facebook.fresco:animated-gif:${fresco}"
implementation "com.facebook.fresco:animated-drawable:${fresco}"
implementation "com.squareup.okhttp3:okhttp:${okHttp}"
implementation "com.leon.channel:helper:${apkChannelPackage}"
implementation "com.squareup.retrofit2:retrofit:${retrofit}"
implementation "com.squareup.retrofit2:converter-gson:${retrofit}" // include gson 2.7
implementation "com.squareup.retrofit2:adapter-rxjava2:${retrofit}"
implementation "com.j256.ormlite:ormlite-android:${ormlite}"
implementation "com.j256.ormlite:ormlite-core:${ormlite}"
implementation "com.jakewharton:butterknife:${butterKnife}"
kapt "com.jakewharton:butterknife-compiler:${butterKnife}"
implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}"
implementation "org.greenrobot:eventbus:${eventbus}"
kapt "org.greenrobot:eventbus-annotation-processor:${eventbusApt}"
implementation "io.reactivex.rxjava2:rxjava:${rxJava2}"
implementation "io.reactivex.rxjava2:rxandroid:${rxAndroid2}"
implementation "com.jakewharton.rxbinding2:rxbinding:${rxBinding2}"
implementation "com.google.zxing:core:${zxing}"
implementation "com.google.zxing:android-core:${zxing}"
implementation "com.daimajia.swipelayout:library:${swipeLayout}"
implementation "com.sina.weibo.sdk:core:${weiboSDK}"
// bugly with tinker support
// implementation "com.tencent.bugly:crashreport_upgrade:${buglyTinkerSupport}"
implementation "com.google.android:flexbox:${flexbox}"
implementation "pub.devrel:easypermissions:${easypermissions}"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.contrarywind:Android-PickerView:${pickerView}"
implementation "com.scwang.smartrefresh:SmartRefreshLayout:${smartRefreshLayout}"
implementation "net.cachapa.expandablelayout:expandablelayout:${expandableLayout}"
// 用于比较 versionName 是大于小于或等于
implementation "com.g00fy2:versioncompare:${versioncompare}"
implementation "top.zibin:Luban:${luban}"
implementation "com.squareup.picasso:picasso:${picasso}"
// for video streaming
implementation("com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:$gsyVideo", {
implementation("com.shuyu:gsyVideoPlayer-java:$gsyVideo", {
exclude module: "gsyvideoplayer-androidvideocache"
exclude group: "tv.danmaku.ijk.media"
})
implementation "com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-exo_player2:$gsyVideo"
implementation "com.shuyu:GSYVideoPlayer-exo2:$gsyVideo"
// implementation "androidx.work:work-runtime:${workManager}"
implementation "android.arch.work:work-runtime:${workManager}"
implementation "com.llew.huawei:verifier:${verifier}"
teaImplementation "com.bytedance.applog:RangersAppLog-Lite-cn:${bytedanceApplog}"
implementation "com.github.tbruyelle:rxpermissions:${rxPermissions}"
implementation "com.ethanhua:skeleton:${skeleton}"
implementation "io.supercharge:shimmerlayout:${shimmerlayout}"
implementation "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:${mta}"
implementation "com.walkud.rom.checker:RomChecker:${romChecker}"
debugImplementation "com.github.nichbar.chucker:library:$chucker"
releaseImplementation "com.github.nichbar.chucker:library-no-op:$chucker"
// implementation "com.bytedance.applog:RangersAppLog-Lite-cn:$bytedanceApplog"
// implementation "com.bytedance.ies.ugc.aweme:opensdk-china-external:$bytedanceAweme"
// implementation "com.bytedance.ies.ugc.aweme:opensdk-common:$bytedanceAweme"
implementation "com.aliyun.dpa:oss-android-sdk:${oss}"
implementation "com.airbnb.android:lottie:$lottie"
implementation "net.lingala.zip4j:zip4j:${zip4j}"
implementation "com.github.hsiafan:apk-parser:${apkParser}"
implementation "org.nanohttpd:nanohttpd:${nanohttpd}"
implementation "com.lg:easyfloat:${easyFloat}"
implementation "io.github.florent37:shapeofview:${shapeOfView}"
implementation "com.lg:apksig:${apksig}"
implementation "com.lg:gid:${gid}"
compileOnly "com.github.axen1314.lancet:lancet-base:${lancet_version}"
kapt "com.alibaba:arouter-compiler:$arouterVersion"
implementation project(':ndownload')
implementation project(':vspace-bridge:vspace')
implementation (project(':module_common')) {
implementation("com.github.piasy:BigImageViewer:$bigImageViewer", {
exclude group: 'com.squareup.okhttp3'
exclude group: 'androidx.swiperefreshlayout'
}
implementation(project(':module_login')) {
exclude group: 'androidx.swiperefreshlayout'
}
implementation(project(':module_setting')) {
exclude group: 'androidx.swiperefreshlayout'
}
// implementation(project(':module_setting_compose')) {
// exclude group: 'androidx.swiperefreshlayout'
// }
implementation(project(':module_core_feature')) {
exclude group: 'androidx.swiperefreshlayout'
}
implementation(project(':module_feedback')) {
exclude group: 'androidx.swiperefreshlayout'
}
implementation(project(':module_sensors_data')) {
exclude group: 'androidx.swiperefreshlayout'
}
implementation(project(':module_vpn'))
implementation(project(':module_pkg'))
// 默认不接入光能模块,提高编译速度
// debugImplementation(project(':module_energy')) {
// exclude group: 'androidx.swiperefreshlayout'
// }
exclude group: 'com.github.bumptech.glide'
})
implementation "com.github.PhilJay:MPAndroidChart:${chart}"
implementation project(':libraries:LGLibrary')
// implementation project(':libraries:MTA')
implementation project(':libraries:QQShare')
// implementation project(':libraries:TalkingData')
// implementation project(':libraries:UmengPush')
// implementation project(':libraries:WechatShare')
// implementation project(':libraries:im')
implementation project(':libraries:Matisse')
}
File propFile = file('sign.properties')
if (propFile.exists()) {
@ -381,182 +371,3 @@ if (propFile.exists()) {
// task.name.startsWith('merge') && task.name.endsWith('Resources')
// }.each { t -> t.dependsOn generateMetaJson }
//}
andResGuard {
mappingFile = null
use7zip = true
useSign = true
// 打开这个开关会keep住所有资源的原始路径只混淆资源的名字
keepRoot = false
// 设置这个值会把arsc name列混淆成相同的名字减少string常量池的大小
fixedResName = "arg"
// 打开这个开关会合并所有哈希值相同的资源,但请不要过度依赖这个功能去除去冗余资源
mergeDuplicatedRes = true
whiteList = [
"R.drawable.icon",
"R.drawable.ic_bar_back",
"R.drawable.toolbar_search_icon",
"R.drawable.bg_notification_answer_style_1",
"R.drawable.bg_notification_answer_style_2",
"R.drawable.bg_notification_article_style_1",
"R.drawable.bg_notification_article_style_2",
"R.drawable.bg_notification_feedback_style_1",
"R.drawable.bg_notification_feedback_style_2",
"R.drawable.bg_notification_gift_style_1",
"R.drawable.bg_notification_gift_style_2",
"R.drawable.bg_notification_login_style_1",
"R.drawable.bg_notification_login_style_2",
"R.drawable.bg_notification_question_style_1",
"R.drawable.bg_notification_question_style_2",
"R.drawable.bg_notification_rating_style_1",
"R.drawable.bg_notification_rating_style_2",
"R.drawable.bg_notification_reserve_game_style_1",
"R.drawable.bg_notification_reserve_game_style_2",
"R.drawable.bg_notification_video_style_1",
"R.drawable.bg_notification_video_style_2",
"R.drawable.ic_recommend_activity",
"R.drawable.ic_recommend_discount",
"R.drawable.ic_recommend_function",
"R.drawable.ic_recommend_gift",
"R.drawable.ic_recommend_role",
"R.drawable.download_button_normal_style",
"R.drawable.ic_selector_selected",
"R.drawable.ic_selector_default",
"R.id.download_speed",
"R.id.download_percentage",
"R.id.comment",
"R.id.vote",
"R.id.watermark_hint",
"R.id.watermark_sb",
"R.id.bottomShareIv",
"R.id.bottomShareTv",
"R.id.recommendStarPref",
"R.id.recommendStar",
"R.id.iv_vmode_badge",
"R.id.tv_vmode",
"R.id.iv_vmode",
"R.drawable.help_search_delete",
"R.drawable.suggest_type_normal",
"R.drawable.suggest_type_crash",
"R.drawable.suggest_type_game_question",
"R.drawable.suggest_type_game_collect",
"R.drawable.suggest_type_function_suggest",
"R.drawable.suggest_type_article_collect",
"R.drawable.suggest_type_copyright",
"R.drawable.news_comment_detail_read",
"R.drawable.news_comment_detail_comment",
"R.drawable.news_comment_detail_share",
"R.drawable.ic_libao",
"R.drawable.ic_link",
"R.drawable.concern_message_icon",
"R.drawable.reuse_blank_hint",
"R.drawable.ic_concern",
"R.drawable.concern_down",
"R.drawable.concern_up",
"R.drawable.ic_libao_more",
"R.drawable.ic_libao_delete",
"R.drawable.ic_dialog_close",
"R.drawable.occupy2",
"R.drawable.kc_checkbox_unselect",
"R.drawable.kc_checkbox_select",
"R.drawable.ic_type_unselect",
"R.drawable.ic_type_selected",
"R.drawable.suggest_add_pic_icon",
"R.drawable.icon_pic_add",
"R.drawable.ask_search_input_delete",
"R.drawable.suggest_pic_delete"
]
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
]
sevenzip {
artifact = 'io.github.leon406:SevenZip:1.2.22.5'
}
}
project.afterEvaluate {
def variants = null
try {
variants = android.applicationVariants
} catch (Throwable t) {
t.printStackTrace()
try {
variants = android.libraryVariants
} catch (Throwable tt) {
tt.printStackTrace()
}
}
if (variants != null) {
variants.all { variant ->
variant.outputs.each { output ->
def task = output.processManifestProvider.get()
if (task == null) {
return
}
/**
* 为 Manifest 的 Activity 的 configChanges 添加自己手动处理 configurationChanges 配置 [https://developer.android.com/guide/topics/resources/runtime-changes]
* AGP 4.1.0 从 ProcessManifest task 里拿 manifest 的 API 变更调整可以参考这里 [https://github.com/Tencent/tinker/pull/1476/commits/d71645729b13d545ca4ba6826f93fbf558751434]
* (搞半天还是不会抽离方法,有空再把 gradle 改成用 kotlin 实现吧)
*/
task.doLast {
def manifestFile = new File(multiApkManifestOutputDirectory.get().asFile, "AndroidManifest.xml")
if (manifestFile == null || !manifestFile.exists()) {
return
}
String[] configChanges = [
"density",
"fontScale",
"keyboard",
"keyboardHidden",
"layoutDirection",
"locale",
"mcc",
"mnc",
"navigation",
"orientation",
"screenLayout",
"screenSize",
"smallestScreenSize",
"touchscreen",
"uiMode"]
def parser = new XmlSlurper(false, true)
def manifest = parser.parse(manifestFile)
def app = manifest.'application'[0]
app.'activity'.each { act ->
String value = act.attributes()['android:configChanges']
if (value == null || value.isEmpty()) {
if (value == null) value = ""
configChanges.eachWithIndex { config, index ->
if (index != configChanges.length - 1) {
value += config + "|"
} else {
value += config
}
}
act.attributes()['androidconfigChanges'] = value
} else {
String[] valueSplit = value.split("\\|")
println configChanges
configChanges.eachWithIndex { config, index ->
if (!valueSplit.contains(config)) {
value += ("|" + config)
}
}
act.attributes()['android:configChanges'] = value
}
}
def tmpManifest = XmlUtil.serialize(manifest).replaceAll("androidconfigChanges", "android:configChanges")
manifest = parser.parseText(tmpManifest)
manifestFile.setText(XmlUtil.serialize(manifest), "utf-8")
}
}
}
}
}

BIN
app/libs/gid-1.3.jar Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,82 +1,261 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# inline getter method
-allowaccessmodification
# Add any project specific keep options here:
# TODO Dicard sourceFile in final release build but remain in internal build.
-renamesourcefileattribute SourceFile
# Keep Attribute
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod,SourceFile,LineNumberTable
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# OrmLite
-keep class com.j256.*
-keepclassmembers class com.j256.* { *; }
-keep enum com.j256.*
-keepclassmembers enum com.j256.* { *; }
-keep interface com.j256.*
-keepclassmembers interface com.j256.* { *; }
-dontwarn com.j256.**
### app models
-keep class com.gh.gamecenter.db.info.* {*;}
-keep class com.gh.gamecenter.entity.** {<fields>;}
-keep class com.gh.gamecenter.qa.entity.** {<fields>;}
-keep class com.gh.gamecenter.BR
-keep class com.gh.gamecenter.retrofit.* {*;}
-keep class com.gh.gamecenter.eventbus.* {*;}
-keep class com.gh.gamecenter.home.gamecollection.GameCollectionStackLayout {*;}
-keep class com.gh.gamecenter.home.gamecollection.GameCollectionStackAnimation {*;}
# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
#--------- remove logs start ----------------
-assumenosideeffects class com.lightgame.config.CommonDebug {
private static String getLogTag(...);
private static String getMethodName();
public static void logMethodName(...);
public static void logParams(...);
public static void logFields(...);
public static void logMethodWithParams(...);
}
# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
#-assumenosideeffects class com.lightgame.config.CommonDebug {*;}
#-dontoptimize
#--------- remove logs end ----------------
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
-dontwarn InnerClasses
# OrmLite uses reflection
-keep class com.j256.**
-keepclassmembers class com.j256.** { *; }
-keep enum com.j256.**
-keepclassmembers enum com.j256.** { *; }
-keep interface com.j256.**
-keepclassmembers interface com.j256.** { *; }
-dontwarn com.j256.**
#okhttp3
-dontwarn com.squareup.okhttp3.**
-dontwarn okio.**
-keep class com.squareup.okhttp3.** { *;}
# stetho
-keep class com.facebook.stetho.** { *; }
-dontwarn com.facebook.stetho.**
# Retrofit 2.2
# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions
# Retrofit 2.X
## https://square.github.io/retrofit/ ##
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
-keepclasseswithmembers class * {
@retrofit2.http.* <methods>;
}
# rxjava
-keep class rx.schedulers.Schedulers {
public static <methods>;
}
-keep class rx.schedulers.ImmediateScheduler {
public <methods>;
}
-keep class rx.schedulers.TestScheduler {
public <methods>;
}
-keep class rx.schedulers.Schedulers {
public static ** test();
}
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
long producerNode;
long consumerNode;
}
-dontwarn rx.internal.util.**
## AutoScrollViewPager
-keep class cn.trinea.android.** { *; }
-keepclassmembers class cn.trinea.android.** { *; }
-dontwarn cn.trinea.android.**
## butterknife
# Retain generated class which implement Unbinder.
#-keep public class * implements butterknife.Unbinder { public <init>(**, android.view.View); }
#
## Prevent obfuscation of types which use ButterKnife annotations since the simple name
## is used to reflectively look up the generated ViewBinding.
#-keep class butterknife.*
#-keepclasseswithmembernames class * { @butterknife.* <methods>; }
#-keepclasseswithmembernames class * { @butterknife.* <fields>; }
-dontwarn butterknife.internal.**
-keep class **$$ViewInjector { *; }
-keepnames class * { @butterknife.InjectView *;}
-dontwarn butterknife.Views$InjectViewProcessor
-dontwarn com.gc.materialdesign.views.**
# eventbus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
# weiboSdk
-keep class com.sina.weibo.sdk.** { *; }
-dontwarn android.webkit.WebView
-dontwarn android.webkit.WebViewClient
# app models
-keep class com.gh.common.view.** {*;}
-keep class com.gh.gamecenter.db.info.** {*;}
-keep class com.gh.gamecenter.entity.** {*;}
-keep class com.gh.gamecenter.qa.entity.** {*;}
-keep class com.gh.gamecenter.retrofit.** {*;}
-keep class com.gh.gamecenter.eventbus.** {*;}
-keep class * extends rx.Subscriber
#---------------------------------webview------------------------------------
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String);
}
#----------------------------------------------------------------------------
##---------------Begin: proguard configuration for Gson ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }
# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
-keepclassmembers enum * { *; }
##---------------End: proguard configuration for Gson ----------
# ------ bugly ---------
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# easypermission
-keepclassmembers class * {
@pub.devrel.easypermissions.AfterPermissionGranted <methods>;
}
# 重命名文件为SourceFile再配合mapping符号表可以拿到真实的类名
-renamesourcefileattribute SourceFile
# 保留源文件行号
-keepattributes SourceFile,LineNumberTable
# TODO What's this ?
-ignorewarnings
### 视频相关
-keep class com.shuyu.gsyvideoplayer.video.* { *; }
-dontwarn com.shuyu.gsyvideoplayer.video.**
-keep class com.shuyu.gsyvideoplayer.video.base.* { *; }
-dontwarn com.shuyu.gsyvideoplayer.video.base.**
-keep class com.shuyu.gsyvideoplayer.utils.* { *; }
-dontwarn com.shuyu.gsyvideoplayer.utils.**
-keep class tv.danmaku.ijk.* { *; }
-dontwarn tv.danmaku.ijk.**
-keep public class * extends android.view.View {
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
-keep @androidx.annotation.Keep class *
-keepclassmembers class ** {
@androidx.annotation.Keep *;
}
### TEA
-keep class com.gh.gamecenter.TeaHelper { *; }
-keep class com.gh.loghub.** { *; }
### EasyFloat
-keep class com.lzf.easyfloat.* {*;}
### greenDAO 3
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
-keep class org.greenrobot.greendao.** { *; }
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use RxJava:
-dontwarn rx.**
-dontwarn org.greenrobot.greendao.rx.**
-dontwarn org.greenrobot.greendao.**
### dokit
-keep class com.didichuxing.** {*;}
### fastJson
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.** { *; }
-keepattributes Signature
-keepattributes Annotation
### 广点通SDK
### 广点通
-dontwarn com.qq.gdt.action.**
-keep class com.qq.gdt.action.** {*;}
-keep public class com.tencent.turingfd.sdk.**
-keepclasseswithmembers class * {
native <methods>;
}
### AndroidX
-keep class androidx.core.app.CoreComponentFactory { *; }
#阿里云上传
-keep class com.alibaba.sdk.android.oss.** { *; }
-dontwarn okio.**
-dontwarn org.apache.commons.codec.binary.**
#视频相关
-keep class com.shuyu.gsyvideoplayer.video.** { *; }
-dontwarn com.shuyu.gsyvideoplayer.video.**
-keep class com.shuyu.gsyvideoplayer.video.base.** { *; }
-dontwarn com.shuyu.gsyvideoplayer.video.base.**
-keep class com.shuyu.gsyvideoplayer.utils.** { *; }
-dontwarn com.shuyu.gsyvideoplayer.utils.**
-keep class tv.danmaku.ijk.** { *; }
-dontwarn tv.danmaku.ijk.**
#穿山甲
-keep class com.bytedance.sdk.openadsdk.** { *; }
-keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;}
-keep class com.pgl.sys.ces.* {*;}
-keep class com.gyf.immersionbar.* {*;}
-dontwarn com.gyf.immersionbar.**
-keep class com.taobao.securityjni.**{*;}
-keep class com.taobao.wireless.security.**{*;}
-keep class com.ut.secbody.**{*;}
-keep class com.taobao.dp.**{*;}
-keep class com.alibaba.wireless.security.**{*;}
-keep class com.alibaba.sdk.android.**{*;}
-keep class com.ut.**{*;}
-keep class com.ta.**{*;}

View File

@ -0,0 +1,38 @@
package com.gh.gamecenter;
import android.app.Application;
import com.facebook.stetho.Stetho;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
/**
* @author CsHeng
* @Date 03/09/2017
* @Time 4:34 PM
*/
public class Injection {
public static boolean appInit(Application application) {
// init stetho
Stetho.initializeWithDefaults(application);
// 监控Bundle大小,预防溢出(需要调试的时候再开启吧!)
// TooLargeTool.startLogging(application);
return true;
}
public static OkHttpClient.Builder provideRetrofitBuilder() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addNetworkInterceptor(interceptor);
builder.addNetworkInterceptor(new StethoInterceptor());
return builder;
}
}

View File

@ -1,36 +0,0 @@
package com.gh.gamecenter.provider
import android.app.Activity
import android.app.Application
import android.text.TextUtils
import com.gh.gamecenter.common.constant.Config
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.PackageFlavorHelper
import com.gh.gamecenter.core.provider.IFlavorProvider
import com.gh.gamecenter.core.utils.SPUtils
import com.leon.channel.helper.ChannelReaderUtil
class FlavorProviderImp : IFlavorProvider {
override fun getChannelStr(application: Application): String {
var channel = ChannelReaderUtil.getChannel(application)
if (channel == null || TextUtils.isEmpty(channel.trim())) {
channel = if (PackageFlavorHelper.IS_TEST_FLAVOR) {
Config.DEFAULT_CHANNEL
} else {
Config.DEFAULT_CHANNEL_FOR_RELEASE
}
}
if (PackageFlavorHelper.IS_TEST_FLAVOR) {
channel = SPUtils.getString(Constants.SP_TEST_FLAVOR_CHANNEL, channel)
}
return channel
}
override fun init(application: Application, activity: Activity) {
// do nothing
}
override fun logEvent(content: String) {
// do nothing
}
}

View File

@ -1,37 +0,0 @@
package com.gh.gamecenter
import android.app.Application
import com.lightgame.utils.Utils
import com.qq.gdt.action.ActionParam
import com.qq.gdt.action.ActionType
import com.qq.gdt.action.GDTAction
import org.json.JSONObject
object GdtHelper {
private const val USER_ACTION_SET_ID = "1201041887"
private const val APP_SECRET_ID = "c29cc5c48cf540c43b4b97363bb09216"
private const val KS_USER_ACTION_SET_ID = "1201032128"
private const val KS_APP_SECRET_ID = "9bdbbb81d4e0bd333a2a581f9ee36986"
@JvmStatic
fun init(application: Application, channel: String) {
if (channel == "KS_GDT_GHZS_MC01") {
GDTAction.init(application, KS_USER_ACTION_SET_ID, KS_APP_SECRET_ID, channel)
} else {
GDTAction.init(application, USER_ACTION_SET_ID, APP_SECRET_ID, channel)
}
Utils.log("init GdtHelper")
}
@JvmStatic
fun logAction(type: String) {
when (type) {
"EVENT_ACTIVE" -> GDTAction.logAction(ActionType.START_APP)
"active_register" -> GDTAction.logAction(ActionType.REGISTER)
"EVENT_NEXTDAY_STAY" -> GDTAction.logAction(ActionType.START_APP, JSONObject().apply {
put(ActionParam.Key.LENGTH_OF_STAY, 1)
})
}
}
}

View File

@ -1,48 +0,0 @@
package com.gh.gamecenter.provider
import android.app.Activity
import android.app.Application
import android.text.TextUtils
import com.gh.gamecenter.GdtHelper
import com.gh.gamecenter.core.provider.IFlavorProvider
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.TimeUtils
import com.halo.assistant.HaloApp
import com.leon.channel.helper.ChannelReaderUtil
class FlavorProviderImp : IFlavorProvider {
override fun init(application: Application, activity: Activity) {
GdtHelper.init(application, getChannelStr(application))
if (HaloApp.getInstance().isBrandNewInstall) {
logEvent("EVENT_ACTIVE")
SPUtils.setLong("TIME_OF_BRAND_NEW_INSTALL", System.currentTimeMillis() / 1000)
} else {
val shouldSendRetentionLogEvent =
SPUtils.getBoolean("SHOULD_SEND_RETENTION_EVENT", true)
val installTimeNotToday =
!TimeUtils.isToday(SPUtils.getLong("TIME_OF_BRAND_NEW_INSTALL", System.currentTimeMillis() / 1000))
if (shouldSendRetentionLogEvent && installTimeNotToday) {
logEvent("EVENT_NEXTDAY_STAY")
SPUtils.setBoolean("SHOULD_SEND_RETENTION_EVENT", false)
}
}
}
override fun getChannelStr(application: Application): String {
var channel = ChannelReaderUtil.getChannel(application)
if (channel == null || TextUtils.isEmpty(channel.trim())) {
channel = GDT_DEFAULT_CHANNEL
}
return channel
}
override fun logEvent(content: String) {
GdtHelper.logAction(content)
}
companion object {
private const val GDT_DEFAULT_CHANNEL = "GDT_GHZS_01"
}
}

View File

@ -1,30 +0,0 @@
package com.gh.gamecenter
import android.content.Context
import com.kwai.monitor.log.TurboAgent
import com.kwai.monitor.log.TurboConfig
object KuaishouHelper {
private const val APP_ID = "80655"
private const val APP_NAME = "guanghuanzhushou_1"
@JvmStatic
fun init(context: Context, channel: String) {
TurboAgent.init(
TurboConfig.TurboConfigBuilder.create(context)
.setAppId(APP_ID)
.setAppName(APP_NAME)
.setAppChannel(channel)
.build()
)
}
@JvmStatic
fun onEvent(type: String) {
when (type) {
"EVENT_ACTIVE" -> TurboAgent.onAppActive()
"active_register" -> TurboAgent.onRegister()
"EVENT_NEXTDAY_STAY" -> TurboAgent.onNextDayStay()
}
}
}

View File

@ -1,48 +0,0 @@
package com.gh.gamecenter.provider
import android.app.Activity
import android.app.Application
import android.text.TextUtils
import com.gh.gamecenter.KuaishouHelper
import com.gh.gamecenter.core.provider.IFlavorProvider
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.TimeUtils
import com.halo.assistant.HaloApp
import com.leon.channel.helper.ChannelReaderUtil
class FlavorProviderImp : IFlavorProvider {
override fun init(application: Application, activity: Activity) {
KuaishouHelper.init(application, getChannelStr(application))
if (HaloApp.getInstance().isBrandNewInstall) {
logEvent("EVENT_ACTIVE")
SPUtils.setLong("TIME_OF_BRAND_NEW_INSTALL", System.currentTimeMillis() / 1000)
} else {
val shouldSendRetentionLogEvent =
SPUtils.getBoolean("SHOULD_SEND_RETENTION_EVENT", true)
val installTimeNotToday =
!TimeUtils.isToday(SPUtils.getLong("TIME_OF_BRAND_NEW_INSTALL", System.currentTimeMillis() / 1000))
if (shouldSendRetentionLogEvent && installTimeNotToday) {
logEvent("EVENT_NEXTDAY_STAY")
SPUtils.setBoolean("SHOULD_SEND_RETENTION_EVENT", false)
}
}
}
override fun getChannelStr(application: Application): String {
var channel = ChannelReaderUtil.getChannel(application)
if (channel == null || TextUtils.isEmpty(channel.trim())) {
channel = KUAISHOU_CHANNEL
}
return channel
}
override fun logEvent(content: String) {
KuaishouHelper.onEvent(content)
}
companion object {
private const val KUAISHOU_CHANNEL = "KS-GHZS-01"
}
}

View File

@ -3,45 +3,44 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.gh.gamecenter">
<queries>
<package android:name="com.gh.gamecenter" />
</queries>
<queries>
<package android:name="com.lg.vspace" />
</queries>
<!-- 允许应用程序访问网络连接 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 允许应用程序写入外部存储如SD卡上写文件 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 允许应用程序读取扩展存储器 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 允许挂载和反挂载文件系统可移动存储 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<!-- 允许应用程序访问Wi-Fi网络状态信息 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 允许应用程序获取网络信息状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 允许应用程序读取电话状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 允许应用程序获取当前或最近运行的应用 -->
<uses-permission android:name="android.permission.GET_TASKS" />
<!-- 允许访问振动设备 -->
<uses-permission android:name="android.permission.VIBRATE" />
<!-- 允许应用程序改变Wi-Fi连接状态 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- 允许应用程序改变网络连接状态 -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<!-- 允许应用程序快捷方式 -->
<!-- 允许应用程序打开系统窗口,显示其他应用程序 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<!-- 修改系统设置的权限 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!-- 创建快捷方式的权限 -->
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- 应用安装相关 -->
<uses-permission android:name="com.android.permission.GET_INSTALLED_APPS" />
<!-- 前台服务权限-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<!-- bugly with tinker -->
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<!--可选,穿山甲提供“获取地理位置权限”和“不给予地理位置权限,开发者传入地理位置参数”两种方式上报用户位置,两种方式均可不选,添加位置权限或参数将帮助投放定位广告-->
<!--请注意:无论通过何种方式提供给穿山甲用户地理位置,均需向用户声明地理位置权限将应用于穿山甲广告投放,穿山甲不强制获取地理位置信息-->
<!--<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />-->
<!-- 如果有视频相关的广告且使用textureView播放请务必添加否则黑屏 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
@ -52,45 +51,15 @@
com.shuyu.gsyvideoplayer.armv7a,
com.shuyu.gsyvideoplayer.x86,
com.shuyu.gsy.base,
shuyu.com.androidvideocache,
com.google.android.exoplayer2,
tv.danmaku.ijk.media.exo2,
pl.droidsonroids.gif,
com.lzf.easyfloat,
com.airbnb.lottie.compose,
androidx.compose.ui.platform,
androidx.compose.material.icons,
androidx.activity.compose,
androidx.compose.ui.tooling,
androidx.compose.ui.tooling.data,
androidx.compose.material.ripple,
androidx.compose.foundation,
androidx.compose.animation,
androidx.compose.foundation.layout,
androidx.compose.ui.text,
androidx.compose.ui.graphics,
androidx.compose.ui.unit,
androidx.compose.ui.util,
androidx.compose.ui.geometry,
androidx.compose.runtime.saveable,
androidx.compose.animation.core,
androidx.constraintlayout.compose,
androidx.compose.ui.test.manifest,
androidx.compose.ui.tooling.preview"/>
pl.droidsonroids.gif" />
<!-- 去掉 SDK 一些流氓权限 -->
<uses-permission
android:name="android.permission.READ_CONTACTS"
tools:node="remove" />
<uses-permission
android:name="android.permission.READ_PHONE_STATE"
tools:node="remove" />
<uses-permission
android:name="android.permission.GET_TASKS"
tools:node="remove" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
@ -107,18 +76,10 @@
android:largeHeap="true"
android:resizeableActivity="true"
android:theme="@style/AppCompatTheme.APP"
tools:replace="android:name,android:allowBackup"
tools:replace="android:allowBackup"
tools:targetApi="n">
<meta-data
android:name="io.sentry.auto-init"
android:value="false" />
<!-- 不让 sentry 读取系统事件 -->
<meta-data
android:name="io.sentry.breadcrumbs.system-events"
android:value="false" />
<!--android:launchMode = "singleTask"-->
<activity
android:name="com.gh.gamecenter.SplashScreenActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -145,6 +106,7 @@
android:launchMode="singleTask"
android:screenOrientation="portrait" />
<!--android:theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen" 退出时屏幕抖动 -->
<activity
android:name="com.gh.gamecenter.ImageViewerActivity"
android:theme="@style/Theme.Transparent" />
@ -173,6 +135,10 @@
android:name="com.gh.gamecenter.NewsDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.SettingActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.ConcernActivity"
android:screenOrientation="portrait" />
@ -198,10 +164,6 @@
android:name="com.gh.gamecenter.WebActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.SingletonWebActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.FullScreenWebActivity"
android:screenOrientation="portrait" />
@ -240,6 +202,18 @@
android:name="com.gh.gamecenter.SelectUserIconActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.AboutActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.security.SecurityActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.security.BindPhoneActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.CommentDetailActivity"
android:screenOrientation="portrait"
@ -254,11 +228,19 @@
android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.SuggestionActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.VoteActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
<activity
android:name="com.gh.gamecenter.ToolBoxActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden" />
<activity
android:name="com.gh.gamecenter.WeiBoShareActivity"
android:screenOrientation="portrait"
@ -272,6 +254,11 @@
android:name=".category.CategoryListActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.LoginActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden" />
<activity
android:name="com.gh.gamecenter.UserInfoActivity"
android:screenOrientation="portrait" />
@ -293,10 +280,22 @@
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden" />
<activity
android:name="com.gh.gamecenter.qa.search.AskSearchActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.answer.fold.AnswerFoldActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.answer.edit.AnswerEditActivity"
android:screenOrientation="portrait" />
@ -313,6 +312,14 @@
android:name="com.gh.gamecenter.MessageKeFuActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.select.CommunitiesSelectActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.subject.CommunitySubjectActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.MessageInviteActivity"
android:screenOrientation="portrait" />
@ -329,6 +336,10 @@
android:name="com.gh.gamecenter.qa.myqa.MyAskActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.column.order.AskTabOrderActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.questions.edit.QuestionEditActivity"
android:screenOrientation="portrait" />
@ -350,6 +361,14 @@
android:name="com.gh.gamecenter.amway.AmwayActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.column.detail.AskColumnDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.NetworkDiagnosisActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.personalhome.fans.FansActivity"
android:screenOrientation="portrait" />
@ -378,6 +397,10 @@
android:name="com.gh.gamecenter.qa.article.detail.ArticleDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.article.detail.comment.ArticleDetailCommentActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.draft.CommunityDraftWrapperActivity"
android:screenOrientation="portrait" />
@ -387,6 +410,14 @@
android:screenOrientation="portrait"
android:windowSoftInputMode="stateVisible" />
<activity
android:name="com.gh.gamecenter.qa.questions.edit.manager.HistoryDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.questions.edit.manager.HistoryActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.editor.InsertAnswerWrapperActivity"
android:screenOrientation="portrait" />
@ -420,6 +451,10 @@
android:name="com.gh.gamecenter.tag.TagsActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.article.SimpleArticleListActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.video.videomanager.VideoManagerActivity"
android:screenOrientation="portrait" />
@ -427,14 +462,18 @@
<activity
android:name="com.gh.gamecenter.video.upload.view.UploadVideoActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden" />
android:windowSoftInputMode="stateHidden"/>
<activity
android:name="com.gh.gamecenter.video.game.GameVideoActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.editor.LocalMediaActivity"
android:name="com.gh.gamecenter.qa.editor.VideoActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.mygame.PlayedGameActivity"
android:screenOrientation="portrait" />
<activity
@ -454,12 +493,17 @@
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.comment.CommentActivity"
android:theme="@style/Theme.Transparent"
android:windowSoftInputMode="adjustNothing" />
android:name="com.gh.gamecenter.HelpAndFeedbackActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".qa.dialog.ChooseForumActivity"
android:name="com.gh.gamecenter.help.HelpDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.comment.CommentActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.Transparent"
android:windowSoftInputMode="adjustNothing" />
@ -476,6 +520,10 @@
android:name="com.gh.gamecenter.gamedetail.fuli.kaifu.ServersCalendarActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.QaActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.answer.draft.AnswerDraftActivity"
android:screenOrientation="portrait" />
@ -494,6 +542,14 @@
android:name=".video.poster.PosterClipActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.select.ForumSelectActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.follow.ForumMyFollowActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.detail.ForumDetailActivity"
android:screenOrientation="portrait" />
@ -502,10 +558,6 @@
android:name=".forum.moderator.ModeratorListActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.moderator.ApplyModeratorActivity"
android:screenOrientation="portrait" />
<activity
android:name=".video.label.VideoLabelActivity"
android:screenOrientation="portrait" />
@ -525,7 +577,11 @@
<activity
android:name=".personalhome.background.BackgroundClipActivity"
android:screenOrientation="portrait"
android:theme="@style/TransparentStatusBarAndNavigationBar" />
android:theme="@style/TransparentStatusBarAndNavigationBar"/>
<activity
android:name=".personalhome.excellentcomments.ExcellentCommentsActivity"
android:screenOrientation="portrait" />
<activity
android:name=".simulatorgame.SimulatorGameActivity"
@ -535,91 +591,14 @@
android:name=".simulatorgame.SimulatorManagementActivity"
android:screenOrientation="portrait" />
<!-- 使用小米/华为推送弹窗功能提高推送成功率-->
<activity
android:name=".catalog.CatalogActivity"
android:screenOrientation="portrait" />
<activity
android:name=".catalog.NewCatalogListActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.search.ForumOrUserSearchActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.questions.draft.QuestionDraftActivity"
android:screenOrientation="portrait" />
<activity
android:name=".servers.GameServerTestActivity"
android:screenOrientation="portrait" />
<activity
android:name=".category2.CategoryV2Activity"
android:screenOrientation="portrait" />
<activity
android:name=".personal.DeliveryInfoActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.editor.PreviewVideoActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.video.publish.VideoPublishActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.video.detail.ForumVideoDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name=".video.videomanager.VideoDraftActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.questions.newdetail.NewQuestionDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.editor.FullScreenVideoActivity"
android:theme="@style/AppFullScreenTheme" />
<activity
android:name=".forum.list.ForumListActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.answer.detail.SimpleAnswerDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name=".game.commoncollection.detail.CommonCollectionDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name=".gamecollection.detail.GameCollectionDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name=".gamecollection.detail.GameCollectionPosterActivity"
android:screenOrientation="portrait" />
<activity
android:name=".home.skip.PackageSkipActivity"
android:screenOrientation="portrait" />
<!-- &lt;!&ndash; 使用小米/华为推送弹窗功能提高推送成功率&ndash;&gt;-->
<!-- <activity-->
<!-- android:name="com.gh.gamecenter.PushProxyActivity"-->
<!-- android:exported="true"-->
<!-- android:launchMode="singleTask"-->
<!-- android:theme="@android:style/Theme.Translucent" />-->
<activity
android:name="com.gh.gamecenter.PushProxyActivity"
android:exported="true"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Translucent" />
<activity
android:name="com.gh.gamecenter.SkipActivity"
android:theme="@style/Theme.AppCompat.Light.Fullscreen.Transparent">
<intent-filter>
@ -629,113 +608,21 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter>
<data android:scheme="market" />
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
<activity
android:name="com.gh.gamecenter.teenagermode.TeenagerModeActivity"
android:screenOrientation="portrait" />
android:name="${applicationId}.wxapi.WXEntryActivity"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Translucent.NoTitleBar"></activity>
<activity
android:name=".gamecollection.publish.GameCollectionEditActivity"
android:screenOrientation="portrait" />
<activity
android:name=".gamecollection.choose.ChooseGamesActivity"
android:screenOrientation="portrait" />
<activity
android:name=".gamecollection.choose.AddGamesActivity"
android:screenOrientation="portrait" />
<activity
android:name=".gamecollection.mine.MyGameCollectionActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.gamecollection.square.GameCollectionSquareActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.editor.InsertGameCollectionWrapperActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.editor.InsertVideoWrapperActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.toolbox.ToolBoxBlockActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.vspace.VDownloadManagerActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.vspace.VSpaceLoadingActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.subject.CommunitySubjectActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.comment.NewCommentDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.mypost.MyPostActivity"
android:screenOrientation="portrait" />
<activity
android:name=".VerifyPhoneActivity"
android:screenOrientation="portrait" />
<activity
android:name=".BbsCertificationActivity"
android:screenOrientation="portrait" />
<activity
android:name=".discovery.DiscoveryActivity"
android:screenOrientation="portrait" />
<activity
android:name=".cloudarchive.CloudArchiveManagerActivity"
android:screenOrientation="portrait" />
<activity
android:name=".savegame.GameArchiveListActivity"
android:screenOrientation="portrait" />
<activity
android:name=".discovery.interestedgame.InterestedGameActivity"
android:screenOrientation="portrait" />
<activity
android:name=".servers.gametest2.GameServerTestV2Activity"
android:screenOrientation="portrait" />
<!-- <activity-->
<!-- android:name="${applicationId}.douyinapi.DouYinEntryActivity"-->
<!-- android:launchMode="singleTask"-->
<!-- android:taskAffinity="${applicationId}"-->
<!-- android:exported="true" />-->
<!-- <activity-->
<!-- android:name="${applicationId}.douyinapi.DouYinEntryActivity"-->
<!-- android:launchMode="singleTask"-->
<!-- android:taskAffinity="${applicationId}"-->
<!-- android:exported="true" />-->
<provider
android:name="androidx.core.content.FileProvider"
@ -747,44 +634,6 @@
android:resource="@xml/provider_paths" />
</provider>
<provider
android:name="com.gh.gamecenter.provider.GhContentProvider"
android:authorities="${applicationId}.provider"
android:enabled="true"
android:exported="true" />
<provider
android:name="com.gh.vspace.VFileProvider"
android:authorities="${applicationId}.virtual_file_provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<service
android:name=".aidl.CommunicationService"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.gh.gamecenter.aidl.CommunicationService"/>
</intent-filter>
</service>
<!-- <provider-->
<!-- android:name="androidx.startup.InitializationProvider"-->
<!-- android:authorities="${applicationId}.androidx-startup"-->
<!-- android:exported="false"-->
<!-- tools:node="merge">-->
<!-- &lt;!&ndash; If you are using androidx.startup to initialize other components &ndash;&gt;-->
<!-- <meta-data-->
<!-- android:name="androidx.work.WorkManagerInitializer"-->
<!-- android:value="androidx.startup"-->
<!-- tools:node="remove" />-->
<!-- </provider>-->
<receiver
android:name="com.gh.gamecenter.receiver.DownloadReceiver"
android:exported="false">
@ -808,6 +657,48 @@
</intent-filter>
</receiver>
<!-- <receiver android:name="com.gh.gamecenter.receiver.UmengMessageReceiver">-->
<!-- <intent-filter>-->
<!-- <action android:name="com.gh.gamecenter.UMENG" />-->
<!-- </intent-filter>-->
<!-- </receiver>-->
<!-- &lt;!&ndash;魅族push应用定义消息receiver声明 &ndash;&gt;-->
<!-- <receiver android:name="com.gh.gamecenter.receiver.UmengMeizuPushReceiver">-->
<!-- <intent-filter>-->
<!-- &lt;!&ndash; 接收push消息 &ndash;&gt;-->
<!-- <action android:name="com.meizu.flyme.push.intent.MESSAGE" />-->
<!-- &lt;!&ndash; 接收register消息 &ndash;&gt;-->
<!-- <action android:name="com.meizu.flyme.push.intent.REGISTER.FEEDBACK" />-->
<!-- &lt;!&ndash; 接收unregister消息&ndash;&gt;-->
<!-- <action android:name="com.meizu.flyme.push.intent.UNREGISTER.FEEDBACK" />-->
<!-- &lt;!&ndash; 兼容低版本Flyme3推送服务配置 &ndash;&gt;-->
<!-- <action android:name="com.meizu.c2dm.intent.REGISTRATION" />-->
<!-- <action android:name="com.meizu.c2dm.intent.RECEIVE" />-->
<!-- <category android:name="${applicationId}" />-->
<!-- </intent-filter>-->
<!-- </receiver>-->
<!-- <receiver-->
<!-- android:name="com.gh.common.im.ImReceiver"-->
<!-- android:enabled="true">-->
<!-- <intent-filter android:priority="2147483647">-->
<!-- <action android:name="com.gh.im" />-->
<!-- <action android:name="action_finish" />-->
<!-- </intent-filter>-->
<!-- </receiver>-->
<!-- <meta-data-->
<!-- android:name="com.huawei.hms.client.appid"-->
<!-- android:value="@string/huawei_push_appid" />-->
<!-- <service-->
<!-- android:name="com.gh.base.GHUmengNotificationService"-->
<!-- android:permission="android.permission.BIND_JOB_SERVICE" />-->
<!--<service android:name = "com.gh.gamecenter.statistics.AppStaticService" />-->
<!-- 梦工厂配置 开始 -->
<!--<meta-data
android:name="MGC_APPID"

View File

@ -0,0 +1,40 @@
emoji_kf_1.png,:smile:
emoji_kf_2.png,:smiley:
emoji_kf_3.png,:laughing:
emoji_kf_4.png,:blush:
emoji_kf_5.png,:heart_eyes:
emoji_kf_6.png,:smirk:
emoji_kf_7.png,:flushed:
emoji_kf_8.png,:kissing_heart:
emoji_kf_9.png,:grin:
emoji_kf_10.png,:wink:
emoji_kf_11.png,:stuck_out_tongue_winking_eye:
emoji_kf_12.png,:stuck_out_tongue_closed eyes:
emoji_kf_13.png,:worried:
emoji_kf_14.png,:sleeping:
emoji_kf_15.png,:expressionless:
emoji_kf_16.png,:sweat_smile:
emoji_kf_17.png,:joy:
emoji_kf_18.png,:cold_sweat:
emoji_kf_19.png,:sob:
emoji_kf_20.png,:angry:
emoji_kf_21.png,:mask:
emoji_kf_22.png,:scream:
emoji_kf_23.png,:sunglasses:
emoji_kf_24.png,:heart:
emoji_kf_25.png,:broken_heart:
emoji_kf_26.png,:star:
emoji_kf_27.png,:anger:
emoji_kf_28.png,:exclamation:
emoji_kf_29.png,:question:
emoji_kf_30.png,:zzz:
emoji_kf_31.png,:thumbsup:
emoji_kf_32.png,:thumbsdown:
emoji_kf_33.png,:ok_hand:
emoji_kf_34.png,:punch:
emoji_kf_35.png,:yeah:
emoji_kf_36.png,:clap:
emoji_kf_37.png,:muscle:
emoji_kf_38.png,:pray:
emoji_kf_39.png,:skull:
emoji_kf_40.png,:trollface:

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"v":"5.9.1","fr":60,"ip":0,"op":100,"w":64,"h":64,"nm":"多版本下载提示_dark","ddd":0,"assets":[{"id":"comp_0","nm":"arrow 合成_dark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"arrow","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.333],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":76,"s":[100]},{"t":100,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.333,"y":1},"o":{"x":0.364,"y":0},"t":0,"s":[28,-12,0],"to":[0,6.382,0],"ti":[0,-0.284,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":24,"s":[28,28,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":76,"s":[28,28,0],"to":[0,6.667,0],"ti":[0,-6.667,0]},{"t":100,"s":[28,68,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,2],[0,-2]],"c":false},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2,0],[0,2]],"c":false},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[2,0],[0,2]],"c":false},"ix":2},"nm":"路径 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.333,0.333],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[80,120]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":24,"s":[100,100]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":76,"s":[100,100]},{"t":100,"s":[80,120]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.156862750649,0.533333361149,0.878431379795,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"stroke","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[400,400],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"Vector 97","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"arrow 合成_dark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[32,32,0],"ix":2,"l":2},"a":{"a":0,"k":[28,28,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":56,"h":56,"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"base","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[32,32,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[15,15],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.156862750649,0.533333361149,0.878431379795,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"stroke","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.137254908681,0.137254908681,0.137254908681,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"filling","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[400,400],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"Ellipse 44","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0}],"markers":[]}

View File

@ -1 +0,0 @@
{"v":"5.9.1","fr":60,"ip":0,"op":100,"w":64,"h":64,"nm":"多版本下载提示_light","ddd":0,"assets":[{"id":"comp_0","nm":"arrow 合成_light","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"arrow","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.333],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":76,"s":[100]},{"t":100,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.333,"y":1},"o":{"x":0.364,"y":0},"t":0,"s":[28,-12,0],"to":[0,6.382,0],"ti":[0,-0.284,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":24,"s":[28,28,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":76,"s":[28,28,0],"to":[0,6.667,0],"ti":[0,-6.667,0]},{"t":100,"s":[28,68,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,2],[0,-2]],"c":false},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2,0],[0,2]],"c":false},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[2,0],[0,2]],"c":false},"ix":2},"nm":"路径 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.333,0.333],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[80,120]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":24,"s":[100,100]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":76,"s":[100,100]},{"t":100,"s":[80,120]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.141176477075,0.588235318661,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"stroke","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[400,400],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"Vector 97","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"arrow 合成_light","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[32,32,0],"ix":2,"l":2},"a":{"a":0,"k":[28,28,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":56,"h":56,"ip":0,"op":100,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"base","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[32,32,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[15,15],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.141176477075,0.588235318661,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"stroke","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"filling","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[400,400],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"Ellipse 44","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0}],"markers":[]}

View File

@ -0,0 +1,5 @@
# This is a simple Microlog configuration file
microlog.level=DEBUG
microlog.appender=LogCatAppender;FileAppender
microlog.formatter=PatternFormatter
microlog.formatter.PatternFormatter.pattern=%c [%P] %m %T

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,360 @@
<html lang="en"><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>隐私政策</title>
<style>
* {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
font-size: 16px;
}
</style>
</head>
<body>
<div class="content-area">
<p>
<b>欢迎您使用光环game</b>
<br>
</p>
<p>更新日期2020年11月20日</p>
<p>生效日期2020年11月30日</p>
<p>
当您使用或开启相关功能或使用服务时,为了实现功能、服务所必需,受制于手机系统限制,我们会申请您的设备信息权限;除非是为实现基本业务服务功能或根据法律法规要求所必需的必要信息,您均可以拒绝提供且不影响其他功能或服务。
</p>
<p>
为了让您正常观看视频和记录浏览账户登录功能,我们会申请您的储存权限;
</p>
<p>以下为完整《隐私权限政策》</p>
<p>
光环game简称“我们”深知个人信息对您的重要性我们将依据《中华人民共和国网络安全法》、《信息安全技术
个人信息安全规范》GB/T
35273-2017以及其他相关法律法规和技术规范收集和使用您的个人信息以帮助我们向您提供更优质的产品和/或服务,
保护您的个人信息及隐私安全。我们制定本“隐私指引”并特别提示希望您在使用光环game及相关服务前仔细阅读并理解本隐私政策以便做出适当的选择。
</p>
<p>
下文将帮您详细了解我们如何收集、使用、存储、传输、共享、转让(如适用)与保护个人信息;帮您了解查询、访问、删除、更正、撤回授权个人信息的方式。其中,
<b>
有关您个人信息权益的条款重要内容我们已用加粗形式提示,请特别关注。
</b>
</p>
<p><b>1.我们处理个人信息的法律依据</b></p>
<p><b>2.我们如何共享、转让、公开披露个人信息</b></p>
<p><b>3.我们如何收集和使用个人信息</b></p>
<p><b>4.我们如何存储个人信息</b></p>
<p><b>5.我们如何保护个人信息的安全</b></p>
<p><b>6.管理您的个人信息</b></p>
<p><b>7.未成年人使用条款</b></p>
<p><b>8.隐私政策的修订和通知</b></p>
<p><b>9.联系我们</b></p>
<p><b>1.我们处理个人信息的法律依据</b></p>
<p>
如果您是中华人民共和国大陆地区的用户我们将依据《中华人民共和国网络安全法》、《信息安全技术个人信息安全规范》GB/T
35273-2017以及其他相关法律法规收集和使用您的个人信息为您提供产品或服务。
</p>
<p>
我们通常只会在征得您同意的情况下收集您的个人信息。在某些情况下,我们可能还会基于法律义务或者履行合同之必需向您收集个人信息,或者可能需要个人信息来保护您的重要利益或其他人的利益。
</p>
<p><b>2.我们如何共享、转让、公开披露个人信息</b></p>
<p>
<b>2.</b>
<b>1</b>
<b>共享您的个人信息</b>
</p>
<p>
1我们不会与任何公司、组织和个人共享您的个人信息但以下情况除外
</p>
<p>
2事先获得您的明确授权或同意
获得您的明确同意后,我们会与其他方共享您的个人信息;
</p>
<p>
3在法定情形下的共享
根据适用的法律法规、法律程序、政府的强制命令或司法裁定而需共享您的个人信息;
</p>
<p>
4在法律要求或允许的范围内为了保护光环game及其用户或社会公众的利益、财产或安全免遭损害而有必要提供您的个人信息给第三方
</p>
<p>
5与我们的关联公司共享
您的个人信息可能会在我们的关联公司之间共享。我们会对共享的个人信息进行匿名化处理,且这种共享受本指引所声明目的的约束。关联公司如要改变个人信息的处理目的,将再次征求您的授权同意。
</p>
<p>
<b>2.</b>
<b>2</b>
<b>转让</b>
</p>
<p>
1我们不会转让您的个人信息给任何其他第三方除非征得您的明确同意。
</p>
<p>
2随着我们业务的持续发展我们将有可能进行合并、收购、资产转让您的个人信息有可能因此而被转移。在发生前述变更时我们将按照法律法规及不低于本隐私政策所载明的安全标准要求继受方保护您的个人信息否则我们将要求继受方重新征得您的授权同意。
</p>
<p>
<b>2.</b>
<b>3</b>
<b>披露</b>
</p>
<p>
1我们不会公开披露您的信息除非遵循国家法律法规规定或者获得您的同意。我们公开披露您的个人信息会采用符合行业内标准的安全保护措施。
</p>
<p>
2基于法律、法律程序、诉讼或政府主管部门强制性要求的情况下我们可能会向有权机关披露您的个人信息。但我们保证在上述情况发生时我们会要求披露请求方必须出具与之相应的有效法律文件并对被披露的信息采取符合法律和业界标准的安全防护措施。
</p>
<p>3对违规账号、欺诈行为进行处罚公告时我们会披露相关账号的信息。</p>
<p>
<b>2.</b>
<b>4</b>
<b>依法豁免征得同意共享、转让、公开披露的个人信息</b>
</p>
<p>
请您理解,在下列情形中,根据法律法规及国家标准,我们共享、转让、公开披露您的个人信息无需征得您的授权同意:
</p>
<p>1与国家安全、国防安全直接相关的</p>
<p>2与公共安全、公共卫生、重大公共利益直接相关的</p>
<p>3与犯罪侦查、起诉、审判和判决执行等直接相关的</p>
<p>
4出于维护您或其他个人的生命、财产等重大合法权益但又很难得到本人同意的
</p>
<p>5您自行向社会公众公开的个人信息</p>
<p>
6从合法公开披露的信息中收集个人信息的如合法的新闻报道、政府信息公开等渠道。
</p>
<p><b>3.我们如何收集和使用个人信息</b></p>
<p>
我们会遵循正当、合法、必要的原则,出于本指引所述的以下目的,收集和使用您在使用服务过程中主动提供或因使用产品或服务而产生的个人信息。
</p>
<p>
我们收集和使用的您的个人信息类型包括两种:第一种:我们产品或服务的核心业务功能所必需的信息:此类信息为产品或服务正常运行的必备信息,您须授权我们收集。如您拒绝提供,您将无法正常使用我们的功能,以"仅浏览(游客身份)"
的状态体验;第二种:我们产品或服务的附加业务功能可能需要收集的信息:此信息为非核心业务功能所需的信息,您可以选择是否授权我们收集。如您拒绝提供,将导致附加业务功能无法实现或无法达到我们拟达到的效果,但不影响您对核心业务功能的正常使用。
</p>
<p>
如果我们要将您的个人信息用于本指引未载明的其它用途,或基于特定目的将收集而来的信息用于其他目的,我们将以合理的方式向您告知,并在使用前再次征得您的同意。
</p>
<p><b>3.1实现产品或服务的基本功能</b></p>
<p>
1手机管理和内容资源下载功能。为实现手机管理及手机内容资源下载的基本功能我们会通过手机系统的公用接口收集经过MD5算法加密的国际移动设备身份码IMEI和网络设备地址MAC以及手机型号、手机系统版本号、系统编号、系统ID号、屏幕分辨率、上网类型、手机中软件的名称、版本号、版本名、包名、软件使用时间和频率、软件崩溃信息、设备和软件相关的信息。这些信息是提供服务所必须收集的基础信息如您拒绝提供上述权限将可能导致您无法使用我们的服务。
</p>
<p>
2软件升级管理功能。为实现手机软件下载、安装、升级、卸载软件管理功能在您使用产品时我们会采集您手机中已安装软件的软件名称、版本号、版本名、软件包名信息并上传到我们的服务器进行软件版本比对。发现有更新的版本我们会提示您升级相应的软件。上述软件信息为实现此功能所必需不涉及您个人身份敏感信息。
</p>
<p>
3过滤无法使用的软件功能。为了过滤您手机无法使用的软件我们会收集您手机的手机型号、手机系统版本号、系统版本号、屏幕分辨率信息并依据这些信息排除您手机无法使用的软件以保证您在光环game下载的软件都可安装使用。
</p>
<p><b>3.2关于获取手机设备信息的说明</b></p>
<p>
1为方便区分每个用户的个人信息等本软件需获取用户的手机设备信息。2为了保障软件与服务的安全运行我们会收集您的硬件型号、操作系统版本号、国际移动设备识别码、唯一设备标识符、网络设备硬件地址、IP
地址、WLAN接入点、蓝牙、基站、软件版本号、网络接入方式、类型、状态、网络质量数据、操作、使用、服务日志。
</p>
<p>
3为了预防恶意程序及安全运营所必需我们会收集安装的应用信息或正在运行的进程信息、应用程序的总体运行、使用情况与频率、应用崩溃情况、总体安装使用情况、性能数据、应用来源。
</p>
<p>
4我们可能使用您的账户信息、设备信息、服务日志信息以及我们关联公司、合作方在获得您授权或依法可以共享的信息用于判断账户安全、进行身份验证、检测及防范安全事件。
</p>
<p>5具体会发生获取手机设备信息场景如下说明</p>
<p>
&nbsp;&nbsp;①首次启动光环game&nbsp;②评论详情-发送评论功能&nbsp;③视频投稿-上传视频功能&nbsp;④视频详情-关注up主功能&nbsp;
</p>
<p><b>3.3帮助您成为我们的在线用户</b></p>
<p>1注册账号/登录账号</p>
<p>
a.当您注册、登录我们相关服务时,您可以通过手机号创建账号,并且您可以完善相关的网络身份识别信息(头像、昵称、密码),收集这些信息是为了帮助您完成注册。您还可以根据自身需求选择填写性别、生日、地区及个人介绍来完善您的信息。
</p>
<p>2认证用户</p>
<p>
a.在您使用身份认证的功能或服务时,根据相关法律法规,您可能需要提供您的真实身份信息(真实姓名、身份证号码、电话号码)以完成实名验证。
</p>
<p>
b.这些信息属于个人敏感信息,您可以拒绝提供,但您将可能无法获得相关服务,但不影响其他功能与服务的正常使用。
</p>
<p>
<b>3.</b>
<b>4</b>
<b>信息发布功能</b>
</p>
<p>
1注册成为光环game用户后可在光环game平台上发布评论、视频还可以对别人的视频评论作出回复、赞同、感谢。
</p>
<p>
2上述功能基于相册图片库/视频库)的图片/视频访问及上传的附加服务,我们会请求您授权相机、照片、麦克风权限,您可以使用该功能上传您的照片/图片/视频,以实现发布照片/图片/视频的功能、与其他用户进行照片/图片分享等功能。如您拒绝提供该权限和内容的,仅会使您无法使用该功能,但并不影响您正常使用产品与/或服务的其他功能。
</p>
<p>
3您发布视频内容或回答时我们将收集您发布的信息并展示您的昵称、头像、发布内容。
</p>
<p>
4用户因使用我们的产品或者服务而被我们收集的信息例如其他用户发布的信息中可能含有您的部分信息在评论、留言、发布图文、音视频中涉及到与您相关的信息
</p>
<p>
<b>3.</b>
<b>5</b>
<b>浏览、关注与收藏功能</b>
</p>
<p>1您可浏览的内容包括视频、评论。</p>
<p>
2在浏览的过程中您还可以关注您感兴趣的用户、收藏并收藏上述内容。
</p>
<p>
3为此
我们可能会收集您使用时的设备信息,如设备型号、唯一设备标识符、操作系统、分辨率、电信运营商等软硬件信息。
我们还可能收集您的浏览器类型,以此来为您提供信息展示的最优方案。
</p>
<p>
4此外在您使用浏览和收藏功能的过程中我们会自动收集您使用的详细情况并作为有关的
网络日志保存。
</p>
<p>
5您浏览和发布的内容及评论信息您上传的视频作品、您使用的语言、访问的日期和时间、及您的操作系统、软件版本号、登录IP
信息。
</p>
<p>
6在此过程中
我们会收集您的浏览记录,浏览记录包括您浏览的视频、评论,您可以自主清楚浏览缓存。
</p>
<p>
<b>3.</b>
<b>6</b>
<b>互动交流</b>
</p>
<p>
1您主动关注您感兴趣的up主、视频并与之进行互动进行浏览、评论、收藏、点赞或分享内容时我们会收集您关注的账号并向您展示您关注账号发布内容。
</p>
<p>
2您使用推荐通讯录好友功能时我们会请求通讯录权限并将通讯录中的信息进行高强度加密算法处理后用于向您推荐通信录中的好友。通讯录信息属于个人敏感信息拒绝提供该信息仅会使您无法使用上述功能但不影响您正常使用“光环game”及相关服务的其他功能。
</p>
<p>
<b>3.</b>
<b>7</b>
<b>收集、使用个人信息目的变更</b>
</p>
<p>
1请您了解随着我们业务的发展可能会对“光环game”的功能和提供的服务有所调整变化。
</p>
<p>
2原则上当新功能或服务与我们当前提供的功能或服务相关时收集与使用的个人信息将与原处理目的具有直接或合理关联。
</p>
<p>
3在与原处理目的无直接或合理关联的场景下我们收集、使用您的个人信息会再次进行告知并征得您的同意。
</p>
<p>
<b>3.</b>
<b>8</b>
<b>依法豁免征得同意收集和使用的个人信息</b>
</p>
<p>
请您理解,在下列情形中,根据法律法规及相关国家标准,我们收集和使用您的个人信息无需征得您的授权同意:
</p>
<p>1与国家安全、国防安全直接相关的</p>
<p>2与公共安全、公共卫生、重大公共利益直接相关的</p>
<p>3与犯罪侦查、起诉、审判和判决执行等直接相关的</p>
<p>
4出于维护个人信息主体或其他个人的生命、财产等重大合法权益但又很难得到本人同意的
</p>
<p>5所收集的您的个人信息是您自行向社会公众公开的</p>
<p>
6从合法公开披露的信息中收集的您的个人信息的如合法的新闻报道、政府信息公开等渠道
</p>
<p>7根据您的要求签订或履行合同所必需的</p>
<p>
8用于维护软件及相关服务的安全稳定运行所必需的例如发现、处置软件及相关服务的故障
</p>
<p>9为合法的新闻报道所必需的</p>
<p>
10学术研究机构基于公共利益开展统计或学术研究所必要且对外提供学术研究或描述的结果时对结果中所包含的个人信息进行去标识化处理的。
</p>
<p>11法律法规规定的其他情形。</p>
<p>
特别提示您注意,如信息无法单独或结合其他信息识别到您的个人身份,其不属于法律意义上您的个人信息;当您的信息可以单独或结合其他信息识别到您的个人身份时或我们将无法与任何特定个人信息建立联系的数据与其他您的个人信息结合使用时,这些信息在结合使用期间,将作为您的个人信息按照本隐私政策处理与保护。
</p>
<p><b>4.我们如何存储个人信息</b></p>
<p><b>4.1 存储地点</b></p>
<p>
1我们依照法律法规的规定将在境内运营过程中收集和产生的您的个人信息存储于中华人民共和国境内。
</p>
<p>
2目前我们不会将上述信息传输至境外如果我们向境外传输我们将会遵循相关国家规定或者征求您的同意。
</p>
<p><b>4.2存储期限</b></p>
<p>
1我们仅在为提供“光环game”及服务之目的所必需的期间内保留您的个人信息您发布的视频、评论、点赞及相关信息在您未撤回、删除或未注销账号期间我们会保留相关信息。
</p>
<p>
2超出必要期限后我们将对您的个人信息进行删除或匿名化处理但法律法规另有规定的除外。
</p>
<p><b>5.我们如何保护个人信息的安全</b></p>
<p>
1我们非常重视您个人信息的安全将努力采取合理的安全措施包括技术方面和管理方面来保护您的个人信息防止您提供的个人信息被不当使用或未经授权的情况下被访问、公开披露、使用、修改、损坏、丢失或泄漏。
</p>
<p>
2我们会使用不低于行业同行的加密技术、匿名化处理及相关合理可行的手段保护您的个人信息并使用安全保护机制防止您的个人信息遭到恶意攻击。
</p>
<p>
3我们会建立专门的安全部门、安全管理制度、数据安全流程保障您的个人信息安全。我们采取严格的数据使用和访问制度确保只有授权人员才可访问您的个人信息并适时对数据和技术进行安全审计。
</p>
<p>
4尽管已经采取了上述合理有效措施并已经遵守了相关法律规定要求的标准但请您理解由于技术的限制以及可能存在的各种恶意手段在互联网行业即便竭尽所能加强安全措施也不可能始终保证信息百分之百的安全我们将尽力确保您提供给我们的个人信息的安全性。
</p>
<p>
5您知悉并理解您接入我们的服务所用的系统和通讯网络有可能因我们可控范围外的因素而出现问题。因此我们强烈建议您采取积极措施保护个人信息的安全包括但不限于使用复杂密码、定期修改密码、不将自己的账号密码及相关个人信息透露给他人。
</p>
<p>
6我们会制定应急处理预案并在发生用户信息安全事件时立即启动应急预案努力阻止这些安全事件的影响和后果扩大。一旦发生用户信息安全事件泄露、丢失我们将按照法律法规的要求及时向您告知安全事件的基本情况和可能的影响、我们已经采取或将要采取的处置措施、您可自主防范和降低风险的建议、对您的补救措施。我们将及时将事件相关情况以推送通知、邮件、信函、短信及相关形式告知您难以逐一告知时我们会采取合理、有效的方式发布公告。同时我们还将按照相关监管部门要求上报用户信息安全事件的处置情况。
</p>
<p>
7您一旦离开“光环game”及相关服务浏览或使用其他网站、服务及内容资源我们将没有能力和直接义务保护您在光环game及相关服务之外的软件、网站提交的任何个人信息无论您登录、浏览或使用上述软件、网站是否基于“光环game”的链接或引导。
</p>
<p><b>6.管理您的个人信息</b></p>
<p>
我们非常重视您对个人信息的管理,并尽全力保护您的隐私,对于您个人信息的查询、访问、修改、删除、撤回同意授权、注销账号、投诉举报以及设置隐私功能的相关权利,以使您有能力保障您的隐私和信息安全。
</p>
<p><b>6.1 访问、删除、更正您的个人信息</b></p>
<p>1访问个人账号信息</p>
<p>a. 您可以查询、访问您的头像、用户名、简介、性别、生日、地区</p>
<p>b.您可以在光环game的“个人主页”中进行查询、访问。</p>
<p>2查询访问、更正、取消您关注账号、查询访问粉丝</p>
<p>a.进入“关注”在关注列表中查询、访问、取消关注您关注的账号。</p>
<p>b.您可以通过点击“设置”—点击“清理缓存”。</p>
<p>3访问隐私政策</p>
<p>
a.您可以在注册页面,或者在登录个人账号“设置”—“关于”查看本隐私政策的全部内容
</p>
<p>
b.请您了解本隐私政策中所述的“光环game”及相关服务可能会根据您所使用的手机型号、系统版本、软件应用程序版本、移动客户端等因素而有所不同。最终的产品和服务以您所使用的“光环game”软件及相关服务为准。
</p>
<p>6停止运营向您告知</p>
<p>
a.如我们停止运营,我们将及时停止收集您个人信息的活动,将停止运营的通知以逐一送达或公告的形式通知您,并对所持有的您的个人信息进行删除或匿名化处理。
</p>
<p><b>7.未成年人条款</b></p>
<p>
a.若您是未满18周岁的未成年人在使用“光环game”及相关服务前应在您的父母或其他监护人监护、指导下共同阅读并同意本隐私政策。
</p>
<p>
b.我们根据国家相关法律法规的规定保护未成年人的个人信息,只会在法律允许、父母或其他监护人明确同意或保护未成年人所必要的情况下收集、使用、储存、共享、转让或披露未成年人的个人信息;如果我们发现在未事先获得可证实的父母同意的情况下收集了未成年人的个人信息,则会设法尽快删除相关信息。
</p>
<p>
c.若您是未成年人的监护人,当您对您所监护的未成年人的个人信息有相关疑问时,请通过公司本隐私政策公示的联系方式与我们联系。
</p>
<p><b>8.隐私政策的修订和通知</b></p>
<p>
1为了给您提供更好的服务光环game及相关服务将不时更新与变化我们会适时对本隐私政策进行修订这些修订构成本隐私政策的一部分并具有等同于本隐私政策的效力未经您明确同意我们不会削减您依据当前生效的本隐私政策所应享受的权利。
</p>
<p>
2本隐私政策更新后我们会在光环game发出更新版本并在更新后的条款生效前通过公告或其他适当的方式提醒您更新的内容以便您及时了解本隐私政策的最新版本。
</p>
<p><b>9.联系我们</b></p>
<p>
如果您对我们的隐私政策及对您个人信息的处理有任何疑问、意见、建议、或投诉,请通过以下方式与我们联系
</p>
<p>
广州市来特悦游信息科技有限公司注册地址广州市天河区中山大道建工路13、15号3-6层5楼512房仅限办公用途
</p>
<p>在线客服QQ3624425003</p>
<p>信息保护事务联系电话020-85526920</p>
<p>在一般情况下我们会在15个工作日内对您的请求予以答复</p>
</div>
</body></html>

View File

@ -280,8 +280,12 @@ RE.replaceAllDfImage = function(imgRuleFlag, gifRuleFlag) {
i--;
} else {
if(img.src.indexOf(".gif") > 0) {
img.src = img.src.split("?")[0] + gifRuleFlag
if(gifRuleFlag.indexOf(",default") > 0) {
img.style.cssText = "max-width: 100%; display:block; margin:8px auto; height: auto;"
img.src = img.src.split("?")[0] + gifRuleFlag
}
} else {
img.style.cssText = "max-width: 100%; display:block; margin:8px auto; height: auto;"
img.src = img.src.split("?")[0] + imgRuleFlag
}
}
@ -297,7 +301,7 @@ RE.hideShowBigPic = function() {
var img = imgs[i];
var imageClassName = img.className;
if (imageClassName == "image-link" || img.className == "poster") continue;
if (img.src.indexOf(".gif") == -1) {
if(img.src.indexOf(",thumbnail") > 0 && img.src.indexOf(".gif") == -1) {
j++;
}
}
@ -323,6 +327,7 @@ RE.replaceDfImageByUrl = function(imgUrl, imgRuleFlag, gifRuleFlag) {
var imageClassName = img.className;
if (imageClassName == "image-link" || img.className == "poster") continue;
if (img.src.indexOf(imgUrl) != -1) {
img.style.cssText = "max-width: 100%; display:block; margin:8px auto; height: auto;"
if(img.src.indexOf(".gif") > 0) {
img.src = img.src.split("?")[0] + gifRuleFlag
} else {

View File

@ -0,0 +1,161 @@
<html lang="en"><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>光环助手软件许可及服务协议</title>
<style>
* {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
font-size: 16px;
}
</style>
</head>
<body>
<div class="content-area">
<p><b></b></p>
<p>
<b>光环</b>
<b>game</b>
<b>软件许可及服务协议</b>
</p>
<p><b>更新日期2020年11月20日</b></p>
<p><b>生效日期2020年11月30日</b></p>
<p><b>首部及导言</b></p>
<p>欢迎使用光环game软件许可及服务</p>
<p>
各位用户在使用光环game前请您务必审慎阅读、并充分理解本协议中的各项条款
<b>
特别是免除或者限制责任的条款,以及开通或使用某项服务的单独协议,并选择接受或不接受。
</b>
除非您已阅读并接受本协议所有条款,否则您无权下载、安装或使用本软件及相关服务。您的下载、安装、使用、登录等行为即视为您已阅读并同意上述协议的约束。
</p>
<p>如果您未满18周岁请在法定监护人的陪同下阅读本协议及其他上述协议。</p>
<p><b>一、权利声明</b></p>
<p>
“光环game”的一切知识产权以及与“光环game”相关的所有信息内容包括但不限于文字表述及其组合、图标、图饰、图像、图表、色彩、界面设计、版面框架、有关数据、附加程序、印刷材料或电子文档等均为光环game所有受著作权法和国际著作权条约以及其他知识产权法律法规的保护。
</p>
<p><b>二、软件使用规范</b></p>
<p>
2.1
本软件是基于Android安卓系统手机、平板电脑(PAD)等设备开发的一款软件,提供注册登录、观看视频、视频投稿等功能
</p>
<p>2.2 软件的下载、安装和使用</p>
<p>
本软件为免费软件用户可以非商业性、无限制数量地从光环game授权的渠道下载、安装及使用本软件。
</p>
<p>
<b>如果您从未经光环</b>
<b>game</b>
<b>授权的第三方获取本软件或与本软件名称相同的安装程序,光环</b>
<b>game</b>
<b>无法保证该软件能够正常使用,并对因此给您造成的损失不予负责。</b>
</p>
<p>2.3 软件的复制、分发和传播</p>
<p>
本产品以研究交流为目的。用户可以非商业性、无限制数量地复制、分发和传播本软件产品。但必须保证每一份复制、分发和传播都是完整和真实的,
包括所有有关本软件产品的软件、电子文档, 版权和商标,亦包括本协议。
</p>
<p>2.4 软件的更新</p>
<p>
为了改善用户体验、完善服务内容光环game将不断努力开发新的服务并为您不时提供软件更新这些更新可能会采取软件替换、修改、功能强化、版本升级等形式。为了保证本软件及服务的安全性和功能的一致性光环game有权不经向您特别通知而对软件进行更新或者对软件的部分功能效果进行改变或限制。本软件新版本发布后旧版本的软件可能无法使用。光环game不保证旧版本软件继续可用及相应的客户服务请您随时核对并下载最新版本。
</p>
<p><b>三、用户使用须知</b></p>
<p>3.1 您理解并同意:</p>
<p>
为了向您提供有效的服务,本软件会利用您移动通讯终端的处理器和带宽等资源。本软件使用过程中可能产生数据流量的费用,用户需自行向运营商了解相关资费信息,并自行承担相关费用.
</p>
<p>3.2您理解并同意:</p>
<p>
<b>
如果因您不正当使用本软件造成了不良影响,或因使用本软件造成的包括但不限于数据异常等问题,均由使用者自行承担,光环团队不对任意类型的使用结果承担责任;
</b>
</p>
<p>3.3您理解并同意:</p>
<p>
本软件不含任何破坏用户移动通讯设备数据和获取用户隐私信息的恶意代码,不会泄露用户的个人信息和隐私;
</p>
<p>3.4您理解并同意:</p>
<p>
<b>
对于包括但不限于互联网网络故障、计算机故障、手机故障或病毒、信息损坏或丢失、计算机系统问题,或其它任何基于不可抗力原因而产生的损失,光环团队不承担任何责任。
</b>
</p>
<p>3.5您理解并同意:</p>
<p>光环game发布、收录的视频均不代表光环立场。</p>
<p>3.6您理解并同意:</p>
<p>
用户应在遵守法律及本协议的前提下使用本软件。用户无权实施包括但不限于下列行为:
</p>
<p>3.6.1 不得删除或者改变本软件上的所有权利管理电子信息</p>
<p>
3.6.2
不得故意避开或者破坏著作权人为保护本软件著作权而采取的技术措施;&nbsp;
</p>
<p>3.6.3 用户不得利用本软件误导、欺骗他人;&nbsp;</p>
<p>
3.6.4
违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机信息系统不能正常运行;&nbsp;
</p>
<p>3.6.5 未经允许,进入计算机信息网络或者使用计算机信息网络资源;&nbsp;</p>
<p>3.6.6 未经允许,对计算机信息网络功能进行删除、修改或者增加;&nbsp;</p>
<p>
3.6.7
未经允许,对计算机信息网络中存储、处理或者传输的数据和应用程序进行删除、修改或者增加;&nbsp;
</p>
<p>
3.6.8
破坏本软件系统或网站的正常运行,故意传播计算机病毒等破坏性程序;&nbsp;
</p>
<p>3.6.9 其他任何危害计算机信息网络安全的行为。&nbsp;</p>
<p>3.7 您理解并同意:</p>
<p>
本软件经过详细的测试,但不能保证与所有的软硬件系统完全兼容,不能保证本软件完全没有错误。如果出现不兼容及软件错误的情况,用户可通过各反馈途径将情况告知光环团队,获得技术支持。如果无法解决兼容性问题,用户可以删除本软件。
</p>
<p><b>四、争议解决处理</b></p>
<p>
本《协议》的解释、效力及纠纷的解决适用于中华人民共和国法律。若用户和光环game之间发生任何纠纷或争议首先应友好协商解决协商不成的用户在此完全同意将纠纷或争议提交光环game所在地法院管辖
</p>
<p>
<b></b>
<b>、关于获取手机设备信息的说明</b>
</p>
<p>
1为方便区分每个用户的个人信息等本软件需获取用户的手机设备信息用于用户登录、视频评论互动交流等用户相关的行为
</p>
<p>
2为了保障软件与服务的安全运行我们会收集您的硬件型号、操作系统版本号、国际移动设备识别码、唯一设备标识符、网络设备硬件地址、IP
地址、WLAN接入点、蓝牙、基站、软件版本号、网络接入方式、类型、状态、网络质量数据、操作、使用、服务日志。
</p>
<p>
3为了预防恶意程序及安全运营所必需我们会收集安装的应用信息或正在运行的进程信息、应用程序的总体运行、使用情况与频率、应用崩溃情况、总体安装使用情况、性能数据、应用来源。
</p>
<p>
4我们可能使用您的账户信息、设备信息、服务日志信息以及我们关联公司、合作方在获得您授权或依法可以共享的信息用于判断账户安全、进行身份验证、检测及防范安全事件。
</p>
<p>5具体会发生获取手机设备信息场景如下说明</p>
<p>
&nbsp;&nbsp;1) 首次启动光环game&nbsp;2) 评论详情-发送评论功能&nbsp;3)
视频投稿-上传视频功能&nbsp;4) 视频详情-关注up主功能
</p>
<p><b>七、其他</b></p>
<p>
7.1
本协议所有条款的标题仅为阅读方便,本身并无实际涵义,不能作为本协议涵义解释的依据。
</p>
<p>
&nbsp;7.2
如果本协议中的任何条款无论因何种原因完全或部分无效或不具有执行力,或违反任何适用的法律,则该条款被视为删除,但本协议的其余条款仍应有效并且有约束力。
</p>
<p>
&nbsp;7.3
光环有权随时根据有关法律、法规的变化以及公司经营状况和经营策略的调整等修改本协议。修改后的协议会在软件设置内发布。
当发生有关争议时,以最新的协议文本为准。如果不同意改动的内容,用户可以自行删除本软件。如果用户继续使用本软件,则视为您接受本协议的变动。
</p>
<p>&nbsp;7.4 光环在法律允许的最大范围内对本协议拥有解释权与修改权。</p>
</div>
</body></html>

View File

@ -151,7 +151,7 @@ class CircleImageView extends ImageView {
private void updateRadialGradient(int diameter) {
mRadialGradient = new RadialGradient(diameter / 2, diameter / 2,
mShadowRadius, new int[]{FILL_SHADOW_COLOR, Color.TRANSPARENT},
mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT },
null, Shader.TileMode.CLAMP);
mShadowPaint.setShader(mRadialGradient);
}

View File

@ -44,9 +44,8 @@ import androidx.annotation.RestrictTo;
import androidx.core.util.Preconditions;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import com.gh.gamecenter.common.R;
import com.gh.gamecenter.core.HaloApp;
import com.gh.gamecenter.R;
import com.halo.assistant.HaloApp;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -812,7 +811,7 @@ public class CircularProgressDrawable extends Drawable implements Animatable {
void drawImageToRing(Canvas canvas, Rect bounds, Animator animator) {
Bitmap bitmap;
Resources resources = HaloApp.getInstance().getResources();
Resources resources = HaloApp.getInstance().getApplication().getResources();
int offset = (int) (mInterpolatedTime * 100) / 2;
if (!animator.isRunning()) {

View File

@ -137,8 +137,8 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
// refresh was triggered.
private boolean mReturningToStart;
private final DecelerateInterpolator mDecelerateInterpolator;
private static final int[] LAYOUT_ATTRS = new int[]{
android.R.attr.enabled
private static final int[] LAYOUT_ATTRS = new int[] {
android.R.attr.enabled
};
CircleImageView mCircleView;
@ -250,9 +250,9 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
* up rather than clipped.
* @param start The offset in pixels from the top of this view at which the
* progress spinner should appear.
* @param end The offset in pixels from the top of this view at which the
* progress spinner should come to rest after a successful swipe
* gesture.
* @param end The offset in pixels from the top of this view at which the
* progress spinner should come to rest after a successful swipe
* gesture.
*/
public void setProgressViewOffset(boolean scale, int start, int end) {
mScale = scale;
@ -265,7 +265,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
/**
* @return The offset in pixels from the top of this view at which the progress spinner should
* appear.
* appear.
*/
public int getProgressViewStartOffset() {
return mOriginalOffsetTop;
@ -273,7 +273,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
/**
* @return The offset in pixels from the top of this view at which the progress spinner should
* come to rest after a successful swipe gesture.
* come to rest after a successful swipe gesture.
*/
public int getProgressViewEndOffset() {
return mSpinnerOffsetEnd;
@ -288,9 +288,9 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
* @param scale Set to true if there is no view at a higher z-order than where the progress
* spinner is set to appear. Setting it to true will cause indicator to be scaled
* up rather than clipped.
* @param end The offset in pixels from the top of this view at which the
* progress spinner should come to rest after a successful swipe
* gesture.
* @param end The offset in pixels from the top of this view at which the
* progress spinner should come to rest after a successful swipe
* gesture.
*/
public void setProgressViewEndTarget(boolean scale, int end) {
mSpinnerOffsetEnd = end;
@ -304,6 +304,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
* @param slingshotDistance The distance in pixels that the refresh indicator can be pulled
* beyond its resting position. Use
* {@link #DEFAULT_SLINGSHOT_DISTANCE} to reset to the default value.
*
*/
public void setSlingshotDistance(@Px int slingshotDistance) {
mCustomSlingshotDistance = slingshotDistance;
@ -453,7 +454,6 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
/**
* Pre API 11, this does an alpha animation.
*
* @param progress
*/
void setAnimationProgress(float progress) {
@ -575,7 +575,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
/**
* @return Whether the SwipeRefreshWidget is actively showing refresh
* progress.
* progress.
*/
public boolean isRefreshing() {
return mRefreshing;
@ -666,7 +666,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
/**
* @return Whether it is possible for the child view of this layout to
* scroll up. Override this if the child view is a custom view.
* scroll up. Override this if the child view is a custom view.
*/
public boolean canChildScrollUp() {
if (mChildScrollUpCallback != null) {
@ -681,7 +681,6 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
/**
* Set a callback to override {@link SwipeRefreshLayout#canChildScrollUp()} method. Non-null
* callback will return the value provided by the callback and ignore all internal logic.
*
* @param callback Callback that should be called when canChildScrollUp() is called.
*/
public void setOnChildScrollUpCallback(@Nullable OnChildScrollUpCallback callback) {
@ -830,7 +829,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
@Override
public void onNestedScroll(final View target, final int dxConsumed, final int dyConsumed,
final int dxUnconsumed, final int dyUnconsumed) {
final int dxUnconsumed, final int dyUnconsumed) {
// Dispatch up to the nested parent first
dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
mParentOffsetInWindow);
@ -876,7 +875,7 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow) {
int dyUnconsumed, int[] offsetInWindow) {
return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, offsetInWindow);
}
@ -889,13 +888,13 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
@Override
public boolean onNestedPreFling(View target, float velocityX,
float velocityY) {
float velocityY) {
return dispatchNestedPreFling(velocityX, velocityY);
}
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY,
boolean consumed) {
boolean consumed) {
return dispatchNestedFling(velocityX, velocityY, consumed);
}
@ -923,8 +922,8 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
float slingshotDist = mCustomSlingshotDistance > 0
? mCustomSlingshotDistance
: (mUsingCustomStart
? mSpinnerOffsetEnd - mOriginalOffsetTop
: mSpinnerOffsetEnd);
? mSpinnerOffsetEnd - mOriginalOffsetTop
: mSpinnerOffsetEnd);
float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2)
/ slingshotDist);
float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
@ -1147,13 +1146,13 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
};
private void startScaleDownReturnToStartAnimation(int from,
AnimationListener listener) {
AnimationListener listener) {
mFrom = from;
mStartingScale = mCircleView.getScaleX();
mScaleDownToStartAnimation = new Animation() {
@Override
public void applyTransformation(float interpolatedTime, Transformation t) {
float targetScale = (mStartingScale + (-mStartingScale * interpolatedTime));
float targetScale = (mStartingScale + (-mStartingScale * interpolatedTime));
setAnimationProgress(targetScale);
moveToStart(interpolatedTime);
}
@ -1204,7 +1203,8 @@ public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingPare
* is called to allow the implementer to override its behavior.
*
* @param parent SwipeRefreshLayout that this callback is overriding.
* @param child The child view of SwipeRefreshLayout.
* @param child The child view of SwipeRefreshLayout.
*
* @return Whether it is possible for the child view of parent layout to scroll up.
*/
boolean canChildScrollUp(@NonNull SwipeRefreshLayout parent, @Nullable View child);

View File

@ -0,0 +1,124 @@
package com.gh.base;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.util.Log;
import com.gh.common.constant.Config;
import com.gh.common.util.DataCollectionUtils;
import com.gh.common.util.DataUtils;
import com.gh.gamecenter.SplashScreenActivity;
import com.lightgame.config.CommonDebug;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.AppManager;
import com.lightgame.utils.Utils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class AppUncaughtHandler implements UncaughtExceptionHandler {
private Context mContext;
public AppUncaughtHandler(Context context) {
// 获取系统默认的UncaughtException处理器
mContext = context;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Utils.toast(mContext.getApplicationContext(), "\"光环助手\"发生错误");
Looper.loop();
}
});
saveLocalLog(mContext, ex);
restart(mContext);
}
public static void restart(final Context context) {
// 防止重复奔溃导致助手一直重启20秒内不做处理
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
long curTime = System.currentTimeMillis();
long time = sp.getLong("last_restart_time", 0);
if (curTime - time > 20 * 1000) {
sp.edit().putLong("last_restart_time", curTime).apply();
Intent intent = new Intent(context, SplashScreenActivity.class);
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// 退出程序并重启
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, curTime + 3000, restartIntent); // 1秒钟后重启应用
}
//error restart
// System.exit(2);
AppManager.getInstance().finishAllActivity();
}
// 保存log到本地
public static void saveLocalLog(Context context, Throwable ex) {
String errorMsg = Log.getStackTraceString(ex);
Config.setExceptionMsg(errorMsg);
// 保存到本地
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
File file = new File(FileUtils.getLogPath(context.getApplicationContext(),
format.format(new Date()) + "_gh_assist" + ".log"));
FileWriter writer = null;
try {
file.createNewFile();
writer = new FileWriter(file);
writer.write(errorMsg);
writer.flush();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 下次应用启动再上报
*
* @param context
* @param throwable
*/
public static void reportException(Context context, Throwable throwable) {
CommonDebug.logMethodWithParams(context, "ERRMSG", throwable);
// 上传错误数据
try {
DataCollectionUtils.uploadError(context, Log.getStackTraceString(throwable));
} catch (Exception e) {
}
DataUtils.onError(context, throwable);
}
}

View File

@ -0,0 +1,341 @@
package com.gh.base;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import com.gh.base.fragment.BaseFragment;
import com.gh.common.constant.Constants;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.EntranceUtils;
import com.gh.common.util.ExtensionsKt;
import com.gh.common.util.MtaHelper;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.RunningUtils;
import com.gh.common.util.ShareUtils;
import com.gh.common.util.StringUtils;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.LoginActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.eventbus.EBShowDialog;
import com.lightgame.BaseAppCompatActivity;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.Utils;
import com.tencent.tauth.Tencent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.json.JSONObject;
import java.lang.ref.WeakReference;
import java.util.List;
import butterknife.ButterKnife;
import pub.devrel.easypermissions.EasyPermissions;
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
/**
* 只提供基础的服务(EventBus/ButterKnife/Share/GlobalDialog/Permissions)
* <p>
* 需要工具栏的页面请继承{@link ToolBarActivity}
*/
public abstract class BaseActivity extends BaseAppCompatActivity implements EasyPermissions.PermissionCallbacks {
// global dialog key
public final static String DOWNLOAD_HIJACK = "hijack";
public final static String LOGIN_EXCEPTION = "loginException";
public final static String PLUGGABLE = "plugin";
@NonNull
protected String mEntrance;
private boolean mIsExistLogoutDialog;
protected final Handler mBaseHandler = new BaseHandler(this);
protected static class BaseHandler extends Handler {
private final WeakReference<BaseActivity> mActivityWeakReference;
BaseHandler(BaseActivity activity) {
mActivityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
BaseActivity activity = mActivityWeakReference.get();
if (activity != null) activity.handleMessage(msg);
}
}
protected void handleMessage(Message msg) {
}
//接收QQ或者QQ空间分享回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
ExtensionsKt.tryCatchInRelease(() -> {
if (requestCode == com.tencent.connect.common.Constants.REQUEST_QQ_SHARE
|| requestCode == com.tencent.connect.common.Constants.REQUEST_QZONE_SHARE) {
Tencent.onActivityResultData(requestCode, resultCode, data, ShareUtils.getInstance(this).QqShareListener);
}
return null;
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (useEventBus()) EventBus.getDefault().register(this);
if (useButterKnife()) ButterKnife.bind(this);
mEntrance = getIntent().getStringExtra(KEY_ENTRANCE);
if (TextUtils.isEmpty(mEntrance)) {
mEntrance = Constants.ENTRANCE_UNKNOWN;
}
if (BuildConfig.DEBUG) {
Utils.log("ACTIVITY_ENTRANCE -> " + mEntrance);
}
}
@SuppressWarnings("ConstantConditions")
@Override
public void setContentView(View view) {
if (BuildConfig.DEBUG || BuildConfig.BUILD_TIME != 0) {
view = getRootViewWithEnvIndicator(view);
}
super.setContentView(view);
}
@Override
protected void onDestroy() {
if (useEventBus()) EventBus.getDefault().unregister(this);
mBaseHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
public void toast(String msg) {
Utils.toast(this, msg);
}
public void toast(int msg) {
toast(getString(msg));
}
public void showShare(String url,
String icon,
String shareTitle,
String shareSummary,
ShareUtils.ShareEntrance shareEntrance, String id) {
ShareUtils.getInstance(this).showShareWindows(this,
getWindow().getDecorView(),
url,
icon,
shareTitle,
shareSummary,
shareEntrance, id);
if (shareEntrance == ShareUtils.ShareEntrance.game || shareEntrance == ShareUtils.ShareEntrance.plugin) {
MtaHelper.onEvent("内容分享", "内容分享", shareTitle + shareSummary);
} else {
MtaHelper.onEvent("内容分享", "内容分享", shareTitle);
}
}
private View getRootViewWithEnvIndicator(View view) {
RelativeLayout screenRootView = new RelativeLayout(this);
screenRootView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
LinearLayout ll = new LinearLayout(this);
TextView tv = new TextView(this);
String envText = "正式环境";
tv.setBackground(ContextCompat.getDrawable(this, R.color.theme));
if (BuildConfig.FLAVOR.equals("internal")) {
envText = "测试环境";
tv.setBackground(ContextCompat.getDrawable(this, R.color.red));
}
tv.setText(envText);
tv.setGravity(Gravity.CENTER);
tv.setTextColor(Color.WHITE);
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
tv.measure(0, 0);
tv.setAlpha(0.15F);
int height = tv.getMeasuredHeight();
int width = tv.getMeasuredWidth();
tv.setPadding(DisplayUtils.dip2px(20), 0, DisplayUtils.dip2px(20), 0);
ll.setTranslationX(DisplayUtils.dip2px(20));
ll.setRotation(45);
ll.addView(tv);
ll.setPadding(0, (width - height) / 2, 0, (width - height) / 2);
if (BuildConfig.DEBUG) {
tv.setOnLongClickListener(v -> {
EntranceUtils.saveShortcut(this.getClass().getName(), getIntent().getExtras());
return true;
});
}
screenRootView.addView(view);
screenRootView.addView(ll);
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) ll.getLayoutParams();
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
view.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
return screenRootView;
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(final EBShowDialog showDialog) {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)
&& this.getClass().getName().equals(RunningUtils.getTopActivity(this))) {
if (DOWNLOAD_HIJACK.equals(showDialog.getType())) {
DialogUtils.showQqSessionDialog(this);// 建议用户联系客服
} else if (PLUGGABLE.equals(showDialog.getType())) {
DialogUtils.showPluginDialog(this, () -> {
if (FileUtils.isEmptyFile(showDialog.getPath())) {
toast(R.string.install_failure_hint);
} else {
PackageInstaller.uninstall(BaseActivity.this, showDialog.getPath());
}
});
} else if (LOGIN_EXCEPTION.equals(showDialog.getType())) {
if (mIsExistLogoutDialog) return;
mIsExistLogoutDialog = true;
try {
JSONObject object = new JSONObject(showDialog.getPath());
JSONObject device = object.getJSONObject("device");
String model = device.getString("model");
DialogUtils.showAlertDialog(this, "你的账号已在另外一台设备登录"
, StringUtils.buildString("", model, "")
, "知道了", "重新登录"
, null
, () -> startActivity(LoginActivity.getIntent(BaseActivity.this,
"你的账号已在另外一台设备登录多设备-重新登录"))
);
mBaseHandler.postDelayed(() -> mIsExistLogoutDialog = false, 5000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
protected void onPause() {
super.onPause();
if (isFinishing()) {
onFinish();
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
if (fragment.isAdded() && fragment instanceof BaseFragment) {
((BaseFragment) fragment).onParentActivityFinish();
}
}
}
}
@Override
protected void onResume() {
super.onResume();
}
/**
* 此回调可用于确认当前 activity 已经执行了 finish() 方法并处于 isFinishing 状态
*/
protected void onFinish() {
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
}
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
}
protected void setStatusBarColor(int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(color);
}
}
/**
* 提供当前 activity 的中文名 (不重载的话为类名)
*/
public String getActivityNameInChinese() {
return getClass().getSimpleName();
}
/**
* @param entrance 上一个页面的链式入口名称
* @param path 当前页面名称
* @return 完整的链式入口名称
*/
public static String mergeEntranceAndPath(String entrance, String path) {
if (TextUtils.isEmpty(entrance) && TextUtils.isEmpty(path)) return "";
if (TextUtils.isEmpty(entrance) && !TextUtils.isEmpty(path)) {
return StringUtils.buildString("(", path, ")");
}
if (!TextUtils.isEmpty(entrance) && TextUtils.isEmpty(path)) {
return entrance;
}
return StringUtils.buildString(entrance, "+(", path, ")");
}
protected boolean useEventBus() {
return true;
}
protected boolean useButterKnife() {
return true;
}
@Override
public Resources getResources() {
Resources resources = super.getResources();
if (resources.getConfiguration().fontScale != 1.0f) {
Configuration configuration = resources.getConfiguration();
configuration.fontScale = 1.0f;
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
}
return resources;
}
}

View File

@ -0,0 +1,133 @@
package com.gh.base;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import com.gh.base.adapter.FragmentAdapter;
import com.gh.base.fragment.BaseFragment_TabLayout;
import com.gh.common.view.TabIndicatorView;
import com.gh.gamecenter.R;
import com.google.android.material.tabs.TabLayout;
import com.lightgame.view.NoScrollableViewPager;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
/**
* Created by khy on 15/03/18.
*/
public abstract class BaseActivity_TabLayout extends ToolBarActivity implements ViewPager.OnPageChangeListener {
public static final String PAGE_INDEX = "PAGE_INDEX";
@BindView(R.id.activity_tab_layout)
protected TabLayout mTabLayout;
@BindView(R.id.activity_view_pager)
protected NoScrollableViewPager mViewPager;
@BindView(R.id.activity_tab_indicator)
protected TabIndicatorView mTabIndicatorView;
protected List<Fragment> mFragmentsList;
protected List<String> mTabTitleList;
protected int mCheckedIndex = 0;
protected abstract void initFragmentList(List<Fragment> fragments);
protected abstract void initTabTitleList(List<String> tabTitleList);
protected int provideIndicatorWidth() {
return 20;
}
protected View provideTabView(int position, String tabTitle) {
return null;
}
@Override
protected int getLayoutId() {
return R.layout.activity_tablayout_viewpager;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent() != null) mCheckedIndex = getIntent().getIntExtra(PAGE_INDEX, 0);
mTabTitleList = new ArrayList<>();
initTabTitleList(mTabTitleList);
mFragmentsList = new ArrayList<>(restoreFragments());
if (mFragmentsList.isEmpty() || mFragmentsList.size() != mTabTitleList.size()) {
mFragmentsList.clear();
initFragmentList(mFragmentsList);
}
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
mViewPager.addOnPageChangeListener(this);
mViewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), mFragmentsList, mTabTitleList));
mViewPager.setCurrentItem(mCheckedIndex);
mTabLayout.setupWithViewPager(mViewPager);
mTabIndicatorView.setupWithTabLayout(mTabLayout);
mTabIndicatorView.setupWithViewPager(mViewPager);
mTabIndicatorView.setIndicatorWidth(provideIndicatorWidth());
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
TabLayout.Tab tab = mTabLayout.getTabAt(i);
if (tab == null) continue;
String tabTitle = tab.getText() != null ? tab.getText().toString() : "";
View tabView = provideTabView(i, tabTitle);
if (tabView == null)
tabView = BaseFragment_TabLayout.createDefaultTabCustomView(tabTitle);
tab.setCustomView(tabView);
}
BaseFragment_TabLayout.initTabStyle(mTabLayout, mCheckedIndex);
}
private ArrayList<Fragment> restoreFragments() {
String tag = "android:switcher:" + mViewPager.getId() + ":";
ArrayList<Fragment> fragments = new ArrayList<>();
int childCount = mTabTitleList.size();
for (int index = 0; index < childCount; index++) {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag + index);
if (fragment != null) {
fragments.add(fragment);
}
}
return fragments;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
}

View File

@ -1,10 +1,9 @@
package com.gh.gamecenter.common.base;
import android.view.View;
package com.gh.base;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import com.gh.gamecenter.common.callback.OnListClickListener;
import butterknife.ButterKnife;
/**
* 目前仅提供butterknife bind方法
@ -21,6 +20,7 @@ public abstract class BaseRecyclerViewHolder<T> extends RecyclerView.ViewHolder
public BaseRecyclerViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
/**

View File

@ -1,602 +1,221 @@
package com.gh.base
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.webkit.JavascriptInterface
import android.widget.CheckBox
import android.widget.FrameLayout
import android.widget.TextView
import androidx.lifecycle.Observer
import butterknife.OnClick
import com.gh.common.util.DialogUtils
import com.gh.common.util.NewLogUtils
import com.gh.common.util.MtaHelper
import com.gh.common.view.RichEditor
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.qa.editor.*
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.entity.MyVideoEntity
import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity
import com.gh.gamecenter.qa.editor.GameActivity
import com.gh.gamecenter.qa.editor.InsertAnswerWrapperActivity
import com.gh.gamecenter.qa.editor.InsertArticleWrapperActivity
import com.gh.gamecenter.qa.editor.VideoActivity
import com.gh.gamecenter.qa.entity.AnswerEntity
import com.gh.gamecenter.qa.entity.ArticleEntity
import com.gh.gamecenter.qa.entity.EditorInsertEntity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.UploadManager
import com.google.gson.JsonObject
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.lightgame.view.CheckableImageView
import io.reactivex.disposables.Disposable
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
import kotterknife.bindView
// TODO: 移动到module_bbs模块
abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarActivity(),
KeyboardHeightObserver, UploadVideoListener {
abstract class BaseRichEditorActivity : ToolBarActivity() {
lateinit var mRichEditor: RichEditor
val mRichEditor by bindView<RichEditor>(R.id.rich_editor)
val mDraftBtn by bindView<TextView>(R.id.draft_btn)
private lateinit var mEditorTextNumTv: TextView
private lateinit var mEditorFont: CheckableImageView
private lateinit var mEditorLink: CheckableImageView
private lateinit var mEditorFontBold: CheckableImageView
private lateinit var mEditorFontItalic: CheckableImageView
private lateinit var mEditorFontStrikeThrough: CheckableImageView
private lateinit var mEditorFontUnderline: CheckableImageView
private lateinit var mEditorParagraphH1: CheckableImageView
private lateinit var mEditorParagraphH2: CheckableImageView
private lateinit var mEditorParagraphH3: CheckableImageView
private lateinit var mEditorParagraphH4: CheckableImageView
private lateinit var mEditorParagraphQuote: CheckableImageView
private lateinit var mEditorFontContainer: View
private lateinit var mEditorParagraphContainer: View
private lateinit var mEditorLinkContainer: View
private lateinit var mEditorInsertDetailContainer: View
private lateinit var mTagsContainer: FrameLayout
private lateinit var mUploadVideoGuideContainer: View
protected lateinit var mOriginalCb: CheckBox
private lateinit var mOriginalTipsContainer: View
private lateinit var mOriginalTipsClose: TextView
private val mEditorTextNumTv by bindView<TextView>(R.id.editorTextNumTv)
private val mEditorFont by bindView<CheckableImageView>(R.id.editor_font)
private val mEditorLink by bindView<CheckableImageView>(R.id.editor_link)
private val mEditorParagraph by bindView<CheckableImageView>(R.id.editor_paragraph)
private val mEditorFontBold by bindView<CheckableImageView>(R.id.editor_font_bold)
private val mEditorFontItalic by bindView<CheckableImageView>(R.id.editor_font_italic)
private val mEditorFontStrikeThrough by bindView<CheckableImageView>(R.id.editor_font_strikethrough)
private val mEditorParagraphH1 by bindView<CheckableImageView>(R.id.editor_paragraph_h1)
private val mEditorParagraphH2 by bindView<CheckableImageView>(R.id.editor_paragraph_h2)
private val mEditorParagraphH3 by bindView<CheckableImageView>(R.id.editor_paragraph_h3)
private val mEditorParagraphH4 by bindView<CheckableImageView>(R.id.editor_paragraph_h4)
private val mEditorParagraphQuote by bindView<CheckableImageView>(R.id.editor_paragraph_quote)
private val mEditorFontContainer by bindView<View>(R.id.editor_font_container)
private val mEditorParagraphContainer by bindView<View>(R.id.editor_paragraph_container)
private val mEditorLinkContainer by bindView<View>(R.id.editor_link_container)
private val mEditorInsertDetail by bindView<View>(R.id.editor_insert_detail)
private var mCurrentParagraphStyle = ""
private var mIsExtendedKeyboardShow = false
private var mAgreePostPic: Boolean = false
private var mGuideDisposable: Disposable? = null
protected lateinit var mViewModel: VM
protected var mIsKeyBoardShow = false
private var mKeyboardHeightProvider: KeyboardHeightProvider? = null
private var mMaxUploadVideoGuideCount = 2
val FILE_HOST = "file:///"
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
DialogUtils.fixWebViewKeyboardNotWorking(this)
if (resultCode != Activity.RESULT_OK) return
val insertData: EditorInsertEntity?
var insertData: EditorInsertEntity? = null
when (requestCode) {
INSERT_ANSWER_CODE -> {
val answer =
data?.getParcelableExtra<AnswerEntity>(AnswerEntity::class.java.simpleName)
if (answer != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(answer)
mRichEditor.insertCustomStyleLink(insertData)
}
val answer = data?.getParcelableExtra<AnswerEntity>(AnswerEntity::class.java.simpleName)
if (answer != null) insertData = EditorInsertEntity.transform(answer)
}
INSERT_ARTICLE_CODE -> {
val article =
data?.getParcelableExtra<ArticleEntity>(ArticleEntity::class.java.simpleName)
if (article != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(article)
mRichEditor.insertCustomStyleLink(insertData)
}
val article = data?.getParcelableExtra<ArticleEntity>(ArticleEntity::class.java.simpleName)
if (article != null) insertData = EditorInsertEntity.transform(article)
}
INSERT_GAME_CODE -> {
val game = data?.getParcelableExtra<GameEntity>(GameEntity::class.java.simpleName)
if (game != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(game)
mRichEditor.insertCustomStyleLink(insertData)
}
if (game != null) insertData = EditorInsertEntity.transform(game)
}
INSERT_GAME_COLLECTION_CODE -> {
val gameCollectionEntity =
data?.getParcelableExtra<GamesCollectionEntity>(GamesCollectionEntity::class.java.simpleName)
if (gameCollectionEntity != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(gameCollectionEntity)
mRichEditor.insertCustomStyleLink(insertData)
}
}
REQUEST_CODE_IMAGE -> {
if (data != null) mViewModel.uploadPic(data)
}
INSERT_MEDIA_VIDEO_CODE -> {
val localVideoList =
data?.getParcelableArrayListExtra<LocalVideoEntity>(LocalVideoEntity::class.java.name)
?: arrayListOf()
if (localVideoList.isNotEmpty()) {
mRichEditor.focusEditor()
uploadVideo(localVideoList)
}
}
REQUEST_CODE_IMAGE_CROP -> {
val imagePath = data?.getStringExtra(CropImageActivity.RESULT_CLIP_PATH)
if (!imagePath.isNullOrEmpty()) {
mViewModel.uploadPoster(imagePath)
}
}
INSERT_VIDEO_CODE -> {
val videoEntity = data?.getParcelableExtra<MyVideoEntity>(MyVideoEntity::class.java.simpleName)
if (videoEntity != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(videoEntity)
mRichEditor.insertCustomStyleLink(insertData)
}
VideoActivity.INSERT_VIDEO_CODE -> {
val video = data?.getParcelableExtra<MyVideoEntity>(MyVideoEntity::class.java.simpleName)
if (video != null) mRichEditor.insertCustomVideo(video)
return
}
}
closeExtendedKeyboard()
AppExecutor.uiExecutor.executeWithDelay(Runnable {
Util_System_Keyboard.showSoftKeyboard(this)
}, 100)
mRichEditor.insertCustomStyleLink(insertData)
}
private fun uploadVideo(localVideoList: ArrayList<LocalVideoEntity>) {
mViewModel.localVideoList.addAll(localVideoList)
runOnIoThread {
localVideoList.forEach {
if (it.poster.startsWith("http")) {
runOnUiThread {
mRichEditor.focusEditor()
mRichEditor.insertPlaceholderVideo(it.id, it.poster)
}
} else {
val videoThumbnail = BitmapUtils.getVideoThumbnail(it.filePath)
val filePath = "${cacheDir.absolutePath}${File.separator}${it.id}.webp"
BitmapUtils.saveBitmap(videoThumbnail, filePath)
it.poster = filePath
runOnUiThread {
mRichEditor.focusEditor()
mRichEditor.insertPlaceholderVideo(it.id, "$FILE_HOST${it.poster}")
}
}
}
mViewModel.uploadVideo()
}
}
private fun findView() {
mRichEditor = findViewById(R.id.rich_editor)
mEditorTextNumTv = findViewById(R.id.editorTextNumTv)
mEditorFont = findViewById(R.id.editor_font)
mEditorLink = findViewById(R.id.editor_link)
mEditorFontBold = findViewById(R.id.editor_font_bold)
mEditorFontItalic = findViewById(R.id.editor_font_italic)
mEditorFontStrikeThrough = findViewById(R.id.editor_font_strikethrough)
mEditorFontUnderline = findViewById(R.id.editor_font_underline)
mEditorParagraphH1 = findViewById(R.id.editor_paragraph_h1)
mEditorParagraphH2 = findViewById(R.id.editor_paragraph_h2)
mEditorParagraphH3 = findViewById(R.id.editor_paragraph_h3)
mEditorParagraphH4 = findViewById(R.id.editor_paragraph_h4)
mEditorParagraphQuote = findViewById(R.id.editor_paragraph_quote)
mEditorFontContainer = findViewById(R.id.editor_font_container)
mEditorParagraphContainer = findViewById(R.id.editor_paragraph_container)
mEditorLinkContainer = findViewById(R.id.editor_link_container)
mEditorInsertDetailContainer = findViewById(R.id.editor_insert_detail_container)
mTagsContainer = findViewById(R.id.tagsContainer)
mUploadVideoGuideContainer = findViewById(R.id.uploadVideoGuideContainer)
mOriginalCb = findViewById(R.id.originalCb)
mOriginalTipsContainer = findViewById(R.id.originalTipsContainer)
mOriginalTipsClose = findViewById(R.id.originalTipsClose)
}
@SuppressLint("AddJavascriptInterface", "ClickableViewAccessibility")
@SuppressLint("AddJavascriptInterface")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
findView()
onRichClick()
mViewModel = provideViewModel()
mViewModel.setUploadVideoListener(this)
mKeyboardHeightProvider = KeyboardHeightProvider(this)
mRichEditor.post { mKeyboardHeightProvider?.start() }
mRichEditor.enableForceDark(DarkModeUtils.isDarkModeOn(this))
mRichEditor.setEditorBackgroundColor(R.color.background_white.toColor(this))
mRichEditor.setEditorFontColor(if (mIsDarkModeOn) Color.parseColor("#C2C2C2") else Color.parseColor("#4A4A4A"))
mRichEditor.setPadding(20, 15, 20, 15)
// 防止个别手机在Js里无法获取粘贴内容
mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener")
mRichEditor.addJavascriptInterface(OnCursorChangeListener(), "OnCursorChangeListener")
mRichEditor.addJavascriptInterface(
OnEditorTextChangeListener(),
"OnEditorTextChangeListener"
)
mRichEditor.addJavascriptInterface(OnVideoListener(), "onVideoListener")
mRichEditor.addJavascriptInterface(
OnQuoteCountChangeListener(),
"OnQuoteCountChangeListener"
)
mRichEditor.addJavascriptInterface(OnEditorTextChangeListener(), "OnEditorTextChangeListener")
mRichEditor.setInputEnabled(true)
mRichEditor.setPadding(16, 12, 16, 12)
mRichEditor.setOnTouchListener { _, _ ->
if (mIsExtendedKeyboardShow) {
closeExtendedKeyboard()
Util_System_Keyboard.showSoftKeyboard(this)
//是否消费事件根据mRichEditor是否含有焦点决定mRichEditor没有焦点则不消费事件
mRichEditor.hasFocus()
} else false
}
mOriginalCb.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
mOriginalTipsContainer.alpha = 0f
mOriginalTipsContainer.visibility = View.VISIBLE
ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 0f, 1f).setDuration(200).start()
}
}
observeData()
mDraftBtn.text = if (this is AnswerEditActivity) {
"回答草稿"
} else "帖子草稿"
}
private fun observeData() {
mViewModel.chooseImagesUpload.observe(this, Observer {
mRichEditor.focusEditor()
for (key in it.keys) {
mRichEditor.insertPlaceholderImage(key)
@OnClick(R.id.editor_image, R.id.editor_font, R.id.editor_link, R.id.editor_paragraph,
R.id.editor_font_bold, R.id.editor_font_italic, R.id.editor_font_strikethrough,
R.id.editor_paragraph_h1, R.id.editor_paragraph_h2, R.id.editor_paragraph_h3,
R.id.editor_paragraph_h4, R.id.editor_font_container, R.id.editor_paragraph_container,
R.id.editor_paragraph_quote, R.id.editor_link_answer, R.id.editor_link_article,
R.id.editor_link_game, R.id.editor_link_video)
fun onRichClick(view: View) {
when (view.id) {
R.id.editor_font -> {
mEditorFont.isChecked = !mEditorFont.isChecked
mEditorParagraph.isChecked = false
mEditorLink.isChecked = false
mEditorFontContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorParagraphContainer.visibility = if (!mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorLinkContainer.visibility = if (!mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorInsertDetail.visibility = mEditorFontContainer.visibility
}
})
mViewModel.chooseImagesUploadSuccess.observe(this, Observer {
val jsonArray = JSONArray()
for (key in it.keys) {
val jsonObject = JSONObject()
jsonObject.put("id", key)
jsonObject.put("url", it[key])
jsonArray.put(jsonObject)
R.id.editor_paragraph -> {
mEditorParagraph.isChecked = !mEditorParagraph.isChecked
mEditorFont.isChecked = false
mEditorLink.isChecked = false
mEditorParagraphContainer.visibility = if (mEditorParagraph.isChecked) View.VISIBLE else View.GONE
mEditorFontContainer.visibility = if (!mEditorParagraph.isChecked) View.VISIBLE else View.GONE
mEditorLinkContainer.visibility = if (!mEditorParagraph.isChecked) View.VISIBLE else View.GONE
mEditorInsertDetail.visibility = mEditorParagraphContainer.visibility
}
mRichEditor.replacePlaceholderImage(jsonArray.toString())
})
}
override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
mIsKeyBoardShow = height > 0
if (height > 0) {
closeExtendedKeyboard()
}
}
fun closeExtendedKeyboard() {
mEditorInsertDetailContainer.visibility = View.GONE
mEditorFont.isChecked = false
mEditorLink.isChecked = false
mIsExtendedKeyboardShow = false
}
protected fun controlEditorInsertContainerEnabled(isEnabled: Boolean) {
mEditorFont.isEnabled = isEnabled
}
private fun onRichClick() {
mEditorFont.setOnClickListener {
controlEditorFontContainer()
}
mEditorLink.setOnClickListener {
controlEditorLinkContainer()
}
mEditorFontBold.setOnClickListener {
mEditorFontBold.isChecked = !mEditorFontBold.isChecked
mRichEditor.setBold()
if (mEditorFontBold.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-加粗")
R.id.editor_link -> {
mEditorLink.isChecked = !mEditorLink.isChecked
mEditorFont.isChecked = false
mEditorParagraph.isChecked = false
mEditorLinkContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
mEditorParagraphContainer.visibility = if (!mEditorLink.isChecked) View.VISIBLE else View.GONE
mEditorFontContainer.visibility = if (!mEditorLink.isChecked) View.VISIBLE else View.GONE
mEditorInsertDetail.visibility = mEditorLinkContainer.visibility
}
}
mEditorFontItalic.setOnClickListener {
mEditorFontItalic.isChecked = !mEditorFontItalic.isChecked
mRichEditor.setItalic()
if (mEditorFontItalic.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-斜体")
}
}
mEditorFontStrikeThrough.setOnClickListener {
mEditorFontStrikeThrough.isChecked = !mEditorFontStrikeThrough.isChecked
mRichEditor.setStrikeThrough()
if (mEditorFontStrikeThrough.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-删除线")
}
}
mEditorFontUnderline.setOnClickListener {
mEditorFontUnderline.isChecked = !mEditorFontUnderline.isChecked
mRichEditor.setUnderline()
if (mEditorFontUnderline.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-下滑线")
}
}
mEditorParagraphH1.setOnClickListener {
if (mEditorParagraphH1.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-1级标题")
mRichEditor.setHeading(1)
}
mEditorParagraphH1.isChecked = !mEditorParagraphH1.isChecked
}
mEditorParagraphH2.setOnClickListener {
if (mEditorParagraphH2.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-2级标题")
mRichEditor.setHeading(2)
}
mEditorParagraphH2.isChecked = !mEditorParagraphH2.isChecked
}
mEditorParagraphH3.setOnClickListener {
if (mEditorParagraphH3.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-3级标题")
mRichEditor.setHeading(3)
}
mEditorParagraphH3.isChecked = !mEditorParagraphH3.isChecked
}
mEditorParagraphH4.setOnClickListener {
if (mEditorParagraphH4.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-4级标题")
mRichEditor.setHeading(4)
}
mEditorParagraphH4.isChecked = !mEditorParagraphH4.isChecked
}
mEditorParagraphQuote.setOnClickListener {
if (mEditorParagraphQuote.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-引用")
mRichEditor.setBlockquote()
}
mEditorParagraphQuote.isChecked = !mEditorParagraphQuote.isChecked
}
findViewById<View>(R.id.editor_link_answer).setOnClickListener {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-回答")
startActivityForResult(
InsertAnswerWrapperActivity.getIntent(this),
INSERT_ANSWER_CODE
)
}
findViewById<View>(R.id.editor_link_article).setOnClickListener {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-文章")
startActivityForResult(
InsertArticleWrapperActivity.getIntent(this),
INSERT_ARTICLE_CODE
)
}
findViewById<View>(R.id.editor_link_game).setOnClickListener {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-游戏")
startActivityForResult(
GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE),
INSERT_GAME_CODE
)
}
findViewById<View>(R.id.editor_link_video).setOnClickListener {
startActivityForResult(
InsertVideoWrapperActivity.getIntent(this),
INSERT_VIDEO_CODE
)
}
findViewById<View>(R.id.editor_link_game_collection).setOnClickListener {
startActivityForResult(
InsertGameCollectionWrapperActivity.getIntent(this),
INSERT_GAME_COLLECTION_CODE
)
}
findViewById<View>(R.id.editor_video).setOnClickListener {
chooseVideo()
}
findViewById<View>(R.id.editor_image).setOnClickListener {
if (!mAgreePostPic && !NetworkUtils.isWifiOr4GOr3GConnected(this)) {
mAgreePostPic = true
DialogHelper.showDialog(
this,
"警告",
"当前使用移动网络,上传图片会消耗手机流量",
"我知道了", "", { chooseImage() },
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
return@setOnClickListener
}
chooseImage()
NewLogUtils.logChooseMedia(
"view_media",
if (mtaEventName() == "提问帖") "提问帖" else "帖子",
"图片"
)
}
findViewById<View>(R.id.uploadVideoGuideClose).setOnClickListener {
hideUploadVideoGuide()
if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
mGuideDisposable!!.dispose()
mGuideDisposable = null
}
}
findViewById<View>(R.id.originalTipsClose).setOnClickListener {
val animator = ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 1f, 0f).setDuration(200)
animator.doOnEnd {
mOriginalTipsContainer.visibility = View.GONE
}
animator.start()
}
}
private fun chooseVideo() {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
val videoCount = mViewModel.quoteCountEntity.videoCount
if (videoCount >= MAX_MEDIA_COUNT) {
toast(R.string.answer_edit_max_video_hint)
return
}
try {
PermissionHelper.checkStoragePermissionBeforeAction(this,
object : EmptyCallback {
override fun onCallback() {
val maxChooseCount = if (videoCount + 3 <= MAX_MEDIA_COUNT) 3 else MAX_MEDIA_COUNT - videoCount
startActivityForResult(
LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
LocalMediaActivity.ChooseType.VIDEO,
maxChooseCount,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
), INSERT_MEDIA_VIDEO_CODE
)
NewLogUtils.logChooseMedia(
"view_media",
if (mtaEventName() == "提问帖") "提问帖" else "帖子",
"视频"
)
}
})
} catch (e: Exception) {
toast(R.string.media_image_hint)
e.printStackTrace()
}
}
private fun chooseImage() {
MtaHelper.onEvent(mtaEventName(), "插入图片", "插入图片")
val imageCount = mViewModel.quoteCountEntity.imageCount
if (imageCount >= MAX_IMAGE_COUNT) {
toast(R.string.answer_edit_max_img_hint)
return
}
try {
PermissionHelper.checkStoragePermissionBeforeAction(this, object : EmptyCallback {
override fun onCallback() {
val maxChooseCount = if (imageCount + 10 <= MAX_IMAGE_COUNT) 10 else MAX_IMAGE_COUNT - imageCount
val intent = LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
LocalMediaActivity.ChooseType.IMAGE,
maxChooseCount,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
)
startActivityForResult(intent, REQUEST_CODE_IMAGE)
}
})
} catch (e: Exception) {
toast(R.string.media_image_hint)
e.printStackTrace()
}
}
private fun controlEditorFontContainer() {
mEditorFont.isChecked = !mEditorFont.isChecked
mEditorLink.isChecked = false
val isShouldDelay = if (mEditorFont.isChecked) {
Util_System_Keyboard.hideSoftKeyboard(this)
true
} else {
Util_System_Keyboard.showSoftKeyboard(this)
false
}
mEditorInsertDetailContainer.postDelayed({
mEditorInsertDetailContainer.visibility =
if (mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorFontContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorParagraphContainer.visibility =
if (mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorLinkContainer.visibility = View.GONE
mTagsContainer.visibility = View.GONE
mIsExtendedKeyboardShow = mEditorFont.isChecked
}, if (isShouldDelay) 200 else 0L)
}
private fun controlEditorLinkContainer() {
mEditorLink.isChecked = !mEditorLink.isChecked
mEditorFont.isChecked = false
val isShouldDelay = if (mEditorLink.isChecked) {
Util_System_Keyboard.hideSoftKeyboard(this)
true
} else {
Util_System_Keyboard.showSoftKeyboard(this)
false
}
mEditorInsertDetailContainer.postDelayed({
mEditorInsertDetailContainer.visibility =
if (mEditorLink.isChecked) View.VISIBLE else View.GONE
mEditorLinkContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
mEditorFontContainer.visibility = View.GONE
mEditorParagraphContainer.visibility = View.GONE
mTagsContainer.visibility = View.GONE
mIsExtendedKeyboardShow = mEditorLink.isChecked
}, if (isShouldDelay) 200 else 0L)
}
override fun handleBackPressed(): Boolean {
if (mIsExtendedKeyboardShow) {
closeExtendedKeyboard()
return true
}
return super.handleBackPressed()
}
override fun onResume() {
super.onResume()
mKeyboardHeightProvider?.setKeyboardHeightObserver(this)
}
override fun onPause() {
super.onPause()
mKeyboardHeightProvider?.setKeyboardHeightObserver(null)
}
//视频上传功能引导
fun showUploadVideoGuide() {
mUploadVideoGuideContainer.postDelayed({
val count = SPUtils.getInt(getVideoGuideKey(), 0)
if (count >= mMaxUploadVideoGuideCount) return@postDelayed
mUploadVideoGuideContainer.alpha = 0f
mUploadVideoGuideContainer.visibility = View.VISIBLE
mUploadVideoGuideContainer.animate().alpha(1f).setDuration(200).start()
mGuideDisposable = countDownTimer(3) { finish, _ ->
if (finish) {
hideUploadVideoGuide()
R.id.editor_font_bold -> {
mEditorFontBold.isChecked = !mEditorFontBold.isChecked
mRichEditor.setBold()
if (mEditorFontBold.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-加粗")
}
}
SPUtils.setInt(getVideoGuideKey(), count + 1)
}, 1000)
}
R.id.editor_font_italic -> {
mEditorFontItalic.isChecked = !mEditorFontItalic.isChecked
mRichEditor.setItalic()
if (mEditorFontItalic.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-斜体")
}
}
R.id.editor_font_strikethrough -> {
mEditorFontStrikeThrough.isChecked = !mEditorFontStrikeThrough.isChecked
mRichEditor.setStrikeThrough()
fun hideUploadVideoGuide() {
val animate = mUploadVideoGuideContainer.animate().alpha(0f).setDuration(200)
animate.doOnEnd {
mUploadVideoGuideContainer.visibility = View.GONE
}
animate.start()
}
override fun onDestroy() {
super.onDestroy()
mKeyboardHeightProvider?.close()
val path = mViewModel.currentUploadingVideo?.filePath
if (path != null && UploadManager.isUploading(path)) {
UploadManager.cancelTask(path)
}
if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
mGuideDisposable!!.dispose()
mGuideDisposable = null
if (mEditorFontStrikeThrough.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-删除线")
}
}
R.id.editor_paragraph_h1 -> {
if (mEditorParagraphH1.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-1级标题")
mRichEditor.setHeading(1)
}
mEditorParagraphH1.isChecked = !mEditorParagraphH1.isChecked
}
R.id.editor_paragraph_h2 -> {
if (mEditorParagraphH2.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-2级标题")
mRichEditor.setHeading(2)
}
mEditorParagraphH2.isChecked = !mEditorParagraphH2.isChecked
}
R.id.editor_paragraph_h3 -> {
if (mEditorParagraphH3.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-3级标题")
mRichEditor.setHeading(3)
}
mEditorParagraphH3.isChecked = !mEditorParagraphH3.isChecked
}
R.id.editor_paragraph_h4 -> {
if (mEditorParagraphH4.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-4级标题")
mRichEditor.setHeading(4)
}
mEditorParagraphH4.isChecked = !mEditorParagraphH4.isChecked
}
R.id.editor_paragraph_quote -> {
if (mEditorParagraphQuote.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-引用")
mRichEditor.setBlockquote()
}
mEditorParagraphQuote.isChecked = !mEditorParagraphQuote.isChecked
}
R.id.editor_link_answer -> {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-回答")
startActivityForResult(InsertAnswerWrapperActivity.getIntent(this), INSERT_ANSWER_CODE)
}
R.id.editor_link_article -> {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-文章")
startActivityForResult(InsertArticleWrapperActivity.getIntent(this), INSERT_ARTICLE_CODE)
}
R.id.editor_link_game -> {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-游戏")
startActivityForResult(GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE), INSERT_GAME_CODE)
}
R.id.editor_link_video -> {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
startActivityForResult(VideoActivity.getIntent(this), VideoActivity.INSERT_VIDEO_CODE)
}
}
}
@ -618,7 +237,6 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarAct
mEditorFontBold.isChecked = elements.contains(ELEMENT_NAME_BOLD)
mEditorFontItalic.isChecked = elements.contains(ELEMENT_NAME_ITALIC)
mEditorFontStrikeThrough.isChecked = elements.contains(ELEMENT_NAME_STRIKE)
mEditorFontUnderline.isChecked = elements.contains(ELEMENT_NAME_UNDERLINE)
mEditorParagraphH1.isChecked = elements.contains(ELEMENT_PARAGRAPH_H1)
mEditorParagraphH2.isChecked = elements.contains(ELEMENT_PARAGRAPH_H2)
mEditorParagraphH3.isChecked = elements.contains(ELEMENT_PARAGRAPH_H3)
@ -632,12 +250,11 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarAct
@JavascriptInterface
fun onPaste() {
val clipboard =
HaloApp.getInstance().application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
HaloApp.getInstance().application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clipText = clipboard.text.toString()
if (!TextUtils.isEmpty(clipText)) {
// 替换换行符号否则 插入失败
val text = clipText.replace("[ ]".toRegex(), "&nbsp;")
.replace("[\r\n]".toRegex(), "<br/>")
val text = clipText.replace("[ ]".toRegex(), "&nbsp;").replace("[\r\n]".toRegex(), "<br/>")
mBaseHandler.post { mRichEditor.insertHtml(text) }
}
}
@ -647,122 +264,16 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarAct
@JavascriptInterface
fun onTextChange(count: Int) {
val num = if (count > MAX_INPUT_TEXT_NUM) MAX_INPUT_TEXT_NUM - count else count
mEditorTextNumTv.post {
mEditorTextNumTv.text = num.toString()
mViewModel.quoteCountEntity.textCount = num
}
mEditorTextNumTv.text = num.toString()
}
}
private inner class OnQuoteCountChangeListener {
@JavascriptInterface
fun onQuoteCountChange(
imageCount: Int,
articleCount: Int,
answerCount: Int,
videoCount: Int,
gameCount: Int
) {
mEditorTextNumTv.post {
mViewModel.quoteCountEntity.apply {
this.imageCount = imageCount
this.articleCount = articleCount
this.answerCount = answerCount
this.videoCount = videoCount
this.gameCount = gameCount
}
}
}
}
private inner class OnVideoListener {
@JavascriptInterface
fun showDeleteDialog(id: String) {
DialogHelper.showDialog(this@BaseRichEditorActivity, "提示", "确定删除吗?", "确定", "取消", {
runOnUiThread {
mRichEditor.delPlaceholderVideo(id)
mViewModel.deleteVideo(id)
}
})
}
@JavascriptInterface
fun updatePoster(id: String, videoId: String, url: String) {
mViewModel.id = id
mViewModel.videoId = videoId
val videoEntity = VideoEntity(url = url)
val intent =
PosterEditActivity.getIntentByVideo(this@BaseRichEditorActivity, videoEntity)
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
}
@JavascriptInterface
fun deleteUploadingVideo(id: String) {
mViewModel.deleteVideo(id)
}
@JavascriptInterface
fun reUploadVideo(id: String) {
val video = mViewModel.uploadVideoErrorList.find { it.id == id }
if (video != null) {
mViewModel.localVideoList.add(video)
mViewModel.uploadVideoErrorList.remove(video)
mViewModel.uploadVideo()
}
}
}
override fun insertPlaceholderVideo(id: String, poster: String) {
mRichEditor.insertPlaceholderVideo(id, poster)
}
override fun updateVideoProgress(id: String, progress: String) {
mRichEditor.updateVideoProgress(id, progress)
}
override fun videoUploadFinished(id: String, url: String, msg: JsonObject) {
try {
val obj = JSONObject()
obj.put("poster", msg.get("poster").asString)
obj.put("url", msg.get("url").asString)
obj.put("duration", RichEditor.formatVideoDuration(msg.get("length").asLong))
obj.put("id", msg.get("_id").asString)
obj.put("status", "pending")
mRichEditor.videoUploadFinished(id, url, obj.toString())
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
override fun changePoster(id: String, poster: String) {
mRichEditor.changePoster(id, poster)
}
override fun videoUploadFailed(id: String) {
mRichEditor.videoUploadFailed(id)
}
open fun getSelectedLabel(): Int = 0
open fun onActivityDialogResult(requestCode: Int, resultCode: Int, data: Intent?) {}
abstract fun mtaEventName(): String
abstract fun provideViewModel(): VM
abstract fun getVideoGuideKey(): String
override fun isAutoResetViewBackgroundEnabled(): Boolean = true
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.background_white, R.color.background_white)
mRichEditor.enableForceDark(DarkModeUtils.isDarkModeOn(this))
mRichEditor.setEditorBackgroundColor(R.color.background_white.toColor(this))
mRichEditor.setEditorFontColor(if (mIsDarkModeOn) Color.parseColor("#C2C2C2") else Color.parseColor("#4A4A4A"))
}
companion object {
const val ELEMENT_NAME_BOLD = " b "
const val ELEMENT_NAME_ITALIC = " i "
const val ELEMENT_NAME_STRIKE = " strike "
const val ELEMENT_NAME_UNDERLINE = " u "
const val ELEMENT_PARAGRAPH_H1 = " h1 "
const val ELEMENT_PARAGRAPH_H2 = " h2 "
const val ELEMENT_PARAGRAPH_H3 = " h3 "
@ -772,14 +283,6 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarAct
const val INSERT_ANSWER_CODE = 411
const val INSERT_ARTICLE_CODE = 412
const val INSERT_GAME_CODE = 413
const val INSERT_GAME_COLLECTION_CODE = 414
const val INSERT_VIDEO_CODE = 415
const val MAX_INPUT_TEXT_NUM = 10000
const val MAX_MEDIA_COUNT = 20
const val MAX_IMAGE_COUNT = 35
const val REQUEST_CODE_IMAGE = 120
const val INSERT_MEDIA_VIDEO_CODE = 121
const val REQUEST_CODE_IMAGE_CROP = 122
}
}

View File

@ -1,465 +0,0 @@
package com.gh.base
import android.app.Application
import android.content.Intent
import android.graphics.Bitmap
import android.media.ThumbnailUtils
import android.provider.MediaStore
import android.text.TextUtils
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.common.entity.ErrorEntity
import com.gh.gamecenter.entity.LocalVideoEntity
import com.gh.gamecenter.entity.QuoteCountEntity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.retrofit.service.ApiService
import com.gh.gamecenter.video.upload.OnUploadListener
import com.gh.gamecenter.video.upload.UploadManager
import com.google.gson.JsonObject
import com.lightgame.utils.Utils
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import io.reactivex.disposables.Disposable
import okhttp3.ResponseBody
import retrofit2.HttpException
import java.io.File
import java.io.FileOutputStream
import kotlin.collections.set
// TODO: 移动到module_bbs模块
abstract class BaseRichEditorViewModel(application: Application) : AndroidViewModel(application) {
val mApi: ApiService = RetrofitManager.getInstance().api
val processDialog = MediatorLiveData<WaitingDialogFragment.WaitingDialogData>()
val chooseImagesUpload = MutableLiveData<LinkedHashMap<String, String>>()
val chooseImagesUploadSuccess = MutableLiveData<LinkedHashMap<String, String>>()
var uploadImageSubscription: Disposable? = null
val mapImages = HashMap<String, String>()
val localVideoList = ArrayList<LocalVideoEntity>()
val uploadVideoErrorList = ArrayList<LocalVideoEntity>()
var currentUploadingVideo: LocalVideoEntity? = null
var type: String = "" //游戏论坛game_bbs 官方论坛official_bbs
private var mUploadVideoListener: UploadVideoListener? = null
val TITLE_MIN_LENGTH = 6
val MIN_TEXT_LENGTH = 6
val MAX_TEXT_LENGTH = 10000
val FILE_HOST = "file:///"
var id = ""//视频标记
var videoId = ""//更改封面视频id
val quoteCountEntity = QuoteCountEntity()//数据上报用
fun setUploadVideoListener(uploadVideoListener: UploadVideoListener) {
this.mUploadVideoListener = uploadVideoListener
}
//检查图片是否符合规则并上传图片
fun uploadPic(data: Intent) {
val uris = Matisse.obtainResult(data)
val rawImgUrlList = ArrayList<String>() // 需要上传图片的原始地址列表
val uploadingImgList = ArrayList<String>() // 正在上传图片的地址列表(已压缩处理)
val compressedImgUrlList = ArrayList<String>() // 压缩处理后图片的地址列表
for (uri in uris) {
val picturePath = PathUtils.getPath(getApplication(), uri)
if (picturePath != null) {
if (File(picturePath).length() > ImageUtils.getUploadFileMaxSize()) {
val count = ImageUtils.getUploadFileMaxSize() / 1024 / 1024
val application: Application = getApplication()
Utils.toast(
getApplication(),
application.getString(R.string.pic_max_hint, count)
)
continue
}
Utils.log("picturePath = $picturePath")
rawImgUrlList.add(picturePath)
} else {
Utils.log("picturePath is null")
}
}
if (rawImgUrlList.size == 0) return
val imageType = when (getRichType()) {
RichType.ARTICLE -> UploadImageUtils.UploadType.community_article
RichType.QUESTION -> UploadImageUtils.UploadType.question
else -> UploadImageUtils.UploadType.poster
}
uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(
imageType,
rawImgUrlList,
false,
object : UploadImageUtils.OnUploadImageListListener {
override fun onProgress(total: Long, progress: Long) {}
override fun onCompressSuccess(imageUrls: List<String>) {
val chooseImageMd5Map = LinkedHashMap<String, String>()
imageUrls.forEach {
chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = ""
}
uploadingImgList.addAll(imageUrls)
compressedImgUrlList.addAll(imageUrls)
chooseImagesUpload.postValue(chooseImageMd5Map)
}
override fun onSingleSuccess(imageUrlMap: Map<String, String>) {
imageUrlMap.forEach {
if (uploadingImgList.contains(it.key)) {
uploadingImgList.remove(it.key)
}
}
val map = LinkedHashMap<String, String>()
for (key in imageUrlMap.keys) {
map[MD5Utils.getUrlMD5(key)] = FILE_HOST + key.decodeURI()
mapImages[TextUtils.htmlEncode(key).decodeURI()] = imageUrlMap[key] ?: ""
}
chooseImagesUploadSuccess.value = map
}
override fun onSuccess(imageUrlMap: LinkedHashMap<String, String>, errorMap: Map<String, Exception>) {
imageUrlMap.forEach {
if (uploadingImgList.contains(it.key)) {
uploadingImgList.remove(it.key)
}
}
var errorSize = compressedImgUrlList.size - imageUrlMap.size
if (errorSize > 0 || uploadingImgList.isNotEmpty()) {
val errorImageMap = LinkedHashMap<String, String>()
for (key in errorMap.keys) {
errorImageMap[MD5Utils.getUrlMD5(key)] = ""
}
for (rawImgUrl in compressedImgUrlList) {
if (!imageUrlMap.containsKey(rawImgUrl)) {
errorImageMap[MD5Utils.getUrlMD5(rawImgUrl)] = ""
}
}
errorSize = if (errorMap.isEmpty()) {
errorImageMap.size
} else {
errorMap.size + errorImageMap.size
}
// value为空会删除PlaceholderImage
chooseImagesUploadSuccess.value = errorImageMap
if (handleUploadError(errorSize, errorMap)) return
if (errorSize > 0) {
ToastUtils.showToast(errorSize.toString() + "张图片上传失败")
}
}
}
override fun onError(errorMap: Map<String, Exception>) {
val errorSize = uploadingImgList.size
if (uploadingImgList.size > 0) {
val map = LinkedHashMap<String, String>()
for (key in uploadingImgList) {
map[MD5Utils.getUrlMD5(key)] = ""
}
// value为空会删除PlaceholderImage
chooseImagesUploadSuccess.value = map
}
if (handleUploadError(errorSize, errorMap)) return
if (errorSize == 0) return
if (errorSize == 1) {
ToastUtils.showToast("图片上传失败")
} else {
ToastUtils.showToast(errorSize.toString() + "张图片上传失败")
}
}
})
}
/**
* 处理上传错误 (包括超时 cancel 的异常)
* @return 如果 toast 过了就返回 true
*/
private fun handleUploadError(
errorSize: Int,
errorMap: Map<String, Exception>
): Boolean {
for (error in errorMap.values) {
if (error is HttpException && error.code() == 403) {
val e = error.response()?.errorBody()?.string()?.toObject<ErrorEntity>()
if (e != null && e.code == 403017) {
ToastUtils.showToast(errorSize.toString() + "张图片的宽或高超过限制,请裁剪后上传")
} else {
ToastUtils.showToast(errorSize.toString() + "张违规图片上传失败")
}
return true
}
}
return false
}
fun uploadPoster(picturePath: String) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", true))
uploadImageSubscription =
UploadImageUtils.compressAndUploadImage(
UploadImageUtils.UploadType.poster,
picturePath,
false,
object : UploadImageUtils.OnUploadImageListener {
override fun onSuccess(imageUrl: String) {
patchVideoPoster(imageUrl)
}
override fun onError(e: Throwable?) {
handleUploadPosterResult(true)
}
override fun onProgress(total: Long, progress: Long) {
}
})
}
private fun patchVideoPoster(poster: String) {
if (id.isEmpty() || videoId.isEmpty()) return
val map = hashMapOf("poster" to poster, "type" to getVideoType())
mApi.patchInsertVideo(videoId, map.toRequestBody())
.compose(observableToMain())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
mUploadVideoListener?.changePoster(id, poster)
handleUploadPosterResult(false)
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
handleUploadPosterResult(true)
}
})
}
private fun handleUploadPosterResult(isFailure: Boolean = false) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
if (isFailure) {
ToastUtils.showToast("封面更改失败")
}
id = ""
videoId = ""
}
fun deleteVideo(id: String) {
if (localVideoList.isNotEmpty()) {
val video = localVideoList.find { it.id == id }
if (video != null) {
if (UploadManager.isUploading(video.filePath)) {
UploadManager.cancelTask(video.filePath)
}
localVideoList.remove(video)
}
}
if (uploadVideoErrorList.isNotEmpty()) {
val video = uploadVideoErrorList.find { it.id == id }
if (video != null) {
uploadVideoErrorList.remove(video)
}
}
if (currentUploadingVideo?.id == id) {
currentUploadingVideo = null
uploadVideo()
}
}
fun uploadVideo() {
if (currentUploadingVideo != null) return
if (localVideoList.isEmpty()) return
currentUploadingVideo = localVideoList[0]
UploadManager.createUploadTask(currentUploadingVideo?.filePath
?: "", object : OnUploadListener {
override fun onProgressChanged(
uploadFilePath: String,
currentSize: Long,
totalSize: Long,
speed: Long
) {
runOnUiThread {
val percent = (currentSize * 100 / totalSize.toFloat()).roundTo(1)
currentUploadingVideo?.id?.let {
mUploadVideoListener?.updateVideoProgress(it, percent.toString())
}
}
}
override fun onUploadSuccess(uploadFilePath: String, url: String) {
if (currentUploadingVideo != null) {
postVideoPosterAndInfo(uploadFilePath, url)
}
}
override fun onUploadFailure(uploadFilePath: String, errorMsg: String) {
uploadVideoFailure()
}
})
}
private fun postVideoPosterAndInfo(uploadFilePath: String, url: String) {
val localVideoPoster =
getApplication<Application>().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg"
try {
val bmp = ThumbnailUtils.createVideoThumbnail(
uploadFilePath,
MediaStore.Images.Thumbnails.MINI_KIND
)
// bmp 可能为空
FileOutputStream(localVideoPoster).use { out ->
bmp?.compress(Bitmap.CompressFormat.PNG, 100, out)
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
ToastUtils.showToast("视频封面操作失败")
uploadVideoFailure()
return
}
uploadImageSubscription =
UploadImageUtils.compressAndUploadImage(
UploadImageUtils.UploadType.poster,
localVideoPoster,
false,
object : UploadImageUtils.OnUploadImageListener {
override fun onSuccess(imageUrl: String) {
postVideoInfo(url, imageUrl)
}
override fun onError(e: Throwable?) {
uploadVideoFailure()
}
override fun onProgress(total: Long, progress: Long) {
}
})
}
private fun postVideoInfo(url: String, poster: String) {
val map = HashMap<String, Any>().apply {
put("poster", poster)
put("url", url)
put("format", currentUploadingVideo?.format ?: "")
put("size", currentUploadingVideo?.size ?: 0)
put("length", (currentUploadingVideo?.duration ?: 0) / 1000)
put("type", getVideoType())
}
val requestBody = map.toRequestBody()
mApi.insertVideo(requestBody)
.compose(observableToMain())
.subscribe(object : Response<JsonObject>() {
override fun onResponse(response: JsonObject?) {
super.onResponse(response)
if (response != null) {
uploadVideoSuccess(poster, url, response)
}
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
uploadVideoFailure()
}
})
}
private fun uploadVideoSuccess(poster: String, url: String, data: JsonObject) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
currentUploadingVideo?.let {
mUploadVideoListener?.changePoster(it.id, poster)
mUploadVideoListener?.videoUploadFinished(it.id, url, data)
UploadManager.cancelTask(it.filePath)
localVideoList.remove(it)
}
currentUploadingVideo = null
uploadVideo()
}
private fun uploadVideoFailure() {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
currentUploadingVideo?.let {
runOnUiThread {
mUploadVideoListener?.videoUploadFailed(it.id)
}
uploadVideoErrorList.add(it)
localVideoList.remove(it)
UploadManager.cancelTask(it.filePath)
}
currentUploadingVideo = null
uploadVideo()
}
fun checkIsAllUploadedAndToast(): Boolean {
if (localVideoList.isNotEmpty() || uploadVideoErrorList.isNotEmpty()) {
ToastUtils.showToast("视频未上传完成,视频内容保存失败")
return false
}
return true
}
private fun getVideoType(): String {
return when (type) {
BbsType.GAME_BBS.value -> {
when (getRichType()) {
RichType.ARTICLE -> BbsType.GAME_BBS_ARTICLE_INSERT.value
RichType.QUESTION -> BbsType.GAME_BBS_QUESTION_INSERT.value
else -> ""
}
}
BbsType.OFFICIAL_BBS.value -> {
when (getRichType()) {
RichType.ARTICLE -> BbsType.OFFICIAL_BBS_ARTICLE_INSERT.value
RichType.QUESTION -> BbsType.OFFICIAL_BBS_QUESTION_INSERT.value
else -> ""
}
}
else -> ""
}
}
abstract fun getRichType(): RichType
}
interface UploadVideoListener {
/**
* 插入视频占位图
*/
fun insertPlaceholderVideo(id: String, poster: String)
/**
* 更新视频进度条
*/
fun updateVideoProgress(id: String, progress: String)
/**
* 上传视频完成
*/
fun videoUploadFinished(id: String, url: String, msg: JsonObject)
/**
* 更换封面图
*/
fun changePoster(id: String, poster: String)
/**
* 上传失败
*/
fun videoUploadFailed(id: String)
}
enum class RichType {
ARTICLE,
QUESTION,
ANSWER
}

View File

@ -1,11 +1,11 @@
package com.gh.gamecenter.core.utils
package com.gh.base
import android.app.Activity
object CurrentActivityHolder {
@JvmStatic
val activitySet by lazy { HashSet<Activity>() }
val activitySet = HashSet<Activity>()
@JvmStatic
fun getCurrentActivity(): Activity? {

View File

@ -1,86 +0,0 @@
package com.gh.base
import android.view.Gravity
import androidx.appcompat.app.AppCompatActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.DarkModeUtils
import com.gh.gamecenter.common.utils.DialogHelper
import com.gh.gamecenter.common.utils.PackageFlavorHelper
import com.gh.gamecenter.common.utils.dip2px
import com.lzf.easyfloat.EasyFloat
import com.lzf.easyfloat.enums.ShowPattern
import com.lzf.easyfloat.enums.SidePattern
object DarkModeSwitchHelper {
fun showDarkModeSwitchFloatingView(activity: AppCompatActivity) {
if (PackageFlavorHelper.IS_TEST_FLAVOR) {
EasyFloat.with(activity)
.setLayout(R.layout.layout_day_mode_night_mode_switch)
.setTag("night_mode_switch_floating_view")
.setAnimator(null)
.setGravity(Gravity.TOP.xor(Gravity.END), 0, 114F.dip2px())
.setSidePattern(SidePattern.RESULT_SIDE)
.setDragEnable(true)
.setShowPattern(ShowPattern.CURRENT_ACTIVITY)
.registerCallback {
createResult { _, _, view ->
view?.setOnClickListener {
showDarkModeSwitchDialog(activity)
}
}
}
.show()
}
}
fun dismissDarkModeSwitchFloatingView() {
if (PackageFlavorHelper.IS_TEST_FLAVOR) {
EasyFloat.dismiss("night_mode_switch_floating_view")
}
}
private fun showDarkModeSwitchDialog(activity: AppCompatActivity) {
if (PackageFlavorHelper.IS_TEST_FLAVOR) {
//切换深色模式
var mode = "普通模式"
var positive = "跟随系统模式"
var negative = "深色模式"
if (DarkModeUtils.isFollowSystemDarkModeFromSp()) {
mode = "跟随系统模式"
positive = "普通模式"
negative = "深色模式"
} else if (DarkModeUtils.isAppDarkModeEnabledFromSp()) {
mode = "深色模式"
positive = "跟随系统模式"
negative = "普通模式"
}
DialogHelper.showDialog(
context = activity,
title = "选择模式",
content = "当前为 $mode",
confirmText = positive,
cancelText = negative,
confirmClickCallback = {
if (DarkModeUtils.isFollowSystemDarkModeFromSp()) {
DarkModeUtils.updateAppDarkModeStatusToSp(false)
DarkModeUtils.updateFollowSystemDarkModeToSp(false)
} else {
DarkModeUtils.updateFollowSystemDarkModeToSp(true)
}
DarkModeUtils.initDarkMode()
},
cancelClickCallback = {
if (DarkModeUtils.isFollowSystemDarkModeFromSp()) {
DarkModeUtils.updateAppDarkModeStatusToSp(true)
} else {
DarkModeUtils.updateAppDarkModeStatusToSp(!DarkModeUtils.isAppDarkModeEnabledFromSp())
}
DarkModeUtils.updateFollowSystemDarkModeToSp(false)
DarkModeUtils.initDarkMode()
},
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
}
}

View File

@ -1,112 +0,0 @@
package com.gh.base
import android.os.Bundle
import android.text.TextUtils
import android.view.MenuItem
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.gh.download.DownloadManager
import com.gh.gamecenter.DownloadManagerActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils.getBoolean
import com.gh.gamecenter.entity.GameUpdateEntity
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.packagehelper.PackageViewModel
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
// TODO:移动到module_download模块中
abstract class DownloadToolbarActivity : ToolBarActivity() {
private var mDownloadCountHint: TextView? = null
private var mPackageViewModel: PackageViewModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!getBoolean(Constants.SP_TEENAGER_MODE) && showDownloadMenu()) {
mPackageViewModel = viewModelProvider(PackageViewModel.Factory())
mPackageViewModel?.filterSameUpdateLiveData?.observe(this) { updateList: List<GameUpdateEntity> ->
updateDownloadCountHint(updateList)
}
}
}
override fun setToolbarMenu(res: Int) {
// 青少年模式下要隐藏下载按钮
if (getBoolean(Constants.SP_TEENAGER_MODE) && res == R.menu.menu_download) return
super.setToolbarMenu(res)
}
override fun inflateMenu(res: Int) {
super.inflateMenu(res)
if (showDownloadMenu()) {
createDownloadMenu(res)
}
}
private fun createDownloadMenu(res: Int) {
if (res != R.menu.menu_download) {
menuInflater.inflate(R.menu.menu_download, mActionMenuView.menu)
}
if (mPackageViewModel != null) {
updateDownloadCountHint(mPackageViewModel?.filterSameUpdateLiveData?.value)
}
val downloadMenuView = mActionMenuView.menu.findItem(R.id.menu_download).actionView
mDownloadCountHint = downloadMenuView?.findViewById(R.id.menu_download_count_hint)
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
if (item!!.itemId == R.id.menu_download) {
// MtaHelper.onEvent("下载管理", "下载管理入口", getActivityNameInChinese());
val intent = DownloadManagerActivity.getDownloadMangerIntent(this, mEntrance)
startActivity(intent)
return true
}
return super.onMenuItemClick(item)
}
private fun updateDownloadCountHint(updateList: List<GameUpdateEntity>?) {
if (mDownloadCountHint == null) return
val count = DownloadManager.getInstance().getDownloadOrUpdateCount(updateList)
if (count != null) {
mDownloadCountHint!!.visibility = View.VISIBLE
mDownloadCountHint!!.text = count
val params = mDownloadCountHint!!.layoutParams
if (TextUtils.isEmpty(count)) {
params.width = DisplayUtils.dip2px(6f)
params.height = DisplayUtils.dip2px(6f)
} else {
params.width = DisplayUtils.dip2px(12f)
params.height = DisplayUtils.dip2px(12f)
}
mDownloadCountHint!!.layoutParams = params
} else {
mDownloadCountHint!!.visibility = View.GONE
}
}
protected open fun showDownloadMenu(): Boolean {
return false
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(status: EBDownloadStatus?) {
if (!getBoolean(Constants.SP_TEENAGER_MODE) && showDownloadMenu() && mPackageViewModel != null) {
updateDownloadCountHint(mPackageViewModel?.filterSameUpdateLiveData?.value)
}
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
if (showDownloadMenu() && getMenuItem(R.id.menu_download) != null) {
(getMenuItem(R.id.menu_download).actionView?.findViewById(R.id.menu_download_iv) as ImageView).setImageResource(
R.drawable.toolbar_download
)
}
}
}

View File

@ -0,0 +1,77 @@
package com.gh.base;
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;
import com.gh.common.notifier.Notifier;
import com.gh.common.util.DataUtils;
import com.gh.download.DownloadManager;
import com.halo.assistant.HaloApp;
import com.lightgame.utils.AppManager;
/**
* 1、写点针对生命周期的统计代码
* 2、写点通用的逻辑
* 3、接口解耦
*
* @author CsHeng
* @Date 09/05/2017
* @Time 6:22 PM
*/
public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
AppManager.getInstance().addActivity(activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
CurrentActivityHolder.getActivitySet().add(activity);
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
DataUtils.onResume(activity);
//FIXME 这里应该只是部分Activity需要
try {
// 初始化gameMap
DownloadManager.getInstance(activity).initGameMap();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onActivityPaused(Activity activity) {
CurrentActivityHolder.getActivitySet().remove(activity);
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
DataUtils.onPause(activity);
}
}
@Override
public void onActivityStopped(Activity activity) {
Notifier.hide();
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
if (activity.isFinishing()) {
AppManager.getInstance().finishActivity(activity);
}
}
}

View File

@ -0,0 +1,229 @@
//package com.gh.base
//
//import android.app.Notification
//import android.app.NotificationChannel
//import android.app.NotificationManager
//import android.app.PendingIntent
//import android.content.Context
//import android.content.Intent
//import android.os.Build
//import android.os.Bundle
//import android.preference.PreferenceManager
//import android.text.TextUtils
//import android.view.View
//import androidx.core.app.NotificationCompat
//import androidx.core.text.htmlEncode
//import com.gh.common.notifier.Notifier
//import com.gh.common.util.*
//import com.gh.gamecenter.R
//import com.gh.gamecenter.entity.PushEntity
//import com.gh.gamecenter.entity.PushMessageEntity
//import com.gh.gamecenter.entity.PushMessageUnreadEntity
//import com.gh.gamecenter.entity.PushNotificationEntity
//import com.gh.gamecenter.manager.UserManager
//import com.gh.gamecenter.message.MessageUnreadRepository
//import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity
//import com.gh.gamecenter.receiver.UmengMessageReceiver
//import com.gh.gamecenter.receiver.UmengMessageReceiver.Companion.TYPE_CLICK
//import com.gh.gamecenter.receiver.UmengMessageReceiver.Companion.TYPE_REMOVE
//import com.gh.gamecenter.retrofit.Response
//import com.gh.gamecenter.retrofit.RetrofitManager
//import com.google.gson.Gson
//import com.umeng.message.UmengMessageService
//import io.reactivex.android.schedulers.AndroidSchedulers
//import io.reactivex.schedulers.Schedulers
//import okhttp3.MediaType
//import okhttp3.RequestBody
//import okhttp3.ResponseBody
//import org.android.agoo.common.AgooConstants
//import org.json.JSONObject
//import retrofit2.HttpException
//import java.util.*
//
//class GHUmengNotificationService : UmengMessageService() {
//
// companion object {
// const val ACTION_UMENG = "com.gh.gamecenter.UMENG"
// const val MESSAGE_FROM_SYSTEM = "message_from_system"
// const val HALO_MESSAGE_DIALOG = "HALO_MESSAGE_DIALOG"
// const val HALO_MESSAGE_CENTER = "HALO_MESSAGE_CENTER"
// const val ANSWER = "answer"
// const val FOLLOW_QUESTION = "follow_question"
// const val NOTIFICATION_ID = 2015
// const val DISPLAY_TYPE_NOTIFICATION = "notification"
// const val DISPLAY_TYPE_CUSTOM = "custom"
// const val MESSAGE_ID = "message_id"
// const val NOTIFICATION_MESSAGE_ID = "notification_message_id" // 通知中心消息 ID
// const val PUSH_ID = "push_id"
// }
//
// val notificationTags = arrayOf("GH_UMENG_TAG_1", "GH_UMENG_TAG_2", "GH_UMENG_TAG_3")
// val gson = Gson()
//
// override fun onMessage(context: Context, intent: Intent) {
// val message = intent.getStringExtra(AgooConstants.MESSAGE_BODY)
// val isMessageFromSystem = intent.getBooleanExtra(MESSAGE_FROM_SYSTEM, false)
//
// try {
// val pushData = message.toObject<PushEntity>()
// pushData?.let { handlePushData(context, it, message, isMessageFromSystem) }
// } catch (e: Exception) {
// e.printStackTrace()
// }
// }
//
// private fun handlePushData(context: Context, pushData: PushEntity, message: String, isMessageFromSystem: Boolean) {
// val notificationManager = context.applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//
// if (pushData.displayType == DISPLAY_TYPE_NOTIFICATION) {
// // 其它类型的透传信息
// // 显示到通知栏
// val msg = message.toObject<PushNotificationEntity>()
// val data = msg?.extra?.data
//
// // 系统推送(非自定义信息),直接处理跳转
// if (isMessageFromSystem) {
// val intent = Intent()
// intent.setClass(context, UmengMessageReceiver::class.java)
// intent.putExtra(EntranceUtils.KEY_DATA, data?.link)
// intent.putExtra(EntranceUtils.KEY_TYPE, UmengMessageReceiver.DIRECT_ONLY)
// intent.putExtra(EntranceUtils.KEY_MESSAGE, message)
// intent.putExtra(NOTIFICATION_MESSAGE_ID, data?.messageId)
// context.sendBroadcast(intent)
// return
// }
//
// // 用户未登录的情况下不生成消息中心通知,避免用户掉登录了还收到跳转至消息中心的通知
// if (data != null
// && data.link?.link == "system"
// && !UserManager.getInstance().isLoggedIn) {
// return
// }
//
// val clickIntent = Intent()
// val removeIntent = Intent()
//
// clickIntent.setClass(context, UmengMessageReceiver::class.java)
// clickIntent.putExtra(EntranceUtils.KEY_DATA, data?.link)
// clickIntent.putExtra(EntranceUtils.KEY_MESSAGE, message)
// clickIntent.putExtra(MESSAGE_ID, msg?.msgId)
// clickIntent.putExtra(PUSH_ID, data?.pushId)
// clickIntent.putExtra(NOTIFICATION_MESSAGE_ID, data?.messageId)
// clickIntent.putExtra(EntranceUtils.KEY_TYPE, TYPE_CLICK)
//
// removeIntent.setClass(context, UmengMessageReceiver::class.java)
// removeIntent.putExtra(EntranceUtils.KEY_TYPE, TYPE_REMOVE)
// removeIntent.putExtra(EntranceUtils.KEY_MESSAGE, message)
//
// val clickPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(),
// clickIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//
// val deletePendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt() + 1,
// removeIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// val channel = NotificationChannel("Halo_Push", "Halo_Push", NotificationManager.IMPORTANCE_DEFAULT)
// notificationManager.createNotificationChannel(channel)
// }
//
// val notification = NotificationCompat.Builder(context, "Halo_Push")
// .setSmallIcon(R.drawable.ic_notification)
// .setTicker(pushData.body?.ticker)
// .setContentTitle(pushData.body?.title)
// .setContentText(pushData.body?.text?.fromHtml())
// .setContentIntent(clickPendingIntent)
// .setDeleteIntent(deletePendingIntent)
// .build()
// notification.flags = notification.flags or Notification.FLAG_AUTO_CANCEL
//
// notificationManager.notify(getNotificationTag(context), NOTIFICATION_ID, notification)
// } else {
// if (UserManager.getInstance().isLoggedIn &&
// HALO_MESSAGE_DIALOG == pushData.body?.custom &&
// MessageUnreadRepository.unreadLiveData.value != null) {
// // 回答了问题或者关注了问题的消息
// val msg = gson.fromJson(message, PushMessageEntity::class.java)
// val data = msg?.extra?.data
//
// val type = if (ANSWER == data?.type) {
// "回答了你的问题"
// } else {
// "回答了你关注的问题"
// }
//
// val userName = StringUtils.shrinkStringWithDot(data?.userEntity?.name, 8)
// val displayText = userName + type
//
// if (Notifier.isActivityValid(CurrentActivityHolder.getCurrentActivity()) &&
// Notifier.shouldShowNotifier(data?.answer?.id + displayText)) {
// Notifier.create(CurrentActivityHolder.getCurrentActivity())
// .setText(displayText)
// .setDuration(5000)
// .setIcon(data?.userEntity?.icon)
// .setOnClickListener(View.OnClickListener {
// val bundle = Bundle()
// bundle.putString(EntranceUtils.KEY_ANSWER_ID, data?.answer?.id)
// bundle.putString(EntranceUtils.KEY_ENTRANCE, EntranceUtils.ENTRANCE_UMENG)
// bundle.putString(EntranceUtils.KEY_TO, AnswerDetailActivity::class.java.name)
// EntranceUtils.jumpActivity(context, bundle)
//
// MtaHelper.onEvent("消息弹窗", type, "Does not contains any parameter.")
//
// // 标记已读
// val jsonObject = JSONObject()
// jsonObject.put("type", type)
// val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString())
//
// RetrofitManager.getInstance(application).api.postMessageRead(UserManager.getInstance().userId, data?.id, body)
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(object : Response<ResponseBody>() {
// override fun onResponse(response: ResponseBody?) {
// super.onResponse(response)
// MessageUnreadRepository.loadMessageUnreadData()
// }
//
// override fun onFailure(e: HttpException?) {
// e?.printStackTrace()
// }
// })
// Notifier.hide()
// })
// .show(false)
// Notifier.tagNotifierAsShowed(data?.answer?.id + displayText)
// }
// } else if (HALO_MESSAGE_CENTER == pushData.body?.custom) {
// // 消息中心逻辑
// val msg = gson.fromJson(message, PushMessageUnreadEntity::class.java)
// val data = msg?.extra?.data
// data?.let { MessageUnreadRepository.loadMessageUnreadData() }
// }
// }
// }
//
// /**
// * 规则:最多三条消息,以旧换新
// *
// * @return NotificationTag
// */
// private fun getNotificationTag(context: Context): String {
// val sp = PreferenceManager.getDefaultSharedPreferences(context)
// val edit = sp.edit()
//
// val timeTagMap = HashMap<Long, String>()
// for (tag in notificationTags) {
// val time = sp.getLong(tag, 0)
// if (time == 0L) {
// edit.putLong(tag, System.currentTimeMillis()).apply()
// return tag
// } else {
// timeTagMap[time] = tag
// }
// }
//
// val minTime = Collections.min(timeTagMap.keys)
// val tag = timeTagMap[minTime]
// edit.putLong(tag, System.currentTimeMillis()).apply()
// return if (TextUtils.isEmpty(tag)) notificationTags[0] else tag!!
// }
//}

View File

@ -1,82 +0,0 @@
package com.gh.base
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.gh.common.util.FloatingBackViewManager
import com.gh.download.DownloadManager
import com.gh.gamecenter.SingletonWebActivity
import com.gh.gamecenter.SplashScreenActivity
import com.gh.gamecenter.common.utils.PackageFlavorHelper
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
// TODO移动到对应的模块
class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
// do nothing
}
override fun onActivityStarted(activity: Activity) {
GlobalActivityManager.currentActivity = activity
}
override fun onActivityResumed(activity: Activity) {
// 判断是否需要显示或隐藏返回小浮窗
if (FloatingBackViewManager.getType().isNotEmpty()) {
if ((activity::class.java.name == "EnergyCenterActivity")
&& FloatingBackViewManager.getType() == FloatingBackViewManager.TYPE_TASK
) {
FloatingBackViewManager.disableBackView()
} else if (activity is SingletonWebActivity
&& FloatingBackViewManager.getType() == FloatingBackViewManager.TYPE_ACTIVITY
) {
FloatingBackViewManager.disableBackView()
} else {
FloatingBackViewManager.showBackView(activity)
}
}
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
// FIXME 这里应该只是部分Activity需要
try {
// 初始化gameMap
if (activity !is SplashScreenActivity) {
DownloadManager.getInstance().initGameMap()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
if (PackageFlavorHelper.IS_TEST_FLAVOR && activity is AppCompatActivity) {
DarkModeSwitchHelper.showDarkModeSwitchFloatingView(activity)
}
if (activity is AppCompatActivity && activity !is SplashScreenActivity) {
VHelper.showFeedbackDialogIfLastSuccessfulLaunchedGameExitUnexpectedly(activity)
}
}
override fun onActivityPaused(activity: Activity) {
FloatingBackViewManager.dismissBackView()
if (PackageFlavorHelper.IS_TEST_FLAVOR && activity is AppCompatActivity) {
DarkModeSwitchHelper.dismissDarkModeSwitchFloatingView()
}
}
override fun onActivityStopped(activity: Activity) {
// do nothing
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
// do nothing
}
override fun onActivityDestroyed(activity: Activity) {
// do nothing
}
}

View File

@ -1,14 +0,0 @@
package com.gh.base
import android.app.Activity
import java.lang.ref.WeakReference
object GlobalActivityManager {
private var mCurrentActivityWeakRef: WeakReference<Activity>? = null
var currentActivity: Activity?
get() = mCurrentActivityWeakRef?.get()
set(activity) {
mCurrentActivityWeakRef = WeakReference<Activity>(activity)
}
}

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.common.callback;
package com.gh.base;
import android.content.Context;
import android.view.GestureDetector;

View File

@ -0,0 +1,19 @@
package com.gh.base;
import android.view.View;
/**
* Created by khy on 26/09/17.
*/
public interface OnListClickListener {
/**
*
* @param view
* @param position list position
* @param data list data (直接强转 如果列表传入不同数据类型 请做好判断)
* @param <T>
*/
<T> void onListClick(View view, int position, T data);
}

View File

@ -0,0 +1,20 @@
package com.gh.base;
/**
* Created by Administrator on 2016/9/8.
*
* 逐步移除
*/
@Deprecated
public interface OnRequestCallBackListener<T> {
void loadDone();
void loadDone(T obj);
void loadError();
void loadEmpty();
}

View File

@ -0,0 +1,7 @@
package com.gh.base;
import android.view.View;
public interface OnViewClickListener<T> {
void onClick(View v, T data);
}

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.core.iinterface;
package com.gh.base;
import java.util.ArrayList;

View File

@ -0,0 +1,262 @@
package com.gh.base;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import com.gh.common.util.DisplayUtils;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.entity.GameUpdateEntity;
import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.normal.ToolbarController;
import com.gh.gamecenter.packagehelper.PackageViewModel;
import com.lightgame.OnTitleClickListener;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
/**
* 需要用到工具栏的页面使用
* <p>
* 特殊页面请参考{@link BaseActivity}
*/
public abstract class ToolBarActivity extends BaseActivity implements ToolbarController, Toolbar.OnMenuItemClickListener {
@Nullable
private PackageViewModel mPackageViewModel;
protected View mToolbarContainer;
protected Toolbar mToolbar;
protected TextView mTitleTv;
@Nullable
private TextView mDownloadCountHint;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStatusBarDarkMode(true, this);
initToolbar();
if (showDownloadMenu()) {
mPackageViewModel = ViewModelProviders.of(this, new PackageViewModel.Factory()).get(PackageViewModel.class);
mPackageViewModel.getFilterSameUpdateLiveData().observe(this, this::updateDownloadCountHint);
}
}
// 小米沉浸式黑色字体
@SuppressLint("PrivateApi")
public void setStatusBarDarkMode(boolean darkmode, Activity activity) {
Class<? extends Window> clazz = activity.getWindow().getClass();
try {
Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
extraFlagField.invoke(activity.getWindow(), darkmode ? darkModeFlag : 0, darkModeFlag);
} catch (Exception ignore) {
}
}
private void initToolbar() {
mToolbarContainer = findViewById(R.id.normal_toolbar_container);
mToolbar = findViewById(R.id.normal_toolbar);
mTitleTv = findViewById(R.id.normal_title);
if (mToolbar != null) {
// setSupportActionBar(mToolbar); // 替换actionBar后 toolBar无法控制
mToolbar.setNavigationIcon(provideNavigationIcon());
mToolbar.setNavigationOnClickListener(provideNavigationItemClickListener());
if (mTitleTv != null) {
mTitleTv.setOnClickListener(view -> {
final List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragmentList) {
if (fragment instanceof OnTitleClickListener) {
((OnTitleClickListener) fragment).onTitleClick();
}
}
});
}
}
}
@DrawableRes
public int provideNavigationIcon() {
return R.drawable.ic_bar_back; // default navigation icon
}
@Override
public void setNavigationTitle(String title) {
if (mTitleTv != null) mTitleTv.setText(title);
}
@Override
public void setNavigationTitle(@StringRes int res) {
setNavigationTitle(getString(res));
}
/**
* 重写此方法以将标题靠左显示
*/
public boolean showToolbarAtLeft() {
return false;
}
@Override
public void setToolbarMenu(int res) {
if (mToolbar == null) return;
mToolbar.inflateMenu(res);
mToolbar.setOnMenuItemClickListener(this);
if (showDownloadMenu()) {
createDownloadMenu(res);
}
Menu menu = mToolbar.getMenu();
for (int i = 0; i < menu.size(); i++) {
MenuItem menuItem = menu.getItem(i);
// menu设置actionLayout后无法捕捉点击事件以icon为tag如果icon is null 手动设置menuItem点击事件
if (menuItem != null && menuItem.getIcon() == null) {
if (menuItem.getActionView() != null) {
menuItem.getActionView().setOnClickListener((v) -> this.onMenuItemClick(menuItem));
}
}
}
// 限制标题实际宽度 防止标题挡住toolbar menu按钮
if (menu.size() > 2 && mTitleTv != null) {
ViewGroup.LayoutParams layoutParams = mTitleTv.getLayoutParams();
if (layoutParams instanceof RelativeLayout.LayoutParams) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) layoutParams;
if (showToolbarAtLeft()) {
params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
params.addRule(RelativeLayout.CENTER_VERTICAL);
params.setMargins(DisplayUtils.dip2px(55), 0, DisplayUtils.dip2px(48 * menu.size()), 0);
} else {
params.setMargins(DisplayUtils.dip2px(90), 0, DisplayUtils.dip2px(90), 0);
}
mTitleTv.setLayoutParams(params);
}
} else {
if (showToolbarAtLeft()) {
ViewGroup.LayoutParams layoutParams = mTitleTv.getLayoutParams();
if (layoutParams instanceof RelativeLayout.LayoutParams) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) layoutParams;
params.addRule(RelativeLayout.CENTER_VERTICAL);
params.setMargins(DisplayUtils.dip2px(55), 0, DisplayUtils.dip2px(48 * menu.size()), 0);
mTitleTv.setLayoutParams(params);
}
}
}
}
private void createDownloadMenu(int res) {
if (res != R.menu.menu_download) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_download, mToolbar.getMenu());
}
if (mPackageViewModel != null) {
updateDownloadCountHint(mPackageViewModel.getFilterSameUpdateLiveData().getValue());
}
View downloadMenuView = mToolbar.getMenu().findItem(R.id.menu_download).getActionView();
mDownloadCountHint = downloadMenuView.findViewById(R.id.menu_download_count_hint);
}
private void updateDownloadCountHint(List<GameUpdateEntity> updateList) {
if (mDownloadCountHint == null) return;
String count = DownloadManager.getInstance(getApplicationContext()).getDownloadOrUpdateCount(updateList);
if (count != null) {
mDownloadCountHint.setVisibility(View.VISIBLE);
mDownloadCountHint.setText(count);
ViewGroup.LayoutParams params = mDownloadCountHint.getLayoutParams();
if (TextUtils.isEmpty(count)) {
params.width = DisplayUtils.dip2px(6);
params.height = DisplayUtils.dip2px(6);
} else {
params.width = DisplayUtils.dip2px(12);
params.height = DisplayUtils.dip2px(12);
}
mDownloadCountHint.setLayoutParams(params);
} else {
mDownloadCountHint.setVisibility(View.GONE);
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(EBDownloadStatus status) {
if (showDownloadMenu() && mPackageViewModel != null) {
updateDownloadCountHint(mPackageViewModel.getFilterSameUpdateLiveData().getValue());
}
}
@Override
public MenuItem getMenuItem(int res) {
if (mToolbar == null) return null; //后续页面做好判断
return mToolbar.getMenu().findItem(res);
}
public void clearMenu() {
if (mToolbar != null) {
mToolbar.getMenu().clear();
}
}
public Menu getMenu() {
return mToolbar.getMenu();
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.menu_download) {
// MtaHelper.onEvent("下载管理", "下载管理入口", getActivityNameInChinese());
Intent intent = DownloadManagerActivity.getDownloadMangerIntent(this, mEntrance);
startActivity(intent);
}
return false;
}
protected View.OnClickListener provideNavigationItemClickListener() {
return view -> onBackPressed();
}
protected boolean showDownloadMenu() {
return false;
}
@Override
public void hideToolbar(boolean isHide) {
if (mToolbarContainer != null) {
mToolbarContainer.setVisibility(isHide ? View.GONE : View.VISIBLE);
}
}
}

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.common.base.adapter;
package com.gh.base.adapter;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

View File

@ -0,0 +1,31 @@
package com.gh.base.adapter;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import java.util.List;
/**
* Created by khy on 7/12/28.
*/
public class FragmentStateAdapter extends FragmentStatePagerAdapter {
private List<Fragment> mFragmentList;
public FragmentStateAdapter(FragmentManager fm, List<Fragment> fragmentList) {
super(fm);
this.mFragmentList = fragmentList;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
}

View File

@ -0,0 +1,80 @@
package com.gh.base.fragment;
import android.app.Dialog;
import android.os.Bundle;
import android.view.KeyEvent;
import com.gh.common.util.ClickUtils;
import com.gh.gamecenter.R;
import com.lightgame.utils.RuntimeUtils;
import com.lightgame.utils.Utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle;
/**
* @author CsHeng
* @Date 17/05/2017
* @Time 4:30 PM
*/
public class BaseDialogFragment extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Dialog dialog = new Dialog(getActivity(), R.style.DialogWindowTransparent);
dialog.setCanceledOnTouchOutside(false);
dialog.setOnKeyListener((dialog1, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK && !ClickUtils.isFastDoubleClick()) {
return onBack();
}
return false;
});
dialog.setCancelable(false);
return dialog;
}
public void toast(@StringRes int res) {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
toast(getString(res));
}
public void toast(String msg) {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
Utils.toast(getContext(), msg);
}
public void toastLong(@StringRes int msg) {
toastLong(getString(msg));
}
public void toastLong(String msg) {
RuntimeUtils.getInstance().toastLong(getContext(), msg);
}
public boolean onBack() {
return false;
}
@Override
public void show(@NonNull FragmentManager manager, @Nullable String tag) {
Fragment fragment = manager.findFragmentByTag(tag);
if (fragment != null) {
FragmentTransaction transaction = manager.beginTransaction();
transaction.show(fragment);
transaction.commit();
} else {
super.show(manager, tag);
}
}
}

View File

@ -1,18 +1,16 @@
package com.gh.gamecenter.common.base.fragment;
package com.gh.base.fragment;
import android.app.Dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.gh.gamecenter.common.R;
import com.gh.gamecenter.R;
/**
* Wrap another fragment with dialog fragment.

View File

@ -1,9 +1,6 @@
package com.gh.gamecenter.common.base.fragment;
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_ENTRANCE;
package com.gh.base.fragment;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@ -20,16 +17,14 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.recyclerview.widget.RecyclerView;
import com.gh.gamecenter.common.BuildConfig;
import com.gh.gamecenter.common.R;
import com.gh.gamecenter.common.callback.OnListClickListener;
import com.gh.gamecenter.common.callback.OnRequestCallBackListener;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.eventbus.EBMiPush;
import com.gh.gamecenter.common.syncpage.ISyncAdapterHandler;
import com.gh.gamecenter.common.syncpage.SyncDataEntity;
import com.gh.gamecenter.common.syncpage.SyncPageRepository;
import com.gh.gamecenter.common.utils.DarkModeUtils;
import com.gh.base.OnListClickListener;
import com.gh.base.OnRequestCallBackListener;
import com.gh.common.constant.Constants;
import com.gh.common.syncpage.ISyncAdapterHandler;
import com.gh.common.syncpage.SyncDataEntity;
import com.gh.common.syncpage.SyncPageRepository;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.eventbus.EBMiPush;
import com.lightgame.OnTitleClickListener;
import com.lightgame.utils.RuntimeUtils;
import com.lightgame.utils.Utils;
@ -42,11 +37,14 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import butterknife.ButterKnife;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import kotlin.Pair;
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
/**
* Created by LGT on 2016/9/4.
* Fragment 基类
@ -60,13 +58,9 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
protected boolean isEverPause;
protected boolean mIsDarkModeOn;
@NonNull
protected String mEntrance = "";
public long startPageTime = 0;
protected final Handler mBaseHandler = new BaseFragment.BaseHandler(this);
protected static class BaseHandler extends Handler {
@ -114,12 +108,6 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
protected void initView(View view) {
View mBackBtn = view.findViewById(R.id.backBtn);
View mBackContainer = view.findViewById(R.id.backContainer);
if (mBackBtn != null && mBackContainer != null) {
mBackBtn.setOnClickListener(v -> requireActivity().onBackPressed());
mBackContainer.setOnClickListener(v -> requireActivity().onBackPressed());
}
}
protected void postRunnable(Runnable runnable) {
@ -152,21 +140,21 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
EventBus.getDefault().register(this);
// For data binding.
mCachedView = getInflatedLayout();
if (mCachedView == null) {
if (getInflatedLayout() != null) {
mCachedView = getInflatedLayout();
} else {
mCachedView = View.inflate(getContext(), getLayoutId(), null);
}
if (useButterKnife()) {
ButterKnife.bind(this, mCachedView);
}
initView(mCachedView);
if (addSyncPageObserver()) {
initSyncPageObserver();
}
if (BuildConfig.IS_DARK_MODE_ON) {
mIsDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(requireContext());
} else {
mIsDarkModeOn = false;
}
}
private void initSyncPageObserver() {
@ -219,10 +207,6 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (container != null) {
container.removeView(mCachedView);
// TODO 页面重建 (framgent 的重新获取) 有大问题这里只是修修补补
if (mCachedView != null && mCachedView.getParent() instanceof ViewGroup) {
((ViewGroup) mCachedView.getParent()).removeView(mCachedView);
}
}
return mCachedView;
}
@ -238,13 +222,6 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
public void onResume() {
super.onResume();
isEverPause = false;
startPageTime = System.currentTimeMillis();
if (BuildConfig.IS_DARK_MODE_ON
&& !DarkModeUtils.INSTANCE.isFollowSystemDarkModeFromSp()
&& mIsDarkModeOn != DarkModeUtils.INSTANCE.isDarkModeOn(requireContext())) {
onDarkModeChanged();
}
}
@Override
@ -283,27 +260,22 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
@Override
public void loadDone() {
// do nothing
}
@Override
public void loadDone(T obj) {
// do nothing
}
@Override
public void loadError() {
// do nothing
}
@Override
public void loadEmpty() {
// do nothing
}
@Override
public void loadNotFound() {
// do nothing
}
@Override
@ -335,9 +307,7 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
// fragment 附加 bundle (setArgument())
public BaseFragment with(Bundle bundle) {
if (!isStateSaved()) {
this.setArguments(bundle);
}
this.setArguments(bundle);
return this;
}
@ -354,17 +324,7 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
return false;
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (BuildConfig.IS_DARK_MODE_ON && mIsDarkModeOn != DarkModeUtils.INSTANCE.isDarkModeOn(requireContext())) {
mIsDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(requireContext());
onDarkModeChanged();
}
}
protected void onDarkModeChanged() {
protected boolean useButterKnife() {
return true;
}
}

View File

@ -1,6 +1,5 @@
package com.gh.gamecenter.common.base.fragment;
package com.gh.base.fragment;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
@ -9,35 +8,38 @@ import android.view.View;
import android.widget.CheckedTextView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import com.gh.gamecenter.common.R;
import com.gh.gamecenter.common.base.adapter.FragmentAdapter;
import com.gh.gamecenter.common.view.TabIndicatorView;
import com.gh.base.adapter.FragmentAdapter;
import com.gh.common.view.TabIndicatorView;
import com.gh.gamecenter.R;
import com.gh.gamecenter.normal.NormalFragment;
import com.google.android.material.tabs.TabLayout;
import com.halo.assistant.HaloApp;
import com.lightgame.utils.Utils;
import com.lightgame.view.NoScrollableViewPager;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
/**
* Created by khy on 15/03/18.
*/
public abstract class BaseFragment_TabLayout extends ToolbarFragment implements ViewPager.OnPageChangeListener {
public abstract class BaseFragment_TabLayout extends NormalFragment implements ViewPager.OnPageChangeListener {
public static final String PAGE_INDEX = "PAGE_INDEX";
@BindView(R.id.fragment_tab_layout)
protected TabLayout mTabLayout;
@BindView(R.id.fragment_view_pager)
protected NoScrollableViewPager mViewPager;
@BindView(R.id.fragment_tab_indicator)
protected TabIndicatorView mTabIndicatorView;
@Nullable
protected View mDividerLineView;
protected List<Fragment> mFragmentsList;
@ -73,6 +75,19 @@ public abstract class BaseFragment_TabLayout extends ToolbarFragment implements
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) mCheckedIndex = getArguments().getInt(PAGE_INDEX, 0);
mTabTitleList = new ArrayList<>();
initTabTitleList(mTabTitleList);
mFragmentsList = new ArrayList<>(restoreFragments());
if (mFragmentsList.isEmpty() || mFragmentsList.size() != mTabTitleList.size()) {
mFragmentsList.clear();
initFragmentList(mFragmentsList);
}
}
private ArrayList<Fragment> restoreFragments() {
String tag = "android:switcher:" + mViewPager.getId() + ":";
ArrayList<Fragment> fragments = new ArrayList<>();
@ -90,21 +105,6 @@ public abstract class BaseFragment_TabLayout extends ToolbarFragment implements
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mTabLayout = view.findViewById(R.id.fragment_tab_layout);
mViewPager = view.findViewById(R.id.fragment_view_pager);
mTabIndicatorView = view.findViewById(R.id.fragment_tab_indicator);
mDividerLineView = view.findViewById(R.id.dividerLine);
if (getArguments() != null) mCheckedIndex = getArguments().getInt(PAGE_INDEX, 0);
mTabTitleList = new ArrayList<>();
initTabTitleList(mTabTitleList);
mFragmentsList = new ArrayList<>(restoreFragments());
if (mFragmentsList.isEmpty() || mFragmentsList.size() != mTabTitleList.size()) {
mFragmentsList.clear();
initFragmentList(mFragmentsList);
}
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
mViewPager.addOnPageChangeListener(this);
mViewPager.setAdapter(new FragmentAdapter(getChildFragmentManager(), mFragmentsList, mTabTitleList));
@ -119,7 +119,7 @@ public abstract class BaseFragment_TabLayout extends ToolbarFragment implements
if (tab == null) continue;
String tabTitle = tab.getText() != null ? tab.getText().toString() : "";
View tabView = provideTabView(i, tabTitle);
if (tabView == null) tabView = createDefaultTabCustomView(requireContext(), tabTitle);
if (tabView == null) tabView = createDefaultTabCustomView(tabTitle);
tab.setCustomView(tabView);
}
@ -152,7 +152,7 @@ public abstract class BaseFragment_TabLayout extends ToolbarFragment implements
});
}
public static void updateTabStyle(TabLayout.Tab tab, boolean isChecked) {
private static void updateTabStyle(TabLayout.Tab tab, boolean isChecked) {
View tabView = tab.getCustomView();
if (tabView == null) {
Utils.log("TabLayout->Tab样式不是通用样式,请检查");
@ -171,13 +171,12 @@ public abstract class BaseFragment_TabLayout extends ToolbarFragment implements
return;
}
tabTitle.setTypeface(isChecked ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tabTitle.setTextColor(ContextCompat.getColor(tabTitle.getContext(), isChecked ? R.color.theme_font : R.color.text_subtitle));
}
// 如果不设置View的话无法动态设置字体样式
@NonNull
public static View createDefaultTabCustomView(Context context, String title) {
View view = LayoutInflater.from(context).inflate(R.layout.tab_item, null);
public static View createDefaultTabCustomView(String title) {
View view = LayoutInflater.from(HaloApp.getInstance().getApplication().getBaseContext()).inflate(R.layout.tab_item, null);
View tabTitle = view.findViewById(R.id.tab_title);
if (tabTitle instanceof CheckedTextView) {
((CheckedTextView) tabTitle).setText(title);
@ -199,22 +198,4 @@ public abstract class BaseFragment_TabLayout extends ToolbarFragment implements
public void onPageScrollStateChanged(int state) {
}
@Override
protected void onDarkModeChanged() {
super.onDarkModeChanged();
View container = requireView().findViewById(R.id.fragment_tab_container);
if (container != null) {
container.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.background_white));
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
TabLayout.Tab tab = mTabLayout.getTabAt(i);
if (tab != null) {
updateTabStyle(tab, tab.isSelected());
}
}
}
if (mDividerLineView != null) {
mDividerLineView.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.divider));
}
}
}

View File

@ -7,19 +7,19 @@
* 2013-3-6 CsHeng
*/
package com.gh.gamecenter.common.base.fragment;
package com.gh.base.fragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import android.view.View;
import com.gh.gamecenter.normal.NormalFragment;
import com.lightgame.adapter.BaseFragmentPagerAdapter;
import com.lightgame.config.CommonDebug;
import com.lightgame.view.DoubleTapTextView;
@ -34,7 +34,7 @@ import java.util.List;
* @author CsHeng
* @date 2013-3-6
*/
public abstract class BaseFragment_ViewPager extends ToolbarFragment implements DoubleTapTextView.OnDoubleTapListener {
public abstract class BaseFragment_ViewPager extends NormalFragment implements DoubleTapTextView.OnDoubleTapListener {
public static final String ARGS_INDEX = "index";
protected int mCheckedIndex = 0;
@ -53,21 +53,19 @@ public abstract class BaseFragment_ViewPager extends ToolbarFragment implements
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = view.findViewById(getViewPagerId());
mFragmentsList = restoreFragments();
if (mFragmentsList.size() == 0) {
initFragmentList(mFragmentsList);
}
mFragmentsList = new ArrayList<>();
initFragmentList(mFragmentsList);
mAdapter = BaseFragmentPagerAdapter.newInstance(getChildFragmentManager(), mFragmentsList);
final Bundle args = getArguments();
if (args != null) {
mCheckedIndex = args.getInt(ARGS_INDEX);
}
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = (ViewPager) view.findViewById(getViewPagerId());
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
mViewPager.setAdapter(mAdapter);
if (mCheckedIndex < mFragmentsList.size()) {
@ -118,24 +116,9 @@ public abstract class BaseFragment_ViewPager extends ToolbarFragment implements
fragment.onActivityResult(requestCode, resultCode, data);
}
}
}
public ArrayList<Fragment> restoreFragments() {
String tag = "android:switcher:" + mViewPager.getId() + ":";
ArrayList<Fragment> fragments = new ArrayList<>();
int childCount = getChildCount();
for (int index = 0; index < childCount; index++) {
Fragment fragment = getChildFragmentManager().findFragmentByTag(tag + index);
if (fragment != null) {
fragments.add(fragment);
}
}
return fragments;
}
public abstract int getChildCount();
public int getCurrentItem() {
return mViewPager != null ? mViewPager.getCurrentItem() : 0;
}

View File

@ -7,21 +7,23 @@
* 2013-3-6 CsHeng
*/
package com.gh.gamecenter.common.base.fragment;
package com.gh.base.fragment;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Checkable;
import com.gh.gamecenter.fragment.MainWrapperFragment;
import java.util.List;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager.widget.ViewPager;
import java.util.List;
/**
* ViewPager 配合ViewGroup Checkable实现双切换<br/>
@ -36,7 +38,7 @@ public abstract class BaseFragment_ViewPager_Checkable extends BaseFragment_View
protected ViewGroup mCheckableGroup;
private int mLastPosition = 0;
private int mLastPosition = MainWrapperFragment.INDEX_HOME;
@IdRes
protected abstract int getCheckableGroupId();
@ -80,14 +82,14 @@ public abstract class BaseFragment_ViewPager_Checkable extends BaseFragment_View
if (mFragmentsList.size() > mLastPosition) {
Fragment fragment = mFragmentsList.get(mLastPosition);
fragment.onPause();
FragmentManager childFragmentManager = fragment.getChildFragmentManager();
List<Fragment> fragments = childFragmentManager.getFragments();
for (Fragment childFragment : fragments) {
childFragment.onPause();
}
}
if (mFragmentsList.size() > index) {
Fragment fragment = mFragmentsList.get(index);
fragment.onResume();

View File

@ -1,11 +1,13 @@
package com.gh.gamecenter.common.base.fragment
package com.gh.base.fragment
import android.os.Bundle
import com.gh.gamecenter.normal.NormalFragment
import com.lightgame.utils.Utils
/**
* 懒加载支持多层嵌套
*/
abstract class BaseLazyFragment : ToolbarFragment() {
abstract class BaseLazyFragment : NormalFragment() {
private var mIsFirstVisible = true
@ -103,25 +105,15 @@ abstract class BaseLazyFragment : ToolbarFragment() {
isSupportVisible = visible
if (visible) {
// TODO 当 fragment 重建时这里的被调用很奇怪onActivityCreated 回调触发,但此时的 view 是空的,原因是 createView 还没被调用
// TODO 这样就造成了 onFragmentResume 里可能用到 view 的地方出现空指针异常,所以这里遇到 view 为空的时候 return 等下一次被调用才进去
if (view == null) {
return
}
if (mIsFirstVisible) {
if (mIsFirstVisible && view != null) {
mIsFirstVisible = false
onFragmentFirstVisible()
}
onFragmentResume()
dispatchChildVisibleState(true)
} else {
// 当 fragment 重建时,这个代码块可能在第一次 view 为空的 visible 后调用导致在 onFragmentPause 里可能用到 view 的地方出现空指针异常
if (!mIsFirstVisible) {
dispatchChildVisibleState(false)
onFragmentPause()
}
dispatchChildVisibleState(false)
onFragmentPause()
}
}

View File

@ -0,0 +1,123 @@
package com.gh.base.fragment
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager
import butterknife.BindView
import com.gh.base.adapter.FragmentAdapter
import com.gh.common.view.TabIndicatorView
import com.gh.gamecenter.R
import com.google.android.material.tabs.TabLayout
import com.lightgame.view.NoScrollableViewPager
import java.util.*
abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeListener {
@BindView(R.id.fragment_tab_layout)
lateinit var mTabLayout: TabLayout
@BindView(R.id.fragment_view_pager)
lateinit var mViewPager: NoScrollableViewPager
@BindView(R.id.fragment_tab_indicator)
lateinit var mTabIndicatorView: TabIndicatorView
var mFragmentsList: MutableList<Fragment> = arrayListOf()
var mTabTitleList: MutableList<String> = arrayListOf()
var mCheckedIndex = 0
abstract fun initFragmentList(fragments: MutableList<Fragment>)
abstract fun initTabTitleList(tabTitleList: MutableList<String>)
protected open fun provideIndicatorWidth(): Int {
return 20
}
protected open fun provideTabView(position: Int, tabTitle: String?): View? {
return null
}
override fun getLayoutId(): Int {
return R.layout.fragment_tablayout_viewpager
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val fragments = childFragmentManager.fragments
if (fragments != null) {
for (fragment in fragments) {
fragment.onActivityResult(requestCode, resultCode, data)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (arguments != null) mCheckedIndex = requireArguments().getInt(PAGE_INDEX, 0)
}
open fun providePagerAdapter(): PagerAdapter? {
return null
}
override fun onFragmentFirstVisible() {
super.onFragmentFirstVisible()
mTabTitleList.clear()
mFragmentsList.clear()
initTabTitleList(mTabTitleList)
mFragmentsList.addAll(restoreFragments())
if (mFragmentsList.isEmpty() || mFragmentsList.size != mTabTitleList.size) {
mFragmentsList.clear()
initFragmentList(mFragmentsList)
}
mViewPager.offscreenPageLimit = mFragmentsList.size
mViewPager.addOnPageChangeListener(this)
mViewPager.adapter = providePagerAdapter()
?: FragmentAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
mViewPager.currentItem = mCheckedIndex
mTabLayout.setupWithViewPager(mViewPager)
mTabIndicatorView.setupWithTabLayout(mTabLayout)
mTabIndicatorView.setupWithViewPager(mViewPager)
mTabIndicatorView.setIndicatorWidth(provideIndicatorWidth())
for (i in 0 until mTabLayout.tabCount) {
val tab = mTabLayout.getTabAt(i) ?: continue
val tabTitle = if (tab.text != null) tab.text.toString() else ""
var tabView = provideTabView(i, tabTitle)
if (tabView == null) tabView = BaseFragment_TabLayout.createDefaultTabCustomView(tabTitle)
tab.customView = tabView
}
BaseFragment_TabLayout.initTabStyle(mTabLayout, mCheckedIndex)
}
private fun restoreFragments(): ArrayList<Fragment> {
val tag = "android:switcher:${mViewPager.id}:"
val fragments = ArrayList<Fragment>()
val childCount = mTabTitleList.size
for (index in 0 until childCount) {
val fragment = childFragmentManager.findFragmentByTag("$tag$index")
if (fragment != null) {
fragments.add(fragment)
}
}
return fragments
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {}
override fun onPageScrollStateChanged(state: Int) {}
companion object {
const val PAGE_INDEX = "PAGE_INDEX"
}
}

View File

@ -0,0 +1,5 @@
package com.gh.base.fragment;
public interface OnDialogBackListener {
void onBack();
}

View File

@ -1,19 +1,16 @@
package com.gh.gamecenter.common.base.fragment;
package com.gh.base.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import com.gh.gamecenter.common.R;
import com.gh.gamecenter.common.callback.OnDialogBackListener;
import com.gh.gamecenter.core.utils.DisplayUtils;
import com.gh.gamecenter.R;
/**
* @author CsHeng
@ -55,14 +52,6 @@ public class WaitingDialogFragment extends BaseDialogFragment {
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
WindowManager.LayoutParams layoutParams = getDialog().getWindow().getAttributes();
layoutParams.width = DisplayUtils.dip2px(160);
getDialog().getWindow().setAttributes(layoutParams);
}
@Override
public void show(FragmentManager manager, String tag) {
try {
@ -92,17 +81,8 @@ public class WaitingDialogFragment extends BaseDialogFragment {
@Override
public void dismiss() {
dismissAllowingStateLoss();
}
@Override
public void dismissAllowingStateLoss() {
mBackListener = null;
try {
super.dismissAllowingStateLoss();
} catch (Exception e) {
e.printStackTrace();
}
super.dismiss();
}
public static class WaitingDialogData {

View File

@ -0,0 +1,54 @@
package com.gh.common
import android.os.Handler
import android.os.Looper
import com.gh.common.AppExecutor.ioExecutor
import com.gh.common.AppExecutor.lightWeightIoExecutor
import com.gh.common.AppExecutor.uiExecutor
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.Executor
import java.util.concurrent.Executors
/**
* APP 线程池管理类
*
* [ioExecutor] 是一个最大线程数固定的线程池,较为繁重的 IO 任务可以交给它
* [uiExecutor] 是主线程的包裹,需要切换至主线程执行可以用它
* [lightWeightIoExecutor] 是一个单线程的线程池,轻量级且需要保证同一线程的 IO 任务可以交给它
*
*/
object AppExecutor {
@JvmStatic
val uiExecutor by lazy { MainThreadExecutor() }
@JvmStatic
val lightWeightIoExecutor by lazy { Executors.newSingleThreadExecutor() }
@JvmStatic
val ioExecutor = Executors.newCachedThreadPool() // 用 by lazy 可能影响初始化速度
val cachedScheduler by lazy { Schedulers.from(ioExecutor) }
class MainThreadExecutor : Executor {
private val mainThreadHandler = Handler(Looper.getMainLooper())
override fun execute(command: Runnable) {
mainThreadHandler.post(command)
}
fun executeWithDelay(command: Runnable, delay: Long) {
mainThreadHandler.postDelayed(command, delay)
}
}
}
fun runOnIoThread(isLightWeightTask: Boolean = false, f: () -> Unit) {
if (isLightWeightTask) {
AppExecutor.lightWeightIoExecutor.execute(f)
} else {
AppExecutor.ioExecutor.execute(f)
}
}
fun runOnUiThread(f: () -> Unit) {
AppExecutor.uiExecutor.execute(f)
}

View File

@ -0,0 +1,5 @@
package com.gh.common
object Base64ImageHolder {
var image: String = ""
}

View File

@ -3,71 +3,37 @@ package com.gh.common
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.util.Base64
import android.view.View
import android.webkit.JavascriptInterface
import androidx.annotation.Keep
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.base.CurrentActivityHolder
import com.gh.common.constant.Constants
import com.gh.common.util.*
import com.gh.common.util.LogUtils
import com.gh.download.DownloadManager
import com.gh.download.PackageObserver
import com.gh.common.view.dsbridge.CompletionHandler
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.ImageViewerActivity
import com.gh.gamecenter.LoginActivity
import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.common.callback.BiCallback
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.entity.NotificationUgc
import com.gh.gamecenter.common.loghub.LoghubUtils
import com.gh.gamecenter.common.provider.IHelpAndFeedbackProvider
import com.gh.gamecenter.common.tracker.Tracker
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.dsbridge.CompletionHandler
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.energy.EnergyBridge
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.Badge
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.login.user.LoginTag
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.login.user.UserRepository
import com.gh.gamecenter.login.utils.LoginHelper
import com.gh.gamecenter.login.utils.QuickLoginHelper
import com.gh.gamecenter.login.view.LoginActivity
import com.gh.gamecenter.personalhome.border.AvatarBorderActivity
import com.gh.gamecenter.setting.SettingBridge
import com.gh.vspace.VHelper
import com.gh.gamecenter.entity.Badge
import com.gh.gamecenter.entity.MtaEvent
import com.gh.gamecenter.entity.NotificationUgc
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.retrofit.BiResponse
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.security.BindPhoneActivity
import com.gh.gamecenter.user.LoginTag
import com.gh.gamecenter.user.UserRepository
import com.halo.assistant.HaloApp
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus.*
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.json.JSONObject
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import retrofit2.HttpException
import java.util.*
import kotlin.collections.ArrayList
class DefaultJsApi(var context: Context, val entrance: String = "", private var mFragment: Fragment? = null) {
private var mLoginHandler: CompletionHandler<Any>? = null
private var mDownloadWatcher: DataWatcher? = null
private var mDownloadUrlSet: HashSet<String>? = null
private var mDownloadHandler: CompletionHandler<Any>? = null
init {
if (mFragment != null) {
autoUnregisterDownloadObserverIfNeeded(mFragment)
}
}
class DefaultJsApi(var context: Context) {
@JavascriptInterface
fun isGhzs(msg: Any): String {
@ -81,7 +47,9 @@ class DefaultJsApi(var context: Context, val entrance: String = "", private var
@JavascriptInterface
fun logMtaEvent(event: Any) {
// do nothing, mta is deprecated
val mtaEvent = event.toString().toObject() ?: MtaEvent()
MtaHelper.onEvent(mtaEvent.name, mtaEvent.key, mtaEvent.value)
}
@JavascriptInterface
@ -96,12 +64,8 @@ class DefaultJsApi(var context: Context, val entrance: String = "", private var
@JavascriptInterface
fun login(msg: Any) {
if (SPUtils.getBoolean(Constants.SP_HAS_GET_PHONE_INFO) || NetworkUtils.isOpenMobileData(context)) {
QuickLoginHelper.startLogin(context, "浏览器")
} else {
val intent = LoginActivity.getIntent(context, "浏览器")
context.startActivity(intent)
}
val intent = LoginActivity.getIntent(context, "浏览器")
context.startActivity(intent)
}
@JavascriptInterface
@ -128,7 +92,7 @@ class DefaultJsApi(var context: Context, val entrance: String = "", private var
@JavascriptInterface
fun getAppVersionCode(msg: Any): Int {
return PackageUtils.getGhVersionCode()
return PackageUtils.getVersionCode()
}
@JavascriptInterface
@ -144,56 +108,41 @@ class DefaultJsApi(var context: Context, val entrance: String = "", private var
wechatLoginInfoMap["access_token"] = jsonContent.getString("access_token")
wechatLoginInfoMap["refresh_token"] = jsonContent.getString("refresh_token")
WechatBindHelper.bindWechat(wechatLoginInfoMap, object : BiCallback<Boolean, Boolean> {
override fun onFirst(first: Boolean) {
EnergyBridge.postEnergyTask("bind_wechat")
handler.complete(true)
}
RetrofitManager.getInstance(HaloApp.getInstance().application)
.api
.postBindWechat(wechatLoginInfoMap.createRequestBody())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<ResponseBody>() {
override fun onSuccess(data: ResponseBody) {
handler.complete(true)
}
override fun onSecond(second: Boolean) {
handler.complete(false)
}
})
LoginHelper.unregisterCallback()
override fun onFailure(exception: Exception) {
handler.complete(false)
if (exception is HttpException) {
ErrorHelper.handleError(HaloApp.getInstance().application, exception.response().errorBody()?.string())
}
}
})
}
override fun onLoginFailure(loginType: LoginTag, error: String) {
handler.complete(false)
LoginHelper.unregisterCallback()
}
})
}
}
@JavascriptInterface
fun refreshWechatBindData(msg: Any) {
WechatBindHelper.getWechatConfig(null)
}
@JavascriptInterface
fun copyText(msg: Any) {
runOnUiThread {
msg.toString().copyTextAndToast()
}
msg.toString().copyTextAndToast()
}
@JavascriptInterface
fun startApp(msg: Any) {
val packageName = msg.toString()
val context = HaloApp.getInstance().application
runOnUiThread {
// 若畅玩列表中安装了,优先启动畅玩游戏
if (VHelper.isInstalled(packageName)) {
if (!VHelper.showDialogIfVSpaceIsNeeded(context)) {
VHelper.launch(context, packageName)
}
} else {
PackageUtils.launchApplicationByPackageName(context, packageName)
}
}
PackageUtils.launchApplicationByPackageName(HaloApp.getInstance().application, packageName)
}
@JavascriptInterface
@ -223,7 +172,7 @@ class DefaultJsApi(var context: Context, val entrance: String = "", private var
fun openBase64Image(event: Any) {
val context = CurrentActivityHolder.getCurrentActivity()
ImageViewerActivity.base64Image = event.toString()
Base64ImageHolder.image = event.toString()
context?.startActivity(ImageViewerActivity.getBase64Intent(context, true))
}
@ -249,7 +198,6 @@ class DefaultJsApi(var context: Context, val entrance: String = "", private var
@JavascriptInterface
fun updateRegulationTestStatus(msg: Any) {
if (msg.toString().toLowerCase(Locale.getDefault()) == "pass") {
EnergyBridge.postEnergyTask("finish_etiquette_exam")
SPUtils.setString(Constants.SP_REGULATION_TEST_PASS_STATUS, "pass")
}
}
@ -267,36 +215,22 @@ class DefaultJsApi(var context: Context, val entrance: String = "", private var
@JavascriptInterface
fun showIncompatibleVersionDialog(msg: Any) {
DialogHelper.showUpgradeDialog(context)
DialogUtils.showLowVersionDialog(context)
}
@JavascriptInterface
fun shareBase64Image(event: Any) {
val imageShareEvent = event.toString().toObject() ?: ImageShareEvent()
val context = CurrentActivityHolder.getCurrentActivity()
ImageViewerActivity.base64Image = imageShareEvent.image.run {
Base64ImageHolder.image = imageShareEvent.image.run {
if (this.startsWith("data:image/png;base64")) this.split(",")[1] else this
}
MessageShareUtils.getInstance(context).shareFromWeb(context, imageShareEvent.type)
}
@JavascriptInterface
fun inviteFriends(event: Any) {
val inviteEvent = event.toString().toObject() ?: InviteFriendsEvent()
val context = CurrentActivityHolder.getCurrentActivity()
if ("poster" == inviteEvent.type) {
ImageViewerActivity.base64Image = inviteEvent.poster.run {
if (this.startsWith("data:image/png;base64")) this.split(",")[1] else this
}
MessageShareUtils.getInstance(context).shareInviteFriends(context, inviteEvent.way)
} else {
ShareUtils.getInstance(context).shareInviteFriends(context, inviteEvent.url, inviteEvent.way)
}
}
@JavascriptInterface
fun bindPhone(msg: Any) {
val intent = SettingBridge.getBindPhoneNormalIntent(context, false)
val intent = BindPhoneActivity.getNormalIntent(context, false)
context.startActivity(intent)
}
@ -309,7 +243,7 @@ class DefaultJsApi(var context: Context, val entrance: String = "", private var
@JavascriptInterface
fun logoutExitWebViewAndRedirectToLogin() {
UserRepository.getInstance().logout()
UserRepository.getInstance(context).logout()
if (context is Activity) {
AppExecutor.uiExecutor.executeWithDelay(Runnable {
context.ifLogin("内部网页")
@ -318,366 +252,10 @@ class DefaultJsApi(var context: Context, val entrance: String = "", private var
}
}
@JavascriptInterface
fun openInNewWebview(url: Any) {
runOnUiThread { DirectUtils.directToWebView(context, url.toString(), entrance.ifBlank { "内部网页" }) }
}
@JavascriptInterface
fun postWearBadgeTask(msg: Any) {
EnergyBridge.postEnergyTask("wear_badge")
}
@JavascriptInterface
fun startEnergyCenter(msg: Any) {
context.startActivity(EnergyBridge.getEnergyCenterIntent(context))
}
@JavascriptInterface
fun startEnergyHouse(msg: Any) {
context.startActivity(EnergyBridge.getEnergyHouseIntent(context, null))
}
@JavascriptInterface
fun showQaFeedbackDialog(msg: Any) {
val mHelpAndFeedbackHelp =
ARouter.getInstance().build(RouteConsts.provider.helpAndFeedback).navigation() as? IHelpAndFeedbackProvider
mHelpAndFeedbackHelp?.showQaFeedbackDialogFragment(context as AppCompatActivity, msg.toString())
}
@JavascriptInterface
fun getMetaObject(msg: Any): String {
return LogUtils.getMetaObject().toString()
}
@JavascriptInterface
fun getLaunchId(msg: Any): String {
return Tracker.launchId
}
@JavascriptInterface
fun getSessionId(msg: Any): String {
return Tracker.sessionId
}
@JavascriptInterface
fun postLogEvent(event: Any) {
val logEvent = event.toString().toObject() ?: LogEvent()
debugOnly {
Utils.log("LogUtils->${logEvent.jsonString}")
}
LoghubUtils.log(logEvent.jsonString, logEvent.logStore, false)
}
@JavascriptInterface
fun startAvatarBorderPage(msg: Any) {
context.startActivity(AvatarBorderActivity.getIntent(context, msg.toString()))
}
@JavascriptInterface
fun isNetworkConnected(): Boolean {
return NetworkUtils.isNetworkConnected(context)
}
@JavascriptInterface
fun isWifiConnected(): Boolean {
return NetworkUtils.isWifiConnected(context)
}
@JavascriptInterface
fun enableBackToActivity(msg: Any) {
FloatingBackViewManager.enableBackView(FloatingBackViewManager.TYPE_ACTIVITY, msg.toString())
}
@JavascriptInterface
fun saveBase64ImageToGallery(msg: Any) {
val base64StringData = msg.toString()
runOnUiThread {
(context as? FragmentActivity)?.checkStoragePermissionBeforeAction {
runOnIoThread {
val base64String = base64StringData.replace("data:image/png;base64", "")
tryWithDefaultCatch {
val imageFile =
File(HaloApp.getInstance().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".png")
val decodedString = Base64.decode(base64String, Base64.DEFAULT)
val bos = BufferedOutputStream(FileOutputStream(imageFile))
bos.write(decodedString)
bos.flush()
bos.close()
ImageUtils.saveImageToFile(imageFile, "", true)
}
}
}
}
}
@JavascriptInterface
fun loginWithCallback(msg: Any, handler: CompletionHandler<Any>) {
mLoginHandler = handler
login(msg)
}
fun onLogin() {
mLoginHandler?.complete(true)
mLoginHandler = null
}
@JavascriptInterface
fun openInNewFullWebview(url: Any) {
runOnUiThread { DirectUtils.directToFullScreenWebPage(context, url.toString(), true, entrance) }
}
@JavascriptInterface
fun checkUpdateGhzs(msg: Any) {
context.startActivity(SettingBridge.getAboutIntent(context, true))
}
@JavascriptInterface
public fun clickGameActivityDownloadBtn(event: Any) {
val gameActivityEvent = event.toString().toObject() ?: GameActivityEvent()
GameActivityDownloadHelper.start(context, gameActivityEvent)
}
@JavascriptInterface
fun isGameActivityTaskCompleted(event: Any, handler: CompletionHandler<Any>) {
val gameActivityEvent = event.toString().toObject() ?: GameActivityEvent()
GameActivityDownloadHelper.checkTaskComplete(context, gameActivityEvent, handler)
}
@JavascriptInterface
fun postGameActivityExposureEvent(event: Any) {
val gameActivityEvent = event.toString().toObject() ?: GameActivityEvent()
GameActivityDownloadHelper.postExposureEvent(gameActivityEvent)
}
@JavascriptInterface
fun getEntrance(msg: Any): String {
return when {
entrance.contains("论坛-活动") -> "社区-活动tab-活动banner"
entrance.contains("启动弹窗") -> "首页_弹窗"
entrance.contains("新首页-轮播图") -> "首页banner"
entrance.contains("论坛banner") -> "社区banner"
entrance.contains("启动广告") -> "app_开屏文案"
// entrance.contains("资讯广场-轮播图") -> "资讯_活动banner"
entrance.contains("通用链接合集") -> "资讯_活动banner"
entrance.contains("视频流广告位") -> "视频流_广告位"
entrance.contains("我的光环banner") -> "我的光环_banner"
entrance.contains("论坛详情页置顶栏") -> "社区_论坛置顶"
else -> entrance
}
}
@JavascriptInterface
fun getInstallStatus(event: Any): String {
val localInstalledPackageList = PackageUtils.getAllPackageName(HaloApp.getInstance().application)
val packageNameList: ArrayList<String> = event.toString().toObject() ?: ArrayList()
val installStatusMap: HashMap<String, Boolean> = hashMapOf()
for (packageName in packageNameList) {
installStatusMap[packageName] =
localInstalledPackageList.contains(packageName) || VHelper.isInstalled(packageName)
}
return installStatusMap.toJson()
}
@JavascriptInterface
fun installDownloadedGame(event: Any) {
val url = event.toString()
val vUrl = VHelper.getVUrl(url)
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(url, null, false)
?: DownloadManager.getInstance().getDownloadEntitySnapshot(vUrl, null, false)
downloadEntity?.let {
PackageInstaller.install(context, it, showUnzipToast = false)
}
}
@JavascriptInterface
fun registerDownloadCallback(msg: Any, handler: CompletionHandler<Any>) {
val downloadUrlSet: HashSet<String> = msg.toString().toObject() ?: return
mDownloadHandler = handler
mDownloadUrlSet = downloadUrlSet
if (mDownloadWatcher == null) {
mDownloadWatcher = object : DataWatcher() {
override fun onDataInit(downloadEntity: DownloadEntity) {
onDataChanged(downloadEntity)
}
override fun onDataChanged(downloadEntity: DownloadEntity?) {
val url = VHelper.getOriginalUrl(downloadEntity?.url)
if (downloadEntity != null && mDownloadUrlSet?.contains(url) == true) {
mDownloadHandler?.setProgressData(
SimpleDownloadEntity.fromDownloadEntity(downloadEntity).toJson()
)
}
}
}
DownloadManager.getInstance().addObserver(mDownloadWatcher)
}
}
@JavascriptInterface
fun registerPackageChangesCallback(msg: Any, handler: CompletionHandler<Any>) {
PackageObserver.registerPackageChangeChangeListener(object : PackageObserver.PackageChangeListener {
override fun onChanged(data: EBPackage) {
handler.setProgressData(data.toJson())
}
})
}
@JavascriptInterface
fun startDownload(msg: Any) {
val gameEntity: GameEntity? = msg.toString().toObject()
if (gameEntity == null) {
ToastUtils.toast("下载异常,请稍后重试")
return
}
runOnUiThread {
// 用一个假的 view 来当 downloadView
val stubView = View(context)
DownloadItemUtils.setOnClickListener(
position = 0,
context = context,
downloadBtn = stubView,
gameEntity = gameEntity,
adapter = null,
entrance = "(网页活动)",
location = "",
traceEvent = null,
clickCallback = null,
refreshCallback = null,
allStateClickCallback = null
)
stubView.performClick()
}
}
@JavascriptInterface
fun resumeDownload(msg: Any) {
val url = msg.toString()
DownloadManager.getInstance().resumeDownload(url)
}
@JavascriptInterface
fun pauseDownload(msg: Any) {
val url = msg.toString()
DownloadManager.getInstance().pause(url)
}
@JavascriptInterface
fun shareText(event: Any) {
val textShareEvent = event.toString().toObject() ?: TextShareEvent()
if (textShareEvent.text.isNotEmpty() && textShareEvent.type.isNotEmpty()) {
val activity = CurrentActivityHolder.getCurrentActivity()
MessageShareUtils.getInstance(activity).shareTextFromWeb(activity, textShareEvent.text, textShareEvent.type)
}
}
private fun autoUnregisterDownloadObserverIfNeeded(fragment: Fragment?) {
fragment?.parentFragmentManager?.registerFragmentLifecycleCallbacks(
object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
super.onFragmentViewDestroyed(fm, f)
if (f === fragment) {
fragment.parentFragmentManager.unregisterFragmentLifecycleCallbacks(this)
if (mDownloadWatcher != null) {
DownloadManager.getInstance().removeObserver(mDownloadWatcher)
}
}
}
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
super.onFragmentResumed(fm, f)
if (f === fragment && mDownloadWatcher != null) {
DownloadManager.getInstance().addObserver(mDownloadWatcher)
}
}
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
super.onFragmentPaused(fm, f)
if (f === fragment && mDownloadWatcher != null) {
DownloadManager.getInstance().removeObserver(mDownloadWatcher)
}
}
}, false
)
}
@Keep
internal data class ImageEvent(var imageList: ArrayList<String> = arrayListOf(), var position: Int = 0)
@Keep
internal class ImageEvent(var imageList: ArrayList<String> = arrayListOf(), var position: Int = 0)
internal data class ImageShareEvent(var image: String = "", var type: String = "")
@Keep
internal class ImageShareEvent(var image: String = "", var type: String = "")
@Keep
internal class TextShareEvent(var text: String = "", var type: String = "")
@Keep
internal class InviteFriendsEvent(
var type: String = "",
var way: String = "",
var url: String = "",
var poster: String = ""
)
@Keep
internal class LogEvent(var jsonString: String = "", var logStore: String = "")
@Keep
class GameActivityEvent(
var gameId: String = "",
var activityTitle: String = "",
var activityId: String = "",
var platform: String = ""
)
@Keep
class SimpleDownloadEntity(
var url: String = "",
var progress: Float = 0F,
var status: String = "" // DOWNLOADING, PAUSED, DOWNLOADED, ERROR, UNKNOWN
) {
companion object {
fun fromDownloadEntity(downloadEntity: DownloadEntity): SimpleDownloadEntity {
val status: String = when (downloadEntity.status) {
add,
download,
downloading -> "DOWNLOADING"
done -> "DOWNLOADED"
pause,
resume,
subscribe,
waiting -> "PAUSED"
cancel,
delete -> "UNKNOWN"
else -> "ERROR"
}
return SimpleDownloadEntity(
url = VHelper.getOriginalUrl(downloadEntity.url),
progress = downloadEntity.percent.toFloat(),
status = status
)
}
}
}
}

View File

@ -5,53 +5,29 @@ import android.content.Intent
import android.net.Uri
import android.text.TextUtils
import android.util.Base64
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.common.constant.Constants
import com.gh.common.util.*
import com.gh.common.util.DirectUtils.directToFeedback
import com.gh.common.util.DirectUtils.directToGameDetailVideoStreaming
import com.gh.common.util.DirectUtils.directToGameServerCalendar
import com.gh.common.util.DirectUtils.directToGameVideo
import com.gh.common.util.DirectUtils.directToLegacyVideoDetail
import com.gh.common.util.DirectUtils.directToLinkPage
import com.gh.common.util.DirectUtils.directToQa
import com.gh.gamecenter.*
import com.gh.gamecenter.core.utils.GsonUtils.gson
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.common.entity.SimpleGameEntity
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.common.utils.DialogHelper
import com.gh.gamecenter.common.utils.EnvHelper
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.common.util.DirectUtils.directToVideoDetail
import com.gh.common.util.GsonUtils.gson
import com.gh.gamecenter.LibaoDetailActivity
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.NewsDetailActivity
import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBSkip
import com.gh.gamecenter.fragment.MainWrapperFragment
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.utils.PlatformUtils
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
import com.gh.gamecenter.subject.SubjectActivity
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
import com.lightgame.utils.Utils
import org.greenrobot.eventbus.EventBus
import java.nio.charset.Charset
object DefaultUrlHandler {
@JvmStatic
fun interceptUrl(context: Context, url: String, entrance: String): Boolean {
return interceptUrl(context, url, entrance, false)
}
/**
* @param bringAppToFront 是否需要在不匹配 host 的时候把 APP 调回到前台 (如微信调起)
*/
@JvmStatic
fun interceptUrl(context: Context, url: String, entrance: String, bringAppToFront: Boolean = false): Boolean {
val uri = Uri.parse(url)
if ("ghzhushou" == uri.scheme) {
Utils.log("url = $url")
@ -62,29 +38,13 @@ object DefaultUrlHandler {
if (!TextUtils.isEmpty(path)) {
id = path!!.substring(1)
}
if (TextUtils.isEmpty(id)) {
id = uri.getQueryParameter("id") ?: ""
}
val intent: Intent
when (host) {
"article" -> context.startActivity(NewsDetailActivity.getIntentById(context, id, entrance))
"game" -> DirectUtils.directToGameDetail(
context,
id = id,
tab = uri.getQueryParameter("to"),
autoDownload = uri.getQueryParameter("auto_download") == "true",
entrance = entrance
)
"game" -> DirectUtils.directToGameDetail(context, id = id, tab = uri.getQueryParameter("to"), autoDownload = uri.getQueryParameter("auto_download") == "true", entrance = entrance)
"column" -> SubjectActivity.startSubjectActivity(
context,
id,
uri.getQueryParameter("name"),
false,
entrance
)
"column" -> SubjectActivity.startSubjectActivity(context, id, uri.getQueryParameter("name"), false, entrance)
"libao" -> context.startActivity(LibaoDetailActivity.getIntentById(context, id, entrance))
@ -95,7 +55,7 @@ object DefaultUrlHandler {
e.printStackTrace()
}
EntranceConsts.HOST_QQ_QUN -> {
EntranceUtils.HOST_QQ_QUN -> {
val key = uri.getQueryParameter("key")
if (!DirectUtils.directToQqGroup(context, key)) {
Utils.toast(context, "请检查是否已经安装手机QQ")
@ -103,7 +63,9 @@ object DefaultUrlHandler {
}
"inurl" -> {
DirectUtils.directToWebView(context, uri.getQueryParameter("url") ?: "", entrance)
intent = Intent(context, WebActivity::class.java)
intent.putExtra(EntranceUtils.KEY_URL, uri.getQueryParameter("url"))
context.startActivity(intent)
}
"outurl" -> {
@ -120,8 +82,6 @@ object DefaultUrlHandler {
}
"question" -> DirectUtils.directToQuestionDetail(context, id, entrance, "文章链接")
"real_name" -> DirectUtils.directToRealName(context)
"community" -> {
val community = CommunityEntity()
community.id = id
@ -160,12 +120,11 @@ object DefaultUrlHandler {
}
if ("articles" == type) {
DirectUtils.directToCommunityArticle(
context, typeId, communityId,
entrance, "文章链接"
)
context, typeId, communityId,
entrance, "文章链接")
}
}
EntranceConsts.HOST_UPLOAD_VIDEO -> {
EntranceUtils.HOST_UPLOAD_VIDEO -> {
val titleParameter = uri.getQueryParameter("title")
val title = if (titleParameter.isNullOrEmpty()) "" else "#$titleParameter#"
val categoryId = uri.getQueryParameter("category_id") ?: ""
@ -176,31 +135,15 @@ object DefaultUrlHandler {
val tagActivityName = uri.getQueryParameter("tagActivityName") ?: ""
val linkEntity = VideoLinkEntity(title, categoryId, link, tagActivityId, tagActivityName)
val simpleGameEntity = SimpleGameEntity(gameId, gameName)
CheckLoginUtils.checkLogin(context, null, true, EntranceConsts.ENTRANCE_BROWSER) {
DirectUtils.directToVideoManager(
context,
linkEntity,
simpleGameEntity,
EntranceConsts.ENTRANCE_BROWSER,
""
)
CheckLoginUtils.checkLogin(context, null, true, EntranceUtils.ENTRANCE_BROWSER) {
DirectUtils.directToVideoManager(context, linkEntity, simpleGameEntity, EntranceUtils.ENTRANCE_BROWSER, "")
}
}
EntranceConsts.HOST_USERHOME -> {
EntranceUtils.HOST_USERHOME -> {
val position = uri.getQueryParameter("position")
val subtype = uri.getQueryParameter("sub_type") ?: ""
val subGameType = uri.getQueryParameter("sub_game_type") ?: "game_collection"
DirectUtils.directToHomeActivity(
context,
id,
subtype,
subGameType,
if (position.isNullOrEmpty()) -1 else position.toInt(),
entrance,
""
)
DirectUtils.directToHomeActivity(context, id, if (position.isNullOrEmpty()) -1 else position.toInt(), entrance, "")
}
EntranceConsts.HOST_VIDEO_MORE -> {
EntranceUtils.HOST_VIDEO_MORE -> {
val referer = uri.getQueryParameter("referer") ?: ""
val type = uri.getQueryParameter("type") ?: ""
val act = uri.getQueryParameter("act") ?: ""
@ -208,7 +151,7 @@ object DefaultUrlHandler {
val fieldId = uri.getQueryParameter("fieldId") ?: ""
val sectionName = uri.getQueryParameter("sectionName") ?: ""
val paginationType = uri.getQueryParameter("paginationType")
?: "page"//活动分页方式 page filter
?: "page"//活动分页方式 page filter
val location = if (!TextUtils.isEmpty(act)) {
VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value
} else if (!TextUtils.isEmpty(fieldId)) {
@ -216,92 +159,88 @@ object DefaultUrlHandler {
} else {
id
}
directToLegacyVideoDetail(
context,
id,
location,
false,
gameId,
entrance,
"",
referer,
type,
act,
paginationType,
fieldId,
sectionName
)
directToVideoDetail(context, id, location, false, gameId, entrance, "", referer, type, act, paginationType, fieldId, sectionName)
}
EntranceConsts.HOST_VIDEO_DETAIL -> {
DirectUtils.directToVideoDetail(context, id, entrance, path)
}
EntranceConsts.HOST_VIDEO_SINGLE -> {
EntranceUtils.HOST_VIDEO_SINGLE -> {
val referer = uri.getQueryParameter("referer") ?: ""
DirectUtils.directToVideoDetail(
context, id, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.value,
false, "", entrance, "", if (TextUtils.isEmpty(referer)) "" else referer
)
directToVideoDetail(context, id, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.value,
false, "", entrance, "", if (TextUtils.isEmpty(referer)) "" else referer)
}
EntranceConsts.HOST_VIDEO_STREAMING_HOME -> {
EntranceUtils.HOST_VIDEO_STREAMING_HOME -> {
intent = Intent(context, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
intent.putExtra(MainActivity.SWITCH_TO_VIDEO, true)
context.startActivity(intent)
}
EntranceConsts.HOST_VIDEO_STREAMING_DESC -> {
EntranceUtils.HOST_VIDEO_STREAMING_DESC -> {
directToGameDetailVideoStreaming(context, id, entrance)
}
EntranceConsts.HOST_VIDEO_COLLECTION -> {
EntranceUtils.HOST_VIDEO_COLLECTION -> {
directToGameVideo(context, id, entrance, "")
}
EntranceConsts.HOST_CATEGORY -> {
EntranceUtils.HOST_CATEGORY -> {
val title = uri.getQueryParameter("title")
DirectUtils.directCategoryDirectory(context, id, title ?: "", entrance, "")
}
EntranceConsts.HOST_COLUMN_COLLECTION -> {
EntranceUtils.HOST_COLUMN_COLLECTION -> {
val name = uri.getQueryParameter("name")
DirectUtils.directToColumnCollection(context, id, -1, entrance, name ?: "")
}
EntranceConsts.HOST_COLUMN -> {
DirectUtils.directToSubject(context, id, uri.getQueryParameter(EntranceConsts.KEY_NAME), entrance)
EntranceUtils.HOST_COLUMN -> {
DirectUtils.directToSubject(context, id, uri.getQueryParameter(EntranceUtils.KEY_NAME), entrance)
}
EntranceConsts.HOST_BLOCK -> {
EntranceUtils.HOST_COMMUNITY_QUESTION_LABEL_DETAIL -> {
val community = CommunityEntity()
community.id = uri.getQueryParameter("community_id") ?: ""
community.name = uri.getQueryParameter("community_name") ?: ""
val tag = uri.getQueryParameter("tag") ?: ""
DirectUtils.directAskColumnLabelDetail(context, tag, community, entrance, "")
}
EntranceUtils.HOST_COMMUNITY_COLUMN_DETAIL -> {
val community = CommunityEntity()
community.id = uri.getQueryParameter("community_id") ?: ""
community.name = uri.getQueryParameter("community_name") ?: ""
val columnId = uri.getQueryParameter("column_id") ?: ""
DirectUtils.directAskColumnDetail(context, columnId, community, entrance, "")
}
EntranceUtils.HOST_BLOCK -> {
val name = uri.getQueryParameter("name")
?: ""
?: ""
val entity = SubjectRecommendEntity(link = id, name = name, text = name)
DirectUtils.directToBlock(context, entity, entrance)
}
EntranceConsts.HOST_SERVER_BLOCK -> {
EntranceUtils.HOST_SERVER_BLOCK -> {
DirectUtils.directToGameServers(context, entrance = entrance, path = "")
}
EntranceConsts.HOST_AMWAY_BLOCK -> {
EntranceUtils.HOST_AMWAY_BLOCK -> {
DirectUtils.directToAmway(context, entrance = entrance, path = "")
}
EntranceConsts.HOST_HELP -> {
EntranceUtils.HOST_HELP -> {
val name = uri.getQueryParameter("name")
?: ""
?: ""
DirectUtils.directToQa(context, name, id)
}
EntranceConsts.HOST_HELP_COLLECTION -> {
EntranceUtils.HOST_HELP_COLLECTION -> {
val name = uri.getQueryParameter("name")
?: ""
?: ""
DirectUtils.directToQaCollection(context, name, id)
}
EntranceConsts.HOST_GAME_UPLOAD -> {
EntranceUtils.HOST_GAME_UPLOAD -> {
DirectUtils.directGameUpload(context, entrance = entrance, path = "")
}
EntranceConsts.HOST_GAME_ZONE -> {
EntranceUtils.HOST_GAME_ZONE -> {
val zoneUrl = uri.getQueryParameter("url") ?: ""
DirectUtils.directGameZone(context, id, zoneUrl, entrance)
}
EntranceConsts.HOST_LINK -> {
EntranceUtils.HOST_LINK -> {
try {
val dataString = uri.getQueryParameter("data")
if (!TextUtils.isEmpty(dataString)) {
@ -315,198 +254,60 @@ object DefaultUrlHandler {
}
}
EntranceConsts.HOST_GAME_NEWS -> {
EntranceUtils.HOST_GAME_NEWS -> {
DirectUtils.directToGameNews(
context,
uri.getQueryParameter(EntranceConsts.KEY_GAME_ID),
uri.getQueryParameter(EntranceConsts.KEY_GAME_NAME),
entrance
);
context,
uri.getQueryParameter(EntranceUtils.KEY_GAME_ID),
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
entrance);
}
EntranceConsts.HOST_GAME_CALENDAR -> {
directToGameServerCalendar(context, uri.getQueryParameter(EntranceConsts.KEY_GAME_ID))
EntranceUtils.HOST_GAME_CALENDAR -> {
directToGameServerCalendar(context, uri.getQueryParameter(EntranceUtils.KEY_GAME_ID))
}
EntranceConsts.HOST_HISTORY_APK -> {
DirectUtils.directToHistoryApk(context, uri.getQueryParameter(EntranceConsts.KEY_GAME_ID))
EntranceUtils.HOST_HISTORY_APK -> {
DirectUtils.directToHistoryApk(context, uri.getQueryParameter(EntranceUtils.KEY_GAME_ID))
}
EntranceConsts.HOST_FORUM_DETAIL -> {
EntranceUtils.HOST_FORUM_DETAIL -> {
DirectUtils.directForumDetail(context, id, entrance)
}
EntranceConsts.HOST_GAME_RATING_DETAIL -> {
DirectUtils.directToGameRatingDetail(
context,
uri.getQueryParameter(EntranceConsts.KEY_GAME_ID),
uri.getQueryParameter(EntranceConsts.KEY_COMMENT_ID),
EntranceConsts.ENTRANCE_BROWSER
)
EntranceUtils.HOST_GAME_RATING_DETAIL -> {
DirectUtils.directToGameRatingDetail(context, uri.getQueryParameter(EntranceUtils.KEY_GAME_ID), uri.getQueryParameter(EntranceUtils.KEY_COMMENT_ID), EntranceUtils.ENTRANCE_BROWSER)
}
EntranceConsts.HOST_FORUM -> {
val position = uri.getQueryParameter(EntranceConsts.KEY_POSITION)?.toInt()
DirectUtils.directToForum(context, position ?: 0)
EntranceUtils.HOST_FORUM -> {
DirectUtils.directToForum(context)
}
EntranceConsts.HOST_UPLOAD_VIDEO_NEW -> {
val activityName = uri.getQueryParameter("activity_name") ?: ""
val activityId = uri.getQueryParameter("activity_id") ?: ""
val original = uri.getQueryParameter("original") ?: ""
val forumName = uri.getQueryParameter("forum_name") ?: ""
val forumId = uri.getQueryParameter("forum_id") ?: ""
val forumIcon = uri.getQueryParameter("forum_icon") ?: ""
val forumType = uri.getQueryParameter("forum_type") ?: BbsType.OFFICIAL_BBS.value
val gameId = uri.getQueryParameter("game_id") ?: ""
val gameName = uri.getQueryParameter("game_name") ?: ""
val icon = uri.getQueryParameter("game_icon") ?: ""
val iconSubscript = uri.getQueryParameter("game_icon_subscript") ?: ""
val gameEntity =
if (forumType == BbsType.OFFICIAL_BBS.value && gameId.isNotEmpty() && gameName.isNotEmpty() && icon.isNotEmpty()) {
GameEntity(id = gameId, mName = gameName, mIcon = icon, mIconSubscript = iconSubscript)
} else null
val activityLabelEntity = if (activityId.isNotEmpty() && activityName.isNotEmpty()) {
ActivityLabelEntity(
id = activityId,
name = activityName,
original = original.ifEmpty { "false" }.toBoolean()
)
} else null
val communityEntity =
if (forumId.isNotEmpty() && forumName.isNotEmpty() && forumIcon.isNotEmpty()) {
CommunityEntity(id = forumId, name = forumName, icon = forumIcon)
} else null
context.startActivity(
VideoPublishActivity.getIntent(
context,
communityEntity,
gameEntity,
activityLabelEntity,
forumType,
disableForumSelection = false,
isFromCommunityActivity = true,
entrance,
""
)
)
}
EntranceConsts.HOST_SUGGESTION -> {
val platform = uri.getQueryParameter(EntranceConsts.KEY_PLATFORM)
EntranceUtils.HOST_SUGGESTION -> {
val platform = uri.getQueryParameter(EntranceUtils.KEY_PLATFORM)
val platformName = PlatformUtils.getInstance(context).getPlatformName(platform)
val gameId = uri.getQueryParameter(EntranceConsts.KEY_GAMEID)
val packageMd5 = uri.getQueryParameter(EntranceConsts.KEY_PACKAGE_MD5)
val isQaFeedback = uri.getQueryParameter(EntranceConsts.KEY_IS_QA_FEEDBACK) == "true"
val content = if (TextUtils.isEmpty(gameId) || TextUtils.isEmpty(packageMd5)) String.format(
"%s-%s-V%s",
uri.getQueryParameter(EntranceConsts.KEY_GAME_NAME),
if (TextUtils.isEmpty(platformName)) platform else platformName,
uri.getQueryParameter(EntranceConsts.KEY_VERSION)
) else String.format(
"%s-%s-V%s\n游戏ID%s\n游戏包MD5%s\n",
uri.getQueryParameter(EntranceConsts.KEY_GAME_NAME),
if (TextUtils.isEmpty(platformName)) platform else platformName,
uri.getQueryParameter(EntranceConsts.KEY_VERSION), gameId, packageMd5
)
val gameId = uri.getQueryParameter(EntranceUtils.KEY_GAMEID)
val packageMd5 = uri.getQueryParameter(EntranceUtils.KEY_PACKAGE_MD5)
val content = if (TextUtils.isEmpty(gameId) || TextUtils.isEmpty(packageMd5)) String.format("%s-%s-V%s",
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
if (TextUtils.isEmpty(platformName)) platform else platformName,
uri.getQueryParameter(EntranceUtils.KEY_VERSION)) else String.format("%s-%s-V%s\n游戏ID%s\n游戏包MD5%s\n",
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
if (TextUtils.isEmpty(platformName)) platform else platformName,
uri.getQueryParameter(EntranceUtils.KEY_VERSION), gameId, packageMd5)
val qaId = uri.getQueryParameter("qa_id") ?: ""
val qaContentId = uri.getQueryParameter(EntranceConsts.KEY_QA_CONTENT_ID) ?: ""
val qaTitle = uri.getQueryParameter(EntranceConsts.KEY_QA_TITLE)
val qaTitle = uri.getQueryParameter(EntranceUtils.KEY_QA_TITLE)
if (!TextUtils.isEmpty(qaId)) {
directToQa(context, qaTitle, qaId)
} else {
directToFeedback(
context,
content,
null,
isQaFeedback,
qaContentId,
EntranceConsts.ENTRANCE_BROWSER
)
directToFeedback(context, content, EntranceUtils.ENTRANCE_BROWSER)
}
}
EntranceConsts.HOST_HELP_AND_FEEDBACK -> {
EntranceUtils.HOST_HELP_AND_FEEDBACK -> {
val position = uri.getQueryParameter("position") ?: ""
DirectUtils.directToHelpAndFeedback(context, position.toInt())
}
EntranceConsts.HOST_HELP_DETAIL -> {
var url = uri.getQueryParameter("url")
if (!url.isNullOrEmpty()) {
context.startActivity(WebActivity.getIntent(context, url, false))
} else {
url = if (EnvHelper.isDevEnv) {
Constants.HELP_ADDRESS_DEV
} else {
Constants.HELP_ADDRESS
}
val id = uri.getQueryParameter("id")
val name = uri.getQueryParameter("name")
val qaCollectionId = uri.getQueryParameter("collection_id")
context.startActivity(
WebActivity.getIntent(
context,
"$url$id",
name,
true,
!qaCollectionId.isNullOrEmpty()
)
)
}
}
EntranceConsts.HOST_GAME_COLLECTION_DETAIL -> {
DirectUtils.directToGameCollectionDetail(context, id, entrance)
}
EntranceConsts.HOST_GAME_COLLECTION_SQUARE -> {
DirectUtils.directToGameCollectionSquare(context, entrance)
}
EntranceConsts.HOST_GAME_COLLECTION_EDIT -> {
val activityId = uri.getQueryParameter("activity_id") ?: ""
val activityName = uri.getQueryParameter("activity_name") ?: ""
val gameId = uri.getQueryParameter("game_id") ?: ""
context.startActivity(
GameCollectionEditActivity.getIntent(
context,
activityId,
activityName,
gameId,
entrance
)
)
}
EntranceConsts.HOST_GAME_LIBRARY -> {
DirectUtils.directToMainActivity(context)
EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_GAME))
}
EntranceConsts.HOST_HOME_GAME_COLLECTION_SQUARE -> {
DirectUtils.directToMainActivity(context)
EventBus.getDefault()
.post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_HOME))
EventBus.getDefault().post(EBReuse(host))
}
else -> {
if (bringAppToFront) {
DirectUtils.directToMainActivity(context)
if (!TextUtils.isEmpty(host)) {
AppExecutor.uiExecutor.executeWithDelay({
CurrentActivityHolder.getCurrentActivity()?.let {
DialogHelper.showUpgradeDialog(it)
}
}, 200)
}
} else {
DialogHelper.showUpgradeDialog(context)
}
}
else -> DialogUtils.showLowVersionDialog(context)
}
return true
} else if ("zhiqu" == uri.scheme) {
@ -543,43 +344,24 @@ object DefaultUrlHandler {
return true
}
// 处理内部页面逻辑
if (transformNormalScheme(context, url, entrance)) {
return true
}
if ("http" != uri.scheme && "https" != uri.scheme) return true
return false
}
@JvmStatic
fun transformNormalScheme(context: Context, url: String, entrance: String): Boolean {
val b = transformNewNormalScheme(context, url, entrance)
if (b) return b
return transformOldNormalScheme(context, url, entrance)
}
@JvmStatic
fun transformOldNormalScheme(context: Context, url: String, entrance: String): Boolean {
val uri = Uri.parse(url)
if (uri.host == "www.ghzs666.com"
|| uri.host == "www.ghzs.com"
|| uri.host == "ask.ghzs.com"
|| uri.host == "m.ghzs.com"
|| uri.host == "m.ghzs666.com"
) {
|| uri.host == "www.ghzs.com"
|| uri.host == "ask.ghzs.com"
|| uri.host == "m.ghzs.com"
|| uri.host == "m.ghzs666.com") {
Utils.log(uri.path)
uri.path?.apply {
when {
contains("game") -> {
val gameId = uri.getQueryParameter("gameId") ?: uri.pathSegments.last() ?: ""
DirectUtils.directToGameDetail(
context,
gameId,
entrance,
autoDownload = false,
traceEvent = null
)
val gameId = uri.getQueryParameter("gameId") ?: ""
DirectUtils.directToGameDetail(context, gameId, entrance, autoDownload = false, traceEvent = null)
}
contains("question") -> {
val questionId = split("/")[2]
@ -590,15 +372,11 @@ object DefaultUrlHandler {
DirectUtils.directToAnswerDetail(context, answerId, entrance, "")
}
}
((contains("bbs")) && contains("article") ||
(contains("communities")) && contains("article")) -> {
contains("communities") && contains("article") -> {
var communityId = ""
var type = ""
var typeId = ""
val split =
replace("/communities", "").replace("/bbs", "").replace(".html", "").split("/".toRegex())
.dropLastWhile { it.isEmpty() }
.toTypedArray()
val split = replace("/communities", "").replace(".html", "").split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (text in split) {
if (TextUtils.isEmpty(communityId)) {
communityId = text
@ -612,20 +390,15 @@ object DefaultUrlHandler {
typeId = text
}
}
if ("articles" == type || "article" == type) {
if ("articles" == type) {
DirectUtils.directToCommunityArticle(
context, typeId, communityId,
entrance, "文章链接"
)
context, typeId, communityId,
entrance, "文章链接")
}
}
contains("article") -> {
val articleId = split("/")[2].replace(".html", "")
if (entrance == "隐私政策") {
DirectUtils.directToArticle(context, articleId, true, entrance)
} else {
DirectUtils.directToArticle(context, articleId, entrance)
}
DirectUtils.directToArticle(context, articleId, entrance)
}
contains("columns") -> {
val columnsId = split("/")[3]
@ -633,84 +406,14 @@ object DefaultUrlHandler {
val name = uri.getQueryParameter("communityName") ?: ""
DirectUtils.directToCommunityColumn(context, CommunityEntity(id, name), columnsId, entrance, "")
}
contains("zone") && split("/").size > 2 -> {
contains("zone") -> {
val gameId = split("/")[2]
DirectUtils.directGameZone(context, gameId, url, entrance)
}
else -> return false
}
}
return true
}
return false
}
@JvmStatic
fun transformNewNormalScheme(context: Context, url: String, entrance: String): Boolean {
val uri = Uri.parse(url)
if (uri.host == "www.ghzs666.com"
|| uri.host == "www.ghzs.com"
|| uri.host == "ask.ghzs.com"
|| uri.host == "m.ghzs.com"
|| uri.host == "m.ghzs666.com"
|| uri.host == "dev-bbs-mobile.ghzs.com"
) {
Utils.log(uri.path)
uri.path?.apply {
val splits = split("/")
when {
//https://m.ghzs666.com/bbs/thread-帖子ID
splits.size >= 3 && splits[1] == "bbs" && splits[2].startsWith("thread-") -> {
val articleId = splits[2].substring(7)
DirectUtils.directToCommunityArticle(
context, articleId, "",
entrance, "文章链接"
)
}
//https://m.ghzs666.com/article/文章ID
splits.size >= 3 && splits[1] == "article" -> {
val articleId = splits[2]
DirectUtils.directToArticle(context, articleId, entrance)
}
//https://m.ghzs666.com/column/专题ID
splits.size >= 3 && splits[1] == "column" -> {
val columnId = splits[2]
DirectUtils.directToSubject(context, columnId, "", entrance)
}
//https://m.ghzs666.com/zone/游戏ID
splits.size >= 3 && splits[1] == "zone" -> {
DirectUtils.directToWebView(context, url, entrance)
}
//https://m.ghzs666.com/bbs/video-视频ID
splits.size >= 3 && splits[1] == "bbs" && splits[2].startsWith("video-") -> {
val videoId = splits[2].substring(6)
DirectUtils.directToVideoDetail(context, videoId, entrance)
}
else -> return false
}
}
return true
}
return false
}
/**
* 将 url 转换为 LinkEntity (实际只有 type 和 link 两个字段,仅供日志,不保证能用)
*/
fun urlToLinkEntity(url: String): LinkEntity? {
val uri = Uri.parse(url)
if ("ghzhushou" == uri.scheme) {
Utils.log("url = $url")
Utils.log("url = " + uri.scheme!!)
val host = uri.host
val path = uri.path
var id = ""
if (!TextUtils.isEmpty(path)) {
id = path!!.substring(1)
}
return LinkEntity(type = host, link = id)
}
return null
}
}

View File

@ -2,31 +2,27 @@ package com.gh.common
import com.gh.common.exposure.ExposureManager
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.util.AdHelper
import com.gh.common.loghub.LoghubUtils
import com.gh.common.util.doOnMainProcessOnly
import com.gh.common.util.tryCatchInRelease
import com.gh.common.videolog.VideoRecordUtils
import com.gh.download.DownloadDataHelper
import com.gh.gamecenter.common.loghub.LoghubUtils
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.doOnMainProcessOnly
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.entity.TimeEntity
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.schedulers.Schedulers
import kotlin.concurrent.fixedRateTimer
object FixedRateJobHelper {
private const val CHECKER_PERIOD: Long = 15 * 1000L
private const val TIME_PERIOD: Long = 10 * 60 * 1000L
private const val LOGHUB_PERIOD: Long = 2 * 60 * 1000L
private const val EXPOSURE_PERIOD: Long = 5 * 60 * 1000L
private const val REGION_SETTING_PERIOD: Long = 30 * 1000L
private const val TIME_PERIOD: Long = 600 * 1000L
private const val LOGHUB_PERIOD: Long = 120 * 1000L
private const val EXPOSURE_PERIOD: Long = 300 * 1000L
private const val REGION_SETTING_PERIOD: Long = 300 * 1000L
private const val VIDEO_RECORD_PERIOD: Long = 60 * 1000L
private const val DOWNLOAD_HEARTBEAT_PERIOD: Long = 60 * 1000L
private const val DOWNLOAD_HEARTBEAT_SHEET_PERIOD: Long = 15 * 1000L
private const val STARTUP_AD: Long = 30 * 60 * 1000L
private var mExecuteCount: Int = 0
@ -35,32 +31,26 @@ object FixedRateJobHelper {
@JvmStatic
fun begin() {
doOnMainProcessOnly {
// 时间检查每15秒检查一次
fixedRateTimer("Global-Fixed-Rate-Timer", initialDelay = 100, period = CHECKER_PERIOD) {
val elapsedTime = mExecuteCount * CHECKER_PERIOD
// 时间校对10分钟一次
if (elapsedTime % TIME_PERIOD == 0L) {
RetrofitManager.getInstance().api.time
.subscribeOn(Schedulers.io())
.subscribe(object : Response<TimeEntity>() {
override fun onResponse(response: TimeEntity?) {
val serverTime = response?.time
serverTime?.let {
timeDeltaBetweenServerAndClient = it * 1000 - System.currentTimeMillis()
if ((mExecuteCount * CHECKER_PERIOD) % TIME_PERIOD == 0L) {
RetrofitManager.getInstance(HaloApp.getInstance().application).api.time
.subscribeOn(AppExecutor.cachedScheduler)
.subscribe(object : Response<TimeEntity>() {
override fun onResponse(response: TimeEntity?) {
val serverTime = response?.time
serverTime?.let { timeDeltaBetweenServerAndClient = it * 1000 - System.currentTimeMillis() }
}
}
})
})
}
// 提交曝光数据
if (elapsedTime % EXPOSURE_PERIOD == 0L) {
runOnUiThread {
ExposureManager.commitSavedExposureEvents(true)
}
if ((mExecuteCount * CHECKER_PERIOD) % EXPOSURE_PERIOD == 0L) {
ExposureManager.commitSavedExposureEvents(true)
}
// 分片检测下载进度
if (elapsedTime % DOWNLOAD_HEARTBEAT_SHEET_PERIOD == 0L) {
if ((mExecuteCount * CHECKER_PERIOD) % DOWNLOAD_HEARTBEAT_SHEET_PERIOD == 0L) {
tryCatchInRelease {
val upload = (mExecuteCount * CHECKER_PERIOD) % DOWNLOAD_HEARTBEAT_PERIOD == 0L
DownloadDataHelper.uploadDownloadHeartbeat(upload)
@ -68,29 +58,23 @@ object FixedRateJobHelper {
}
// 提交普通 loghub 数据
if (elapsedTime % LOGHUB_PERIOD == 0L) {
runOnUiThread {
LoghubUtils.commitSavedLoghubEvents(true)
}
if ((mExecuteCount * CHECKER_PERIOD) % LOGHUB_PERIOD == 0L) {
LoghubUtils.commitSavedLoghubEvents()
}
// 更新游戏屏蔽信息
if (elapsedTime % REGION_SETTING_PERIOD == 0L) {
if ((mExecuteCount * CHECKER_PERIOD) % REGION_SETTING_PERIOD == 0L) {
if (HaloApp.getInstance().isRunningForeground) {
RegionSettingHelper.getRegionSetting()
}
}
// 提交视频浏览记录数据
if (elapsedTime % VIDEO_RECORD_PERIOD == 0L) {
if ((mExecuteCount * CHECKER_PERIOD) % VIDEO_RECORD_PERIOD == 0L) {
VideoRecordUtils.commitVideoRecord()
}
// 获取启动广告 (第一次不需要获取)
if (elapsedTime % STARTUP_AD == 0L && mExecuteCount != 0) {
AdHelper.getSettingAdCache()
}
// ExposureUtils.logADownloadCompleteExposureEvent(GameEntity(id = mExecuteCount.toString(), name = "测试曝光上传"), platform = "", trace = null, downloadType = ExposureUtils.DownloadType.DOWNLOAD)
mExecuteCount++
}
}

View File

@ -0,0 +1,39 @@
//package com.gh.common
//
//import android.content.BroadcastReceiver
//import android.content.Context
//import android.content.Intent
//import com.gh.common.im.ImManager
//import com.gh.gamecenter.manager.UserManager
//import com.gh.gamecenter.retrofit.RetrofitManager
//import com.halo.assistant.HaloApp
//import com.m7.imkfsdk.chat.ChatActivity
//import io.reactivex.schedulers.Schedulers
//
///**
// * 可使用 [LocalBroadcastManager] 来进行简单的模块间消息通知
// */
//
//class LocalBroadcastReceiver : BroadcastReceiver() {
//
// override fun onReceive(context: Context?, intent: Intent?) {
// intent?.let {
// when (intent.action) {
// ChatActivity.ACTION_DISMISS_FLOATING_WINDOW -> {
// ImManager.dismissFloatingWindow()
//
// RetrofitManager.getInstance(HaloApp.getInstance().application).api.postImEnding(UserManager.getInstance().userId)
// .subscribeOn(Schedulers.io())
// .subscribe()
// }
//
// ChatActivity.ACTION_HIDE_UNREAD_DOT -> {
// ImManager.updateShouldShowFloatingWindowDot(false)
// }
//
// else -> return
// }
// }
// }
//
//}

View File

@ -0,0 +1,117 @@
package com.gh.common
import android.annotation.SuppressLint
import com.gh.gamecenter.entity.AliasEntity
import com.halo.assistant.HaloApp
object PushManager {
var deviceToken: String? = ""
private var mPreviousAlias: AliasEntity? = null
private var mApplication = HaloApp.getInstance().application
const val SP_PUSH_ALIAS = "push_alias"
@JvmStatic
fun init(channel: String) {
// tryWithDefaultCatch {
// //初始化友盟推送
// UMConfigure.init(mApplication, Config.UMENG_APPKEY, channel, UMConfigure.DEVICE_TYPE_PHONE, Config.UMENG_MESSAGE_SECRET)
//
// val pushAgent = PushAgent.getInstance(mApplication)
//
// runOnIoThread { registerDevice() }
//
// // 注册小米、华为和魅族通道
// MiPushRegistar.register(mApplication, Config.MIPUSH_APPID, Config.MIPUSH_APPKEY)
// HuaWeiRegister.register(mApplication)
// MeizuRegister.register(mApplication, BuildConfig.MEIZUPUSH_APPID, BuildConfig.MEIZUPUSH_APPKEY)
//
// val aliasInSp = PreferenceManager.getDefaultSharedPreferences(mApplication).getString(SP_PUSH_ALIAS, "")
// mPreviousAlias = aliasInSp?.toObject()
//
// if (mPreviousAlias == null) {
// getAndSetAlias()
// }
//
// // 完全自定义处理(透传)
// pushAgent.setPushIntentServiceClass(GHUmengNotificationService::class.java)
// }
}
private fun registerDevice() {
// PushAgent.getInstance(mApplication).register(object : IUmengRegisterCallback {
// override fun onSuccess(dToken: String) {
// //注册成功会返回device token
// deviceToken = dToken
// getAndSetAlias()
// Utils.log("deviceToken::$dToken")
// }
//
// override fun onFailure(s: String, s1: String) {
// Utils.log("deviceToken::" + "注册失败")
// }
// })
}
@SuppressLint("CheckResult")
@JvmStatic
fun getAndSetAlias() {
// if (deviceToken.isNullOrEmpty()) {
// registerDevice()
// return
// }
//
// val meta = MetaUtil.getMeta()
//
// val jsonObject = JSONObject()
// jsonObject.put("device_token", deviceToken)
// jsonObject.put("imei", meta.imei)
// jsonObject.put("android_id", meta.android_id)
// jsonObject.put("model", meta.model)
// jsonObject.put("manufacturer", meta.manufacturer)
// jsonObject.put("os", meta.os)
// jsonObject.put("os_version", meta.android_version)
// jsonObject.put("mac", meta.mac)
// jsonObject.put("gid", meta.gid)
//
// val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString())
//
// RetrofitManager.getInstance(mApplication).api.getAlias(body)
// .subscribeOn(Schedulers.io())
// .subscribe(
// { setAlias(it) },
// { it.printStackTrace() }
// )
}
@JvmStatic
fun setAlias(alias: AliasEntity) {
// val pushAgent = PushAgent.getInstance(mApplication)
//
// mPreviousAlias = alias
// PreferenceManager.getDefaultSharedPreferences(mApplication).edit {
// putString(SP_PUSH_ALIAS, mPreviousAlias?.toJson())
// }
//
// pushAgent.setAlias(alias.alias, alias.aliasType) { b, s ->
// Utils.log("注册别名 $b + $s")
// }
}
@JvmStatic
fun deleteAlias() {
// val pushAgent = PushAgent.getInstance(mApplication)
//
// mPreviousAlias?.let {
// pushAgent.deleteAlias(it.alias, it.aliasType) { b, s ->
// Utils.log("删除别名 $b + $s")
// }
// }
// PreferenceManager.getDefaultSharedPreferences(mApplication).edit {
// putString(SP_PUSH_ALIAS, "")
// }
// mPreviousAlias = null
}
}

View File

@ -0,0 +1,122 @@
package com.gh.common
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.halo.assistant.HaloApp
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
/**
* 统计用户在当前 Fragment/Activity 的停留时间,在 onViewDestroy 或 onDestroy 里获取 elapsedTime 即可,单位为秒
*/
class TimeElapsedHelper(val fragment: Fragment?, val activity: Activity?) {
constructor() : this(null, null)
constructor(fragment: Fragment) : this(fragment, null)
constructor(activity: Activity) : this(null, activity)
private var isWorking = false
var elapsedTime: Int = 0
var timeout: Int = 0
var timeoutCallback: TimeoutCallback? = null
init {
activity?.application?.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
override fun onActivityStarted(a: Activity?) {
}
override fun onActivitySaveInstanceState(a: Activity?, outState: Bundle?) {
}
override fun onActivityStopped(a: Activity?) {
}
override fun onActivityCreated(a: Activity?, savedInstanceState: Bundle?) {
}
override fun onActivityPaused(a: Activity?) {
if (activity == a) {
pauseCounting()
}
}
override fun onActivityResumed(a: Activity?) {
if (activity == a) {
resumeCounting()
}
}
override fun onActivityDestroyed(a: Activity?) {
if (activity == a) {
HaloApp.getInstance().application.unregisterActivityLifecycleCallbacks(this)
}
}
})
fragment?.fragmentManager?.registerFragmentLifecycleCallbacks(
object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
if (f === fragment) {
resumeCounting()
}
}
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
if (f === fragment) {
pauseCounting()
}
}
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
if (f === fragment) {
fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this)
}
}
}, false)
}
fun resumeCounting() {
isWorking = true
TimeElapsedThreadHolder.threadService.execute {
while (isWorking) {
try {
elapsedTime++
if (timeout != 0 && timeout == elapsedTime) {
timeoutCallback?.run {
AppExecutor.uiExecutor.execute {
onTimeout()
}
}
}
Thread.sleep(1000)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
fun resetCounting() {
elapsedTime = 0
}
fun pauseCounting() {
isWorking = false
}
}
object TimeElapsedThreadHolder {
val threadService: ExecutorService by lazy { Executors.newSingleThreadExecutor() }
}
interface TimeoutCallback {
fun onTimeout()
}

View File

@ -0,0 +1,3 @@
package com.gh.common
typealias OnFastClickListener = (isSuccess: Boolean) -> Unit

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.common.annotation;
package com.gh.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@ -7,7 +7,7 @@ import java.lang.annotation.Target;
/**
* 标记那些不需要检查的同步字段
* <p>
*
* 同步字段冲突时使用
*/
@Target(ElementType.FIELD)

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.common.annotation;
package com.gh.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.common.annotation;
package com.gh.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.common.avoidcallback;
package com.gh.common.avoidcallback;
import android.content.Intent;

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.common.avoidcallback
package com.gh.common.avoidcallback
import android.content.Intent
import android.os.Bundle

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.common.avoidcallback
package com.gh.common.avoidcallback
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
@ -32,9 +32,9 @@ class AvoidOnResultManager {
avoidOnResultFragment = AvoidOnResultFragment()
val fragmentManager = activity.supportFragmentManager
fragmentManager
.beginTransaction()
.add(avoidOnResultFragment, TAG)
.commitAllowingStateLoss()
.beginTransaction()
.add(avoidOnResultFragment, TAG)
.commitAllowingStateLoss()
fragmentManager.executePendingTransactions()
}
return avoidOnResultFragment

View File

@ -0,0 +1,7 @@
package com.gh.common.avoidcallback
import android.content.Intent
interface Callback {
fun onActivityResult(resultCode: Int, data: Intent?)
}

View File

@ -1,21 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.download.server.BrowserInstallHelper
import com.gh.gamecenter.core.utils.EmptyCallback
import com.gh.gamecenter.feature.entity.GameEntity
class BrowserInstallHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
BrowserInstallHelper.showBrowserInstallHintDialog(context, gameEntity.isVGame(), object : EmptyCallback {
override fun onCallback() {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
})
}
}

View File

@ -1,18 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.common.dialog.CertificationDialog
import com.gh.gamecenter.feature.entity.GameEntity
class CertificationHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
CertificationDialog.showCertificationDialog(context, gameEntity) {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
}
}

View File

@ -1,28 +0,0 @@
package com.gh.common.chain
class ChainBuilder {
private val handlers: MutableList<ChainHandler> = ArrayList()
private var processEndCallback: ((Any?) -> Unit)? = null
fun setProcessEndCallback(callback: (Any?) -> Unit): ChainBuilder {
processEndCallback = callback
return this
}
fun addHandler(handler: ChainHandler): ChainBuilder {
handlers.add(handler)
return this
}
fun buildHandlerChain(): ChainHandler? {
for (i in handlers.indices) {
handlers[i].processEndCallback = processEndCallback
if (i + 1 < handlers.size) {
handlers[i].setNext(handlers[i + 1])
}
}
return if (handlers.isEmpty()) null else handlers[0]
}
}

View File

@ -1,24 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.gamecenter.feature.entity.GameEntity
abstract class ChainHandler {
private var next: ChainHandler? = null
var processEndCallback: ((Any?) -> Unit)? = null
fun setNext(next: ChainHandler?) {
this.next = next
}
fun getNext(): ChainHandler? {
return next
}
fun hasNext(): Boolean {
return next != null
}
abstract fun handleRequest(context: Context, gameEntity: GameEntity)
}

View File

@ -1,21 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.common.util.DialogUtils
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.feature.entity.GameEntity
class CheckDownloadHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
val apk = gameEntity.getApk().safelyGetInRelease(0) ?: return
DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean ->
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(isSubscribe)
}
}
}
}

View File

@ -1,21 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.gamecenter.common.utils.PermissionHelper
import com.gh.gamecenter.core.utils.EmptyCallback
import com.gh.gamecenter.feature.entity.GameEntity
class CheckStoragePermissionHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
PermissionHelper.checkStoragePermissionBeforeAction(context, object : EmptyCallback {
override fun onCallback() {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
})
}
}

View File

@ -1,23 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.common.util.DownloadDialogHelper
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.core.utils.EmptyCallback
import com.gh.gamecenter.feature.entity.GameEntity
class DownloadDialogHelperHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
val apk = gameEntity.getApk().safelyGetInRelease(0) ?: return
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, object : EmptyCallback {
override fun onCallback() {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
})
}
}

View File

@ -1,19 +0,0 @@
package com.gh.common.chain
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment
class GamePermissionHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
}
}

View File

@ -1,18 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.common.util.DialogUtils
import com.gh.gamecenter.feature.entity.GameEntity
class OverseaDownloadHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
DialogUtils.showOverseaDownloadDialog(context, gameEntity) {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
}
}

View File

@ -1,19 +0,0 @@
package com.gh.common.chain
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import com.gh.common.dialog.PackageCheckDialogFragment
import com.gh.gamecenter.feature.entity.GameEntity
class PackageCheckHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
PackageCheckDialogFragment.show((context as AppCompatActivity), gameEntity) {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
}
}

View File

@ -1,35 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.common.simulator.NewSimulatorGameManager
import com.gh.common.simulator.SimulatorGameManager
import com.gh.gamecenter.feature.entity.GameEntity
class UpdateNewSimulatorHandler: ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
if (SimulatorGameManager.isSimulatorGame(gameEntity)) {
if (NewSimulatorGameManager.shouldShowUpdateNewSimulatorAlert(context)) {
NewSimulatorGameManager.showUpdateNewsSimulator(context) {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
}
else{
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
}
}
}
else{
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
}
}
}
}

View File

@ -1,18 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.vspace.VHelper
class ValidateVSpaceHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
VHelper.validateVSpaceBeforeAction(context, gameEntity) {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
}
}

View File

@ -1,33 +0,0 @@
package com.gh.common.chain
import android.content.Context
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.DialogHelper
import com.gh.gamecenter.common.utils.toResString
import com.gh.gamecenter.feature.entity.GameEntity
class VersionNumberHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
val confirmCallback = {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {
processEndCallback?.invoke(null)
}
}
if (!gameEntity.isShowVersionNumber()) {
confirmCallback.invoke()
} else {
DialogHelper.showGuideDialog(
context,
"温馨提示",
gameEntity.getVersionNumberString(),
"继续下载",
R.string.cancel.toResString(),
{ confirmCallback.invoke() },
extraConfig = DialogHelper.Config(titleIcon = R.drawable.ic_dialog_tips)
)
}
}
}

View File

@ -1,88 +1,75 @@
package com.gh.common.constant;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import com.gh.common.util.AdHelper;
import com.gh.common.util.GsonUtils;
import com.gh.common.util.PackageHelper;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.SPUtils;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.constant.CommonConsts;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.retrofit.BiResponse;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.utils.DarkModeUtils;
import com.gh.gamecenter.common.utils.EnvHelper;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.core.utils.UrlFilterUtils;
import com.gh.gamecenter.entity.GameGuidePopupEntity;
import com.gh.gamecenter.entity.NewApiSettingsEntity;
import com.gh.gamecenter.entity.NewSettingsEntity;
import com.gh.gamecenter.entity.VSetting;
import com.gh.gamecenter.feature.entity.NewsEntity;
import com.gh.gamecenter.feature.entity.SettingsEntity;
import com.gh.gamecenter.feature.entity.SimulatorEntity;
import com.gh.gamecenter.SuggestionActivity;
import com.gh.gamecenter.entity.NewsEntity;
import com.gh.gamecenter.entity.SettingsEntity;
import com.gh.gamecenter.eventbus.EBReuse;
import com.gh.gamecenter.retrofit.Response;
import com.gh.gamecenter.retrofit.RetrofitManager;
import com.gh.vspace.VHelper;
import com.halo.assistant.HaloApp;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import androidx.annotation.Nullable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;
public class Config {
// 这个 API_HOST 在测试包里会随着选择的环境切换,正式包里会一直保持正式 host
public static final String API_HOST = EnvHelper.getHost();
public static final String NEW_API_HOST = EnvHelper.getNewHost();
public static final String VAPI_HOST = EnvHelper.getVHost();
public static final String API_HOST = BuildConfig.API_HOST;
public static final String SENSITIVE_API_HOST = BuildConfig.SENSITIVE_API_HOST;
/**
* 需要配置的请使用{@link PreferenceManager#getDefaultSharedPreferences(Context)}
*/
// Third-Party confs
public static final String WECHAT_APPID = BuildConfig.WECHAT_APPID;
public static final String WECHAT_SECRET = BuildConfig.WECHAT_SECRET;
public static final String TENCENT_APPID = BuildConfig.TENCENT_APPID;
public static final String WEIBO_APPKEY = BuildConfig.WEIBO_APPKEY;
public static final String QUICK_LOGIN_APPID = BuildConfig.QUICK_LOGIN_APPID;
public static final String QUICK_LOGIN_APPKEY = BuildConfig.QUICK_LOGIN_APPKEY;
public static final String MIPUSH_APPID = BuildConfig.MIPUSH_APPID;
public static final String MIPUSH_APPKEY = BuildConfig.MIPUSH_APPKEY;
public static final String MTA_APPKEY = BuildConfig.MTA_APPKEY;
public static final String TALKINGDATA_APPID = BuildConfig.TD_APPID;// TalkingData
public static final String UMENG_APPKEY = BuildConfig.UMENG_APPKEY;
public static final String UMENG_MESSAGE_SECRET = BuildConfig.UMENG_MESSAGE_SECRET;
public static final String BUGLY_APPID = BuildConfig.BUGLY_APPID;
public static final String LETO_APPID = BuildConfig.LETO_APPID;
public static final String TTAD_APPID = BuildConfig.TTAD_APPID;
public static final String DOUYIN_CLIENTKEY = BuildConfig.DOUYIN_CLIENTKEY;
public static final String DOUYIN_CLIENTSECRET = BuildConfig.DOUYIN_CLIENTSECRET;
// http://www.ghzs666.com/article/${articleId}.html
public static final String URL_ARTICLE = "http://www.ghzs666.com/article/"; // ghzs/ghzs666 统一
public static final String PATCHES = "patches";
private static final String SETTINGS_KEY = "settingsKey";
public static final String DEFAULT_CHANNEL = "GH_TEST";
private static String SETTINGS_KEY = "settingsKey";
private static SettingsEntity mSettingsEntity;
private static NewSettingsEntity mNewSettingsEntity;
private static NewApiSettingsEntity mNewApiSettingsEntity;
private static NewApiSettingsEntity.NightMode mNightModeSetting;
private static SimulatorEntity mNewSimulatorEntity;
private static VSetting mVSetting;
private static GameGuidePopupEntity mGameGuidePopupEntity;
private static SharedPreferences mDefaultSharedPreferences;
public static final String FIX_DOWNLOAD_KEY = "isFixDownload";
public static final String FIX_PLUGIN_KEY = "isFixPlugin";
public static final String FIX_ARTICLE_KEY = "isFixArticle";
public static final String FIX_COMMUNITY_KEY = "isFixCommunity";
public static final int VIDEO_PAGE_SIZE = 21; // 视频列表大多都是一行3个
public static boolean isShow() {
if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) return false;
if (getPreferences().getBoolean(FIX_DOWNLOAD_KEY, false)) return true;
if (!isExistDownloadFilter()) return false;
@ -97,6 +84,13 @@ public class Config {
return false;
}
public static String getExceptionMsg() {
return getPreferences().getString("errMsg", null);
}
public static void setExceptionMsg(String errMsg) {
SPUtils.setString(getPreferences(), "errMsg", errMsg); //先用apply(),保存不了再用commit() 9.0机型保存不了信息
}
public static boolean isShowDownload(String gameId) {
@ -122,29 +116,6 @@ public class Config {
return false;
}
/**
* 是否启用畅玩游戏
*/
public static boolean isVGameEnabled() {
if (getSettings() == null) {
return false;
}
return !"off".equals(getSettings().getGameSmooth());
}
/**
* VPN 开关选项是否开启
*/
public static boolean isVpnOptionEnabled() {
if (mNewApiSettingsEntity == null
|| mNewApiSettingsEntity.getInstall() == null
|| mNewApiSettingsEntity.getInstall().getVpnRequired() == null) {
return false;
}
return mNewApiSettingsEntity.getInstall().getVpnRequired().getShouldShowVpnOption();
}
public static boolean isShowPlugin(String gameId) {
SharedPreferences preferences = getPreferences();
@ -210,15 +181,11 @@ public class Config {
getPreferences().edit().putString(SETTINGS_KEY, GsonUtils.toJson(settingsEntity)).apply();
mSettingsEntity = settingsEntity;
// 更新设置状态
// 更新 FIX_ARTICLE_KEY 状态
mSettingsEntity.showArticleEntrance();
mSettingsEntity.showCommunityEntrance();
// 加载完设置后刷新下
PackageHelper.initList();
// 初始化畅玩相关的东西
VHelper.init(HaloApp.getInstance(), false);
}
@Nullable
@ -236,92 +203,6 @@ public class Config {
return mSettingsEntity;
}
//新模拟器
@Nullable
public static SimulatorEntity getNewSimulatorEntitySetting() {
if (mNewSimulatorEntity != null) {
return mNewSimulatorEntity;
} else if (mNewApiSettingsEntity != null && mNewApiSettingsEntity.getSimulator() != null) {
return mNewApiSettingsEntity.getSimulator();
} else {
return null;
}
}
@Nullable
public static NewApiSettingsEntity.NightMode getNightModeSetting() {
if (mNightModeSetting != null) {
return mNightModeSetting;
} else if (mNewApiSettingsEntity != null && mNewApiSettingsEntity.getNightMode() != null) {
return mNewApiSettingsEntity.getNightMode();
} else {
return null;
}
}
public static boolean getUserInterestedGame() {
if (mNewApiSettingsEntity != null) {
return mNewApiSettingsEntity.getUserInterestedGame();
} else {
return false;
}
}
@Nullable
public static NewSettingsEntity getNewSettingsEntity() {
if (mNewSettingsEntity == null) {
try {
String json = SPUtils.getString(Constants.SP_NEW_SETTINGS);
if (!TextUtils.isEmpty(json)) {
mNewSettingsEntity = GsonUtils.fromJson(json, NewSettingsEntity.class);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return mNewSettingsEntity;
}
@Nullable
public static NewApiSettingsEntity getNewApiSettingsEntity() {
if (mNewApiSettingsEntity == null) {
try {
String json = SPUtils.getString(Constants.SP_NEW_API_SETTINGS);
if (!TextUtils.isEmpty(json)) {
mNewApiSettingsEntity = GsonUtils.fromJson(json, NewApiSettingsEntity.class);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return mNewApiSettingsEntity;
}
public static void updateNewApiSettings(NewApiSettingsEntity settingsEntity) {
mNewApiSettingsEntity = settingsEntity;
SPUtils.setString(Constants.SP_NEW_API_SETTINGS, GsonUtils.toJson(settingsEntity));
}
@Nullable
public static VSetting getVSettingEntity() {
if (mVSetting == null) {
try {
String json = SPUtils.getString(Constants.SP_V_SETTINGS);
if (!TextUtils.isEmpty(json)) {
mVSetting = GsonUtils.fromJson(json, VSetting.class);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return mVSetting;
}
@Nullable
public static GameGuidePopupEntity getGameGuidePopupEntity() {
return mGameGuidePopupEntity;
}
private static boolean isExistDownloadFilter() {
if (getSettings() == null || getSettings().getDownload() == null || getSettings().getDownload().size() == 0) {
return false;
@ -344,22 +225,40 @@ public class Config {
}
public static SharedPreferences getPreferences() {
if (mDefaultSharedPreferences == null) {
mDefaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication());
}
return mDefaultSharedPreferences;
return PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication());
}
public static boolean isExistHideFunction() {
SharedPreferences preferences = getPreferences();
if (!preferences.getBoolean(FIX_DOWNLOAD_KEY, false)) return true;
if (!preferences.getBoolean(FIX_PLUGIN_KEY, false)) return true;
if (!preferences.getBoolean(FIX_COMMUNITY_KEY, false)) return true;
if (!preferences.getBoolean(FIX_ARTICLE_KEY, false)) return true;
return false;
}
public static boolean isGameDomeSwitchOpen() {
return getSettings() != null && getSettings().getGameDomeSwitch().equals("on");
}
public static boolean isPermissionPopupSwitchOpen() {
return getSettings() != null && getSettings().getPermissionPopupSwitch().equals("on");
}
public static void fixHideFunction() {
SharedPreferences preferences = PreferenceManager.
getDefaultSharedPreferences(HaloApp.getInstance().getApplication());
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(Config.FIX_DOWNLOAD_KEY, true);
editor.putBoolean(Config.FIX_ARTICLE_KEY, true);
editor.putBoolean(Config.FIX_COMMUNITY_KEY, true);
editor.putBoolean(Config.FIX_PLUGIN_KEY, true);
editor.apply();
}
@SuppressLint("CheckResult")
public static void getGhzsSettings() {
String channel = HaloApp.getInstance().getChannel();
// 把请求提前,避免请求过多阻塞,首页显示广告时老是用到上一次的缓存
getNewSettings(channel);
RetrofitManager.getInstance()
.getApi().getSettings(PackageUtils.getGhVersionName(), channel)
RetrofitManager.getInstance(HaloApp.getInstance().getApplication())
.getSensitiveApi().getSettings(PackageUtils.getVersionName(), channel)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<SettingsEntity>() {
@ -369,103 +268,15 @@ public class Config {
// 意见反馈
SharedPreferences.Editor edit = getPreferences().edit();
edit.putString(CommonConsts.SUGGESTION_HINT_TYPE,
edit.putString(SuggestionActivity.SUGGESTION_HINT_TYPE,
GsonUtils.toJson(response.getSuggestion()));
edit.apply();
if (!getPreferences().getBoolean(Config.FIX_DOWNLOAD_KEY, false) && Config.isShow()) {
getPreferences().edit().putBoolean(Config.FIX_DOWNLOAD_KEY, true).apply();
}
if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) {
EventBus.getDefault().post(new EBReuse("Refresh"));
}
EventBus.getDefault().post(new EBReuse("Refresh"));
}
});
RetrofitManager.getInstance()
.getApi().getNewSettings(Build.MANUFACTURER, Build.MODEL, channel, Build.VERSION.SDK_INT, BuildConfig.VERSION_NAME)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BiResponse<NewSettingsEntity>() {
@Override
public void onSuccess(NewSettingsEntity data) {
mNewSettingsEntity = data;
SPUtils.setString(Constants.SP_NEW_SETTINGS, GsonUtils.toJson(data));
}
});
if (mVSetting == null) {
RetrofitManager.getInstance()
.getVApi().getSettings(BuildConfig.VERSION_NAME)
.subscribeOn(Schedulers.io())
.subscribe(new BiResponse<VSetting>() {
@Override
public void onSuccess(VSetting data) {
mVSetting = data;
SPUtils.setString(Constants.SP_V_SETTINGS, GsonUtils.toJson(data));
}
});
}
RetrofitManager.getInstance()
.getApi().getGameGuidePopup(Build.MANUFACTURER, Build.VERSION.RELEASE, Build.MODEL, channel, BuildConfig.VERSION_NAME)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BiResponse<GameGuidePopupEntity>() {
@Override
public void onSuccess(GameGuidePopupEntity data) {
mGameGuidePopupEntity = data;
}
});
String manufacturer = Build.MANUFACTURER.toUpperCase(Locale.CHINA);
if (manufacturer.equals("OPPO") || manufacturer.equals("VIVO")) {
RetrofitManager.getInstance().getNewApi().getBrowserHintUrl(manufacturer)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BiResponse<ResponseBody>() {
@Override
public void onSuccess(ResponseBody data) {
try {
JSONObject jsonObject = new JSONObject(data.string());
if (!TextUtils.isEmpty(jsonObject.getJSONObject("data").getString("link"))) {
SPUtils.setString(Constants.SP_BROWSER_HINT_URL, jsonObject.getJSONObject("data").getString("link"));
}
} catch (JSONException | IOException e) {
e.printStackTrace();
}
}
});
}
}
@SuppressLint("CheckResult")
private static void getNewSettings(String channel) {
if (mNewApiSettingsEntity == null) {
String filterString = UrlFilterUtils.getFilterQuery(
"manufacturer", Build.MANUFACTURER,
"model", Build.MODEL,
"android_sdk_version", String.valueOf(Build.VERSION.SDK_INT));
RetrofitManager.getInstance()
.getNewApi().getNewSettings(PackageUtils.getGhVersionName(), channel, filterString)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BiResponse<NewApiSettingsEntity>() {
@Override
public void onSuccess(NewApiSettingsEntity data) {
mNewApiSettingsEntity = data;
mNightModeSetting = data.getNightMode();
mNewSimulatorEntity = data.getSimulator();
if (HaloApp.getInstance().isNewForThisVersion && mNightModeSetting != null && mNightModeSetting.getInstall()) {
DarkModeUtils.INSTANCE.updateFollowSystemDarkModeToSp(true);
DarkModeUtils.INSTANCE.initDarkMode();
}
AdHelper.prefetchStartUpAd(mNewApiSettingsEntity);
SPUtils.setString(Constants.SP_NEW_API_SETTINGS, GsonUtils.toJson(data));
}
});
}
}
}

View File

@ -0,0 +1,206 @@
package com.gh.common.constant;
import com.gh.common.util.PackageUtils;
import com.halo.assistant.HaloApp;
public class Constants {
public static final int SEND_NEWS_FEEDBACK = 0x126;
public static final int SEND_COMMENT_FEEDBACK = 0x127;
public final static int LIST_FOOTER_ITEM = 1;
public final static int LIST_HEAD_ITEM = 1;
public final static int NOT_NETWORK_CODE = 504; // 没有网络的状态码(应该是这个吧!)
public static final String USER_TOKEN_KEY = "userTokenKey";
public static final String USER_INFO_KEY = "userInfoKey";
public static final String WELCOME_DIALOG_ID = "welcome_dialog_id";
public static final String WELCOME_DIALOG_LINK_TITLE = "welcome_dialog_link_title";
public static final String DEVICE_KEY = "deviceKey";
public static final String HAS_REQUESTED_NOTIFICATION_PERMISSIONS = "has_requested_notification_permissions";
public static final String SHOULD_SHOW_VIDEO_MOBILE_WARNING = "should_show_video_mobile_warning";
public static final String GAME_DETAIL_COME_IN = "game_detail_come_in"; // 从游戏详情进入
public static final String XPOSED_INSTALLER_PACKAGE_NAME = "de.robv.android.xposed.installer";
public static final String EB_QUIT_LOGIN = "quit_login";
// 用于避免历史下载掺和到普通下载状态的 ID 修饰符
public static final String GAME_ID_DIVIDER = ":";
// 用于避免历史下载影响到部分依赖名字作为数据更新条件的修饰符
public static final String GAME_NAME_DECORATOR = " ";
// 游戏详情进入时的自定义栏目标签是否已经默认展开过一次的标记
public static final String SP_HAS_EXPANDED_GAME_DETAIL_TAGS = "has_expanded_game_detail_tags";
// 游戏详情进入时的自定义栏目标签是否已经显示过一次展开更多的浮窗提示
public static final String SP_HAS_SHOWN_EXPANDED_GAME_DETAIL_TAGS_HINT = "has_shown_expanded_game_detail_tags_hint";
// 最近显示的弹窗信息
public static final String SP_LAST_OPENING_ID = "last_opening_dialog_id";
public static final String SP_LAST_OPENING_TIME = "last_opening_dialog_time";
// 游戏图标和图标角标
public static final String RAW_GAME_ICON = "raw_game_icon";
public static final String GAME_ICON_SUBSCRIPT = "game_icon_subscript";
public static final String EXTRA_DOWNLOAD_TYPE = "extra_download_type";
public static final String SILENT_UPDATE = "静默更新";
public static final String SIMULATOR_DOWNLOAD = "下载模拟器";
public static final String SIMULATOR_GAME = "simulator_game";
public static final String SIMULATOR_DOWNLOAD_START_TIME = "simulator_download_start_time";
public static final String LAST_GHZS_UPDATE_FILE_SIZE = "last_ghzs_update_file_size";
// 新用户首次启动光环的时间
public static final String SP_INITIAL_USAGE_TIME = "initial_usage_time";
public static final String SP_IMEI = "imei";
public static final String SP_ANDROID_ID = "android_id";
//引导设置 “通知管理” 引导弹窗
public static final String SP_SHOWED_NOTIFICATION_LOGIN = "show_notification_login_hint";
public static final String SP_SHOWED_NOTIFICATION_QUESTION = "show_notification_question_hint";
public static final String SP_SHOWED_NOTIFICATION_ANSWER = "show_notification_answer_hint";
public static final String SP_SHOWED_NOTIFICATION_ARTICLE = "show_notification_article_hint";
public static final String SP_SHOWED_NOTIFICATION_VIDEO = "show_notification_video_hint";
public static final String SP_SHOWED_NOTIFICATION_RATING = "show_notification_rating_hint";
public static final String SP_SHOWED_NOTIFICATION_GIFT = "show_notification_gift_hint";
public static final String SP_SHOWED_NOTIFICATION_RESERVE_GAME = "show_notification_reserve_game_hint";
public static final String SP_SHOWED_NOTIFICATION_FEEDBACK = "show_notification_feedback_hint";
// 新版本 也要触发一次“通知管理” 引导弹窗
public static final String SP_SHOWED_NOTIFICATION_NEW_VERSION = "show_notification_new_version";
// 今天是否已经触发了 “通知管理” 引导弹窗
public static final String SP_IS_SHOWED_NOTIFICATION_TODAY = "show_is_notification_today";
// v4.0.0已废弃,标记安装的游戏为已玩过弹窗最多取消2次 (https://gitlab.ghzs.com/pm/halo-app-issues/issues/722 调整为版本相关) (不是常量了也放这里好像有点奇怪)
public static final String SP_MARK_INSTALLED_GAME = "mark_installed_game" + PackageUtils.getVersionName();
// 标记安装的游戏为已玩过弹窗(个人主页最多弹一次)
public static final String SP_MARK_INSTALLED_GAME_USER_HOME = "mark_installed_game_user_home" + PackageUtils.getVersionName();
// 标记安装的游戏为已玩过弹窗(我的游戏最多弹一次)
public static final String SP_MARK_INSTALLED_GAME_MY_GAME = "mark_installed_game_my_game" + PackageUtils.getVersionName();
//视频详情滑动引导
public static final String SP_SHOW_SLIDE_GUIDE = "show_slide_guide";
//视频详情点击引导
public static final String SP_SHOW_CLICK_GUIDE = "show_click_guide";
//视频详情双击点赞引导
public static final String SP_SHOW_DOUBLE_CLICK_GUIDE = "show_double_click_guide";
//顶部视频声音状态,重启恢复
public static final String SP_TOP_VIDEO_VOICE = "top_video_voice";
//我的光环提醒设置已读
public static final String SP_ADDONS_FUNCS_HAVE_READ = "addons_funcs_have_read";
//视频非wifi提醒只提醒一次重启恢复
public static final String SP_NON_WIFI_TIPS = "non_wifi_tips";
//首页视频最新tab提示
public static final String SP_HOME_NEW_VIDEO_TIPS = "home_new_video";
//游戏设备弹窗提示
public static final String SP_DEVICE_REMIND = "device_remind";
//是否是第一次弹出游戏设备弹窗提示
public static final String SP_FIRST_DEVICE_REMIND = "first_device_remind";
//游戏设备弹窗不再提示
public static final String SP_NO_REMIND_AGAIN = "no_remind_again";
//游戏详情过滤标签数据
public static final String SP_FILTER_TAGS = "filter_tags";
//实名认证弹窗分类数据
public static final String SP_AUTH_DIALOG = "auth_dialog";
//顶部视频进度保存,重启恢复
public static final String SP_TOP_VIDEO_SCHEDULE = "top_video_schedule";
//我的光环小红点提示
public static final String SP_GH_RED_POINT_REMIND = "gh_red_point_remind";
//论坛首页引导
public static final String SP_FORUM_GUIDE = "forum_guide";
//礼仪考试开启状态
public static final String SP_REGULATION_TEST_LAST_REMIND_TIME = "regulation_test_last_remind_time";
public static final String SP_REGULATION_TEST_STATUS = "regulation_test_status";
public static final String SP_REGULATION_TEST_PASS_STATUS = "regulation_test_pass_status";
//相同设备号,每一种第三方登录方式登录后弹出绑定手机页面的次数
public static final String SP_QQ_SHOW_BIND_PHONE_TIME = "qq_show_bind_phone_time" + HaloApp.getInstance().getGid();
public static final String SP_WECHAT_SHOW_BIND_PHONE_TIME = "wechat_show_bind_phone_time" + HaloApp.getInstance().getGid();
public static final String SP_WEIBO_SHOW_BIND_PHONE_TIME = "weibo_show_bind_phone_time" + HaloApp.getInstance().getGid();
public static final String SP_DOUYIN_SHOW_BIND_PHONE_TIME = "douyin_show_bind_phone_time" + HaloApp.getInstance().getGid();
//隐私政策是否有更新
public static final String SP_PRIVACY_CURRENT_MD5 = "sp_privacy_current_md5";
public static final String SP_PRIVACY_MINE_MD5 = "sp_privacy_mine_md5";
public static final String SP_PRIVACY_SETTING_MD5 = "sp_privacy_setting_md5";
public static final String SP_PRIVACY_MD5 = "sp_privacy_md5";
public static final String SP_IS_USER_ACCEPTED_PRIVACY_STATEMENT = "has_user_accepted_privacy_statement";
public static final String SP_BRAND_NEW_USER = "brand_new_user";
//手机号码匹配规则
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
public static final String REGEX_ACCOUNT = "^[a-zA-Z_]\\w{5,17}$";
public static final String REGEX_PASSWORD = "^[a-zA-Z]\\w{5,31}$";
//输入规则
public static final String INPUT_RULE = "0123456789abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ_";
// 微信绑定地址
public static final String WECHAT_BIND_ADDRESS_DEV = "https://resource.ghzs.com/page/wechat_dev/index.html#/";
public static final String WECHAT_BIND_ADDRESS = "https://resource.ghzs.com/page/wechat_pro/index.html#/";
// 礼仪考试地址
public static final String REGULATION_TEST_ADDRESS_DEV = "https://static-web.ghzs.com/etiquette-dev/index.html#/";
public static final String REGULATION_TEST_ADDRESS = "https://static-web.ghzs.com/etiquette/index.html#/";
// 徽章中心
public static final String BADGE_ADDRESS_DEV = "https://static-web.ghzs.com/badge-dev/index.html#/";
public static final String BADGE_ADDRESS = "https://static-web.ghzs.com/badge/index.html#/";
// 徽章详情
public static final String BADGE_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/badge-dev/index.html#/badgedetail";
public static final String BADGE_DETAIL_ADDRESS = "https://static-web.ghzs.com/badge/index.html#/badgedetail";
// 分享个人主页地址
public static final String SHARE_USER_HOME_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs-userhome-dev/index.html#/";
public static final String SHARE_USER_HOME_ADDRESS = "https://static-web.ghzs.com/ghzs-userhome/index.html#/";
// 腾讯企点地址
public static final String TENCENT_QIDIAN_ADDRESS = "https://admin.qidian.qq.com/template/blue/mp/menu/qr-code-jump.html?linkType=0&env=ol&kfuin=2355094296&fid=457&key=c76dcb2e3d582b6ffbfb5bb22cde85ff&cate=1&source=&isLBS=&isCustomEntry=&type=16&ftype=1&_type=wpa&qidian=true";
//版规声明
public static final String FORUM_REGULATIONS_NEWS_ID = "5f4db9cc34d44d01b92fd670";
//帮助内容详情
public static final String HELP_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_help_dev/help.html?content=";
public static final String HELP_ADDRESS = "https://static-web.ghzs.com/ghzs_help/help.html?content=";
// 注销页面
public static final String LOGOUT_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_help_dev/help.html?content=5f6b1f02786564003944a693";
public static final String LOGOUT_ADDRESS = "https://static-web.ghzs.com/ghzs_help/help.html?content=5f534111b1f72909fc225672";
//最少需要多少数据才能上传
public static final int DATA_AMOUNT = 20;
//游戏 cd间隔
public static final int GAME_CD = 5 * 60 * 1000;
//新闻 cd间隔
public static final int NEWS_CD = 10 * 60 * 1000;
//platform cd间隔
public static final int PLATFORM_CD = 10 * 60 * 1000;
//update cd间隔
public static final int UPDATE_CD = 5 * 60 * 1000;
//搜索 cd间隔
public static final int SEARCH_CD = 5 * 60 * 1000;
//评论 cd间隔
public static final int COMMENT_CD = 60 * 1000;
//我的光环功能分组 cd间隔
public static final int ADDONS_CD = 10 * 60 * 1000;
//已收录包名更新 cd间隔
public static final int PACKAGES_CD = 60 * 1000;
public static final String[] REPORT_LIST = new String[]{"垃圾广告营销", "恶意攻击谩骂", "淫秽色情信息", "违法有害信息", "其它"};
public static final String ENTRANCE_UNKNOWN = "(unknown)";
public static final String DEFAULT_TEXT_WRAPPER = "###";
// 触发了安装事件的标记
public static final String MARK_ALREADY_TRIGGERED_INSTALLATION = "triggered_installation";
// 标记下载重试标记(值为任务已下载大小,为空表示需要重试)
public static final String MARK_RETRY_DOWNLOAD = "retry_download";
}

View File

@ -0,0 +1,46 @@
package com.gh.common.constant;
/**
* Created by LGT on 2016/10/16.
*/
public class ItemViewType {
public static final int COLUMN_HEADER = 0; // 专题头部布局
public static final int GAME_SLIDE = 1; // 滚动图布局
public static final int GAME_NORMAL = 2; // 正常游戏布局
public static final int GAME_SUBJECT = 19;
public static final int GAME_SUBJECT_SLIDE = 26;
public static final int GAME_TEST = 3; // 测试游戏布局
public static final int GAME_IMAGE = 4; // 游戏大图布局
public static final int NEWS_HEADER = 5; // 新闻头部布局
public static final int NEWS_TEXT = 6; // 新闻文本布局
public static final int NEWS_IMAGE = 7; // 新闻带图布局
public static final int NEWS_IMAGE1 = 8; // 新闻带一张小图布局
public static final int NEWS_IMAGE2 = 9; // 新闻带三张小图布局
public static final int NEWS_IMAGE3 = 10; // 新闻带一张大图布局
public static final int NEWS_DIGEST = 11; // 新闻摘要布局
public static final int SEARCH_NORMAL = 12; // 搜索正常布局
public static final int LOADING = 14; // 加载布局
public static final int LIBAO_NORMAL = 15; // 礼包正常布局
public static final int LIBAO_SKIP_CONCERN = 16; // 跳转关注管理页面布局
public static final int KC_HINT = 17;
public static final int GAME_PLUGIN = 18; // 游戏插件模块
public static final int ASK_REFRESH = 19; // 问答精选 刷新
public static final int ITEM_EMPTY = 20;
public static final int ASK_CONCERN = 21; // 问答精选 关注
public static final int RATING_ITEM = 22; // 问答精选 关注
public static final int IMAGE_SLIDE_ITEM = 23;
public static final int VERTICAL_SLIDE_ITEM = 24;
public static final int COLUMN_COLLECTION = 25;
public static final int GALLERY_SLIDE = 27; // 首页自动滚动画廊专题
public static final int GALLERY = 28; // 首页倾斜画廊专题
public static final int BLANK_DIVIDER = 29; // 空白补充区域
/**
* 普通列表
*/
public static final int ITEM_BODY = 100;
public static final int ITEM_FOOTER = 101;
public static final int ITEM_HEADER = 102;
}

View File

@ -1,187 +0,0 @@
package com.gh.common.databind
import android.os.Build
import android.text.Editable
import android.text.Html
import android.text.TextWatcher
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.PopupWindow
import androidx.core.content.ContextCompat
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import com.gh.gamecenter.common.callback.OnViewClickListener
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.common.view.BugFixedPopupWindow
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.KaifuAddItemBinding
import com.gh.gamecenter.databinding.LayoutAddKaifuPopupBinding
import com.gh.gamecenter.databinding.LayoutPopupContainerBinding
import com.gh.gamecenter.feature.entity.ServerCalendarEntity
import com.gh.gamecenter.servers.add.AddKaiFuPopupAdapter
import java.text.SimpleDateFormat
import java.util.*
object AddKaiFuBindingAdapter {
private var popupWindow: BugFixedPopupWindow? = null
@JvmStatic
@BindingAdapter("addKaiFuView", "clickListener")
fun addKaiFuView(
view: LinearLayout,
list: List<ServerCalendarEntity?>?,
listener: OnViewClickListener<*>?
) {
if (list == null) return
view.removeAllViews()
view.addView(LayoutInflater.from(view.context).inflate(R.layout.kaifu_new_add_item_title, null))
for (i in list.indices) {
val inflate = LayoutInflater.from(view.context).inflate(R.layout.kaifu_add_item, null)
val binding = KaifuAddItemBinding.bind(inflate)
binding.clickListener = listener
binding.entity = list[i]
binding.position = i
binding.isCloseBottom = list.size - 1 == i
view.addView(inflate)
binding.kaifuAddFirstName.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
binding.kaifuAddFirstName.hint = ""
if (binding.kaifuAddFirstName.text.isNullOrEmpty()) {
showPopupOption(binding.kaifuAddFirstName) {
binding.kaifuAddFirstName.setText(it)
binding.kaifuAddFirstName.setSelection(it.length)
}
}
} else {
binding.kaifuAddFirstName.hint = "点击填写"
}
}
binding.kaifuAddServerName.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
binding.kaifuAddServerName.hint = ""
} else {
binding.kaifuAddServerName.hint = "点击填写"
}
}
binding.kaifuAddFirstName.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (!s.isNullOrEmpty()) {
popupWindow?.dismiss()
}
}
override fun afterTextChanged(s: Editable?) {
}
})
if (i == list.size - 1) {
binding.kaifuAddTime.background = R.drawable.bg_add_kaifu_bottom_left.toDrawable(view.context)
binding.kaifuAddServerName.background = R.drawable.bg_add_kaifu_bottom_right.toDrawable(view.context)
}
}
}
@JvmStatic
@BindingAdapter("addKaiFuTime", "addKaiFuPosition")
fun addKaiFuTime(view: EditText, time: Long, position: Int) {
if (time == 0L) {
view.hint = "点击选择"
view.setText("")
} else {
val pattern = "yyy-MM-dd HH:mm"
val format = SimpleDateFormat(pattern, Locale.CHINA)
view.setText(format.format(time * 1000))
if (position == 0) {
view.append(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) Html.fromHtml(
String.format(
"<font color='#2496FF'>%1\$s</font>",
"&nbsp+"
), Html.FROM_HTML_MODE_LEGACY
) else Html.fromHtml(String.format("<font color='#2496FF'>%1\$s</font>", "&nbsp+"))
)
}
}
}
@JvmStatic
@BindingAdapter("kaiFuTextColor", "kaiFuTextPosition")
fun kaiFuTextColor(view: EditText, dataMark: Int, position: Int) {
if (dataMark == 1 && view.id == R.id.kaifu_add_time || dataMark == 2 && view.id == R.id.kaifu_add_first_name || dataMark == 3 && view.id == R.id.kaifu_add_server_name || dataMark == 4) {
view.setTextColor(ContextCompat.getColor(view.context, R.color.theme_red))
view.setHintTextColor(ContextCompat.getColor(view.context, R.color.theme_red))
} else if (position == 0) {
view.setTextColor(ContextCompat.getColor(view.context, R.color.hint))
view.setHintTextColor(ContextCompat.getColor(view.context, R.color.hint))
} else {
view.setTextColor(ContextCompat.getColor(view.context, R.color.text_title))
view.setHintTextColor(ContextCompat.getColor(view.context, R.color.hint))
}
}
@JvmStatic
private fun showPopupOption(view: View, callback: (text: String) -> Unit) {
val inflater = LayoutInflater.from(view.context)
val mainBinding = LayoutPopupContainerBinding.inflate(inflater)
popupWindow = BugFixedPopupWindow(
mainBinding.root,
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
val windowPos = IntArray(2)
val anchorLoc = IntArray(2)
// 获取锚点View在屏幕上的左上角坐标位置
view.getLocationOnScreen(anchorLoc)
val anchorHeight = view.height
// 获取屏幕的高宽
val screenHeight = view.context.resources.displayMetrics.heightPixels
popupWindow?.let { popupWindow ->
// 测量contentView
popupWindow.contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
val windowHeight = popupWindow.contentView.measuredHeight
// 判断需要向上弹出还是向下弹出显示
val isNeedShowUp = screenHeight - anchorLoc[1] - anchorHeight < windowHeight
windowPos[1] = if (isNeedShowUp) {
anchorLoc[1] - windowHeight
} else anchorLoc[1] + anchorHeight
LayoutAddKaifuPopupBinding.inflate(inflater, mainBinding.container, false).apply {
root.layoutParams = root.layoutParams.apply {
width = view.width + 16F.dip2px()
}
popupRv.adapter = AddKaiFuPopupAdapter(
root.context,
arrayListOf("安卓混服", "硬核专服", "官方专服", "官方渠道服", "腾讯QQ服", "腾讯微信服")
) {
callback.invoke(it)
popupWindow.dismiss()
}
popupRv.layoutManager = LinearLayoutManager(root.context)
mainBinding.container.addView(root)
}
popupWindow.isTouchable = true
popupWindow.inputMethodMode = PopupWindow.INPUT_METHOD_NEEDED
popupWindow.isOutsideTouchable = true
popupWindow.animationStyle = R.style.popwindow_option_anim_style
// 设置偏移
windowPos[1] = windowPos[1] - 12F.dip2px()
windowPos[0] = anchorLoc[0] - 11F.dip2px()
popupWindow.showAtLocation(view, Gravity.TOP or Gravity.START, windowPos[0], windowPos[1])
}
}
}

View File

@ -10,74 +10,58 @@ import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.common.chain.BrowserInstallHandler;
import com.gh.common.chain.CertificationHandler;
import com.gh.common.chain.ChainBuilder;
import com.gh.common.chain.ChainHandler;
import com.gh.common.chain.CheckDownloadHandler;
import com.gh.common.chain.DownloadDialogHelperHandler;
import com.gh.common.chain.GamePermissionHandler;
import com.gh.common.chain.OverseaDownloadHandler;
import com.gh.common.chain.PackageCheckHandler;
import com.gh.common.chain.ValidateVSpaceHandler;
import com.gh.common.chain.VersionNumberHandler;
import com.gh.base.OnViewClickListener;
import com.gh.common.constant.Config;
import com.gh.common.filter.RegionSetting;
import com.gh.common.filter.RegionSettingHelper;
import com.gh.common.dialog.CertificationDialog;
import com.gh.common.dialog.ReserveDialogFragment;
import com.gh.common.exposure.ExposureEvent;
import com.gh.common.history.HistoryHelper;
import com.gh.common.repository.ReservationRepository;
import com.gh.common.simulator.SimulatorDownloadManager;
import com.gh.common.simulator.SimulatorGameManager;
import com.gh.common.util.CheckLoginUtils;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.DownloadDialogHelper;
import com.gh.common.util.GameUtils;
import com.gh.common.util.GameViewUtils;
import com.gh.common.util.ImageUtils;
import com.gh.common.util.LogUtils;
import com.gh.common.util.MtaHelper;
import com.gh.common.util.NewsUtils;
import com.gh.common.util.NumberUtils;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.PermissionHelper;
import com.gh.common.util.PlatformUtils;
import com.gh.common.util.ReservationHelper;
import com.gh.common.view.DownloadProgressBar;
import com.gh.common.view.DrawableView;
import com.gh.common.view.GameIconView;
import com.gh.download.DownloadManager;
import com.gh.download.dialog.DownloadDialog;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.WebActivity;
import com.gh.gamecenter.common.baselist.LoadStatus;
import com.gh.gamecenter.common.callback.OnViewClickListener;
import com.gh.gamecenter.common.entity.LinkEntity;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.utils.DarkModeUtils;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.ImageUtils;
import com.gh.gamecenter.common.view.DrawableView;
import com.gh.gamecenter.core.utils.DisplayUtils;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.core.utils.NumberUtils;
import com.gh.gamecenter.baselist.LoadStatus;
import com.gh.gamecenter.databinding.KaifuAddItemBinding;
import com.gh.gamecenter.databinding.KaifuDetailItemRowBinding;
import com.gh.gamecenter.feature.entity.ApkEntity;
import com.gh.gamecenter.feature.entity.CommunityVideoEntity;
import com.gh.gamecenter.feature.entity.GameEntity;
import com.gh.gamecenter.feature.entity.PluginLocation;
import com.gh.gamecenter.feature.entity.ServerCalendarEntity;
import com.gh.gamecenter.feature.entity.TagStyleEntity;
import com.gh.gamecenter.feature.entity.TestEntity;
import com.gh.gamecenter.feature.exposure.ExposureEvent;
import com.gh.gamecenter.feature.utils.PlatformUtils;
import com.gh.gamecenter.feature.view.DownloadButton;
import com.gh.gamecenter.feature.view.GameIconView;
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment;
import com.gh.gamecenter.entity.ApkEntity;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.entity.LinkEntity;
import com.gh.gamecenter.entity.PluginLocation;
import com.gh.gamecenter.entity.ServerCalendarEntity;
import com.gh.gamecenter.entity.TagStyleEntity;
import com.gh.gamecenter.entity.TestEntity;
import com.gh.gamecenter.eventbus.EBReuse;
import com.gh.gamecenter.manager.PackagesManager;
import com.gh.vspace.VDownloadManagerActivity;
import com.gh.vspace.VHelper;
import com.gh.gamecenter.qa.entity.CommunityVideoEntity;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.Utils;
@ -85,8 +69,16 @@ import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.databinding.BindingAdapter;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
/**
* Created by khy on 12/02/18.
@ -94,18 +86,22 @@ import java.util.List;
public class BindingAdapters {
@BindingAdapter("imageIcon")
public static void loadIcon(SimpleDraweeView view, String imageUrl) {
ImageUtils.displayIcon(view, imageUrl);
}
@BindingAdapter("imageUrl")
public static void loadImage(SimpleDraweeView view, String imageUrl) {
ImageUtils.display(view, imageUrl);
}
@BindingAdapter("setTextSize")
public static void setTextSize(TextView view, int number) {
view.setTextSize(number);
}
@BindingAdapter("setTypeface")
public static void setTypeface(TextView view, String type) {
if (type == null) return;
@ -125,6 +121,7 @@ public class BindingAdapters {
}
}
@BindingAdapter({"addDetailKaiFuView", "addDetailKaiFuViewListener", "isReadyPatch"})
public static void addDetailKaiFuView(LinearLayout view, List<ServerCalendarEntity> list
, OnViewClickListener listener, Boolean isReadyPatch) {
if (list == null) return;
@ -132,13 +129,13 @@ public class BindingAdapters {
for (int i = 0; i < list.size() + 1; i++) { // 1 is Title
View inflate = LayoutInflater.from(view.getContext()).inflate(R.layout.kaifu_detail_item_row, null);
KaifuDetailItemRowBinding binding = KaifuDetailItemRowBinding.bind(inflate);
binding.getRoot().setBackgroundColor(isReadyPatch != null && isReadyPatch ? ExtensionsKt.toColor(R.color.theme) : ExtensionsKt.toColor(R.color.white));
binding.getRoot().setPadding(DisplayUtils.dip2px(1), DisplayUtils.dip2px(1), DisplayUtils.dip2px(1), i == list.size() ? DisplayUtils.dip2px(1) : 0);
ServerCalendarEntity serverEntity = list.get(i - 1);
binding.timeTv.setText(i == 0 ? "时间" : serverEntity.getFormatTime("HH:mm"));
binding.remarkTv.setText(i == 0 ? "备注" : serverEntity.getRemark());
binding.nameTv.setText(i == 0 ? "名字" : (TextUtils.isEmpty(serverEntity.getNote()) ? "-" : serverEntity.getNote()));
if (i != 0) {
binding.setIsCloseBottom(i == list.size());
binding.setIsReadyPatch(isReadyPatch);
if (i == 0) {
binding.setIsTitle(true);
} else {
ServerCalendarEntity serverEntity = list.get(i - 1);
binding.setEntity(serverEntity);
binding.getRoot().setOnClickListener(v -> {
listener.onClick(v, isReadyPatch != null && isReadyPatch ? serverEntity : null);
});
@ -160,10 +157,77 @@ public class BindingAdapters {
}
// 如果超过10000则转换为1.0W
@BindingAdapter("transSimpleCount")
public static void transSimpleCount(TextView view, int count) {
view.setText(NumberUtils.transSimpleCount(count));
}
@BindingAdapter({"addKaiFuView", "clickListener"})
public static void addKaiFuView(LinearLayout view, List<ServerCalendarEntity> list, OnViewClickListener listener) {
if (list == null) return;
view.removeAllViews();
view.addView(LayoutInflater.from(view.getContext()).inflate(R.layout.kaifu_add_item_title, null));
for (int i = 0; i < list.size(); i++) {
View inflate = LayoutInflater.from(view.getContext()).inflate(R.layout.kaifu_add_item, null);
KaifuAddItemBinding binding = KaifuAddItemBinding.bind(inflate);
binding.setClickListener(listener);
binding.setEntity(list.get(i));
binding.setPosition(i);
binding.setIsCloseBottom(list.size() - 1 == i);
view.addView(inflate);
binding.kaifuAddName.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
binding.kaifuAddName.setHint("");
} else {
binding.kaifuAddName.setHint("点击填写");
}
});
binding.kaifuAddRemark.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
binding.kaifuAddRemark.setHint("");
} else {
binding.kaifuAddRemark.setHint("点击填写");
}
});
}
}
@BindingAdapter({"addKaiFuTime", "addKaiFuPosition"})
public static void addKaiFuTime(EditText view, Long time, Integer position) {
if (time == 0) {
view.setHint("点击选择");
view.setText("");
} else {
String pattern;
if (position == 0) {
pattern = "yyy-MM-dd HH:mm +";
} else {
pattern = "yyy-MM-dd HH:mm";
}
SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.CHINA);
view.setText(format.format(time * 1000));
}
}
@BindingAdapter({"kaiFuTextColor", "kaiFuTextPosition"})
public static void kaiFuTextColor(EditText view, Integer dataMark, Integer position) {
if (dataMark == 1 && view.getId() == R.id.kaifu_add_time
|| dataMark == 2 && view.getId() == R.id.kaifu_add_name
|| dataMark == 3 && view.getId() == R.id.kaifu_add_remark
|| dataMark == 4 && (view.getId() == R.id.kaifu_add_time || view.getId() == R.id.kaifu_add_name)) {
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.red));
view.setHintTextColor(ContextCompat.getColor(view.getContext(), R.color.red));
} else if (position == 0) {
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.hint));
view.setHintTextColor(ContextCompat.getColor(view.getContext(), R.color.hint));
} else {
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.title));
view.setHintTextColor(ContextCompat.getColor(view.getContext(), R.color.hint));
}
}
@BindingAdapter("textColorFromString")
public static void textColorFromString(TextView tv, String hexString) {
if (TextUtils.isEmpty(hexString)) return;
@ -174,6 +238,7 @@ public class BindingAdapters {
}
}
@BindingAdapter("visibleGone")
public static void showHide(View view, Boolean show) {
if (show != null && show) {
view.setVisibility(View.VISIBLE);
@ -182,6 +247,7 @@ public class BindingAdapters {
}
}
@BindingAdapter("goneIf")
public static void goneIf(View view, Boolean gone) {
if (gone != null && gone) {
view.setVisibility(View.GONE);
@ -193,13 +259,7 @@ public class BindingAdapters {
/**
* lazy 的 paddingTop
*/
public static void lazyPaddingLeft(View view, int paddingLeftInDp) {
view.setPadding(DisplayUtils.dip2px(paddingLeftInDp), view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());
}
/**
* lazy 的 paddingTop
*/
@BindingAdapter("lazyPaddingTop")
public static void lazyPaddingTop(View view, int paddingTopInDp) {
view.setPadding(view.getPaddingLeft(), DisplayUtils.dip2px(paddingTopInDp), view.getPaddingRight(), view.getPaddingBottom());
}
@ -207,10 +267,12 @@ public class BindingAdapters {
/**
* lazy 的 paddingBottom
*/
@BindingAdapter("lazyPaddingBottom")
public static void lazyPaddingBottom(View view, int paddingBottomInDp) {
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), DisplayUtils.dip2px(paddingBottomInDp));
}
@BindingAdapter("visibleInvisible")
public static void visibleInvisible(View view, Boolean show) {
if (show != null && show) {
view.setVisibility(View.VISIBLE);
@ -219,6 +281,7 @@ public class BindingAdapters {
}
}
@BindingAdapter("messageUnread")
public static void setMessageUnread(TextView view, int unreadCount) {
if (unreadCount < 100) {
view.setText(String.valueOf(unreadCount));
@ -227,6 +290,7 @@ public class BindingAdapters {
}
}
@BindingAdapter("serverTypePadding")
public static void setServerTypePadding(TextView view, String serverType) {
int paddRight = 0;
if (TextUtils.isEmpty(serverType)) {
@ -244,6 +308,7 @@ public class BindingAdapters {
view.setPadding(0, 0, paddRight, 0);
}
@BindingAdapter("serverType")
public static void setServerType(TextView view, String serverType) {
view.setText(serverType);
if ("删档内测".equals(serverType) || "不删档内测".equals(serverType)) {
@ -253,22 +318,19 @@ public class BindingAdapters {
}
}
@BindingAdapter("game")
public static void setGame(View view, GameEntity gameEntity) {
if (gameEntity != null && view instanceof GameIconView) {
((GameIconView) view).displayGameIcon(gameEntity);
}
}
public static void setGameIcon(View view, GameEntity gameEntity) {
if (gameEntity != null && view instanceof GameIconView) {
((GameIconView) view).displayGameIcon(gameEntity.getIcon(), gameEntity.getIconSubscript(), gameEntity.getIconFloat());
}
}
@BindingAdapter("articleType")
public static void setArticleType(TextView view, String articleType) {
NewsUtils.setNewsType(view, articleType, 0, 0);
}
@BindingAdapter("detailDownloadText")
public static void setDetailDownloadText(TextView view, GameEntity gameEntity) {
if (gameEntity == null || gameEntity.getApk().isEmpty()) {
view.setBackgroundResource(R.drawable.game_item_btn_pause_style);
@ -277,6 +339,7 @@ public class BindingAdapters {
}
}
@BindingAdapter("liBaoBtn")
public static void setLiBaoBtn(TextView view, String status) {
if (TextUtils.isEmpty(status)) return;
switch (status) {
@ -341,16 +404,14 @@ public class BindingAdapters {
}
// 大图下的进度条
public static void setDownloadButton(DownloadButton progressBar,
@BindingAdapter({"downloadButton", "traceEvent", "clickCallBack", "entrance", "location"})
public static void setDownloadButton(DownloadProgressBar progressBar,
GameEntity gameEntity,
ExposureEvent traceEvent,
@Nullable View.OnClickListener clickCallBack,
@Nullable String entrance,
@Nullable String location) {
// 恢复DialogFragment
restoreDialogFragment(progressBar);
// 判断是否显示按钮
if (gameEntity != null
&& Config.isShowDownload(gameEntity.getId())
@ -364,77 +425,50 @@ public class BindingAdapters {
// 点击事件
progressBar.setOnClickListener(v -> {
if (clickCallBack != null) clickCallBack.onClick(v);
switch (progressBar.getButtonStyle()) {
switch (progressBar.getDownloadType()) {
case DOWNLOADING_PLUGIN:
case DOWNLOADING_NORMAL:
if (gameEntity.isVGame()) {
v.getContext().startActivity(VDownloadManagerActivity.getIntent(v.getContext(), true));
} else {
Intent intent = DownloadManagerActivity.getDownloadMangerIntent(v.getContext(),
gameEntity.getApk().get(0).getUrl(), entrance);
v.getContext().startActivity(intent);
}
Intent intent = DownloadManagerActivity.getDownloadMangerIntent(v.getContext(),
gameEntity.getApk().get(0).getUrl(), entrance);
v.getContext().startActivity(intent);
break;
case NONE:
Utils.toast(v.getContext(), "该游戏已关闭下载");
break;
case NORMAL:
case PLUGIN:
final RegionSetting.GameH5Download gameH5Download = RegionSettingHelper.getGameH5DownloadByGameId(gameEntity.getId());
if (gameH5Download != null) {
DialogUtils.showGameH5DownloadDialog(v.getContext(), gameEntity, gameH5Download);
} else {
if (gameEntity.getApk().size() == 1) {
ApkEntity apk = gameEntity.getApk().get(0);
DownloadEntity downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(apk.getUrl());
if (gameEntity.getSimulator() != null) {
boolean isInstalled = PackageUtils.isInstalledFromAllPackage(v.getContext(), gameEntity.getSimulator().getApk().getPackageName());
if (downloadEntity != null && SimulatorGameManager.isSimulatorGame(gameEntity) && !isInstalled) {
SimulatorDownloadManager.getInstance().showDownloadDialog(v.getContext(), gameEntity.getSimulator(),
SimulatorDownloadManager.SimulatorLocation.LAUNCH, gameEntity.getId(), gameEntity.getName(), null);
return;
}
if (gameEntity.getApk().size() == 1) {
ApkEntity apk = gameEntity.getApk().get(0);
DownloadEntity downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(apk.getUrl());
if (gameEntity.getSimulator() != null) {
boolean isInstalled = PackageUtils.isInstalledFromAllPackage(v.getContext(), gameEntity.getSimulator().getApk().getPackageName());
if (downloadEntity != null && SimulatorGameManager.isSimulatorGame(gameEntity) && !isInstalled) {
SimulatorDownloadManager.getInstance().showDownloadDialog(v.getContext(), gameEntity.getSimulator(),
SimulatorDownloadManager.SimulatorLocation.LAUNCH, gameEntity.getId(), gameEntity.getName(), null);
return;
}
ChainBuilder builder = new ChainBuilder();
builder.addHandler(new ValidateVSpaceHandler());
builder.addHandler(new GamePermissionHandler());
builder.addHandler(new BrowserInstallHandler());
builder.addHandler(new PackageCheckHandler());
builder.addHandler(new DownloadDialogHelperHandler());
builder.addHandler(new CertificationHandler());
builder.addHandler(new VersionNumberHandler());
builder.addHandler(new OverseaDownloadHandler());
builder.addHandler(new CheckDownloadHandler());
builder.setProcessEndCallback(o -> {
download(progressBar, gameEntity, traceEvent, (boolean) o, entrance, location);
return null;
}
DownloadDialogHelper.findAvailableDialogAndShow(v.getContext(), gameEntity, apk, () -> {
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
DialogUtils.showOverseaDownloadDialog(v.getContext(), gameEntity, () -> {
DialogUtils.checkDownload(v.getContext(), apk.getSize(),
isSubscribe -> download(progressBar, gameEntity, traceEvent, isSubscribe, entrance, location));
});
});
});
final ChainHandler chainHandler = builder.buildHandlerChain();
if (chainHandler != null) {
chainHandler.handleRequest(v.getContext(), gameEntity);
}
} else {
ChainBuilder builder = new ChainBuilder();
builder.addHandler(new GamePermissionHandler());
builder.addHandler(new CertificationHandler());
builder.addHandler(new VersionNumberHandler());
builder.setProcessEndCallback(o -> {
});
} else {
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
DownloadDialog.showDownloadDialog(
v.getContext(),
gameEntity,
traceEvent,
entrance,
location + ":" + gameEntity.getName());
return null;
});
final ChainHandler chainHandler = builder.buildHandlerChain();
if (chainHandler != null) {
chainHandler.handleRequest(v.getContext(), gameEntity);
}
}
});
}
break;
case LAUNCH_OR_OPEN:
@ -453,12 +487,7 @@ public class BindingAdapters {
}
return;
}
if (gameEntity.isVGame()) {
VHelper.installOrLaunch((AppCompatActivity) v.getContext(), gameEntity.getApk().get(0).getPackageName());
return;
}
DataUtils.onGameLaunchEvent(v.getContext(), gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), location);
PackageUtils.launchApplicationByPackageName(v.getContext(), gameEntity.getApk().get(0).getPackageName());
} else {
DownloadDialog.showDownloadDialog(
@ -472,26 +501,20 @@ public class BindingAdapters {
case INSTALL_PLUGIN:
case INSTALL_NORMAL:
if (gameEntity.getApk().size() == 1) {
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity);
String packageName = gameEntity.getApk().get(0).getPackageName();
if (gameEntity.isVGame()) {
VHelper.installOrLaunch(v.getContext(), packageName);
return;
}
DownloadEntity downloadEntity = DownloadManager.getInstance(progressBar.getContext()).getDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl());
if (downloadEntity != null) {
PackageInstaller.install(v.getContext(), downloadEntity);
}
}
break;
case RESERVABLE:
GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> {
CheckLoginUtils.checkLogin(progressBar.getContext(), "", () -> {
ReservationHelper.reserve(v.getContext(), gameEntity.getId(), () -> {
CheckLoginUtils.checkLogin(progressBar.getContext(), "", () -> {
PermissionHelper.checkReadPhoneStatePermissionBeforeAction(progressBar.getContext(), () -> {
ReserveDialogFragment dialogFragment = ReserveDialogFragment.getInstance(gameEntity, () -> {
LogUtils.logReservation(gameEntity, traceEvent);
updateReservation(progressBar, gameEntity);
});
dialogFragment.show(((AppCompatActivity) progressBar.getContext()).getSupportFragmentManager(), "reserve");
});
});
break;
@ -519,10 +542,8 @@ public class BindingAdapters {
HistoryHelper.insertGameEntity(gameEntity);
}
GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> {
Intent i = new Intent(WebActivity.getIntentForWebGame(progressBar.getContext(), linkEntity.getLink(), gameEntity.getName(), isPlay, linkEntity.getCloseButton()));
progressBar.getContext().startActivity(i);
});
Intent i = new Intent(WebActivity.getIntentForWebGame(progressBar.getContext(), linkEntity.getLink(), gameEntity.getName(), isPlay, linkEntity.getCloseButton()));
progressBar.getContext().startActivity(i);
break;
case UPDATING:
Utils.toast(progressBar.getContext(), "正在加急更新版本,敬请后续留意");
@ -534,10 +555,10 @@ public class BindingAdapters {
if (gameEntity.isReservable()) {
if (!ReservationRepository.thisGameHasBeenReserved(gameEntity.getId())) {
progressBar.setText("预约");
progressBar.setButtonStyle(DownloadButton.ButtonStyle.RESERVABLE);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.RESERVABLE);
} else {
progressBar.setText("已预约");
progressBar.setButtonStyle(DownloadButton.ButtonStyle.RESERVED);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.RESERVED);
}
return;
}
@ -552,17 +573,17 @@ public class BindingAdapters {
} else {
progressBar.setText("查看");
}
progressBar.setButtonStyle(DownloadButton.ButtonStyle.H5_GAME);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.H5_GAME);
} else {
if (offStatus != null && "dialog".equals(offStatus)) {
progressBar.setText("查看");
progressBar.setButtonStyle(DownloadButton.ButtonStyle.NONE);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.NONE);
} else if ("updating".equals(offStatus)) {
progressBar.setText("更新中");
progressBar.setButtonStyle(DownloadButton.ButtonStyle.UPDATING);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.UPDATING);
} else {
progressBar.setText("暂无");
progressBar.setButtonStyle(DownloadButton.ButtonStyle.NONE);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.NONE);
}
}
@ -570,14 +591,14 @@ public class BindingAdapters {
String status = GameUtils.getDownloadBtnText(progressBar.getContext(), gameEntity, PluginLocation.only_game);
switch (status) {
case "插件化":
progressBar.setButtonStyle(DownloadButton.ButtonStyle.PLUGIN);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.PLUGIN);
break;
case "打开":
case "启动":
progressBar.setButtonStyle(DownloadButton.ButtonStyle.LAUNCH_OR_OPEN);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.LAUNCH_OR_OPEN);
break;
default:
progressBar.setButtonStyle(DownloadButton.ButtonStyle.NORMAL);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.NORMAL);
break;
}
progressBar.setText(status);
@ -585,7 +606,7 @@ public class BindingAdapters {
// 显示下载过程状态
if (gameEntity.getApk().size() == 1) {
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity);
DownloadEntity downloadEntity = DownloadManager.getInstance(progressBar.getContext()).getDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl());
if (downloadEntity != null) {
progressBar.setProgress((int) (downloadEntity.getPercent() * 10));
switch (downloadEntity.getStatus()) {
@ -596,27 +617,23 @@ public class BindingAdapters {
case waiting:
progressBar.setText(R.string.downloading);
if (downloadEntity.isPluggable() && PackagesManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) {
progressBar.setButtonStyle(DownloadButton.ButtonStyle.DOWNLOADING_PLUGIN);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.DOWNLOADING_PLUGIN);
} else {
progressBar.setButtonStyle(DownloadButton.ButtonStyle.DOWNLOADING_NORMAL);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL);
}
break;
case done:
progressBar.setText(R.string.install);
if (downloadEntity.isPluggable()
&& PackagesManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) {
progressBar.setButtonStyle(DownloadButton.ButtonStyle.INSTALL_PLUGIN);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_PLUGIN);
} else {
progressBar.setButtonStyle(DownloadButton.ButtonStyle.INSTALL_NORMAL);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_NORMAL);
}
break;
case cancel:
case hijack:
case notfound:
case uncertificated:
case unqualified:
case unavailable:
case banned:
break;
default:
break;
@ -625,18 +642,6 @@ public class BindingAdapters {
}
}
/**
* 当页面完全重建时若存在重建的DialogFragment则需要手动恢复该DialogFragment之前配置的回调(因为DialogFragment重建时只会从arguments中获取之前的配置内容
* 而arguments无法传递回调)或者dismiss该DialogFragment
*/
private static void restoreDialogFragment(DownloadButton progressBar) {
GamePermissionDialogFragment gamePermissionDialogFragment =
((GamePermissionDialogFragment) ((AppCompatActivity) progressBar.getContext()).getSupportFragmentManager().findFragmentByTag(GamePermissionDialogFragment.class.getName()));
if (gamePermissionDialogFragment != null) {
gamePermissionDialogFragment.dismissAllowingStateLoss();
}
}
/*private static void download(DownloadProgressBar progressBar, GameEntity gameEntity, ExposureEvent traceEvent, @Nullable String entrance, @Nullable String location, View v) {
if (gameEntity.getApk().size() == 1) {
ApkEntity apk = gameEntity.getApk().get(0);
@ -655,28 +660,28 @@ public class BindingAdapters {
}*/
private static void updateReservation(DownloadButton progressBar, GameEntity gameEntity) {
private static void updateReservation(DownloadProgressBar progressBar, GameEntity gameEntity) {
// 显示预约
if (gameEntity.isReservable()) {
if (!ReservationRepository.thisGameHasBeenReserved(gameEntity.getId())) {
progressBar.setText("预约");
progressBar.setButtonStyle(DownloadButton.ButtonStyle.RESERVABLE);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.RESERVABLE);
} else {
progressBar.setText("已预约");
progressBar.setButtonStyle(DownloadButton.ButtonStyle.RESERVED);
progressBar.setDownloadType(DownloadProgressBar.DownloadType.RESERVED);
}
}
}
// 开始下载
private static void download(DownloadButton progressBar,
private static void download(DownloadProgressBar progressBar,
GameEntity gameEntity,
ExposureEvent traceEvent,
boolean isSubscribe,
String entrance,
String location) {
String str = progressBar.getText().toString();
String str = progressBar.getText();
String method;
if (str.contains("更新")) {
method = "更新";
@ -688,6 +693,8 @@ public class BindingAdapters {
ApkEntity apkEntity = gameEntity.getApk().get(0);
String msg = FileUtils.isCanDownload(progressBar.getContext(), apkEntity.getSize());
if (TextUtils.isEmpty(msg)) {
DataUtils.onGameDownloadEvent(progressBar.getContext(), gameEntity.getName(), apkEntity.getPlatform(), entrance, "下载开始", method);
DownloadManager.createDownload(progressBar.getContext(),
apkEntity,
gameEntity,
@ -698,66 +705,25 @@ public class BindingAdapters {
traceEvent);
progressBar.setProgress(0);
progressBar.setButtonStyle("插件化".equals(method) ?
DownloadButton.ButtonStyle.DOWNLOADING_PLUGIN : DownloadButton.ButtonStyle.DOWNLOADING_NORMAL);
progressBar.setDownloadType("插件化".equals(method) ?
DownloadProgressBar.DownloadType.DOWNLOADING_PLUGIN : DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL);
} else {
Utils.toast(progressBar.getContext(), msg);
}
}
@BindingAdapter("gameLabelList")
public static void setGameLabelList(LinearLayout layout, List<TagStyleEntity> tagStyle) {
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
}
// 包含测试开服标签
@BindingAdapter("setGameTags")
public static void setGameTags(LinearLayout layout, GameEntity gameEntity) {
try {
if (layout.getVisibility() == View.GONE) return;
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();
TestEntity test = gameEntity.getTest();
if (test != null
// 这个判断用于开测表列表
&& !"type_tag".equals(test.getGameTag())) {
if ("custom".equals(test.getGameTag())) {
TagStyleEntity typeTag = new TagStyleEntity();
if (!TextUtils.isEmpty(test.getText())) {
typeTag.setName(test.getText() != null ? test.getText() : "");
} else {
typeTag.setName(test.getType() != null ? test.getType() : "");
}
typeTag.setBackground("E8F3FF");
typeTag.setColor("1383EB");
tagStyle.add(typeTag);
} else {
TagStyleEntity typeTag = new TagStyleEntity();
boolean isDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(layout.getContext());
typeTag.setName(test.getType() != null ? test.getType() : "");
typeTag.setBackground("1AFFA142");
typeTag.setColor(isDarkModeOn ? "EB9238" : "FFA142");
tagStyle.add(typeTag);
TagStyleEntity timeTag = new TagStyleEntity();
timeTag.setName(GameViewUtils.getGameTestDate(test.getStart()));
timeTag.setBackground("1A06CEA8");
timeTag.setColor(isDarkModeOn ? "07A385" : "06CEA8");
tagStyle.add(timeTag);
}
} else {
tagStyle = gameEntity.getTagStyle();
}
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setVideoDetailGameTags(LinearLayout layout, GameEntity gameEntity) {
try {
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();
TestEntity test = gameEntity.getTest();
if (test != null
// 这个判断用于开测表列表
&& !"type_tag".equals(test.getGameTag())) {
if (test != null) {
TagStyleEntity typeTag = new TagStyleEntity();
typeTag.setName(test.getType() != null ? test.getType() : "");
typeTag.setBackground("FFF3E0");
@ -772,39 +738,33 @@ public class BindingAdapters {
} else {
tagStyle = gameEntity.getTagStyle();
}
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle, 4);
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
} catch (Exception e) {
e.printStackTrace();
}
}
@BindingAdapter("isRefreshing")
public static void isRefreshing(SwipeRefreshLayout layout, LoadStatus status) {
if (status != LoadStatus.INIT_LOADING && status != LoadStatus.LIST_LOADING) {
layout.setRefreshing(false);
}
}
@BindingAdapter({"setGameName", "isShowPlatform", "isShowSuffix"})
public static void setGameName(TextView view, GameEntity game, boolean isShowPlatform, @Nullable Boolean isShowSuffix) {
if (isShowSuffix == null) isShowSuffix = true; // 默认显示
String gameName;
if (isShowPlatform && game.getApk().size() > 0) {
gameName = String.format("%s - %s", !isShowSuffix ? game.getNameWithoutSuffix() : game.getName(),
view.setText(String.format("%s - %s", !isShowSuffix ? game.getNameWithoutSuffix() : game.getName(),
PlatformUtils.getInstance(view.getContext()).getPlatformName(
game.getApk().get(0).getPlatform()));
if (!gameName.equals((String) view.getTag(R.string.tag_game_name_id))) {
view.setText(gameName);
view.setTag(R.string.tag_game_name_id, gameName);
}
game.getApk().get(0).getPlatform())));
} else {
gameName = !isShowSuffix ? game.getNameWithoutSuffix() : game.getName();
if (gameName != null && !gameName.equals((String) view.getTag(R.string.tag_game_name_id))) {
view.setText(gameName);
view.setTag(R.string.tag_game_name_id, gameName);
}
view.setText(!isShowSuffix ? game.getNameWithoutSuffix() : game.getName());
}
}
@BindingAdapter({"setCommunityImage", "setCommunityVideoImage"})
public static void setCommunityImage(SimpleDraweeView imageView, List<String> images, List<CommunityVideoEntity> videos) {
if (videos.size() > 0) {
CommunityVideoEntity videoEntity = videos.get(0);
@ -818,10 +778,11 @@ public class BindingAdapters {
}
}
@BindingAdapter({"setCommunityVideoDuration"})
public static void setCommunityVideoDuration(TextView mVideoDuration, List<CommunityVideoEntity> videos) {
if (videos != null && videos.size() > 0) {
CommunityVideoEntity videoEntity = videos.get(0);
mVideoDuration.setBackground(DrawableView.getOvalDrawable(R.color.black_alpha_50, 999F));
mVideoDuration.setBackground(DrawableView.getOvalDrawable(R.color.black_alpha_80, 999F));
mVideoDuration.setText(videoEntity.getDuration());
mVideoDuration.setVisibility(View.VISIBLE);
} else {
@ -829,6 +790,7 @@ public class BindingAdapters {
}
}
@BindingAdapter({"setGameTags", "setMaxGameTags"})
public static void setGameTags(TextView view, List<TagStyleEntity> tags, int maxTags) {
if (tags == null) {
view.setText("");
@ -851,20 +813,21 @@ public class BindingAdapters {
int start = index;
int end = start + tag.getName().length() + ((i != showCount - 1) ? 1 : 0);
index = end;
span.setSpan(new ForegroundColorSpan(ExtensionsKt.hexStringToIntColor("#" + tag.getColor(), Color.WHITE)),
span.setSpan(new ForegroundColorSpan(Color.parseColor("#" + tag.getColor())),
start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
view.setText(span);
}
@BindingAdapter({"setVideoData"})
public static void setVideoData(TextView view, int count) {
if (count > 0) {
ExtensionsKt.setDrawableStart(view, ContextCompat.getDrawable(view.getContext(), R.drawable.ic_video_data_up), null, null);
view.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(view.getContext(), R.drawable.ic_video_data_up), null, null, null);
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.text_EA3333));
view.setText(count + "");
} else {
ExtensionsKt.removeDrawable(view);
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.text_subtitleDesc));
view.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.text_999999));
view.setText("-");
}
}

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