diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..ce8cd12235 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,555 @@ +[*] +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 \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 07ed87e91f..02337707d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,6 @@ [submodule "assistant_flutter"] path = assistant_flutter url = git@git.shanqu.cc:halo/android/flutter-module.git +[submodule "vspace-bridge"] + path = vspace-bridge + url = git@git.shanqu.cc:cwzs/android/vspace-bridge.git diff --git a/app/build.gradle b/app/build.gradle index 732c8e0c22..b482fa465a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,12 +47,10 @@ android { multiDexEnabled true ndk { - // 如果不添加 `arm64` 调用系统的 PackageManager 的方法读取安装包信息的时候会出现 native 层闪退,草 - // 添加了 `arm64` 以后部分 5.0 的设备会报用错 so 的问题, - // couldn't find DSO to load: libimagepipeline.so caused by: dlopen failed: "/data/data/com.gh.gamecenter/lib-main/libimagepipeline.so" is 64-bit instead of 32-bit result: 0 - // 以 OPPO R7PLUS 为例,明明设备是骁龙 615,ARMv8-64 bit 的设备却不支持 arm64 的 abi,限制了只使用 java 后还是报错,只有 5.0,5.1 设备无法复现 : ( - // 惊了 - abiFilters "armeabi-v7a", "arm64-v8a", "x86" + // x86 本来是为了模拟器用户使用 RenderScript 用的,但是其实用到 RenderScript 的人本来就不多 (一天不到 100),用模拟器的人就更少了 + // 加了 x86 反而会导致用户没法使用微博登录,因为微博没有提供 x86 的 SO ... + // 还会增大 APK 体积,所以还是去掉吧。数据可见 https://sentry.shanqu.cc/organizations/lightgame/issues/144232/?project=22 + abiFilters "armeabi-v7a", "arm64-v8a" } renderscriptTargetApi 18 @@ -74,6 +72,7 @@ android { */ buildConfigField "String", "API_HOST", "\"${API_HOST}\"" buildConfigField "String", "NEW_API_HOST", "\"${NEW_API_HOST}\"" + buildConfigField "String", "VAPI_HOST", "\"${VAPI_HOST}\"" buildConfigField "String", "WECHAT_APPID", "\"${WECHAT_APPID}\"" buildConfigField "String", "WECHAT_SECRET", "\"${WECHAT_SECRET}\"" buildConfigField "String", "TENCENT_APPID", "\"${TENCENT_APPID}\"" @@ -157,14 +156,16 @@ android { 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}\"" } - // publish release host˛ + // publish release host publish { 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}\"" } tea { @@ -172,6 +173,7 @@ android { buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\"" buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\"" + buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\"" manifestPlaceholders.put("APPLOG_SCHEME", "rangersapplog.byAx6uYt".toLowerCase()) } @@ -264,7 +266,9 @@ dependencies { compileOnly "com.github.axen1314.lancet:lancet-base:${lancet_version}" kapt "com.alibaba:arouter-compiler:$arouterVersion" - implementation(project(':module_common')) { + implementation project(':vspace-bridge:vspace') + + implementation (project(':module_common')) { exclude group: 'androidx.swiperefreshlayout' } } @@ -397,6 +401,9 @@ andResGuard { "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", diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 09b9b95b80..7244e45ff1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,9 @@ xmlns:tools="http://schemas.android.com/tools" package="com.gh.gamecenter"> + + + @@ -735,6 +738,14 @@ android:name="com.gh.gamecenter.toolbox.ToolBoxBlockActivity" android:screenOrientation="portrait" /> + + + + diff --git a/app/src/main/assets/lottie/switch_turnoff_dark.json b/app/src/main/assets/lottie/switch_turnoff_dark.json new file mode 100644 index 0000000000..4e0290ccb4 --- /dev/null +++ b/app/src/main/assets/lottie/switch_turnoff_dark.json @@ -0,0 +1 @@ +{"v":"5.9.1","fr":60,"ip":0,"op":36,"w":120,"h":66,"nm":"开关动画-关闭","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"按钮手柄","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":0,"s":[87,33,0],"to":[7.682,0,0],"ti":[-13.443,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[31,33,0],"to":[2.306,0,0],"ti":[-1.318,0,0]},{"t":24,"s":[33,33,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":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","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":"填充 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"指示器-on","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,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":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[6.5,6.5]},{"t":18,"s":[4.5,4.5]}],"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":[1,1,1,1],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"指示器-off","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[87,33,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":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[1.5,4]},{"t":18,"s":[1.5,6]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0.75,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":20,"ix":5},"r":1,"bm":0,"nm":"填充 1","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":[300,300],"ix":3},"r":{"a":0,"k":90,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0]},{"t":18,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"按钮背景","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[60,33,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":"rc","d":1,"s":{"a":0,"k":[40,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0.156862750649,0.533333361149,0.878431379795,1]},{"t":18,"s":[1,1,1,1]}],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[8]}],"ix":5},"r":1,"bm":0,"nm":"filling","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.118,0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"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":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/assets/lottie/switch_turnoff_light.json b/app/src/main/assets/lottie/switch_turnoff_light.json new file mode 100644 index 0000000000..7b0d088767 --- /dev/null +++ b/app/src/main/assets/lottie/switch_turnoff_light.json @@ -0,0 +1 @@ +{"v":"5.9.1","fr":60,"ip":0,"op":36,"w":120,"h":66,"nm":"开关动画-关闭","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"按钮手柄","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":0,"s":[87,33,0],"to":[7.682,0,0],"ti":[-13.443,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[31,33,0],"to":[2.306,0,0],"ti":[-1.318,0,0]},{"t":24,"s":[33,33,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":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","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":"填充 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"指示器-on","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,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":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[6.5,6.5]},{"t":18,"s":[4.5,4.5]}],"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":[1,1,1,1],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"指示器-off","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[87,33,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":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[1.5,4]},{"t":18,"s":[1.5,6]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0.75,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"填充 1","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":[300,300],"ix":3},"r":{"a":0,"k":90,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0]},{"t":18,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"按钮背景","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[60,33,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":"rc","d":1,"s":{"a":0,"k":[40,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0.141176477075,0.588235318661,1,1]},{"t":18,"s":[0.933333337307,0.933333337307,0.933333337307,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.118,0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"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":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/assets/lottie/switch_turnon.json b/app/src/main/assets/lottie/switch_turnon.json deleted file mode 100644 index ae34bc0d48..0000000000 --- a/app/src/main/assets/lottie/switch_turnon.json +++ /dev/null @@ -1 +0,0 @@ -{"v":"5.6.9","fr":60,"ip":0,"op":36,"w":120,"h":66,"nm":"开关动画-打开","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"按钮手柄","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":0,"s":[33,33,0],"to":[7.682,0,0],"ti":[-13.443,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[89,33,0],"to":[2.306,0,0],"ti":[-1.318,0,0]},{"t":24,"s":[87,33,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","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":"填充 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"指示器-on","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0]},{"t":18,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[4.5,4.5]},{"t":18,"s":[6.5,6.5]}],"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.966666666667,0.966666666667,0.966666666667,0.420000005762],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"指示器-off","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[87,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[1.5,6]},{"t":18,"s":[1.5,4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0.75,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"填充 1","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":[300,300],"ix":3},"r":{"a":0,"k":90,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"按钮背景","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[60,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sy":[{"c":{"a":0,"k":[0,0,0,1],"ix":2},"o":{"a":0,"k":5,"ix":3},"a":{"a":0,"k":120,"ix":5},"s":{"a":0,"k":1,"ix":8},"d":{"a":0,"k":0,"ix":6},"ch":{"a":0,"k":100,"ix":7},"bm":{"a":0,"k":5,"ix":1},"no":{"a":0,"k":0,"ix":9},"ty":2,"nm":"内阴影"}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0.933332979679,0.933332979679,0.933332979679,1]},{"t":18,"s":[0.141176477075,0.588235318661,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.118,0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"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":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/assets/lottie/switch_turnon_dark.json b/app/src/main/assets/lottie/switch_turnon_dark.json new file mode 100644 index 0000000000..a897cea988 --- /dev/null +++ b/app/src/main/assets/lottie/switch_turnon_dark.json @@ -0,0 +1 @@ +{"v":"5.9.1","fr":60,"ip":0,"op":36,"w":120,"h":66,"nm":"开关动画-打开","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"按钮手柄","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":0,"s":[33,33,0],"to":[7.682,0,0],"ti":[-13.443,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[89,33,0],"to":[2.306,0,0],"ti":[-1.318,0,0]},{"t":24,"s":[87,33,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":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","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":"填充 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"指示器-on","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0]},{"t":18,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,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":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[4.5,4.5]},{"t":18,"s":[6.5,6.5]}],"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":[1,1,1,1],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"指示器-off","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[87,33,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":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[1.5,6]},{"t":18,"s":[1.5,4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0.75,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":20,"ix":5},"r":1,"bm":0,"nm":"填充 1","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":[300,300],"ix":3},"r":{"a":0,"k":90,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"按钮背景","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[60,33,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":"rc","d":1,"s":{"a":0,"k":[40,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[1,1,1,1]},{"t":18,"s":[0.156862750649,0.533333361149,0.878431379795,1]}],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[8]},{"t":18,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"filling","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.118,0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"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":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/assets/lottie/switch_turnon_light.json b/app/src/main/assets/lottie/switch_turnon_light.json new file mode 100644 index 0000000000..31f139b617 --- /dev/null +++ b/app/src/main/assets/lottie/switch_turnon_light.json @@ -0,0 +1 @@ +{"v":"5.9.1","fr":60,"ip":0,"op":36,"w":120,"h":66,"nm":"开关动画-打开","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"按钮手柄","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":0,"s":[33,33,0],"to":[7.682,0,0],"ti":[-13.443,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[89,33,0],"to":[2.306,0,0],"ti":[-1.318,0,0]},{"t":24,"s":[87,33,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":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","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":"填充 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"指示器-on","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0]},{"t":18,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,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":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[4.5,4.5]},{"t":18,"s":[6.5,6.5]}],"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":[1,1,1,1],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","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":[300,300],"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":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"指示器-off","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[87,33,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":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[1.5,6]},{"t":18,"s":[1.5,4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0.75,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"填充 1","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":[300,300],"ix":3},"r":{"a":0,"k":90,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"按钮背景","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[60,33,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":"rc","d":1,"s":{"a":0,"k":[40,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0.933332979679,0.933332979679,0.933332979679,1]},{"t":18,"s":[0.141176477075,0.588235318661,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.118,0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"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":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/java/com/gh/base/GlobalActivityLifecycleObserver.kt b/app/src/main/java/com/gh/base/GlobalActivityLifecycleObserver.kt index ef4524f652..9ece998764 100644 --- a/app/src/main/java/com/gh/base/GlobalActivityLifecycleObserver.kt +++ b/app/src/main/java/com/gh/base/GlobalActivityLifecycleObserver.kt @@ -15,6 +15,7 @@ import com.gh.gamecenter.forum.list.ForumListActivity import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity +import com.gh.vspace.VHelper import com.halo.assistant.HaloApp // TODO:移动到对应的模块 @@ -59,6 +60,10 @@ class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks { if (PackageFlavorHelper.IS_TEST_FLAVOR && activity is AppCompatActivity) { NightModeSwitchHelper.showNightModeSwitchFloatingView(activity) } + + if (activity is AppCompatActivity && activity !is SplashScreenActivity) { + VHelper.showFeedbackDialogIfLastSuccessfulLaunchedGameExitUnexpectedly(activity) + } } private fun shouldShowActivityBackView(activity: Activity): Boolean { diff --git a/app/src/main/java/com/gh/common/DefaultUrlHandler.kt b/app/src/main/java/com/gh/common/DefaultUrlHandler.kt index b02bf3d072..31a5a16ff9 100644 --- a/app/src/main/java/com/gh/common/DefaultUrlHandler.kt +++ b/app/src/main/java/com/gh/common/DefaultUrlHandler.kt @@ -502,6 +502,13 @@ object DefaultUrlHandler { @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" @@ -513,8 +520,7 @@ object DefaultUrlHandler { uri.path?.apply { when { contains("game") -> { - val gameId = uri.getQueryParameter("gameId") ?: uri.pathSegments.last() - ?: "" + val gameId = uri.getQueryParameter("gameId") ?: uri.pathSegments.last() ?: "" DirectUtils.directToGameDetail(context, gameId, entrance, autoDownload = false, traceEvent = null) } contains("question") -> { @@ -580,6 +586,55 @@ object DefaultUrlHandler { 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 两个字段,仅供日志,不保证能用) */ diff --git a/app/src/main/java/com/gh/common/FixedRateJobHelper.kt b/app/src/main/java/com/gh/common/FixedRateJobHelper.kt index 4f39d60084..6896535c4a 100644 --- a/app/src/main/java/com/gh/common/FixedRateJobHelper.kt +++ b/app/src/main/java/com/gh/common/FixedRateJobHelper.kt @@ -2,13 +2,14 @@ package com.gh.common import com.gh.common.exposure.ExposureManager import com.gh.common.filter.RegionSettingHelper -import com.gh.gamecenter.common.loghub.LoghubUtils -import com.gh.gamecenter.common.utils.doOnMainProcessOnly -import com.gh.gamecenter.common.utils.tryCatchInRelease import com.gh.common.videolog.VideoRecordUtils import com.gh.download.DownloadDataHelper -import com.gh.gamecenter.entity.TimeEntity +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.RetrofitManager import com.halo.assistant.HaloApp import io.reactivex.schedulers.Schedulers @@ -60,7 +61,9 @@ object FixedRateJobHelper { // 提交普通 loghub 数据 if ((mExecuteCount * CHECKER_PERIOD) % LOGHUB_PERIOD == 0L) { - LoghubUtils.commitSavedLoghubEvents() + runOnUiThread { + LoghubUtils.commitSavedLoghubEvents(true) + } } // 更新游戏屏蔽信息 diff --git a/app/src/main/java/com/gh/common/constant/Config.java b/app/src/main/java/com/gh/common/constant/Config.java index c0f7371ed5..3f1b7c0955 100644 --- a/app/src/main/java/com/gh/common/constant/Config.java +++ b/app/src/main/java/com/gh/common/constant/Config.java @@ -20,10 +20,12 @@ import com.gh.gamecenter.entity.GameGuidePopupEntity; import com.gh.gamecenter.entity.NewSettingsEntity; import com.gh.gamecenter.entity.NewsEntity; import com.gh.gamecenter.entity.SettingsEntity; +import com.gh.gamecenter.entity.VSetting; import com.gh.gamecenter.eventbus.EBReuse; import com.gh.gamecenter.common.retrofit.BiResponse; import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.retrofit.RetrofitManager; +import com.gh.vspace.VHelper; import com.halo.assistant.HaloApp; import com.lightgame.utils.Utils; @@ -44,6 +46,7 @@ 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(); // Third-Party confs public static final String WECHAT_APPID = BuildConfig.WECHAT_APPID; @@ -59,6 +62,7 @@ public class Config { private static SettingsEntity mSettingsEntity; private static NewSettingsEntity mNewSettingsEntity; + private static VSetting mVSetting; private static GameGuidePopupEntity mGameGuidePopupEntity; private static SharedPreferences mDefaultSharedPreferences; @@ -112,6 +116,17 @@ public class Config { return false; } + /** + * 是否启用畅玩游戏 + */ + public static boolean isVGameEnabled() { + if (getSettings() == null) { + return false; + } + + return !"off".equals(getSettings().getGameSmooth()); + } + public static boolean isShowPlugin(String gameId) { SharedPreferences preferences = getPreferences(); @@ -182,6 +197,9 @@ public class Config { // 加载完设置后刷新下 PackageHelper.initList(); + + // 初始化畅玩相关的东西 + VHelper.init(HaloApp.getInstance()); } @Nullable @@ -214,6 +232,21 @@ public class Config { return mNewSettingsEntity; } + @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; @@ -307,6 +340,17 @@ public class Config { } }); + RetrofitManager.getInstance() + .getVApi().getSettings(BuildConfig.VERSION_NAME) + .subscribeOn(Schedulers.io()) + .subscribe(new BiResponse() { + @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()) diff --git a/app/src/main/java/com/gh/common/databind/BindingAdapters.java b/app/src/main/java/com/gh/common/databind/BindingAdapters.java index bc66ad23de..1226c9bd27 100644 --- a/app/src/main/java/com/gh/common/databind/BindingAdapters.java +++ b/app/src/main/java/com/gh/common/databind/BindingAdapters.java @@ -39,7 +39,6 @@ import com.gh.common.util.NewsUtils; import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; import com.gh.common.util.PlatformUtils; -import com.gh.common.util.RealNameHelper; import com.gh.common.util.ReservationHelper; import com.gh.common.view.DownloadProgressBar; import com.gh.common.view.GameIconView; @@ -70,6 +69,8 @@ import com.gh.gamecenter.eventbus.EBReuse; import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment; import com.gh.gamecenter.manager.PackagesManager; import com.gh.gamecenter.qa.entity.CommunityVideoEntity; +import com.gh.vspace.VDownloadManagerActivity; +import com.gh.vspace.VHelper; import com.lightgame.download.DownloadEntity; import com.lightgame.download.FileUtils; import com.lightgame.utils.Utils; @@ -359,9 +360,13 @@ public class BindingAdapters { switch (progressBar.getDownloadType()) { case DOWNLOADING_PLUGIN: case DOWNLOADING_NORMAL: - Intent intent = DownloadManagerActivity.getDownloadMangerIntent(v.getContext(), - gameEntity.getApk().get(0).getUrl(), entrance); - v.getContext().startActivity(intent); + 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); + } break; case NONE: Utils.toast(v.getContext(), "该游戏已关闭下载"); @@ -383,9 +388,9 @@ public class BindingAdapters { return; } } - RealNameHelper.checkIfAuth(v.getContext(), gameEntity, () -> { + VHelper.validateVSpaceBeforeAction(v.getContext(), gameEntity, true, () -> { GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> { - BrowserInstallHelper.showBrowserInstallHintDialog(v.getContext(), () -> { + BrowserInstallHelper.showBrowserInstallHintDialog(v.getContext(), gameEntity.isVGame(), () -> { PackageCheckDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, () -> { DownloadDialogHelper.findAvailableDialogAndShow(v.getContext(), gameEntity, apk, () -> { CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> { @@ -402,17 +407,15 @@ public class BindingAdapters { }); }); } else { - RealNameHelper.checkIfAuth(v.getContext(), gameEntity, () -> { - GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> { - CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> { - DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> { - DownloadDialog.showDownloadDialog( - v.getContext(), - gameEntity, - traceEvent, - entrance, - location + ":" + gameEntity.getName()); - }); + GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> { + CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> { + DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> { + DownloadDialog.showDownloadDialog( + v.getContext(), + gameEntity, + traceEvent, + entrance, + location + ":" + gameEntity.getName()); }); }); }); @@ -435,6 +438,12 @@ public class BindingAdapters { } return; } + + if (gameEntity.isVGame()) { + VHelper.installOrLaunch((AppCompatActivity) v.getContext(), gameEntity.getApk().get(0).getPackageName()); + return; + } + PackageUtils.launchApplicationByPackageName(v.getContext(), gameEntity.getApk().get(0).getPackageName()); } else { DownloadDialog.showDownloadDialog( @@ -448,20 +457,25 @@ public class BindingAdapters { case INSTALL_PLUGIN: case INSTALL_NORMAL: if (gameEntity.getApk().size() == 1) { - DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByUrl(gameEntity.getApk().get(0).getUrl()); + DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity); + String packageName = gameEntity.getApk().get(0).getPackageName(); + + if (gameEntity.isVGame()) { + VHelper.installOrLaunch(v.getContext(), packageName); + return; + } + if (downloadEntity != null) { PackageInstaller.install(v.getContext(), downloadEntity); } } break; case RESERVABLE: - RealNameHelper.checkIfAuth(v.getContext(), gameEntity, () -> { - GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> { - CheckLoginUtils.checkLogin(progressBar.getContext(), "", () -> { - ReservationHelper.reserve(v.getContext(), gameEntity.getId(), () -> { - LogUtils.logReservation(gameEntity, traceEvent); - updateReservation(progressBar, gameEntity); - }); + GamePermissionDialogFragment.show((AppCompatActivity) v.getContext(), gameEntity, gameEntity.getInfo(), () -> { + CheckLoginUtils.checkLogin(progressBar.getContext(), "", () -> { + ReservationHelper.reserve(v.getContext(), gameEntity.getId(), () -> { + LogUtils.logReservation(gameEntity, traceEvent); + updateReservation(progressBar, gameEntity); }); }); }); @@ -556,7 +570,7 @@ public class BindingAdapters { // 显示下载过程状态 if (gameEntity.getApk().size() == 1) { - DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByUrl(gameEntity.getApk().get(0).getUrl()); + DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity); if (downloadEntity != null) { progressBar.setProgress((int) (downloadEntity.getPercent() * 10)); switch (downloadEntity.getStatus()) { @@ -587,6 +601,7 @@ public class BindingAdapters { case uncertificated: case unqualified: case unavailable: + case banned: break; default: break; @@ -782,7 +797,7 @@ public class BindingAdapters { public static void setCommunityVideoDuration(TextView mVideoDuration, List videos) { if (videos != null && videos.size() > 0) { CommunityVideoEntity videoEntity = videos.get(0); - mVideoDuration.setBackground(DrawableView.getOvalDrawable(R.color.black_alpha_80, 999F)); + mVideoDuration.setBackground(DrawableView.getOvalDrawable(R.color.black_alpha_50, 999F)); mVideoDuration.setText(videoEntity.getDuration()); mVideoDuration.setVisibility(View.VISIBLE); } else { diff --git a/app/src/main/java/com/gh/common/exposure/ExposureConverters.kt b/app/src/main/java/com/gh/common/exposure/ExposureConverters.kt deleted file mode 100644 index 8b3392b110..0000000000 --- a/app/src/main/java/com/gh/common/exposure/ExposureConverters.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.gh.common.exposure - -import androidx.room.TypeConverter -import com.gh.gamecenter.common.entity.ExposureEntity -import com.gh.gamecenter.common.exposure.meta.Meta -import com.gh.gamecenter.core.utils.GsonUtils -import java.util.* -import kotlin.collections.ArrayList - -class ExposureConverters { - - @TypeConverter - fun convertPayload2String(any: ExposureEntity): String { - return GsonUtils.toJson(any) - } - - @TypeConverter - fun convertString2Payload(string: String): ExposureEntity { - return GsonUtils.fromJson(string, ExposureEntity::class.java) - } - - @TypeConverter - fun convertSource2String(sourceList: List): String { - return GsonUtils.toJson(sourceList) - } - - @TypeConverter - fun convertString2Source(sourceList: String): List { - return ArrayList(Arrays.asList(GsonUtils.fromJson(sourceList, Array::class.java))) as List - } - - @TypeConverter - fun convertETrace2String(sourceList: List?): String { - return GsonUtils.toJson(sourceList) - } - - @TypeConverter - fun convertStringToETrace(sourceList: String): List { - return ArrayList(Arrays.asList(GsonUtils.fromJson(sourceList, Array::class.java))) as List - } - - @TypeConverter - fun convertExposeType2String(exposureType: ExposureType): String { - return exposureType.toString() - } - - @TypeConverter - fun convertStringToExposeType(exposureType: String): ExposureType { - return ExposureType.valueOf(exposureType) - } - - @TypeConverter - fun convertMeta2String(any: Meta): String { - return GsonUtils.toJson(any) - } - - @TypeConverter - fun convertString2Meta(string: String): Meta { - return GsonUtils.fromJson(string, Meta::class.java) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/exposure/ExposureDatabase.kt b/app/src/main/java/com/gh/common/exposure/ExposureDatabase.kt deleted file mode 100644 index 9bd3cad34d..0000000000 --- a/app/src/main/java/com/gh/common/exposure/ExposureDatabase.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.gh.common.exposure - -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.TypeConverters -import android.content.Context - -@TypeConverters(ExposureConverters::class) -@Database(entities = [ExposureEvent::class], version = 1, exportSchema = false) -abstract class ExposureDatabase : RoomDatabase() { - companion object { - private const val DATABASE = "exposure_database" - - fun buildDatabase(context: Context): ExposureDatabase { - return Room.databaseBuilder(context, ExposureDatabase::class.java, DATABASE) - .fallbackToDestructiveMigration() - .build() - } - } - - abstract fun logHubEventDao(): ExposureEventDao -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/exposure/ExposureEvent.kt b/app/src/main/java/com/gh/common/exposure/ExposureEvent.kt index 9b5ff1cc4d..9b582f938e 100644 --- a/app/src/main/java/com/gh/common/exposure/ExposureEvent.kt +++ b/app/src/main/java/com/gh/common/exposure/ExposureEvent.kt @@ -4,22 +4,20 @@ import android.os.Parcelable import androidx.annotation.Keep import androidx.room.Entity import androidx.room.PrimaryKey +import com.gh.common.exposure.time.TimeUtil +import com.gh.download.server.BrowserInstallHelper import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.entity.ExposureEntity import com.gh.gamecenter.common.exposure.meta.Meta import com.gh.gamecenter.common.exposure.meta.MetaUtil -import com.gh.common.exposure.time.TimeUtil import com.gh.gamecenter.common.utils.getFirstElementDividedByDivider -import com.gh.download.server.BrowserInstallHelper -import com.gh.gamecenter.common.entity.ExposureEntity import com.gh.gamecenter.entity.GameEntity import com.lightgame.download.DownloadEntity import kotlinx.parcelize.Parcelize import java.util.* -import kotlin.collections.ArrayList @Keep @Parcelize -@Entity(tableName = "exposureEvent") data class ExposureEvent( var payload: ExposureEntity, val source: List, @@ -27,8 +25,9 @@ data class ExposureEvent( val event: ExposureType, val meta: Meta = MetaUtil.getMeta(), val time: Int = TimeUtil.currentTime(), + val timeInMillisecond: Long = System.currentTimeMillis(), @PrimaryKey - val id: String = UUID.randomUUID().toString() + val id: String = UUID.randomUUID().toString(), ) : Parcelable { companion object { diff --git a/app/src/main/java/com/gh/common/exposure/ExposureEventDao.kt b/app/src/main/java/com/gh/common/exposure/ExposureEventDao.kt deleted file mode 100644 index 03c16aa0f9..0000000000 --- a/app/src/main/java/com/gh/common/exposure/ExposureEventDao.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.gh.common.exposure - -import androidx.room.* - -@Dao -interface ExposureEventDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertMany(eventList: List) - - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insert(event: ExposureEvent) - - @Query("SELECT * FROM exposureEvent") - fun getAll(): List - - @Delete - fun deleteMany(eventList: List) -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/exposure/ExposureManager.kt b/app/src/main/java/com/gh/common/exposure/ExposureManager.kt index 2251c86967..9d8244ab91 100644 --- a/app/src/main/java/com/gh/common/exposure/ExposureManager.kt +++ b/app/src/main/java/com/gh/common/exposure/ExposureManager.kt @@ -1,14 +1,11 @@ package com.gh.common.exposure import com.aliyun.sls.android.producer.Log -import com.gh.gamecenter.common.loghub.LoghubHelper -import com.gh.gamecenter.common.utils.toJson -import com.gh.gamecenter.common.utils.tryWithDefaultCatch import com.gh.gamecenter.BuildConfig +import com.gh.gamecenter.common.loghub.LoghubHelper import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet -import com.halo.assistant.HaloApp +import com.gh.gamecenter.common.utils.toJson import com.lightgame.utils.Utils -import java.util.concurrent.ExecutorService /** * A handful tool for committing logs to aliyun loghub. @@ -26,40 +23,17 @@ object ExposureManager { // exposureCache 用来过滤掉具有相同 id 的曝光事件,避免重复发送事件 private val exposureSet by lazy { hashSetOf() } - private var exposureExecutor: ExecutorService? = null private val exposureCache by lazy { FixedSizeLinkedHashSet(300) } - private val exposureDao by lazy { ExposureDatabase.buildDatabase(HaloApp.getInstance().application).logHubEventDao() } - - @JvmStatic - fun init(excutor: ExecutorService) { - exposureExecutor = excutor - exposureExecutor?.execute { - tryWithDefaultCatch { - val eventList = exposureDao.getAll() - exposureSet.addAll(eventList) - } - } - } /** * Log a single exposure event. */ fun log(event: ExposureEvent) { - exposureExecutor?.execute { - try { - if (!exposureCache.contains(event.id)) { - // Catch `android.database.sqlite.SQLiteFullException: database or disk is full` exception. - - exposureSet.add(event) - exposureDao.insert(event) - exposureCache.add(event.id) - - } else { - Utils.log("Exposure", "遇到重复曝光事件,自动过滤 (${event.id} - ${event.payload.gameName})") - } - } catch (e: Exception) { - e.printStackTrace() - } + if (!exposureCache.contains(event.id)) { + exposureSet.add(event) + exposureCache.add(event.id) + } else { + Utils.log("Exposure", "遇到重复曝光事件,自动过滤 (${event.id} - ${event.payload.gameName})") } } @@ -67,51 +41,36 @@ object ExposureManager { * Log a collection of exposure event. */ fun log(eventList: List) { - exposureExecutor?.execute { - for (event in eventList) { - try { - if (!exposureCache.contains(event.id)) { - // Catch `android.database.sqlite.SQLiteFullException: database or disk is full` exception. - exposureSet.add(event) - exposureDao.insert(event) - exposureCache.add(event.id) - } else { - Utils.log("Exposure", "遇到重复曝光事件,自动过滤 (${event.id} - ${event.payload.gameName})") - } - } catch (e: Exception) { - e.printStackTrace() - } + for (event in eventList) { + if (!exposureCache.contains(event.id)) { + exposureSet.add(event) + exposureCache.add(event.id) + } else { + Utils.log("Exposure", "遇到重复曝光事件,自动过滤 (${event.id} - ${event.payload.gameName})") } - commitSavedExposureEvents() } + commitSavedExposureEvents() } /** - * @param forced Ignore all restrictions. + * @param forcedUpload Ignore all restrictions. */ - fun commitSavedExposureEvents(forced: Boolean = false) { - exposureExecutor?.execute { - tryWithDefaultCatch { - // TODO 初始化 loghubHelper 去掉这个 tryCatch 块 - if (exposureSet.size < STORE_SIZE && !forced || exposureSet.size == 0) return@execute + fun commitSavedExposureEvents(forcedUpload: Boolean = false) { + if (exposureSet.size < STORE_SIZE && !forcedUpload || exposureSet.size == 0) return - val exposureList = exposureSet.toList() - uploadExposures(exposureList) + uploadExposures(exposureSet.toList(), forcedUpload) - Utils.log("Exposure", "提交了${exposureList.size}条曝光记录") - exposureSet.removeAll(exposureList) - exposureDao.deleteMany(exposureList) - } - } + Utils.log("Exposure", "提交了${exposureSet.size}条曝光记录") + exposureSet.clear() } private fun eliminateMultipleBrackets(jsonWithMultipleBracket: String): String { return jsonWithMultipleBracket.replace("[[", "[").replace("]]", "]") } - private fun uploadExposures(eventList: List) { + private fun uploadExposures(eventList: List, forced: Boolean) { eventList.forEach { - LoghubHelper.uploadLog(buildLog(it), LOG_STORE) + LoghubHelper.uploadLog(buildLog(it), LOG_STORE, forced) } } @@ -121,9 +80,12 @@ object ExposureManager { putContent("event", event.event.toString()) putContent("source", eliminateMultipleBrackets(event.source.toJson())) putContent("meta", event.meta.toJson()) - putContent("e-traces", if (event.eTrace != null) { - eliminateMultipleBrackets(event.eTrace?.toJson() ?: "") - } else "") + putContent("real_millisecond", event.timeInMillisecond.toString()) + putContent( + "e-traces", if (event.eTrace != null) { + eliminateMultipleBrackets(event.eTrace?.toJson() ?: "") + } else "" + ) logTime = event.time.toLong() } diff --git a/app/src/main/java/com/gh/common/exposure/ExposureUtils.kt b/app/src/main/java/com/gh/common/exposure/ExposureUtils.kt index 63e0018513..72f92e2a25 100644 --- a/app/src/main/java/com/gh/common/exposure/ExposureUtils.kt +++ b/app/src/main/java/com/gh/common/exposure/ExposureUtils.kt @@ -7,6 +7,7 @@ import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet import com.gh.gamecenter.common.utils.toObject import com.gh.gamecenter.entity.ApkEntity import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.manager.PackagesManager import com.halo.assistant.HaloApp import com.lightgame.download.DownloadEntity @@ -75,12 +76,18 @@ object ExposureUtils { exposureEvent.payload.host = host exposureEvent.payload.path = path ExposureManager.log(exposureEvent) - ExposureManager.commitSavedExposureEvents(forced = true) + ExposureManager.commitSavedExposureEvents(forcedUpload = true) } @JvmStatic - fun getDownloadType(apkEntity: ApkEntity, gameId: String): DownloadType { - return if (PackageUtils.isInstalled( + fun getDownloadType(apkEntity: ApkEntity, gameId: String, isVGame: Boolean): DownloadType { + return if (isVGame) { + if (PackagesManager.isCanUpdate(gameId, apkEntity.packageName)) { + DownloadType.FUN_UPDATE + } else { + DownloadType.FUN_DOWNLOAD + } + } else if (PackageUtils.isInstalled( HaloApp.getInstance().application, apkEntity.packageName ) @@ -143,6 +150,10 @@ object ExposureUtils { PLUGIN_UPDATE, - PLUGIN_DOWNLOAD + PLUGIN_DOWNLOAD, + + FUN_DOWNLOAD, + + FUN_UPDATE } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/filter/RegionSetting.kt b/app/src/main/java/com/gh/common/filter/RegionSetting.kt index b699a530d7..3ec3808451 100644 --- a/app/src/main/java/com/gh/common/filter/RegionSetting.kt +++ b/app/src/main/java/com/gh/common/filter/RegionSetting.kt @@ -12,7 +12,9 @@ data class RegionSetting( @SerializedName("channel_control") var channelControl: ChannelControl, @SerializedName("game_h5_download") - var gameH5DownloadList: List + var gameH5DownloadList: List, + @SerializedName("game_special_download") + var gameSpecialDownloadInfoList: List ) { @Keep @@ -34,4 +36,14 @@ data class RegionSetting( @SerializedName("button_text") var buttonText: String,// 按钮文案 ) + data class GameSpecialDownloadInfo( + @SerializedName("game_id") + var gameId: String = "", + @SerializedName("bbs_id") + var bbsId: String = "", + @SerializedName("top_id") + var topId: String = "", + @SerializedName("hint_text") + var hintText: String = "" + ) } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/filter/RegionSettingHelper.kt b/app/src/main/java/com/gh/common/filter/RegionSettingHelper.kt index be4ae2fb75..edf632204f 100644 --- a/app/src/main/java/com/gh/common/filter/RegionSettingHelper.kt +++ b/app/src/main/java/com/gh/common/filter/RegionSettingHelper.kt @@ -20,6 +20,7 @@ object RegionSettingHelper { private var mFilterGameIdSet: HashSet? = hashSetOf() private var mDisplayMirrorIfoGameIdSet: HashSet? = hashSetOf() private var mGameH5DownloadList: List? = listOf() + private var mGameSpecialDownloadInfoList: List? = listOf() private const val SP_SETTING = "region_setting" @@ -32,6 +33,11 @@ object RegionSettingHelper { return mFilterGameIdSet?.contains(gameId) ?: false } + fun shouldThisGameShowSpecialDownload(gameId: String) = mGameSpecialDownloadInfoList?.any { it.gameId == gameId } ?: false + + @JvmStatic + fun getGameSpecialDownloadInfo(gameId: String) = mGameSpecialDownloadInfoList?.find { it.gameId == gameId } + @JvmStatic fun filterGame(list: List?): ArrayList { if (list == null) return arrayListOf() @@ -94,6 +100,7 @@ object RegionSettingHelper { mDisplayMirrorIfoGameIdSet = data.mirrorGameIdSet mChannelControl = data.channelControl mGameH5DownloadList = data.gameH5DownloadList + mGameSpecialDownloadInfoList = data.gameSpecialDownloadInfoList } /** diff --git a/app/src/main/java/com/gh/common/provider/ActivationProviderImpl.kt b/app/src/main/java/com/gh/common/provider/ActivationProviderImpl.kt index 4c7fa2f3b8..a172d9cc2e 100644 --- a/app/src/main/java/com/gh/common/provider/ActivationProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/ActivationProviderImpl.kt @@ -7,7 +7,7 @@ import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.core.provider.IActivationProvider @Route(path = RouteConsts.provider.activation, name = "ActivationHelper暴露服务") -interface ActivationProviderImpl : IActivationProvider { +class ActivationProviderImpl : IActivationProvider { override fun init(context: Context?) { } diff --git a/app/src/main/java/com/gh/common/provider/BuildConfigImpl.kt b/app/src/main/java/com/gh/common/provider/BuildConfigImpl.kt index 9ce6c309f8..d90113b6a4 100644 --- a/app/src/main/java/com/gh/common/provider/BuildConfigImpl.kt +++ b/app/src/main/java/com/gh/common/provider/BuildConfigImpl.kt @@ -28,4 +28,7 @@ class BuildConfigImpl : IBuildConfigProvider { override fun getNewDevApiHost(): String = BuildConfig.NEW_DEV_API_HOST + override fun getVApiHost(): String = BuildConfig.VAPI_HOST + + override fun getVDevApiHost(): String = BuildConfig.DEV_VAPI_HOST } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt b/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt index 7c5bd38bab..4ac8603d0e 100644 --- a/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt +++ b/app/src/main/java/com/gh/common/simulator/SimulatorDownloadManager.kt @@ -9,15 +9,19 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import com.g00fy2.versioncompare.Version -import com.gh.gamecenter.core.AppExecutor.uiExecutor -import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.base.TrackableDialog -import com.gh.common.util.* import com.gh.common.util.LogUtils +import com.gh.common.util.PackageInstaller +import com.gh.common.util.PackageUtils import com.gh.download.DownloadManager import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.TrackableDialog +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.core.AppExecutor.uiExecutor +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.core.utils.SpeedUtils +import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.entity.ApkEntity import com.gh.gamecenter.entity.SimulatorEntity import com.gh.gamecenter.entity.TrackableEntity @@ -73,7 +77,7 @@ class SimulatorDownloadManager private constructor() { val fileName = downloadEntity.path.substring(downloadEntity.path.lastIndexOf('/') + 1).removeSuffix(".apk") val startTime = downloadEntity.getMetaExtra(Constants.SIMULATOR_DOWNLOAD_START_TIME) LogUtils.uploadSimulatorDownload("simulator_download_complete", fileName, simulator?.id, downloadEntity.name, gameId, locationStr, downloadType, startTime) - DownloadManager.getInstance().cancel(downloadEntity.url, false, true) + DownloadManager.getInstance().cancel(downloadEntity.url, false, true, false) val activity = mContextRef?.get() as? AppCompatActivity if (activity?.isFinishing == false) { downloadDialog?.dismiss() @@ -97,6 +101,9 @@ class SimulatorDownloadManager private constructor() { DownloadStatus.unavailable == downloadEntity.status -> { ToastUtils.showToast("该游戏未接入防沉迷系统,暂不支持下载") } + DownloadStatus.banned == downloadEntity.status -> { + ToastUtils.showToast("网络异常") + } DownloadStatus.hijack == downloadEntity.status -> { ToastUtils.showToast("网络劫持,请稍后重试") } diff --git a/app/src/main/java/com/gh/common/simulator/SimulatorGameManager.kt b/app/src/main/java/com/gh/common/simulator/SimulatorGameManager.kt index dc2cb49cc8..157783bfcb 100644 --- a/app/src/main/java/com/gh/common/simulator/SimulatorGameManager.kt +++ b/app/src/main/java/com/gh/common/simulator/SimulatorGameManager.kt @@ -7,19 +7,21 @@ import android.graphics.Bitmap import android.net.Uri import android.text.TextUtils import com.g00fy2.versioncompare.Version -import com.gh.gamecenter.common.json.json -import com.gh.common.util.* +import com.gh.common.util.ApkActiveUtils import com.gh.common.util.LogUtils +import com.gh.common.util.PackageUtils import com.gh.download.DownloadManager import com.gh.gamecenter.common.callback.BiCallback -import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.utils.* -import com.gh.gamecenter.entity.GameEntity -import com.gh.gamecenter.entity.SimulatorGameRecordEntity -import com.gh.gamecenter.manager.UserManager +import com.gh.gamecenter.common.json.json import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.common.retrofit.EmptyResponse import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.core.utils.UrlFilterUtils +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.entity.SimulatorGameRecordEntity +import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.retrofit.RetrofitManager import com.gh.gamecenter.room.AppDatabase import com.halo.assistant.HaloApp @@ -72,7 +74,7 @@ object SimulatorGameManager { @JvmStatic fun findDownloadEntityByUrl(url: String?): DownloadEntity? { - val downloadEntity = DownloadDao.getInstance(HaloApp.getInstance().application).get(url) + val downloadEntity = DownloadManager.getInstance().getDownloadEntityByUrl(url) if (downloadEntity != null) { val isFileCompleted = DownloadManager.getInstance().isDownloadCompleted(url) if (downloadEntity.isSimulatorGame() && isFileCompleted) { diff --git a/app/src/main/java/com/gh/common/util/CommentHelper.kt b/app/src/main/java/com/gh/common/util/CommentHelper.kt index 4ecfc6f8b1..c95b80b8c6 100644 --- a/app/src/main/java/com/gh/common/util/CommentHelper.kt +++ b/app/src/main/java/com/gh/common/util/CommentHelper.kt @@ -5,21 +5,21 @@ import android.view.LayoutInflater import android.view.View import android.widget.LinearLayout import android.widget.TextView -import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.json.json import com.gh.common.util.CommentUtils.copyText -import com.gh.gamecenter.common.view.BugFixedPopupWindow import com.gh.gamecenter.CommentDetailActivity import com.gh.gamecenter.R +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.common.utils.DialogHelper import com.gh.gamecenter.common.utils.ifLogin import com.gh.gamecenter.common.utils.showAutoOrientation +import com.gh.gamecenter.common.view.BugFixedPopupWindow import com.gh.gamecenter.entity.CommentEntity import com.gh.gamecenter.entity.MeEntity import com.gh.gamecenter.entity.Permissions import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.qa.comment.OnCommentOptionClickListener -import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp import com.lightgame.utils.Utils @@ -237,8 +237,6 @@ object CommentHelper { } articleId != null -> { PostCommentUtils.reportCommunityArticleComment( - communityId, - articleId, commentEntity.id, reportType, commentListener @@ -440,11 +438,7 @@ object CommentHelper { context, highlight, highlightDialogHintContent, "确定", "取消", { RetrofitManager.getInstance().api - .highlightCommunityArticleComment( - communityId, - articleId, - comment.id - ) + .highlightCommunityArticleComment(comment.id) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(highlightObserver) @@ -502,7 +496,7 @@ object CommentHelper { context, hide, hideDialogHintContent, "确定", "取消", { RetrofitManager.getInstance().api - .hideCommunityArticleComment(communityId, articleId, comment.id) + .hideCommunityArticleComment(comment.id) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(hideObserver) diff --git a/app/src/main/java/com/gh/common/util/CommentUtils.java b/app/src/main/java/com/gh/common/util/CommentUtils.java index 93a1721937..d809b4b562 100644 --- a/app/src/main/java/com/gh/common/util/CommentUtils.java +++ b/app/src/main/java/com/gh/common/util/CommentUtils.java @@ -298,7 +298,7 @@ public class CommentUtils { commentLikeCountTv.setText(NumberUtils.transSimpleCount(commentEntity.getVote())); commentLikeCountTv.setVisibility(View.VISIBLE); - PostCommentUtils.likeComment(answerId, articleId, articleCommunityId, videoId, questionId, commentEntity.getId(), + PostCommentUtils.likeComment(answerId, articleId, videoId, questionId, commentEntity.getId(), new PostCommentUtils.PostCommentListener() { @Override public void postSuccess(JSONObject response) { @@ -352,7 +352,7 @@ public class CommentUtils { String entrance = "视频流-评论-点赞"; CheckLoginUtils.checkLogin(context, entrance, () -> { - PostCommentUtils.likeComment(answerId, articleId, articleCommunityId, videoId, "", commentEntity.getId(), + PostCommentUtils.likeComment(answerId, articleId, videoId, "", commentEntity.getId(), new PostCommentUtils.PostCommentListener() { @Override public void postSuccess(JSONObject response) { @@ -420,7 +420,7 @@ public class CommentUtils { commentLikeCountTv.setVisibility(View.VISIBLE); try { if (e != null && e.response().errorBody() != null) { - ErrorHelper.handleError(context, e.response().errorBody().string(), false); + ErrorHelper.handleError(context, e.response().errorBody().string(), false, null); } } catch (IOException ex) { ex.printStackTrace(); diff --git a/app/src/main/java/com/gh/common/util/DataUtils.java b/app/src/main/java/com/gh/common/util/DataUtils.java index 065b1bb468..5bc1a0e664 100644 --- a/app/src/main/java/com/gh/common/util/DataUtils.java +++ b/app/src/main/java/com/gh/common/util/DataUtils.java @@ -43,7 +43,7 @@ import kotlin.Pair; /** * Created by LGT on 2016/6/15. - * 数据收集 工具类 (TalkingData、MTA) + * 数据收集工具类 */ public class DataUtils { @@ -161,8 +161,8 @@ public class DataUtils { values.put(GhContentProvider.KEY_IS_ADULT, false); } - new GhContentProvider().localInsert( HaloApp.getInstance().getApplication(),values); -// HaloApp.getInstance().getContentResolver().insert(Uri.parse("content://com.gh.gamecenter.provider/certification"), values); +// new GhContentProvider().localInsert( HaloApp.getInstance().getApplication(),values); + HaloApp.getInstance().getContentResolver().insert(Uri.parse("content://com.gh.gamecenter.provider/certification"), values); } }); } diff --git a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java index 94e1e1a775..0e7b1ab723 100644 --- a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java +++ b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java @@ -19,6 +19,7 @@ import com.gh.gamecenter.core.utils.SPUtils; import com.gh.gamecenter.entity.LinkEntity; import com.gh.gamecenter.entity.PluginLocation; import com.gh.gamecenter.manager.PackagesManager; +import com.gh.vspace.VHelper; import com.lightgame.download.DownloadEntity; /** @@ -30,6 +31,9 @@ public class DetailDownloadUtils { public static void detailInitDownload(DetailViewHolder viewHolder, boolean isCheck) { String downloadAddWord = viewHolder.gameEntity.getDownloadAddWord(); + if (viewHolder.getOverlayTv() != null) { + viewHolder.getOverlayTv().setVisibility(View.GONE); + } if (viewHolder.gameEntity != null && Config.isShowDownload(viewHolder.gameEntity.getId()) @@ -46,9 +50,14 @@ public class DetailDownloadUtils { return; } + if (viewHolder.gameEntity.isSpecialDownload()) { + viewHolder.mDownloadPb.setText("查看下载资源"); + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.SPECIAL_DOWNLOAD); + return; + } + if (viewHolder.gameEntity.isReservable()) { if (!ReservationRepository.thisGameHasBeenReserved(viewHolder.gameEntity.getId())) { - if (TextUtils.isEmpty(downloadAddWord)) { viewHolder.mDownloadPb.setText(String.format("预约" + "《%s》", viewHolder.gameEntity.getName())); } else { @@ -61,12 +70,55 @@ public class DetailDownloadUtils { } return; } + final RegionSetting.GameH5Download gameH5Download = RegionSettingHelper.getGameH5DownloadByGameId(viewHolder.gameEntity.getId()); if (gameH5Download != null) { viewHolder.mDownloadPb.setText(TextUtils.isEmpty(viewHolder.gameEntity.getDownloadOffText()) ? "查看详情" : viewHolder.gameEntity.getDownloadOffText()); viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.NORMAL); return; } + + if (viewHolder.gameEntity.isVGame() && !viewHolder.gameEntity.getApk().isEmpty()) { + String status = GameUtils.getDownloadBtnText(viewHolder.context, viewHolder.gameEntity, PluginLocation.only_game); + if (viewHolder.context.getString(R.string.launch).equals(status)) { + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.LAUNCH_OR_OPEN); + } else if (viewHolder.context.getString(R.string.install).equals(status)) { + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_NORMAL); + } else { + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.NORMAL); + } + + String downloadText; + if (viewHolder.context.getString(R.string.launch).equals(status) || viewHolder.context.getString(R.string.install).equals(status) || viewHolder.context.getString(R.string.download).equals(status)) { + downloadText = ""; + if (viewHolder.getOverlayTv() != null) { + viewHolder.getOverlayTv().setVisibility(View.VISIBLE); + } + } else if (viewHolder.context.getString(R.string.attempt).equals(status)) { + downloadText = status + getDownloadSizeText(viewHolder); + } else { + downloadText = status + (TextUtils.isEmpty(downloadAddWord) ? "" : downloadAddWord) + getDownloadSizeText(viewHolder); + } + viewHolder.mDownloadPb.setText(downloadText); + + DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(viewHolder.gameEntity); + + // 在下载管理找不到下载实体,到畅玩数据库里找 + if (downloadEntity == null && viewHolder.gameEntity.isVGame()) { + String packageName = viewHolder.gameEntity.getUniquePackageName(); + if (!TextUtils.isEmpty(packageName)) { + downloadEntity = VHelper.getDownloadEntitySnapshot(viewHolder.gameEntity.getId(), packageName); + } + } + + if (downloadEntity != null) { + viewHolder.downloadEntity = downloadEntity; + detailInvalidate(viewHolder); + } + + return; + } + if (viewHolder.gameEntity.getApk().isEmpty() || viewHolder.gameEntity.getDownloadOffStatus() != null) { LinkEntity h5LinkEntity = viewHolder.gameEntity.getH5Link(); @@ -121,9 +173,18 @@ public class DetailDownloadUtils { viewHolder.mDownloadPb.setText("选择下载你的版本" + (TextUtils.isEmpty(downloadAddWord) ? "" : "-" + downloadAddWord) + " >"); viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.NORMAL); } + if (isCheck && viewHolder.gameEntity.getApk().size() == 1) { - String url = viewHolder.gameEntity.getApk().get(0).getUrl(); - DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntityByUrl(url); + DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(viewHolder.gameEntity); + + // 在下载管理找不到下载实体,到畅玩数据库里找 + if (downloadEntity == null && viewHolder.gameEntity.isVGame()) { + String packageName = viewHolder.gameEntity.getUniquePackageName(); + if (!TextUtils.isEmpty(packageName)) { + downloadEntity = VHelper.getDownloadEntitySnapshot(viewHolder.gameEntity.getId(), packageName); + } + } + if (downloadEntity != null) { viewHolder.downloadEntity = downloadEntity; detailInvalidate(viewHolder); @@ -148,6 +209,15 @@ public class DetailDownloadUtils { } viewHolder.mDownloadPb.setProgress((int) (viewHolder.downloadEntity.getPercent() * 10)); + if (viewHolder.getOverlayTv() != null) { + viewHolder.getOverlayTv().setVisibility(View.GONE); + } + + if (viewHolder.gameEntity.isVGame()) { + updateVStyleButton(viewHolder); + return; + } + switch (downloadEntity.getStatus()) { case downloading: case pause: @@ -188,6 +258,19 @@ public class DetailDownloadUtils { } viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_NORMAL); } + } else if (viewHolder.gameEntity.isVGame()) { + if (!viewHolder.mDownloadPb.getText().contains("更新")) { + if (VHelper.isInstalled(downloadEntity.getPackageName())) { + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.LAUNCH_OR_OPEN); + } else { + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_NORMAL); + } + + viewHolder.mDownloadPb.setText(""); + if (viewHolder.getOverlayTv() != null) { + viewHolder.getOverlayTv().setVisibility(View.VISIBLE); + } + } } else { if (SPUtils.getBoolean(Constants.SP_USE_BROWSER_TO_INSTALL)) { viewHolder.mDownloadPb.setText(R.string.browser_install_install); @@ -209,10 +292,48 @@ public class DetailDownloadUtils { case uncertificated: case unqualified: case unavailable: + case banned: detailInitDownload(viewHolder, false); break; default: break; } } + + private static void updateVStyleButton(DetailViewHolder viewHolder) { + switch (viewHolder.downloadEntity.getStatus()) { + case downloading: + case overflow: + viewHolder.mDownloadPb.setText("游戏加载中 " + viewHolder.downloadEntity.getPercent() + "%"); + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL); + break; + case timeout: + case neterror: + case waiting: + case subscribe: + viewHolder.mDownloadPb.setText(R.string.waiting); + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL); + break; + case pause: + viewHolder.mDownloadPb.setText("继续加载 " + viewHolder.downloadEntity.getPercent() + "%"); + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL); + break; + case done: + if (!viewHolder.mDownloadPb.getText().contains("更新")) { + if (VHelper.isInstalled(viewHolder.downloadEntity.getPackageName())) { + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.LAUNCH_OR_OPEN); + } else { + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.INSTALL_NORMAL); + } + + viewHolder.mDownloadPb.setText(""); + if (viewHolder.getOverlayTv() != null) { + viewHolder.getOverlayTv().setVisibility(View.VISIBLE); + } + } + break; + default: + break; + } + } } diff --git a/app/src/main/java/com/gh/common/util/DirectUtils.kt b/app/src/main/java/com/gh/common/util/DirectUtils.kt index 2b45a6c673..fe58c6f34f 100644 --- a/app/src/main/java/com/gh/common/util/DirectUtils.kt +++ b/app/src/main/java/com/gh/common/util/DirectUtils.kt @@ -72,6 +72,7 @@ import com.gh.gamecenter.video.detail.VideoDetailActivity import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel import com.gh.gamecenter.video.game.GameVideoActivity import com.gh.gamecenter.video.videomanager.VideoManagerActivity +import com.gh.vspace.VDownloadManagerActivity import com.halo.assistant.HaloApp import com.halo.assistant.fragment.WebFragment import com.lightgame.utils.Utils @@ -81,6 +82,7 @@ import org.greenrobot.eventbus.EventBus import retrofit2.HttpException import java.net.URLEncoder import java.util.* +import kotlin.collections.ArrayList import kotlin.math.roundToInt /** @@ -154,7 +156,8 @@ object DirectUtils { "category_v2", "common_collection", "game_list", - "game_list_detail" + "game_list_detail", + "bbs_video" ) fun directToLinkPage( @@ -190,7 +193,7 @@ object DirectUtils { "column", "游戏专题" -> directToSubject( context, linkEntity.link - ?: "", linkEntity.text, BaseActivity.mergeEntranceAndPath(entrance, path) + ?: "", linkEntity.text, BaseActivity.mergeEntranceAndPath(entrance, path), exposureEvent ) "question", "社区问题" -> directToQuestionDetail( @@ -240,7 +243,7 @@ object DirectUtils { "catalog" -> directCatalog(context, linkEntity.link!!, linkEntity.text!!, entrance, path) - "category_v2" -> directCategoryV2(context, linkEntity.link!!, linkEntity.text!!, entrance, path) + "category_v2" -> directCategoryV2(context, linkEntity.link!!, linkEntity.text!!, entrance, path, exposureEvent) "block", "版块" -> { if (linkEntity.link.isNullOrEmpty()) return @@ -264,7 +267,7 @@ object DirectUtils { "wechat_bind" -> context.startActivity(WebActivity.getBindWechatIntent(context)) - "video", "video_stream", "视频" -> directToVideoDetail( + "video", "bbs_video", "video_stream", "视频" -> directToVideoDetail( context, videoId = linkEntity.link!!, fromLocation = VideoDetailContainerViewModel.Location.VIDEO_CHOICENESS.value, @@ -630,13 +633,14 @@ object DirectUtils { // 专栏 @JvmStatic - fun directToSubject(context: Context, id: String, subjectName: String? = "", entrance: String? = null) { + fun directToSubject(context: Context, id: String, subjectName: String? = "", entrance: String? = null, exposureEvent: ExposureEvent? = null) { if (id.isEmpty()) return val bundle = Bundle() val subjectData = SubjectData(subjectId = id, subjectName = subjectName, isOrder = false) bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER) bundle.putString(KEY_TO, SubjectActivity::class.java.name) bundle.putParcelable(EntranceConsts.KEY_SUBJECT_DATA, subjectData) + if (exposureEvent != null) bundle.putParcelableArrayList(KEY_EXPOSURE_SOURCE_LIST, ArrayList(exposureEvent.source)) jumpActivity(context, bundle) } @@ -1184,6 +1188,7 @@ object DirectUtils { categoryTitle: String, entrance: String? = null, path: String? = "", + exposureEvent: ExposureEvent? = null, ) { if (categoryId.isEmpty()) return val bundle = Bundle() @@ -1191,6 +1196,7 @@ object DirectUtils { bundle.putString(KEY_CATEGORY_ID, categoryId) bundle.putString(KEY_CATEGORY_TITLE, categoryTitle) bundle.putString(KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path)) + if (exposureEvent != null) bundle.putParcelableArrayList(KEY_EXPOSURE_SOURCE_LIST, ArrayList(exposureEvent.source)) jumpActivity(context, bundle) } @@ -1704,7 +1710,13 @@ object DirectUtils { * 跳转至游戏单广场 */ @JvmStatic - fun directToGameCollectionSquare(context: Context, entrance: String = "", forumName: String = "", gameCollectionTitle: String = "", gameCollectionId: String = "") { + fun directToGameCollectionSquare( + context: Context, + entrance: String = "", + forumName: String = "", + gameCollectionTitle: String = "", + gameCollectionId: String = "" + ) { val bundle = Bundle() bundle.putString(KEY_TO, GameCollectionSquareActivity::class.java.name) bundle.putString(KEY_ENTRANCE, entrance) @@ -1766,6 +1778,15 @@ object DirectUtils { } } + @JvmStatic + fun directToVGameDownload(context: Context, switchToDownloadingTab: Boolean = false) { + val bundle = Bundle() + bundle.putString(KEY_ENTRANCE, ENTRANCE_BROWSER) + bundle.putString(KEY_TO, VDownloadManagerActivity::class.java.name) + bundle.putInt(KEY_POSITION, if (switchToDownloadingTab) 1 else 0) + jumpActivity(context, bundle) + } + @JvmStatic fun directToSuggestion(context: Context, type: SuggestType) { directToSuggestion(context = context, type = type, requestCode = null) diff --git a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt index 05b1559e7c..b07b69cdb0 100644 --- a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt +++ b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt @@ -11,7 +11,6 @@ import androidx.collection.ArrayMap import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.Config -import com.gh.gamecenter.common.constant.Constants import com.gh.common.dialog.CertificationDialog import com.gh.common.dialog.DeviceRemindDialog import com.gh.common.dialog.PackageCheckDialogFragment @@ -30,7 +29,8 @@ import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity import com.gh.gamecenter.adapter.viewholder.GameViewHolder -import com.gh.gamecenter.common.callback.ConfirmListener +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.* import com.gh.gamecenter.entity.GameEntity @@ -39,6 +39,8 @@ import com.gh.gamecenter.entity.PluginLocation import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment import com.gh.gamecenter.manager.PackagesManager import com.gh.gamecenter.teenagermode.TeenagerModeActivity +import com.gh.vspace.VDownloadManagerActivity +import com.gh.vspace.VHelper import com.lightgame.download.DownloadConfig import com.lightgame.download.DownloadEntity import com.lightgame.download.DownloadStatus @@ -141,8 +143,11 @@ object DownloadItemUtils { @JvmStatic @JvmOverloads fun updateItem( - context: Context, gameEntity: GameEntity, holder: GameViewHolder, - isShowPlatform: Boolean, pluginLocation: PluginLocation? = PluginLocation.only_game, + context: Context, + gameEntity: GameEntity, + holder: GameViewHolder, + isShowPlatform: Boolean, + pluginLocation: PluginLocation? = PluginLocation.only_game, hideDownloadBtnIfNoAvailableContent: Boolean = false, briefStyle: String? = null, isShowRecommendStar: Boolean = false @@ -173,8 +178,8 @@ object DownloadItemUtils { downloadBtn.background = R.drawable.download_button_normal_style.toDrawable(context) // 控制是否显示下载按钮 downloadBtn.goneIf(!Config.isShowDownload(gameEntity.id) || context.getString(R.string.app_name) == gameEntity.name) - // 青少年模式显示查看 - if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) { + // 青少年模式或者需要特殊处理显示查看 + if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) || gameEntity.isSpecialDownload()) { downloadBtn.text = "查看" return } @@ -231,7 +236,12 @@ object DownloadItemUtils { } } } else if (gameEntity.getApk().size == 1) { - val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByUrl(gameEntity.getApk()[0].url) + var downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity) + + if (downloadEntity == null && gameEntity.isVGame()) { + downloadEntity = VHelper.getDownloadEntitySnapshot(gameEntity.id, gameEntity.getUniquePackageName()) + } + if (downloadEntity != null) { downloadBtn.apply { val status = downloadEntity.status @@ -244,15 +254,9 @@ object DownloadItemUtils { setBackgroundResource(R.drawable.button_round_border_eeeeee) setTextColor(ContextCompat.getColorStateList(context, R.color.text_subtitleDesc)) } else if (status == DownloadStatus.pause || status == DownloadStatus.timeout || status == DownloadStatus.neterror || status == DownloadStatus.subscribe || status == DownloadStatus.overflow) { - if (status == DownloadStatus.waiting) { - setText(R.string.waiting) - setBackgroundResource(R.drawable.button_round_border_eeeeee) - setTextColor(ContextCompat.getColorStateList(context, R.color.text_subtitleDesc)) - } else { - setText(R.string.downloading) - setBackgroundResource(R.drawable.game_item_btn_downloading_style) - setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style)) - } + setText(R.string.downloading) + setBackgroundResource(R.drawable.game_item_btn_downloading_style) + setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style)) } else if (status == DownloadStatus.done) { val xapkStatus = downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] if (XapkUnzipStatus.UNZIPPING.name == xapkStatus) { @@ -261,6 +265,12 @@ object DownloadItemUtils { } if (downloadEntity.isSimulatorGame() && gameEntity.simulator != null) { GameUtils.setDownloadBtnStatus(context, gameEntity, downloadBtn, pluginLocation) + } else if (downloadEntity.isVGame()) { + if (PackagesManager.isCanUpdate(downloadEntity.gameId, downloadEntity.packageName)) { + setText(R.string.update) + } else { + setText(R.string.launch) + } } else { setText(R.string.install) } @@ -335,10 +345,14 @@ object DownloadItemUtils { context: Context, holder: GameViewHolder, downloadEntity: DownloadEntity, isShowPlatform: Boolean, isNormal: Boolean, isShowRecommendStar: Boolean = false ) { - updateItemViewStatus(holder, true, null, null, isShowRecommendStar) - holder.gameProgressbar.progressDrawable = R.drawable.progressbar_bg_style.toDrawable() - val platform = PlatformUtils.getInstance(context).getPlatformName(downloadEntity.platform) val status = downloadEntity.status + // 畅玩游戏下载完成时不再需要显示进度条 + val shouldShowDownload = !(downloadEntity.isVGame() && status == DownloadStatus.done) + val platform = PlatformUtils.getInstance(context).getPlatformName(downloadEntity.platform) + + updateItemViewStatus(holder, shouldShowDownload, null, null, isShowRecommendStar) + holder.gameProgressbar.progressDrawable = R.drawable.progressbar_bg_style.toDrawable() + if (status == DownloadStatus.downloading) { if (DownloadStatus.pause != DownloadManager.getInstance().getStatus(downloadEntity.url)) { holder.gameProgressbar.progress = (downloadEntity.percent * 10).toInt() @@ -394,12 +408,12 @@ object DownloadItemUtils { private fun updateItemViewStatus( holder: GameViewHolder, - hasDownload: Boolean, + showDownload: Boolean, briefStyle: String?, recommendStyle: LinkEntity?, isShowRecommendStar: Boolean = false ) { - if (hasDownload) { + if (showDownload) { if (holder.gameRating != null) holder.gameRating!!.visibility = View.GONE holder.gameDes.visibility = View.GONE holder.gameProgressbar.visibility = View.VISIBLE @@ -542,6 +556,33 @@ object DownloadItemUtils { } return } + if (gameEntity.isSpecialDownload()) { + val info = RegionSettingHelper.getGameSpecialDownloadInfo(gameEntity.id) ?: return + downloadBtn.setOnClickListener { + DialogHelper.showDialog( + context, + "提示", + info.hintText, + "前往论坛", + "", + { + if (info.bbsId.isNotBlank()) { + if (info.topId.isNotBlank()) { + val data = hashMapOf(EntranceConsts.KEY_TOP_ID to info.topId) + PageSwitchDataHelper.pushCurrentPageData(data) + } + DirectUtils.directForumDetail(context, info.bbsId, entrance) + } + }, + {}, + DialogHelper.Config( + centerTitle = true, + centerContent = true + ) + ) + } + return + } if (gameEntity.isReservable) { if (!ReservationRepository.thisGameHasBeenReserved(gameEntity.id)) { downloadBtn.setOnClickListener { @@ -628,27 +669,17 @@ object DownloadItemUtils { downloadBtn.setOnClickListener { view: View -> allStateClickCallback?.onCallback() clickCallback?.onCallback() - RealNameHelper.checkIfAuth(view.context, gameEntity, object : EmptyCallback { - override fun onCallback() { - GamePermissionDialogFragment.show(context, gameEntity, gameEntity.info, object : ConfirmListener { - override fun onConfirm() { - PermissionHelper.checkStoragePermissionBeforeAction(context, object : EmptyCallback { - override fun onCallback() { - CertificationDialog.showCertificationDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DialogUtils.showVersionNumberDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DownloadDialog.showDownloadDialog(view.context, gameEntity, traceEvent, entrance, location) - } - }) - } - }) - } - }) - } - }) - } - }) + GamePermissionDialogFragment.show(context, gameEntity, gameEntity.info) { + PermissionHelper.checkStoragePermissionBeforeAction(context, object : EmptyCallback { + override fun onCallback() { + CertificationDialog.showCertificationDialog(context, gameEntity) { + DialogUtils.showVersionNumberDialog(context, gameEntity) { + DownloadDialog.showDownloadDialog(view.context, gameEntity, traceEvent, entrance, location) + } + } + } + }) + } } } } @@ -669,96 +700,69 @@ object DownloadItemUtils { if (gameEntity.getApk().isEmpty()) return val apk = gameEntity.getApk().safelyGetInRelease(0) ?: return if (str == context.getString(R.string.download)) { - // 先弹下载弹窗(如果需要的话) - RealNameHelper.checkIfAuth(context, gameEntity, object : EmptyCallback { - override fun onCallback() { - GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info, object : ConfirmListener { - override fun onConfirm() { - BrowserInstallHelper.showBrowserInstallHintDialog(context, object : EmptyCallback { + GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) { + BrowserInstallHelper.showBrowserInstallHintDialog(context, gameEntity.isVGame(), object : EmptyCallback { + override fun onCallback() { + PackageCheckDialogFragment.show(context, gameEntity) { + DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, object : EmptyCallback { override fun onCallback() { - PackageCheckDialogFragment.show(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, object : EmptyCallback { - override fun onCallback() { - CertificationDialog.showCertificationDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DialogUtils.showOverseaDownloadDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> - download( - context, - gameEntity, - downloadBtn, - entrance, - location, - isSubscribe, - traceEvent - ) - } - } - }) - } - }) - } - }) + CertificationDialog.showCertificationDialog(context, gameEntity) { + DialogUtils.showOverseaDownloadDialog(context, gameEntity) { + DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> + download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent) + } } - }) + } } }) - } - }) - } - }) + } + }) + } DataLogUtils.uploadGameLog(context, gameEntity.id, gameEntity.name, entrance) } else if (str == context.getString(R.string.attempt)) { - RealNameHelper.checkIfAuth(context, gameEntity, object : EmptyCallback { - override fun onCallback() { - GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info, object : ConfirmListener { - override fun onConfirm() { - BrowserInstallHelper.showBrowserInstallHintDialog(context, object : EmptyCallback { + GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) { + BrowserInstallHelper.showBrowserInstallHintDialog(context, gameEntity.isVGame(), object : EmptyCallback { + override fun onCallback() { + PackageCheckDialogFragment.show(context, gameEntity) { + DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, object : EmptyCallback { override fun onCallback() { - PackageCheckDialogFragment.show(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, object : EmptyCallback { - override fun onCallback() { - CertificationDialog.showCertificationDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DialogUtils.showVersionNumberDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DialogUtils.showOverseaDownloadDialog( - context, - gameEntity, - object : ConfirmListener { - override fun onConfirm() { - DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> - download( - context, - gameEntity, - downloadBtn, - entrance, - location, - isSubscribe, - traceEvent - ) - } - } - }) - } - }) - } - }) + CertificationDialog.showCertificationDialog(context, gameEntity) { + DialogUtils.showVersionNumberDialog(context, gameEntity) { + DialogUtils.showOverseaDownloadDialog(context, gameEntity) { + DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> + download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent) } - }) + } } - }) + } } }) } + } + }) + } + DataLogUtils.uploadGameLog(context, gameEntity.id, gameEntity.name, entrance) + } else if (str == context.getString(R.string.smooth)) { + GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) { + PackageCheckDialogFragment.show(context, gameEntity) { + DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, object : EmptyCallback { + override fun onCallback() { + CertificationDialog.showCertificationDialog(context, gameEntity) { + DialogUtils.showVersionNumberDialog(context, gameEntity) { + DialogUtils.showOverseaDownloadDialog(context, gameEntity) { + VHelper.validateVSpaceBeforeAction(context, gameEntity, true) { + DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> + download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent) + } + } + } + } + } + } }) } - }) - DataLogUtils.uploadGameLog(context, gameEntity.id, gameEntity.name, entrance) + } } else if (str.contains("化")) { if (entrance.contains("我的游戏")) { MtaHelper.onEvent("我的游戏_启动", "插件化", gameEntity.name) @@ -768,13 +772,11 @@ object DownloadItemUtils { } else { DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, object : EmptyCallback { override fun onCallback() { - CertificationDialog.showCertificationDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> - plugin(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent) - } + CertificationDialog.showCertificationDialog(context, gameEntity) { + DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> + plugin(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent) } - }) + } } }) } @@ -790,8 +792,15 @@ object DownloadItemUtils { return } } - install(context, gameEntity, position, adapter, refreshCallback) + + if (gameEntity.isVGame()) { + VHelper.installOrLaunch((context as AppCompatActivity), gameEntity.getUniquePackageName() ?: "") + } else { + install(context, gameEntity, position, adapter, refreshCallback) + } } else if (str == context.getString(R.string.launch)) { + EnergyTaskHelper.postEnergyTask("play_game", gameEntity.id, gameEntity.getApk()[0].packageName) + //启动模拟器游戏 if (SimulatorGameManager.isSimulatorGame(gameEntity)) { val downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(gameEntity.getApk()[0].url) @@ -805,15 +814,24 @@ object DownloadItemUtils { } return } + + if (gameEntity.isVGame()) { + VHelper.installOrLaunch((context as AppCompatActivity), gameEntity.getUniquePackageName() ?: "") + return + } + if (entrance.contains("我的游戏")) { MtaHelper.onEvent("我的游戏_启动", "启动", gameEntity.name) } PackageUtils.launchApplicationByPackageName(context, gameEntity.getApk()[0].packageName) - EnergyTaskHelper.postEnergyTask("play_game", gameEntity.id, gameEntity.getApk()[0].packageName) } else if (str == context.getString(R.string.update)) { if (entrance.contains("我的游戏")) { MtaHelper.onEvent("我的游戏_启动", "更新", gameEntity.name) } + if (gameEntity.isVGame()) { + VHelper.updateOrReDownload(gameEntity) + return + } DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, object : EmptyCallback { override fun onCallback() { DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> @@ -822,12 +840,16 @@ object DownloadItemUtils { } }) } else { - context.startActivity( - DownloadManagerActivity.getDownloadMangerIntent( - context, - apk.url, entrance + "+(" + location.split(":").toTypedArray()[0] + ")" + if (gameEntity.isVGame()) { + context.startActivity(VDownloadManagerActivity.getIntent(context, true)) + } else { + context.startActivity( + DownloadManagerActivity.getDownloadMangerIntent( + context, + apk.url, entrance + "+(" + location.split(":").toTypedArray()[0] + ")" + ) ) - ) + } } } @@ -877,7 +899,7 @@ object DownloadItemUtils { adapter: RecyclerView.Adapter?, refreshCallback: EmptyCallback? ) { val apkEntity = gameEntity.getApk()[0] - val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByUrl(apkEntity.url) + val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity) if (downloadEntity != null) { val path = downloadEntity.path when { diff --git a/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt b/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt index af208da26f..1dc3edfc91 100644 --- a/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt +++ b/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt @@ -8,17 +8,14 @@ import android.content.Context import android.content.Intent import android.os.Build import androidx.core.app.NotificationCompat -import com.gh.gamecenter.core.AppExecutor -import com.gh.gamecenter.common.constant.Constants import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus import com.gh.gamecenter.R +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts -import com.gh.gamecenter.common.utils.getMetaExtra -import com.gh.gamecenter.common.utils.isSimulatorGame -import com.gh.gamecenter.common.utils.toJson -import com.gh.gamecenter.common.utils.tryCatchInRelease -import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.utils.SpeedUtils import com.halo.assistant.HaloApp import com.lightgame.download.DownloadEntity import com.lightgame.download.DownloadStatus @@ -34,6 +31,7 @@ object DownloadNotificationHelper { const val ACTION_INSTALL = "com.gh.gamecenter.INSTALL" const val ACTION_DOWNLOAD = "com.gh.gamecenter.DOWNLOAD" + const val ACTION_VDOWNLOAD = "com.gh.gamecenter.VDOWNLOAD" private val mNotifyMap: MutableMap = mutableMapOf() private val mShouldUseAlternativeNotificationIcon by lazy { RomIdentifier.getRom().versionName == null } @@ -58,6 +56,8 @@ object DownloadNotificationHelper { intent.putExtra(EntranceConsts.KEY_DATA, entity.toJson()) intent.putExtra(EntranceConsts.KEY_PATH, entity.path) intent.action = ACTION_INSTALL + } else if (entity.isVGame()) { + intent.action = ACTION_VDOWNLOAD } else { intent.action = ACTION_DOWNLOAD } @@ -119,6 +119,7 @@ object DownloadNotificationHelper { || entity.status == DownloadStatus.hijack || entity.status == DownloadStatus.unqualified || entity.status == DownloadStatus.unavailable + || entity.status == DownloadStatus.banned || entity.status == DownloadStatus.uncertificated || entity.status == DownloadStatus.notfound || entity.status == DownloadStatus.overflow diff --git a/app/src/main/java/com/gh/common/util/DownloadObserver.kt b/app/src/main/java/com/gh/common/util/DownloadObserver.kt index f0c577ca65..8007d797f1 100644 --- a/app/src/main/java/com/gh/common/util/DownloadObserver.kt +++ b/app/src/main/java/com/gh/common/util/DownloadObserver.kt @@ -18,6 +18,7 @@ import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.GsonUtils import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.SimpleGameEntity import com.gh.gamecenter.entity.SimulatorEntity @@ -25,6 +26,7 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.retrofit.RetrofitManager import com.gh.gamecenter.setting.GameDownloadSettingFragment import com.gh.gamecenter.suggest.SuggestType +import com.gh.vspace.VHelper import com.halo.assistant.HaloApp import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity @@ -115,14 +117,19 @@ object DownloadObserver { downloadManager.cancel(downloadEntity.url) } else if (DownloadStatus.unavailable == downloadEntity.status) { // 未接入防沉迷系统 - val currentActivity = AppManager.getInstance().currentActivity() ?: return + val currentActivity = AppManager.getInstance().currentActivity() - DialogHelper.showDialog( - context = currentActivity, - title = "温馨提示", - content = "该游戏未接入防沉迷系统,暂不支持下载", - confirmText = "知道了", - cancelText = "") + if (currentActivity != null) { + DialogHelper.showDialog( + context = currentActivity, + title = "温馨提示", + content = "该游戏未接入防沉迷系统,暂不支持下载", + confirmText = "知道了", + cancelText = "" + ) + } else { + ToastUtils.toast("该游戏未接入防沉迷系统,暂不支持下载") + } // 删除任务 downloadEntity.status = DownloadStatus.cancel @@ -131,6 +138,11 @@ object DownloadObserver { // 未实名 RealNameHelper.showRealNameUncertificatedDialog(downloadEntity) + // 删除任务 + downloadEntity.status = DownloadStatus.cancel + downloadManager.cancel(downloadEntity.url) + }else if (DownloadStatus.banned == downloadEntity.status) { + ToastUtils.showToast("网络异常") // 删除任务 downloadEntity.status = DownloadStatus.cancel downloadManager.cancel(downloadEntity.url) @@ -210,10 +222,20 @@ object DownloadObserver { ) ) downloadEntity.isPlugin -> Utils.toast(mApplication, downloadEntity.name + " - " + platform + " - 下载完成") - else -> Utils.toast(mApplication, downloadEntity.name + " - 下载完成") + else -> { + if (downloadEntity.isVGame()) { + VHelper.showFloatingWindow(downloadEntity.packageName) + } else { + Utils.toast(mApplication, downloadEntity.name + " - 下载完成") + } + } } } else { - Utils.toast(mApplication, downloadEntity.name + " - 下载完成") + if (downloadEntity.isVGame()) { + VHelper.showFloatingWindow(downloadEntity.packageName) + } else { + Utils.toast(mApplication, downloadEntity.name + " - 下载完成") + } } if (!downloadEntity.isPluggable) { if (downloadEntity.isSimulatorGame()) { @@ -244,7 +266,7 @@ object DownloadObserver { Utils.toast(mApplication, R.string.install_failure_hint) downloadManager.cancel(downloadEntity.url) } else { - if (PackageUtils.isCanLaunchSetup(mApplication, downloadEntity.path)) { + if (PackageUtils.isCanLaunchSetup(mApplication, downloadEntity.path) || downloadType == Constants.SMOOTH_GAME) { downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES" tryWithDefaultCatch { PackageInstaller.install(mApplication, downloadEntity, false) @@ -295,7 +317,7 @@ object DownloadObserver { override fun onConfirm() { val simulator = HaloApp.get(downloadEntity.name, true) as? SimulatorEntity ?: return - DownloadManager.getInstance().cancel(downloadEntity.url, true, true) + DownloadManager.getInstance().cancel(downloadEntity.url, true, true, false) SimulatorDownloadManager.getInstance() .showDownloadDialog(currentActivity, simulator, SimulatorDownloadManager.SimulatorLocation.SIMULATOR_GAME) } @@ -308,12 +330,20 @@ object DownloadObserver { private fun statDoneEvent(downloadEntity: DownloadEntity) { var type: ExposureUtils.DownloadType if (downloadEntity.isUpdate) { - type = ExposureUtils.DownloadType.UPDATE - if (downloadEntity.isPlugin) { - type = ExposureUtils.DownloadType.PLUGIN_UPDATE + if (downloadEntity.isVGame()) { + type = ExposureUtils.DownloadType.FUN_UPDATE + } else { + type = ExposureUtils.DownloadType.UPDATE + if (downloadEntity.isPlugin) { + type = ExposureUtils.DownloadType.PLUGIN_UPDATE + } } } else { - type = ExposureUtils.DownloadType.DOWNLOAD + type = if (downloadEntity.isVGame()) { + ExposureUtils.DownloadType.FUN_DOWNLOAD + } else { + ExposureUtils.DownloadType.DOWNLOAD + } } if (downloadEntity.isPluggable) { diff --git a/app/src/main/java/com/gh/common/util/ErrorHelper.kt b/app/src/main/java/com/gh/common/util/ErrorHelper.kt index 65456ed0f4..8448d49842 100644 --- a/app/src/main/java/com/gh/common/util/ErrorHelper.kt +++ b/app/src/main/java/com/gh/common/util/ErrorHelper.kt @@ -1,13 +1,26 @@ package com.gh.common.util +import android.app.Activity import android.content.Context +import android.content.Intent +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity import com.gh.common.constant.Config import com.gh.gamecenter.R +import com.gh.gamecenter.ShellActivity import com.gh.gamecenter.WebActivity +import com.gh.gamecenter.common.avoidcallback.AvoidOnResultManager +import com.gh.gamecenter.common.avoidcallback.Callback +import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.utils.DialogHelper +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.utils.toObject import com.gh.gamecenter.entity.ErrorEntity +import com.halo.assistant.fragment.user.UserInfoEditFragment +import com.lightgame.utils.AppManager import com.lightgame.utils.Utils import retrofit2.HttpException @@ -26,6 +39,7 @@ object ErrorHelper { context: Context, errorString: String?, showHighPriorityHint: Boolean = false, + realNameConfirmListener: ConfirmListener? = null, customizedHandler: (code: Int) -> Boolean ) { val errorEntity = errorString?.toObject() @@ -44,14 +58,14 @@ object ErrorHelper { return } - handleError(context, showHighPriorityHint, errorEntity) + handleError(context, showHighPriorityHint, errorEntity, realNameConfirmListener) } /** * [showHighPriorityHint] 用来标识有同样错误码可以触发两种处理时,为 true 时选择重要的 */ @JvmStatic - fun handleError(context: Context, errorString: String?, showHighPriorityHint: Boolean = false) { + fun handleError(context: Context, errorString: String?, showHighPriorityHint: Boolean = false, realNameConfirmListener: ConfirmListener? = null) { val errorEntity = errorString?.toObject() if (errorEntity == null) { @@ -64,7 +78,7 @@ object ErrorHelper { return } - handleError(context, showHighPriorityHint, errorEntity) + handleError(context, showHighPriorityHint, errorEntity, realNameConfirmListener) } /*** @@ -87,7 +101,8 @@ object ErrorHelper { private fun handleError( context: Context, showHighPriorityHint: Boolean = false, - errorEntity: ErrorEntity + errorEntity: ErrorEntity, + realNameConfirmListener: ConfirmListener? = null ) { when (errorEntity.code) { 403050, @@ -163,6 +178,48 @@ object ErrorHelper { 400802 -> { // 多设备登录同一帐号,不需要这里处理 } + + 403209 -> { + DialogHelper.showDialog( + context, + "实名提醒", + errorEntity.data?.title ?: "", + "前往实名认证", "以后再说", + uiModificationCallback = { binding -> + binding.hintTv.visibility = View.VISIBLE + binding.hintTv.layoutParams = + (binding.hintTv.layoutParams as ViewGroup.MarginLayoutParams).apply { setMargins(0, 8F.dip2px(), 0, 0) } + binding.lineView.layoutParams = + (binding.lineView.layoutParams as ViewGroup.MarginLayoutParams).apply { setMargins(0, 23F.dip2px(), 0, 0) } + binding.hintTv.text = errorEntity.data?.text + binding.hintTv.setTextColor(R.color.theme_font.toColor(context)) + binding.hintTv.setOnClickListener { + errorEntity.data?.toLinkEntity()?.let { entity -> + DirectUtils.directToLinkPage(context, entity, "实名提醒弹窗", "") + } + } + }, + confirmClickCallback = { + val currentActivity = AppManager.getInstance().currentActivity() ?: return@showDialog + AvoidOnResultManager.getInstance(currentActivity as AppCompatActivity) + .startForResult( + ShellActivity.getIntent( + context, + ShellActivity.Type.REAL_NAME_INFO, + ), object : Callback { + override fun onActivityResult(resultCode: Int, data: Intent?) { + if (resultCode == Activity.RESULT_OK && data != null) { + val isAuthSuccess = + data.getBooleanExtra(UserInfoEditFragment.AUTH_SUCCESS, false) + if (isAuthSuccess) { + realNameConfirmListener?.onConfirm() + } + } + } + }) + } + ) + } else -> Utils.toast(context, R.string.post_failure_hint) } } @@ -248,6 +305,9 @@ object ErrorHelper { errorEntity?.code == 403099 -> { Utils.toast(context, "当前账号正在注销,禁止登录") } + errorEntity?.code == 403401 -> {//禁止登录 + Utils.toast(context, "网络异常") + } errorEntity?.toast?.isNotEmpty() == true -> { Utils.toast(context, errorEntity.toast) } diff --git a/app/src/main/java/com/gh/common/util/GameActivityDownloadHelper.kt b/app/src/main/java/com/gh/common/util/GameActivityDownloadHelper.kt index ff45a0a046..b889ef6919 100644 --- a/app/src/main/java/com/gh/common/util/GameActivityDownloadHelper.kt +++ b/app/src/main/java/com/gh/common/util/GameActivityDownloadHelper.kt @@ -5,7 +5,6 @@ import android.content.Context import android.text.TextUtils import androidx.appcompat.app.AppCompatActivity import com.gh.common.DefaultJsApi -import com.gh.gamecenter.common.constant.Constants import com.gh.common.dialog.CertificationDialog import com.gh.common.exposure.ExposureEvent import com.gh.common.exposure.ExposureManager @@ -13,28 +12,31 @@ import com.gh.common.exposure.ExposureSource import com.gh.common.exposure.ExposureType import com.gh.common.history.HistoryHelper import com.gh.common.repository.ReservationRepository -import com.gh.gamecenter.core.runOnUiThread -import com.gh.gamecenter.common.view.dsbridge.CompletionHandler import com.gh.download.DownloadManager import com.gh.download.dialog.DownloadDialog import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity -import com.gh.gamecenter.common.callback.ConfirmListener +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.retrofit.ApiResponse +import com.gh.gamecenter.common.retrofit.EmptyResponse +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.common.utils.DataLogUtils +import com.gh.gamecenter.common.utils.DialogHelper import com.gh.gamecenter.common.utils.observableToMain import com.gh.gamecenter.common.utils.singleToMain -import com.gh.gamecenter.common.utils.DialogHelper -import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.common.view.dsbridge.CompletionHandler +import com.gh.gamecenter.core.runOnUiThread +import com.gh.gamecenter.core.utils.EmptyCallback +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.entity.ApkEntity import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.PluginLocation import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment import com.gh.gamecenter.manager.UserManager -import com.gh.gamecenter.common.retrofit.EmptyResponse -import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.gh.gamecenter.teenagermode.TeenagerModeActivity -import com.gh.gamecenter.common.retrofit.ApiResponse -import com.gh.gamecenter.common.utils.DataLogUtils +import com.gh.vspace.VHelper import com.lightgame.download.FileUtils /** @@ -165,52 +167,39 @@ object GameActivityDownloadHelper { traceEvent: ExposureEvent ) { val apk = getApk(gameEntity, event, true) ?: return - val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByUrl(apk.url) + val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity) if (downloadEntity != null) { ToastUtils.toast("${gameEntity.name}已加入下载队列") } else { val str = GameUtils.getDownloadBtnText(context, gameEntity, PluginLocation.only_game) - if (str == context.getString(R.string.download)) { - GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info, object : ConfirmListener { - override fun onConfirm() { - CertificationDialog.showCertificationDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> - download(context, gameEntity, apk, isSubscribe, entrance, location, traceEvent) - } - } - }) + if (str == context.getString(R.string.download) || str == context.getString(R.string.attempt)) { + GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) { + CertificationDialog.showCertificationDialog(context, gameEntity) { + DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> + download(context, gameEntity, apk, isSubscribe, entrance, location, traceEvent) + } } - }) + } DataLogUtils.uploadGameLog(context, gameEntity.id, gameEntity.name, entrance) - } else if (str == context.getString(R.string.attempt)) { - RealNameHelper.checkIfAuth(context, gameEntity, object : EmptyCallback { - override fun onCallback() { - GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info, object : ConfirmListener { - override fun onConfirm() { - CertificationDialog.showCertificationDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> - download(context, gameEntity, apk, isSubscribe, entrance, location, traceEvent) - } - } - }) + } else if (str == context.getString(R.string.smooth)) { + VHelper.validateVSpaceBeforeAction(context, gameEntity, true) { + GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) { + CertificationDialog.showCertificationDialog(context, gameEntity) { + DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> + download(context, gameEntity, apk, isSubscribe, entrance, location, traceEvent) } - }) + } } - }) - DataLogUtils.uploadGameLog(context, gameEntity.id, gameEntity.name, entrance) + } } else if (str.contains("化")) { if (gameEntity.pluggableCollection != null) { DownloadDialog.showDownloadDialog(context, gameEntity, traceEvent, entrance, location) } else { - CertificationDialog.showCertificationDialog(context, gameEntity, object : ConfirmListener { - override fun onConfirm() { - DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> - plugin(context, gameEntity, apk, entrance, location, isSubscribe, traceEvent) - } + CertificationDialog.showCertificationDialog(context, gameEntity) { + DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean -> + plugin(context, gameEntity, apk, entrance, location, isSubscribe, traceEvent) } - }) + } } } else if (str == context.getString(R.string.install) || str == context.getString(R.string.launch)) { ToastUtils.toast("${gameEntity.name}已加入下载队列") @@ -275,11 +264,8 @@ object GameActivityDownloadHelper { ) { val msg = FileUtils.isCanDownload(context, apk.size) if (TextUtils.isEmpty(msg)) { - DownloadManager.createDownload( - context, apk, gameEntity, context.getString( - R.string.download - ), entrance, location, isSubscribe, traceEvent - ) + DownloadManager.createDownload(context, apk, gameEntity, context.getString( + R.string.download), entrance, location, isSubscribe, traceEvent) ToastUtils.toast("${gameEntity.name}已加入下载队列") } else { ToastUtils.toast(msg) @@ -383,8 +369,7 @@ object GameActivityDownloadHelper { if (PackageUtils.isInstalled(context, apk.packageName)) { // 是否可更新 if (PackageUtils.isCanUpdate(apk, event.gameId) - || PackageUtils.isNonPluginUpdatable(apk, gameEntity) - ) { + || PackageUtils.isNonPluginUpdatable(apk, gameEntity)) { handler.complete(false) } else { // 已安装且无更新 diff --git a/app/src/main/java/com/gh/common/util/GameUtils.java b/app/src/main/java/com/gh/common/util/GameUtils.java index 0593b34e7c..ef51cfa23c 100644 --- a/app/src/main/java/com/gh/common/util/GameUtils.java +++ b/app/src/main/java/com/gh/common/util/GameUtils.java @@ -18,6 +18,7 @@ import com.gh.gamecenter.entity.GameUpdateEntity; import com.gh.gamecenter.entity.PluginLocation; import com.gh.gamecenter.entity.SettingsEntity; import com.gh.gamecenter.manager.PackagesManager; +import com.gh.vspace.VHelper; import com.lightgame.download.DownloadEntity; import com.lightgame.download.DownloadStatus; @@ -59,7 +60,6 @@ public class GameUtils { AppExecutor.getLightWeightIoExecutor().execute(() -> { String status = getDownloadBtnText(context, gameEntity, pluginLocation); AppExecutor.getUiExecutor().execute(() -> { - // TODO 切换线程时可能存在页面已销毁 view 已不存在这些乱七八糟的问题 downloadBtn.setTextColor(Color.WHITE); downloadBtn.setText(status); if (context.getString(R.string.pluggable).equals(status)) { @@ -90,7 +90,7 @@ public class GameUtils { boolean isRelatedEmulatorInstalled = false; // 若该游戏是模拟器游戏时其对应的模拟器是否已经安装 - DownloadEntity downloadEntity; + DownloadEntity downloadEntity = null; Object gh_id; apkFor: for (ApkEntity apkEntity : gameEntity.getApk()) { @@ -105,7 +105,13 @@ public class GameUtils { } } - downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByUrl(apkEntity.getUrl()); + downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity); + + // 在下载管理找不到下载实体,并且为畅玩游戏的时候到畅玩数据库里找 + if (downloadEntity == null && gameEntity.isVGame()) { + downloadEntity = VHelper.getDownloadEntitySnapshot(gameEntity.getId(), apkEntity.getPackageName()); + } + if (downloadEntity != null) { if (downloadEntity.getStatus().equals(DownloadStatus.done)) { doneCount++; @@ -149,6 +155,19 @@ public class GameUtils { return context.getString(R.string.launch); } + if (gameEntity.isVGame()) { + // 如果 doneCount 为 0 或者数据库为空 + // 表明本地并没有此游戏的数据库数据,需要重新下载 + if (doneCount == 0 + || downloadEntity == null + || !VHelper.isInstalled(downloadEntity.getPackageName())) { + installCount = 0; + } else { + doneCount = 0; + installCount = 1; + } + } + if (doneCount != 0) { return context.getString(R.string.install); } else if (pluginCount != 0 && !SimulatorGameManager.isSimulatorGame(gameEntity)) { @@ -160,50 +179,11 @@ public class GameUtils { } else if ("demo".equals(gameEntity.getDownloadStatus())) { return context.getString(R.string.attempt); } else { - return context.getString(R.string.download); - } - } - - /** - * 获取简单的下载按钮文案,只需要知道是否已下载,是否已安装 - */ - public static String getSimpleDownloadBtnText(Context context, GameEntity gameEntity) { - int doneCount = 0; // 下载完成数量 - int installCount = 0; // 已安装数量 - - DownloadEntity downloadEntity; - Object gh_id; - apkFor: - for (ApkEntity apkEntity : gameEntity.getApk()) { - // filter by packageName - SettingsEntity settings = Config.getSettings(); - if (settings != null && gameEntity.getApk().size() > 1) { - for (String pkgName : settings.getGameDownloadBlackList()) { - if (pkgName.equals(apkEntity.getPackageName())) { - continue apkFor; - } - } + if (gameEntity.isVGame()) { + return context.getString(R.string.smooth); + } else { + return context.getString(R.string.download); } - - downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByUrl(apkEntity.getUrl()); - if (downloadEntity != null) { - if (downloadEntity.getStatus().equals(DownloadStatus.done)) { - doneCount++; - } - } - if (PackagesManager.INSTANCE.isInstalled(apkEntity.getPackageName())) { - gh_id = PackageUtils.getMetaData(context, apkEntity.getPackageName(), "gh_id"); - if (gh_id == null || gh_id.equals(gameEntity.getId())) { - installCount++; - } - } - } - if (doneCount != 0) { - return context.getString(R.string.install); - } else if (installCount != 0) { - return context.getString(R.string.launch); - } else { - return context.getString(R.string.download); } } diff --git a/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt b/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt index bc1c3494dd..9346fc4f1f 100644 --- a/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt +++ b/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt @@ -1,8 +1,10 @@ package com.gh.common.util import com.gh.gamecenter.common.json.JsonObjectBuilder +import com.gh.gamecenter.common.json.json import com.gh.gamecenter.common.loghub.LoghubUtils import com.lightgame.utils.Utils +import org.json.JSONArray import org.json.JSONObject /** @@ -25,4 +27,194 @@ object NewFlatLogUtils { key to value } } + + // 畅玩助手相关事件(下载弹窗展示事件/隐私政策点击事件/下载点击事件/授权弹窗展示事件/更新弹窗展示事件) + // 畅玩管理删除游戏弹窗展示事件/畅玩管理-畅玩广场入口点击事件/退出畅玩助手提示弹窗展示事件 + @JvmStatic + fun logHaloFunEvent(event: String) { + val json = json { + "event" to event + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + // 关联游戏跳转icon点击事件 + @JvmStatic + fun logHaloFunGameDetailJumpClick(downloadStatus: String, gameId: String) { + val json = json { + "event" to "halo_fun_game_detail_jump_click" + "download_state" to downloadStatus + "game_id" to gameId + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + // 下载完成提示条点击事件 + @JvmStatic + fun logHaloFunDownloadCompleteTipClick(buttonType: String, gameId: String) { + val json = json { + "event" to "halo_fun_download_complete_tip_click" + "button_type" to buttonType + "game_id" to gameId + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + // 畅玩助手更新弹窗点击事件 + @JvmStatic + fun logHaloFunUpdateDialogClick(dialogType: String, buttonType: String) { + val json = json { + "event" to "halo_fun_update_dialog_click" + "dialog_type" to dialogType + "button_type" to buttonType + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + // 畅玩管理展示事件 + @JvmStatic + fun logHaloFunManageShow(entrance: String) { + val json = json { + "event" to "halo_fun_manage_show" + "entrance" to entrance + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + // 畅玩管理-最近在玩板块展示管理事件 + @JvmStatic + fun logHaloFunManageRecentGameSwitch(isOn: Boolean) { + val json = json { + "event" to "halo_fun_manage_recent_game_switch" + "is_on" to isOn + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + // 畅玩管理删除游戏弹窗点击事件 + @JvmStatic + fun logHaloFunManageGameDeleteDialogClick(buttonType: String, gamesArray: JSONArray) { + val json = json { + "event" to "halo_fun_manage_game_delete_dialog_click" + "button_type" to buttonType + "games_array" to gamesArray + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + // 退出畅玩助手提示弹窗反馈收集事件 + @JvmStatic + fun logHaloFunGameExitDialogSubmitClick(detail: String, typeTags: JSONArray) { + val json = json { + "event" to "halo_fun_game_exit_dialog_submit_click" + "detail" to detail + "type_tags" to typeTags + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + // 搜索-点击热门标签 + @JvmStatic + fun logSearchHotTagClick( + tag: String, + linkType: String, + linkId: String, + linkText: String + ) { + val json = json { + "event" to "search_click_hot_tag" + "tag" to tag + "link_type" to linkType + "link_id" to linkId + "link_text" to linkText + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + //版块点击内容卡片 + @JvmStatic + fun logBlockGameContentCardClick( + blockId: String, + blockName: String, + linkType: String, + linkId: String, + linkText: String + ) { + val json = json { + "event" to "block_game_content_card_click" + "block_id" to blockId + "block_name" to blockName + "link_type" to linkType + "link_id" to linkId + "link_text" to linkText + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + //首页点击内容卡片 + @JvmStatic + fun logHomeGameContentCardClick( + linkType: String, + linkId: String, + linkText: String + ) { + val json = json { + "event" to "home_game_content_card_click" + "link_type" to linkType + "link_id" to linkId + "link_text" to linkText + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + //游戏详情点击内容卡片 + @JvmStatic + fun logGameDetailGameContentCardClick( + title: String, + gameName: String, + gameId: String, + linkType: String, + linkId: String, + linkText: String + ) { + val json = json { + "event" to "game_detail_game_content_card_click" + "title" to title + "game_name" to gameName + "game_id" to gameId + "link_type" to linkType + "link_id" to linkId + "link_text" to linkText + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } + + //浏览自定义栏目 + @JvmStatic + fun logGameDetailColumnOrderingView( + title: String, + gameName: String, + gameId: String + ) { + val json = json { + "event" to "game_detail_column_ordering_view" + "title" to title + "game_name" to gameName + "game_id" to gameId + parseAndPutMeta().invoke(this) + } + log(json, "event", false) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/NewLogUtils.kt b/app/src/main/java/com/gh/common/util/NewLogUtils.kt index da1aeb639d..9edaa3cff8 100644 --- a/app/src/main/java/com/gh/common/util/NewLogUtils.kt +++ b/app/src/main/java/com/gh/common/util/NewLogUtils.kt @@ -4,18 +4,18 @@ import android.annotation.SuppressLint import com.gh.gamecenter.common.json.JsonObjectBuilder import com.gh.gamecenter.common.json.json import com.gh.gamecenter.common.loghub.LoghubUtils +import com.gh.gamecenter.common.retrofit.EmptyResponse import com.gh.gamecenter.common.tracker.Tracker import com.gh.gamecenter.common.utils.toRequestBody import com.gh.gamecenter.entity.QuoteCountEntity import com.gh.gamecenter.entity.WechatConfigEntity -import com.gh.gamecenter.common.retrofit.EmptyResponse import com.gh.gamecenter.retrofit.RetrofitManager import com.lightgame.utils.Utils import io.reactivex.schedulers.Schedulers import okhttp3.ResponseBody +import org.json.JSONArray import org.json.JSONObject -@Deprecated("新埋点请添加至 NewFlatLogUtils") object NewLogUtils { private fun log(jsonObject: JSONObject, logStore: String, uploadImmediately: Boolean) { diff --git a/app/src/main/java/com/gh/common/util/PackageInstaller.kt b/app/src/main/java/com/gh/common/util/PackageInstaller.kt index 7e86761029..42b3fb113c 100644 --- a/app/src/main/java/com/gh/common/util/PackageInstaller.kt +++ b/app/src/main/java/com/gh/common/util/PackageInstaller.kt @@ -13,15 +13,17 @@ import com.gh.common.xapk.XapkInstaller import com.gh.download.server.BrowserInstallHelper import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.common.utils.DialogHelper import com.gh.gamecenter.common.utils.getExtension +import com.gh.gamecenter.common.utils.getMetaExtra import com.gh.gamecenter.common.utils.toRequestBody import com.gh.gamecenter.core.utils.CurrentActivityHolder import com.gh.gamecenter.core.utils.MD5Utils import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.core.utils.ToastUtils -import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.vspace.VHelper import com.halo.assistant.HaloApp import com.lightgame.download.DownloadEntity import com.lightgame.download.FileUtils @@ -53,9 +55,15 @@ object PackageInstaller { fun install(context: Context, downloadEntity: DownloadEntity, showUnzipToast: Boolean) { val pkgPath = downloadEntity.path val isXapk = XapkInstaller.XAPK_EXTENSION_NAME == pkgPath.getExtension() + val isSmoothGame = downloadEntity.getMetaExtra(Constants.SMOOTH_GAME) == "true" val currentActivity = CurrentActivityHolder.getCurrentActivity() ?: return + if (isSmoothGame) { + VHelper.install(currentActivity, downloadEntity) + return + } + // TODO 此处可能遇到 activity 是 WXEntryActivity // TODO 当 activity 全部出栈,但是应用还在下载游戏,下载完会唤不起安装! if (currentActivity is AppCompatActivity && !currentActivity.isFinishing) { @@ -88,7 +96,12 @@ object PackageInstaller { * 除非你已经确定该文件一定是Apk */ @JvmStatic - fun install(context: Context, isPluggin: Boolean = false, pkgPath: String) { + fun install(context: Context, isPluggin: Boolean = false, pkgPath: String?) { + if (pkgPath.isNullOrEmpty()) { + ToastUtils.toast("下载文件异常") + return + } + try { // 判断是否需要使用浏览器来进行安装 if (BrowserInstallHelper.isUseBrowserToInstallEnabled() diff --git a/app/src/main/java/com/gh/common/util/PackageUtils.java b/app/src/main/java/com/gh/common/util/PackageUtils.java index 0b6332d43d..b053bab97f 100644 --- a/app/src/main/java/com/gh/common/util/PackageUtils.java +++ b/app/src/main/java/com/gh/common/util/PackageUtils.java @@ -22,13 +22,19 @@ import com.android.apksig.ApkVerifier; import com.android.apksig.internal.apk.ApkSigningBlockUtilsLite; import com.g00fy2.versioncompare.Version; import com.gh.common.xapk.XapkInstaller; +import com.gh.download.DownloadManager; import com.gh.gamecenter.BuildConfig; +import com.gh.gamecenter.common.constant.Constants; +import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.core.utils.SentryHelper; import com.gh.gamecenter.entity.ApkEntity; import com.gh.gamecenter.entity.GameEntity; import com.gh.gamecenter.entity.GameUpdateEntity; import com.gh.gamecenter.manager.PackagesManager; +import com.gh.vspace.VHelper; +import com.gh.vspace.db.VGameEntity; import com.halo.assistant.HaloApp; +import com.lightgame.download.DownloadEntity; import com.lightgame.utils.Utils; import net.dongliu.apk.parser.ApkFile; @@ -107,8 +113,11 @@ public class PackageUtils { // 是否需要显示更新 boolean shouldShowUpdate = apkEntity.getForce(); + // 普通游戏根据本地是否有安装来确定是否 qualified,畅玩游戏直接进判断 + boolean isUpdateQualified = + gameEntity.isVGame() || (!TextUtils.isEmpty(versionFromRequest) && !TextUtils.isEmpty(versionFromInstalledApp)); - if (shouldShowUpdate && !TextUtils.isEmpty(versionFromRequest) && !TextUtils.isEmpty(versionFromInstalledApp)) { + if (shouldShowUpdate && isUpdateQualified) { // 根据版本判断是否需要更新 shouldShowUpdate = new Version(versionFromRequest).isHigherThan(versionFromInstalledApp); @@ -118,11 +127,27 @@ public class PackageUtils { shouldShowUpdate = versionCodeFromRequest > versionCodeFromInstalledApp; } + // 畅玩游戏根据 md5 是否一致确定是否需要更新 + if (gameEntity.isVGame()) { + VGameEntity vGameEntity = VHelper.getVGameSnapshot(gameEntity.getId(), apkEntity.getPackageName()); + if (vGameEntity != null) { + String md5FromInstalledVGame = ExtensionsKt.getMetaExtra(vGameEntity.getDownloadEntity(), Constants.APK_MD5); + String md5FromRequest = apkEntity.getMd5(); + apkEntity.setPlatform(VHelper.PLATFORM_V); + + shouldShowUpdate = md5FromRequest != null && !md5FromRequest.equals(md5FromInstalledVGame); + } else { + shouldShowUpdate = false; + } + } + if (shouldShowUpdate) { GameUpdateEntity updateEntity = new GameUpdateEntity(); updateEntity.setId(gameEntity.getId()); updateEntity.setName(gameEntity.getName()); updateEntity.setIcon(gameEntity.getIcon()); + updateEntity.setRawIcon(gameEntity.getRawIcon()); + updateEntity.setIconSubscript(gameEntity.getIconSubscript()); updateEntity.setPackageName(apkEntity.getPackageName()); updateEntity.setSize(apkEntity.getSize()); updateEntity.setVersion(apkEntity.getVersion()); @@ -130,6 +155,8 @@ public class PackageUtils { updateEntity.setUrl(apkEntity.getUrl()); updateEntity.setPlatform(apkEntity.getPlatform()); updateEntity.setEtag(apkEntity.getEtag()); + updateEntity.setMd5(apkEntity.getMd5()); + updateEntity.setDownloadStatus(gameEntity.getDownloadStatus()); updateEntity.setBrief(gameEntity.getBrief()); updateEntity.setTagStyle(gameEntity.getTagStyle()); updateEntity.setIndexPlugin(gameEntity.getIndexPlugin()); @@ -472,8 +499,14 @@ public class PackageUtils { * 注意:目测只对能启动的app有效(有桌面图标),对一些没有桌面图标的应用无效(参考应用:魅族游戏框架) */ public static boolean isInstalled(Context context, String packageName) { - Intent intent = context.getApplicationContext().getPackageManager().getLaunchIntentForPackage(packageName); - return intent != null; + try { + Intent intent = context.getApplicationContext().getPackageManager().getLaunchIntentForPackage(packageName); + return intent != null; + } catch (IllegalArgumentException exception) { + // 一些设备调用获取 intent 的时候会触发 Parcel.readException ! + exception.printStackTrace(); + return false; + } } public static boolean isInstalledFromAllPackage(Context context, String packageName) { diff --git a/app/src/main/java/com/gh/common/util/PlatformUtils.java b/app/src/main/java/com/gh/common/util/PlatformUtils.java index 1d5ccc7f4b..b80a7223dd 100644 --- a/app/src/main/java/com/gh/common/util/PlatformUtils.java +++ b/app/src/main/java/com/gh/common/util/PlatformUtils.java @@ -12,6 +12,7 @@ import com.gh.gamecenter.entity.PlatformEntity; import com.gh.gamecenter.eventbus.EBReuse; import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.retrofit.RetrofitManager; +import com.gh.vspace.VHelper; import com.halo.assistant.HaloApp; import com.lightgame.download.FileUtils; @@ -186,6 +187,11 @@ public class PlatformUtils { if ("".equals(platform) || "官方版".equals(platform)) { return "官方版"; } + + if (VHelper.PLATFORM_V.equals(platform)) { + return VHelper.PLATFORM_V; + } + String platformName = platformMap.get(platform); if (TextUtils.isEmpty(platformName)) { getPlatform(); diff --git a/app/src/main/java/com/gh/common/util/PostCommentUtils.java b/app/src/main/java/com/gh/common/util/PostCommentUtils.java index 897bbe4b08..bc55a53b97 100644 --- a/app/src/main/java/com/gh/common/util/PostCommentUtils.java +++ b/app/src/main/java/com/gh/common/util/PostCommentUtils.java @@ -3,6 +3,7 @@ package com.gh.common.util; import android.content.Context; import android.os.Build; import android.text.TextUtils; + import com.gh.gamecenter.R; import com.gh.gamecenter.common.retrofit.JSONObjectResponse; import com.gh.gamecenter.common.retrofit.Response; @@ -10,7 +11,9 @@ import com.gh.gamecenter.entity.CommentEntity; import com.gh.gamecenter.retrofit.RetrofitManager; import com.lightgame.utils.Utils; import com.walkud.rom.checker.RomIdentifier; + import org.json.JSONObject; + import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -70,48 +73,8 @@ public class PostCommentUtils { }); } - public static void addAnswerComment(final String answerId, final String articleId, - final String articleCommunityId, final String content, - final CommentEntity commentEntity, - final PostCommentListener listener) { - RequestBody body = RequestBody.create(MediaType.parse("application/json"), content); - Observable observable; - if (!TextUtils.isEmpty(articleId)) { - if (commentEntity != null) { - observable = RetrofitManager.getInstance().getApi().postReplyToCommunityArticleComment(articleCommunityId, - articleId, commentEntity.getId(), body); - } else { - observable = RetrofitManager.getInstance().getApi().postCommentToCommunityArticle(articleCommunityId, articleId, body); - } - } else { - if (commentEntity != null) { - observable = RetrofitManager.getInstance().getApi().postReplyToAnswerComment(answerId, commentEntity.getId(), body); - } else { - observable = RetrofitManager.getInstance().getApi().postNewCommentToAnswer(answerId, body); - } - } - observable.subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Response() { - @Override - public void onResponse(ResponseBody response) { - if (listener != null) { - listener.postSuccess(new JSONObject());// 不需要返回 - } - } - - @Override - public void onFailure(HttpException e) { - if (listener != null) { - listener.postFailed(e); - } - } - }); - } - public static void likeComment(final String answerId, String articleId, - String articleCommunityId, String videoId, String questionId, final String commentId, @@ -122,7 +85,7 @@ public class PostCommentUtils { if (!TextUtils.isEmpty(answerId)) { observable = RetrofitManager.getInstance().getApi().postVoteAnswerComment(answerId, commentId); } else if (!TextUtils.isEmpty(articleId)) { - observable = RetrofitManager.getInstance().getApi().postVoteCommunityArticleComment(articleCommunityId, articleId, commentId); + observable = RetrofitManager.getInstance().getApi().postVoteCommunityArticleComment(commentId); } else if (!TextUtils.isEmpty(questionId)) { observable = RetrofitManager.getInstance().getApi().postVoteQuestionComment(questionId, commentId); } else { @@ -159,8 +122,8 @@ public class PostCommentUtils { if (!TextUtils.isEmpty(questionId)) { observable = RetrofitManager.getInstance().getApi().postUnVoteQuestionComment(questionId, commentId); - } else if (!TextUtils.isEmpty(articleCommunityId) && !TextUtils.isEmpty(articleId)) { - observable = RetrofitManager.getInstance().getApi().postUnVoteArticleComment(articleCommunityId, articleId, commentId); + } else if (!TextUtils.isEmpty(articleId)) { + observable = RetrofitManager.getInstance().getApi().postUnVoteArticleComment(commentId); } else { observable = RetrofitManager.getInstance().getApi().postUnVoteVideoComment(videoId, commentId); } @@ -250,14 +213,12 @@ public class PostCommentUtils { }); } - public static void reportCommunityArticleComment(final String communityId, - final String articleId, - final String commentId, + public static void reportCommunityArticleComment(final String commentId, final String reportData, final PostCommentListener listener) { RequestBody body = RequestBody.create(MediaType.parse("application/json"), reportData); RetrofitManager.getInstance().getApi() - .postCommunityArticleCommentReport(communityId, articleId, commentId, body) + .postCommunityArticleCommentReport(commentId, body) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Response() { diff --git a/app/src/main/java/com/gh/common/util/RealNameHelper.kt b/app/src/main/java/com/gh/common/util/RealNameHelper.kt index 13239ee8cb..05720cc4aa 100644 --- a/app/src/main/java/com/gh/common/util/RealNameHelper.kt +++ b/app/src/main/java/com/gh/common/util/RealNameHelper.kt @@ -1,15 +1,12 @@ package com.gh.common.util -import android.content.Context import com.gh.gamecenter.core.utils.CurrentActivityHolder import com.gh.download.DownloadManager import com.gh.gamecenter.ShellActivity import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.DialogHelper -import com.gh.gamecenter.core.utils.EmptyCallback import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.common.utils.getMetaExtra -import com.gh.gamecenter.entity.GameEntity import com.lightgame.download.DownloadEntity import com.lightgame.download.DownloadStatus @@ -17,11 +14,6 @@ object RealNameHelper { var pendingInstallPkgPath = "" - @JvmStatic - fun checkIfAuth(context: Context, gameEntity: GameEntity, callback: EmptyCallback) { - callback.onCallback() - } - /** * 弹未成年人不能下载游戏弹窗 */ diff --git a/app/src/main/java/com/gh/common/view/DownloadProgressBar.java b/app/src/main/java/com/gh/common/view/DownloadProgressBar.java index 4acc1a178f..e9481040b0 100644 --- a/app/src/main/java/com/gh/common/view/DownloadProgressBar.java +++ b/app/src/main/java/com/gh/common/view/DownloadProgressBar.java @@ -41,6 +41,7 @@ public class DownloadProgressBar extends ProgressBar { H5_GAME, UPDATING, TEENAGER_MODEL, + SPECIAL_DOWNLOAD, XAPK_UNZIPPING, XAPK_SUCCESS, @@ -231,6 +232,7 @@ public class DownloadProgressBar extends ProgressBar { mDefaultColor = ContextCompat.getColor(getContext(), R.color.white); break; case TEENAGER_MODEL: + case SPECIAL_DOWNLOAD: setProgressDrawable(getResources().getDrawable(R.drawable.download_button_normal_style)); mDefaultColor = ContextCompat.getColor(getContext(), R.color.white); break; diff --git a/app/src/main/java/com/gh/common/view/GameIconView.kt b/app/src/main/java/com/gh/common/view/GameIconView.kt index 459a69984b..75de6c5d99 100644 --- a/app/src/main/java/com/gh/common/view/GameIconView.kt +++ b/app/src/main/java/com/gh/common/view/GameIconView.kt @@ -10,12 +10,8 @@ import androidx.cardview.widget.CardView import com.facebook.drawee.generic.RoundingParams import com.facebook.drawee.view.SimpleDraweeView import com.gh.gamecenter.R -import com.gh.gamecenter.common.utils.dip2px -import com.gh.gamecenter.common.utils.display -import com.gh.gamecenter.common.utils.goneIf -import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.common.utils.ImageUtils import com.gh.gamecenter.entity.GameEntity import splitties.views.dsl.core.add import splitties.views.dsl.core.lParams @@ -52,11 +48,11 @@ class GameIconView : CardView { fun initView(attrs: AttributeSet?) { val gameIconUi = GameIconUi(context) - if (attrs != null) { + if (attrs != null && !isInEditMode) { val ta = context.obtainStyledAttributes(attrs, R.styleable.GameIconView) mCornerRadius = ta.getDimensionPixelSize( R.styleable.GameIconView_gameIconCornerRadius, - DisplayUtils.dip2px(0F) + DisplayUtils.dip2px(context, 0F) ) mBorderColor = ta.getColor(R.styleable.GameIconView_gameIconBorderColor, 0) mGameIconOverlayColor = ta.getColor(R.styleable.GameIconView_gameIconOverlayColor, 0) @@ -115,7 +111,12 @@ class GameIconView : CardView { override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) - val cornerRadius = getCornerRadius(w) + val cornerRadius = if (!isInEditMode) { + getCornerRadius(w) + } else { + 0F + } + radius = cornerRadius cardElevation = 0F diff --git a/app/src/main/java/com/gh/common/view/SimpleToggleView.kt b/app/src/main/java/com/gh/common/view/SimpleToggleView.kt new file mode 100644 index 0000000000..68474e6ec3 --- /dev/null +++ b/app/src/main/java/com/gh/common/view/SimpleToggleView.kt @@ -0,0 +1,26 @@ +package com.gh.common.view + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import com.airbnb.lottie.LottieAnimationView +import com.gh.gamecenter.R +import com.lightgame.view.CheckableImageView + +class SimpleToggleView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr) { + + val switchIv by lazy { findViewById(R.id.switchIv) } + val hintTv by lazy { findViewById(R.id.hintTv) } + val lottieView by lazy { findViewById(R.id.lottieView)} + + init { + View.inflate(context, R.layout.view_simple_toggle, this) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/download/DownloadDataHelper.kt b/app/src/main/java/com/gh/download/DownloadDataHelper.kt index 4e1a6b2649..5624883246 100644 --- a/app/src/main/java/com/gh/download/DownloadDataHelper.kt +++ b/app/src/main/java/com/gh/download/DownloadDataHelper.kt @@ -88,6 +88,8 @@ object DownloadDataHelper { "未成年" } else if (status == DownloadStatus.unavailable) { "未接入防沉迷系统,暂不支持下载" + }else if (status == DownloadStatus.banned) { + "网络异常" } else if (status == DownloadStatus.redirected) { "重定向至最终地址" } else { diff --git a/app/src/main/java/com/gh/download/DownloadManager.java b/app/src/main/java/com/gh/download/DownloadManager.java index e4e9fdb7a8..5435c8a09d 100644 --- a/app/src/main/java/com/gh/download/DownloadManager.java +++ b/app/src/main/java/com/gh/download/DownloadManager.java @@ -12,6 +12,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import androidx.collection.ArrayMap; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.core.AppExecutor; @@ -67,7 +69,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; public class DownloadManager implements DownloadStatusListener { @@ -81,20 +85,17 @@ public class DownloadManager implements DownloadStatusListener { private final ArrayMap lastTimeMap; private final ArrayMap> platformMap; - private final ArrayMap> gameMap; + private final Map> gameMap; private final ArrayMap statusMap; private final ArrayMap downloadingMap; - // 下载任务列表快照,非完全实时状态,只保证数量和基础状态,不保证下载进度和速度匹配 - // TODO 使用 mDownloadSnapshotList 来服务 getDownloadEntityByUrl - // private final List mDownloadSnapshotList; - private ArrayList mInvisiblePendingTaskList; // 用户不可见的 pending 任务 - private final DownloadDao mDownloadDao; private final DownloadedGameIdAndPackageNameDao mDownloadedGameIdAndPackageNameDao; + private final MutableLiveData> mDownloadEntityListLiveData; // 下载任务变更 (主要还是数量变更) 的 LiveData + private final Set mUpdateMarks; @Override @@ -107,6 +108,8 @@ public class DownloadManager implements DownloadStatusListener { DownloadManager.getInstance().putStatus(entity.getUrl(), DownloadStatus.delete); downloadingMap.remove(entity.getUrl()); + + notifyDownloadLiveDataChanged(); } @Override @@ -117,6 +120,8 @@ public class DownloadManager implements DownloadStatusListener { downloadingMap.put(entity.getUrl(), entity); DownloadWorkManager.addWorker(); + + notifyDownloadLiveDataChanged(); } @Override @@ -125,6 +130,8 @@ public class DownloadManager implements DownloadStatusListener { if (entity.getStatus() != DownloadStatus.overflow) { downloadingMap.remove(entity.getUrl()); } + + notifyDownloadLiveDataChanged(); } @Override @@ -142,6 +149,8 @@ public class DownloadManager implements DownloadStatusListener { if (downloadingMap.isEmpty()) { DownloadWorkManager.cancelWorker(); } + + notifyDownloadLiveDataChanged(); } @Override @@ -153,6 +162,7 @@ public class DownloadManager implements DownloadStatusListener { mContext = HaloApp.getInstance().getApplicationContext(); mDownloadDao = DownloadDao.getInstance(mContext); mDownloadedGameIdAndPackageNameDao = new DownloadedGameIdAndPackageNameDao(); + mDownloadEntityListLiveData = new MutableLiveData<>(); mUpdateMarks = SPUtils.getStringSet(UPDATE_IS_READ_MARK); @@ -165,7 +175,7 @@ public class DownloadManager implements DownloadStatusListener { lastTimeMap = new ArrayMap<>(); platformMap = new ArrayMap<>(); - gameMap = new ArrayMap<>(); + gameMap = new ConcurrentHashMap<>(); statusMap = new ArrayMap<>(); downloadingMap = new ArrayMap<>(); // mDownloadSnapshotList = new ArrayList<>(); @@ -309,6 +319,7 @@ public class DownloadManager implements DownloadStatusListener { downloadEntity.setEntrance(entrance); downloadEntity.setLocation(location); downloadEntity.setVersionName(apkEntity.getVersion()); + ExtensionsKt.addMetaExtra(downloadEntity, Constants.APK_MD5, apkEntity.getMd5()); ExtensionsKt.addMetaExtra(downloadEntity, Constants.DOWNLOAD_ID, downloadId); ExtensionsKt.addMetaExtra(downloadEntity, Constants.RAW_GAME_ICON, gameEntity.getRawIcon()); ExtensionsKt.addMetaExtra(downloadEntity, Constants.GAME_ICON_SUBSCRIPT, gameEntity.getIconSubscript()); @@ -319,6 +330,12 @@ public class DownloadManager implements DownloadStatusListener { ExtensionsKt.addMetaExtra(downloadEntity, Constants.SIMULATOR, GsonUtils.toJson(gameEntity.getSimulator())); } + if (gameEntity.isVGame()) { + ExtensionsKt.addMetaExtra(downloadEntity, Constants.SMOOTH_GAME, "true"); + ExtensionsKt.addMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE, Constants.SMOOTH_GAME); + ExtensionsKt.addMetaExtra(downloadEntity, DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200"); + } + HashMap map = PageSwitchDataHelper.popLastPageData(); if (map != null && map.containsKey(PageSwitchDataHelper.PAGE_GAME_DETAIL_RECOMMEND)) { ExtensionsKt.addMetaExtra(downloadEntity, PageSwitchDataHelper.PAGE_GAME_DETAIL_RECOMMEND, "true"); @@ -342,7 +359,7 @@ public class DownloadManager implements DownloadStatusListener { downloadEntity.setPlugin(!TextUtils.isEmpty(apkEntity.getGhVersion())); - ExposureUtils.DownloadType downloadType = ExposureUtils.getDownloadType(apkEntity, gameEntity.getId()); + ExposureUtils.DownloadType downloadType = ExposureUtils.getDownloadType(apkEntity, gameEntity.getId(), gameEntity.isVGame()); gameEntity.setIsPlatformRecommend(apkEntity.getRecommend() != null); ExposureEvent downloadExposureEvent = ExposureUtils.logADownloadExposureEvent(gameEntity, apkEntity.getPlatform(), traceEvent, downloadType); @@ -531,15 +548,61 @@ public class DownloadManager implements DownloadStatusListener { } /** - * 根据 url 获取下载任务 (仅保证下载状态一致) + * 获取所有的下载任务快照 + * + * @return 下载任务快照列表 + */ + @NonNull + private ArrayList getAllDownloadEntitySnapshots() { + return mDownloadDao.getAllSnapshots(); + } + + /** + * 获取快照 + * + * @param gameEntity 游戏实体 + */ + @Nullable + public DownloadEntity getDownloadEntitySnapshot(GameEntity gameEntity) { + if (gameEntity == null || gameEntity.getApk().size() == 0) return null; + return getDownloadEntitySnapshot(gameEntity.getApk().get(0).getUrl(), gameEntity.getId(), gameEntity.isVGame()); + } + + /** + * 获取快照 + * + * 畅玩游戏优先根据游戏 ID 获取,非畅玩游戏根据 url 获取 * * @param url 下载链接 + * @param isVGame 是不是畅玩游戏 + * @param gameId 游戏 ID * @return null表示下载列表中不存在该任务,否则返回下载任务 */ @Nullable - public DownloadEntity getDownloadEntitySnapshotByUrl(String url) { - if (TextUtils.isEmpty(url)) return null; - return mDownloadDao.getSnapshot(url); + private DownloadEntity getDownloadEntitySnapshot(String url, String gameId, boolean isVGame) { + DownloadEntity snapshot = null; + + if (isVGame && !TextUtils.isEmpty(gameId)) { + snapshot = mDownloadDao.getSnapshotByGameId(gameId); + } + + if (snapshot == null && !TextUtils.isEmpty(url)) { + snapshot = mDownloadDao.getSnapshot(url); + } + + return snapshot; + } + + /** + * 根据 url 获取下载任务快照 (仅保证下载状态一致) + * + * @param packageName 包名 (多包名一样时取第一个,若使用场景里有多包名,请使用 url 获取下载任务) + * @return null 表示下载列表中不存在该任务,否则返回下载任务 + */ + @Nullable + public DownloadEntity getDownloadEntitySnapshotByPackageName(String packageName) { + if (TextUtils.isEmpty(packageName)) return null; + return mDownloadDao.getSnapshotByPackageName(packageName); } /** @@ -587,8 +650,8 @@ public class DownloadManager implements DownloadStatusListener { public void initGameMap() { gameMap.clear(); - List list = getAllDownloadEntity(); - if (list != null && list.size() != 0) { + List list = getAllDownloadEntitySnapshots(); + if (list.size() != 0) { String name; for (DownloadEntity downloadEntity : list) { name = downloadEntity.getName(); @@ -623,13 +686,30 @@ public class DownloadManager implements DownloadStatusListener { } /** - * 获取所有下载列表中的任务排除静默更新 + * 获取下载列表中除静默下载、模拟器下载、畅玩下载以外的任务 */ - public List getAllDownloadEntityExcludeSilentUpdate() { + public List getAllDownloadEntityExcludeSilentTask() { List all = getAllDownloadEntity(); return filterSilentDownloadTask(all); } + /** + * 获取下载列表中的畅玩下载任务快照 + */ + public ArrayList getAllVDownloadTaskSnapshots() { + List downloadList = getAllDownloadEntitySnapshots(); + + ArrayList filteredDownloadEntityList = new ArrayList<>(); + + for (DownloadEntity downloadEntity : downloadList) { + if (Constants.SMOOTH_GAME.equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE))) { + filteredDownloadEntityList.add(downloadEntity); + } + } + + return filteredDownloadEntityList; + } + private ArrayList filterSilentDownloadTask(List downloadEntityList) { ArrayList filteredDownloadEntityList = new ArrayList<>(); @@ -637,9 +717,10 @@ public class DownloadManager implements DownloadStatusListener { for (DownloadEntity downloadEntity : downloadEntityList) { if (!ExtensionsKt.isSimulatorGame(downloadEntity)) { - if (!Constants.SILENT_UPDATE.equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE)) && - !Constants.SIMULATOR_DOWNLOAD.equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE))) { - + if (!Constants.SILENT_UPDATE.equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE)) + && !Constants.SIMULATOR_DOWNLOAD.equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE)) + && !Constants.SMOOTH_GAME.equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE)) + ) { filteredDownloadEntityList.add(downloadEntity); } } else { @@ -647,7 +728,6 @@ public class DownloadManager implements DownloadStatusListener { filteredDownloadEntityList.add(downloadEntity); } } - } return filteredDownloadEntityList; @@ -677,7 +757,7 @@ public class DownloadManager implements DownloadStatusListener { * 根据url取消下载,并删除已下载的文件 */ public void cancel(String url) { - cancel(url, true, false); + cancel(url, true, false, false); } /** @@ -685,8 +765,8 @@ public class DownloadManager implements DownloadStatusListener { * * @param automatic 是否是安装完自动删除 */ - public void cancel(String url, boolean isDeleteFile, boolean automatic) { - DownloadEntity entry = mDownloadDao.get(url); + public void cancel(String url, boolean isDeleteFile, boolean automatic, boolean cancelSilently) { + DownloadEntity entry = mDownloadDao.getSnapshot(url); if (entry != null) { AppExecutor.getIoExecutor().execute(() -> { mDownloadDao.delete(url); @@ -695,54 +775,48 @@ public class DownloadManager implements DownloadStatusListener { FileUtils.deleteFile(entry.getPath()); } Utils.log(DownloadManager.class.getSimpleName(), "cancel==>record were deleted!"); + + if (automatic) { + entry.getMeta().put(DownloadDataHelper.DOWNLOAD_CANCEL_WAY, DownloadDataHelper.DOWNLOAD_CANCEL_AUTO); + } else { + entry.getMeta().put(DownloadDataHelper.DOWNLOAD_CANCEL_WAY, DownloadDataHelper.DOWNLOAD_CANCEL_MANUAL); + } + entry.setStatus(DownloadStatus.cancel); + + initGameMap(); + + if (cancelSilently) { + cancelAndNotify(entry, true); + return; + } + + // 将原来安装完成后在 downloadService 完成的功能放到这里,避免因为 ANR 造成闪退 + AppExecutor.getUiExecutor().executeWithDelay(() -> { + cancelAndNotify(entry, false); + }, 0); }); } - if (entry != null) { - if (automatic) { - entry.getMeta().put(DownloadDataHelper.DOWNLOAD_CANCEL_WAY, DownloadDataHelper.DOWNLOAD_CANCEL_AUTO); - } else { - entry.getMeta().put(DownloadDataHelper.DOWNLOAD_CANCEL_WAY, DownloadDataHelper.DOWNLOAD_CANCEL_MANUAL); - } - entry.setStatus(DownloadStatus.cancel); + } - // 将原来安装完成后在 downloadService 完成的功能放到这里,避免因为 ANR 造成闪退 - AppExecutor.getUiExecutor().executeWithDelay(() -> { - mDownloadDao.removeErrorMessage(entry.getUrl()); + private void cancelAndNotify(DownloadEntity entry, boolean cancelSilently) { + mDownloadDao.removeErrorMessage(entry.getUrl()); - DownloadTask task = DataChanger.INSTANCE.getDownloadingTasks().get(entry.getUrl()); - if (task != null) { - task.cancel(); - // 改任务队列的状态 - DataChanger.INSTANCE.getDownloadingTasks().remove(entry.getUrl()); - DataChanger.INSTANCE.notifyDataChanged(entry); - } - DataChanger.INSTANCE.getDownloadEntries().remove(entry.getUrl()); + DownloadTask task = DataChanger.INSTANCE.getDownloadingTasks().get(entry.getUrl()); + if (task != null) { + task.cancel(); + // 改任务队列的状态 + DataChanger.INSTANCE.getDownloadingTasks().remove(entry.getUrl()); + if (!cancelSilently) { DataChanger.INSTANCE.notifyDataChanged(entry); - DownloadStatusManager.getInstance().onTaskCancelled(entry); - - Utils.log(DownloadManager.class.getSimpleName(), "cancel"); - }, 0); + } } - } - - /** - * 取消并删除所有下载任务(包括下载中、等待、暂停状态的任务) - */ - public void cancelAll() { - for (DownloadEntity entry : DataChanger.INSTANCE.getDownloadEntries().values()) { - cancel(entry.getUrl(), true, false); + DataChanger.INSTANCE.getDownloadEntries().remove(entry.getUrl()); + if (!cancelSilently) { + DataChanger.INSTANCE.notifyDataChanged(entry); + DownloadStatusManager.getInstance().onTaskCancelled(entry); } - Utils.log(DownloadManager.class.getSimpleName(), "cancel all"); - } - /** - * 开始所有下载任务 - */ - public void startAll() { - for (DownloadEntity entry : downloadingMap.values()) { - add(entry); - } - Utils.log(DownloadManager.class.getSimpleName(), "start all"); + Utils.log(DownloadManager.class.getSimpleName(), "cancel"); } /** @@ -797,6 +871,8 @@ public class DownloadManager implements DownloadStatusListener { public void addObserver(DataWatcher dataWatcher) { Utils.log(DownloadManager.class.getSimpleName(), "addObserver"); DataChanger.INSTANCE.addObserver(dataWatcher); + + notifyDownloadedStatusASAP(dataWatcher); } /** @@ -807,6 +883,18 @@ public class DownloadManager implements DownloadStatusListener { DataChanger.INSTANCE.deleteObserver(dataWatcher); } + /** + * 立马通知 dataWatcher 更新已下载完的任务状态,这里的下载完成是持久状态,不是瞬时状态 + * + */ + private void notifyDownloadedStatusASAP(DataWatcher dataWatcher) { + for (DownloadEntity downloadEntity : getAllDownloadEntitySnapshots()) { + if (downloadEntity.getStatus() == DownloadStatus.done) { + dataWatcher.onDataInit(downloadEntity); + } + } + } + /** * 初始化下载服务 */ @@ -868,7 +956,7 @@ public class DownloadManager implements DownloadStatusListener { public void checkAndRetryDownload() { if (!NetworkUtils.isWifiConnected(mContext)) return; - for (DownloadEntity downloadEntity : DownloadManager.getInstance().getAllDownloadEntityExcludeSilentUpdate()) { + for (DownloadEntity downloadEntity : DownloadManager.getInstance().getAllDownloadEntityExcludeSilentTask()) { if (DownloadStatus.neterror.equals(downloadEntity.getStatus()) || DownloadStatus.timeout.equals(downloadEntity.getStatus()) || DownloadStatus.subscribe.equals(downloadEntity.getStatus())) { @@ -894,7 +982,7 @@ public class DownloadManager implements DownloadStatusListener { boolean showRedPoint = false; int downloadingSize = 0; - for (DownloadEntity downloadEntity : getAllDownloadEntityExcludeSilentUpdate()) { + for (DownloadEntity downloadEntity : getAllDownloadEntityExcludeSilentTask()) { if (DownloadStatus.done.equals(downloadEntity.getStatus())) { String mark = downloadEntity.getMeta().get(DOWNLOADED_IS_READ_MARK); if (TextUtils.isEmpty(mark)) showRedPoint = true; @@ -961,7 +1049,7 @@ public class DownloadManager implements DownloadStatusListener { * @return 是否存在未读的下载完成任务 */ public boolean isContainsUnreadDownloadedTask() { - for (DownloadEntity downloadEntity : getAllDownloadEntityExcludeSilentUpdate()) { + for (DownloadEntity downloadEntity : getAllDownloadEntityExcludeSilentTask()) { if (DownloadStatus.done.equals(downloadEntity.getStatus())) { String mark = downloadEntity.getMeta().get(DOWNLOADED_IS_READ_MARK); if (TextUtils.isEmpty(mark)) { @@ -979,7 +1067,7 @@ public class DownloadManager implements DownloadStatusListener { AppExecutor.getIoExecutor().execute(() -> { boolean markHasChanged = false; - List all = getAllDownloadEntityExcludeSilentUpdate(); + List all = getAllDownloadEntityExcludeSilentTask(); for (DownloadEntity downloadEntity : all) { DownloadStatus status = downloadEntity.getStatus(); if (status == DownloadStatus.done) { @@ -1019,7 +1107,7 @@ public class DownloadManager implements DownloadStatusListener { AppExecutor.getIoExecutor().execute(() -> { boolean markHasChanged = false; - List all = getAllDownloadEntityExcludeSilentUpdate(); + List all = getAllDownloadEntityExcludeSilentTask(); for (DownloadEntity downloadEntity : all) { if (downloadEntity.getStatus() != DownloadStatus.done) { if (isRead) { @@ -1134,6 +1222,17 @@ public class DownloadManager implements DownloadStatusListener { mInvisiblePendingTaskList.clear(); } + public LiveData> getDownloadEntityLiveData() { + return mDownloadEntityListLiveData; + } + + /** + * 手动触发下载 LiveData 变更 + */ + public void notifyDownloadLiveDataChanged() { + AppExecutor.getIoExecutor().execute(() -> mDownloadEntityListLiveData.postValue(getAllDownloadEntity())); + } + /** * 更新下载请求头的相关信息 */ diff --git a/app/src/main/java/com/gh/download/PackageObserver.kt b/app/src/main/java/com/gh/download/PackageObserver.kt index cbed3f39c2..8c2c1a29b2 100644 --- a/app/src/main/java/com/gh/download/PackageObserver.kt +++ b/app/src/main/java/com/gh/download/PackageObserver.kt @@ -3,11 +3,17 @@ package com.gh.download import android.annotation.SuppressLint import android.preference.PreferenceManager import android.text.TextUtils +import com.gh.common.util.ConcernUtils +import com.gh.common.util.DataCollectionUtils +import com.gh.common.util.PackageInstaller +import com.gh.common.util.PackageUtils +import com.gh.download.server.BrowserInstallHelper import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.loghub.LoghubUtils +import com.gh.gamecenter.common.retrofit.EmptyResponse +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.common.utils.isVGame import com.gh.gamecenter.core.runOnIoThread -import com.gh.common.util.* -import com.gh.download.server.BrowserInstallHelper import com.gh.gamecenter.core.utils.ThirdPartyPackageHelper import com.gh.gamecenter.core.utils.UrlFilterUtils import com.gh.gamecenter.entity.GameDigestEntity @@ -15,12 +21,11 @@ import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.packagehelper.PackageRepository import com.gh.gamecenter.packagehelper.PackageViewModel -import com.gh.gamecenter.common.retrofit.EmptyResponse -import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.gh.gamecenter.setting.GameDownloadSettingFragment.Companion.CONCERN_GAME_SP_KEY import com.halo.assistant.HaloApp import com.lightgame.download.DownloadEntity +import com.lightgame.download.FileUtils import com.lightgame.utils.Utils import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -34,11 +39,23 @@ object PackageObserver { private val mPackageViewModel: PackageViewModel by lazy { PackageViewModel(HaloApp.getInstance().application, PackageRepository) } + private val mPackageChangeListenerList: ArrayList = arrayListOf() + + fun registerPackageChangeChangeListener(listener: PackageChangeListener) { + mPackageChangeListenerList.add(listener) + } + + fun unregisterPackageChangeChangeListener(listener: PackageChangeListener) { + mPackageChangeListenerList.remove(listener) + } + @JvmStatic fun onPackageChanged(busFour: EBPackage) { val application = HaloApp.getInstance().application val packageName = busFour.packageName val versionName = busFour.versionName + val preciseGameId = busFour.gameId + var gameId = "" var mDownloadEntity: DownloadEntity? = null val sp = PreferenceManager.getDefaultSharedPreferences(application) @@ -47,7 +64,8 @@ object PackageObserver { var foundMatchedGame = false // 是否找到準確的遊戲 (有可能出現同包名同版本的情況) for (downloadEntity in DownloadManager.getInstance().allDownloadEntity) { - if (packageName == downloadEntity.packageName) { + if (packageName == downloadEntity.packageName + && (preciseGameId == null || preciseGameId == downloadEntity.gameId)) { mDownloadEntity = downloadEntity gameId = mDownloadEntity.gameId if (TextUtils.isEmpty(busFour.versionName)) { @@ -84,13 +102,17 @@ object PackageObserver { if (gh_id == null) { ThirdPartyPackageHelper.saveGameId(mDownloadEntity.packageName, mDownloadEntity.gameId) } - DownloadManager.getInstance().cancel( - mDownloadEntity.url, false, true) // 默认不删除安装包 mSp.getBoolean("autodelete", true) - } - if (sp.getBoolean(CONCERN_GAME_SP_KEY, true)) { //设置页面控制是否安装后自动关注 - // 安装后关注游戏 - val finalDownloadEntity = mDownloadEntity - RetrofitManager.getInstance().api + if (mDownloadEntity.isVGame()) { + // 畅玩游戏安装完成的同时直接删除文件 + runOnIoThread { FileUtils.deleteFile(mDownloadEntity.path) } + } + + DownloadManager.getInstance().cancel(mDownloadEntity.url, false, true, false) + + if (sp.getBoolean(CONCERN_GAME_SP_KEY, true)) { //设置页面控制是否安装后自动关注 + // 安装后关注游戏 + val finalDownloadEntity = mDownloadEntity + RetrofitManager.getInstance().api .getGameDigestByPackageName(UrlFilterUtils.getFilterQuery("package", packageName)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -98,13 +120,14 @@ object PackageObserver { override fun onResponse(response: List?) { for (gameDigestEntity in response!!) { if (!TextUtils.isEmpty(gameDigestEntity?.id)) { // 关注游戏 - if (finalDownloadEntity != null && gameDigestEntity?.id == finalDownloadEntity.gameId) { + if (gameDigestEntity?.id == finalDownloadEntity.gameId) { ConcernUtils.postConcernGameId(application, gameDigestEntity?.id ?: "", null, false) } } } } }) + } } runOnIoThread { postNewlyInstalledApp(gameId, packageName) } @@ -113,6 +136,8 @@ object PackageObserver { if ("卸载" == busFour.type) { mPackageViewModel.addUninstalledGame(packageName) mDownloadEntity?.let { + if (it.isVGame()) return@let + if (it.isPluggable || it.isUpdate) { PackageInstaller.install(application, mDownloadEntity) } @@ -121,6 +146,11 @@ object PackageObserver { // 更新已安装游戏 deleteInstalledPackage(packageName) } + + for (packageChangeListener in mPackageChangeListenerList) { + packageChangeListener.onChanged(busFour) + } + DataCollectionUtils.uploadInorunstall(application, busFour.type, busFour.packageName) } @@ -181,6 +211,9 @@ object PackageObserver { e.printStackTrace() } LoghubUtils.log(wrapperObject, "halo-api-device-installed", true) + } + fun interface PackageChangeListener { + fun onChanged(data:EBPackage) } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt b/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt index 8ddb08ae09..cf2ec826d9 100644 --- a/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt +++ b/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt @@ -125,11 +125,11 @@ class DownloadDialogAdapter( rightLink.text = if (links?.size ?: 0 > 1) links?.get(1)?.title else "" leftLink.background = GradientDrawable().apply { cornerRadius = 8F.dip2px().toFloat() - setStroke(0.5F.dip2px(), R.color.divider.toColor()) + setStroke(0.5F.dip2px(), R.color.divider.toColor(mContext)) } rightLink.background = GradientDrawable().apply { cornerRadius = 8F.dip2px().toFloat() - setStroke(0.5F.dip2px(), R.color.divider.toColor()) + setStroke(0.5F.dip2px(), R.color.divider.toColor(mContext)) } } } diff --git a/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt b/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt index 6b75fae923..af829676c0 100644 --- a/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt +++ b/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt @@ -6,23 +6,26 @@ import android.widget.RelativeLayout import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView -import com.gh.gamecenter.common.base.activity.BaseActivity -import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.common.constant.Config import com.gh.common.dialog.CertificationDialog import com.gh.common.dialog.DeviceRemindDialog import com.gh.common.dialog.PackageCheckDialogFragment import com.gh.common.exposure.ExposureEvent -import com.gh.common.util.* import com.gh.common.util.DialogUtils import com.gh.common.util.DirectUtils.directToLinkPage +import com.gh.common.util.DownloadDialogHelper +import com.gh.common.util.PackageInstaller +import com.gh.common.util.PackageUtils import com.gh.download.DownloadManager import com.gh.download.server.BrowserInstallHelper import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.base.activity.BaseActivity import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.core.utils.EmptyCallback +import com.gh.gamecenter.core.utils.SpeedUtils import com.gh.gamecenter.databinding.DownloadDialogItemBinding import com.gh.gamecenter.entity.ApkEntity import com.gh.gamecenter.entity.GameCollectionEntity @@ -211,8 +214,11 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas } else 8f.dip2px() binding.root.layoutParams = this } - if (apkEntity.recommend != null) { + val downloadEntity = DownloadManager.getInstance().getDownloadEntityByUrl(apkEntity.url) + if (apkEntity.recommend != null && downloadEntity == null) { binding.containerView.background = ContextCompat.getDrawable(binding.root.context, R.drawable.bg_download_dialog_item_recommend) + } else { + binding.containerView.background = ContextCompat.getDrawable(binding.root.context, R.drawable.download_dialog_item_background) } } @@ -351,7 +357,7 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas // todo 有时间存储判断统一处理 val msg = FileUtils.isCanDownload(context, apkEntity.size) if (msg.isNullOrEmpty()) { - BrowserInstallHelper.showBrowserInstallHintDialog(context, object : EmptyCallback { + BrowserInstallHelper.showBrowserInstallHintDialog(context, gameEntity.isVGame(), object : EmptyCallback { override fun onCallback() { DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apkEntity, object : EmptyCallback { override fun onCallback() { diff --git a/app/src/main/java/com/gh/download/server/BrowserInstallHelper.kt b/app/src/main/java/com/gh/download/server/BrowserInstallHelper.kt index c5993f66bd..2415efd587 100644 --- a/app/src/main/java/com/gh/download/server/BrowserInstallHelper.kt +++ b/app/src/main/java/com/gh/download/server/BrowserInstallHelper.kt @@ -123,8 +123,8 @@ object BrowserInstallHelper { } @JvmStatic - fun showBrowserInstallHintDialog(context: Context, callback: EmptyCallback) { - if (!shouldShowUseBrowserToInstallHint()) { + fun showBrowserInstallHintDialog(context: Context, skipBrowserInstallDialog: Boolean = false, callback: EmptyCallback) { + if (skipBrowserInstallDialog || !shouldShowUseBrowserToInstallHint()) { callback.onCallback() return } diff --git a/app/src/main/java/com/gh/gamecenter/DownloadManagerActivity.java b/app/src/main/java/com/gh/gamecenter/DownloadManagerActivity.java index e804703ad3..aa82ffe434 100644 --- a/app/src/main/java/com/gh/gamecenter/DownloadManagerActivity.java +++ b/app/src/main/java/com/gh/gamecenter/DownloadManagerActivity.java @@ -3,12 +3,16 @@ package com.gh.gamecenter; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.view.MenuItem; +import com.gh.common.util.DirectUtils; +import com.gh.common.util.NewFlatLogUtils; import com.gh.gamecenter.common.base.activity.ToolBarActivity; import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout; import com.gh.gamecenter.common.constant.EntranceConsts; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.download.DownloadFragment; +import com.gh.vspace.VHelper; /** * 下载更新管理页面 @@ -26,6 +30,9 @@ public class DownloadManagerActivity extends ToolBarActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ExtensionsKt.updateStatusBarColor(this, R.color.background_white, R.color.background_white); + if (VHelper.isVGameOn()) { + setToolbarMenu(R.menu.menu_download_manager); + } } @Override @@ -57,6 +64,13 @@ public class DownloadManagerActivity extends ToolBarActivity { return getTargetIntent(context, DownloadManagerActivity.class, DownloadFragment.class, bundle); } + @Override + public boolean onMenuItemClick(MenuItem item) { + NewFlatLogUtils.logHaloFunManageShow("下载管理"); + DirectUtils.directToVGameDownload(this, false); + return true; + } + @Override protected void onNightModeChange() { super.onNightModeChange(); diff --git a/app/src/main/java/com/gh/gamecenter/GameDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/GameDetailActivity.kt index 7c11a0424c..5767fc9c48 100644 --- a/app/src/main/java/com/gh/gamecenter/GameDetailActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/GameDetailActivity.kt @@ -80,7 +80,7 @@ class GameDetailActivity : DownloadToolbarActivity() { override fun isAutoResetViewBackgroundEnabled(): Boolean = true override fun updateStaticViewBackground(view: View?) { - updateStaticView(view, listOf(R.id.menu_download_iv, R.id.gameBigEvent)) + updateStaticView(view, listOf(R.id.menu_download_iv, R.id.gameBigEvent, R.id.cardContainer)) } companion object { diff --git a/app/src/main/java/com/gh/gamecenter/ImageViewerActivity.kt b/app/src/main/java/com/gh/gamecenter/ImageViewerActivity.kt index e01afdec38..b14def2324 100644 --- a/app/src/main/java/com/gh/gamecenter/ImageViewerActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/ImageViewerActivity.kt @@ -368,7 +368,7 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener { if (mUseEnterAndExitAnimation) { mBigImageView?.translationX = mTranslationX mBigImageView?.translationY = mTranslationY - if (mScaleX < Float.MAX_VALUE) { + if (!mScaleX.isNaN() && mScaleX < Float.MAX_VALUE) { mBigImageView?.scaleX = mScaleX mBigImageView?.scaleY = mScaleY } @@ -439,10 +439,18 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener { addUpdateListener { va -> mBigImageView?.y = (va.animatedValue as Float) } } val scaleYAnimator = ValueAnimator.ofFloat(1F, mScaleY).apply { - addUpdateListener { va -> mBigImageView?.scaleY = (va.animatedValue as Float) } + addUpdateListener { va -> + if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) { + mBigImageView?.scaleY = (va.animatedValue as Float) + } + } } val scaleXAnimator = ValueAnimator.ofFloat(1F, mScaleX).apply { - addUpdateListener { va -> mBigImageView?.scaleX = (va.animatedValue as Float) } + addUpdateListener { va -> + if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) { + mBigImageView?.scaleX = (va.animatedValue as Float) + } + } } val backgroundAlphaAnimation = ValueAnimator.ofFloat(1F, 0F).apply { addUpdateListener { va -> mBackgroundView.alpha = (va.animatedValue as Float) } @@ -591,10 +599,18 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener { addUpdateListener { va -> mBigImageView?.y = (va.animatedValue as Float) } } val scaleYAnimator = ValueAnimator.ofFloat(mScaleY, 1F).apply { - addUpdateListener { va -> mBigImageView?.scaleY = (va.animatedValue as Float) } + addUpdateListener { va -> + if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) { + mBigImageView?.scaleY = (va.animatedValue as Float) + } + } } val scaleXAnimator = ValueAnimator.ofFloat(mScaleX, 1F).apply { - addUpdateListener { va -> mBigImageView?.scaleX = (va.animatedValue as Float) } + addUpdateListener { va -> + if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) { + mBigImageView?.scaleX = (va.animatedValue as Float) + } + } } val backgroundAlphaAnimator = ValueAnimator.ofFloat(0F, 1F).apply { addUpdateListener { va -> mBackgroundView.alpha = (va.animatedValue as Float) } @@ -625,8 +641,10 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener { val scaleAnimator = ValueAnimator.ofFloat(scale, finalScale).apply { addUpdateListener { va -> - view.scaleX = (va.animatedValue as Float) - view.scaleY = (va.animatedValue as Float) + if (va.animatedValue is Float && !(va.animatedValue as Float).isNaN() && (va.animatedValue as Float) < Float.MAX_VALUE) { + view.scaleX = (va.animatedValue as Float) + view.scaleY = (va.animatedValue as Float) + } } } val translateXAnimator = ValueAnimator.ofFloat(view.x, finalTranslationX).apply { diff --git a/app/src/main/java/com/gh/gamecenter/LibaoDetailActivity.java b/app/src/main/java/com/gh/gamecenter/LibaoDetailActivity.java index 12650cb58e..6ed2dd08f5 100644 --- a/app/src/main/java/com/gh/gamecenter/LibaoDetailActivity.java +++ b/app/src/main/java/com/gh/gamecenter/LibaoDetailActivity.java @@ -142,6 +142,11 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA } } } + + @Override + public void onDataInit(@NonNull DownloadEntity downloadEntity) { + onDataChanged(downloadEntity); + } }; diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index dadcb9b729..1c5f59eb8f 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -609,7 +609,7 @@ public class MainActivity extends BaseActivity { if (downloadEntity != null) { File file = new File(downloadEntity.getPath()); if (!file.exists()) { - ToastUtils.INSTANCE.showToast("文件已被删除,无法启动"); + ToastUtils.showToast("文件已被删除,无法启动"); return; } @@ -620,8 +620,8 @@ public class MainActivity extends BaseActivity { toast("模拟器游戏启动失败,请联系客服反馈相关信息"); SentryHelper.INSTANCE.onEvent( "SIMULATOR_SHORTCUT_LAUNCH_ERROR", - "raw_json", - json + "error_digest", + exception.getLocalizedMessage() ); } break; @@ -665,7 +665,7 @@ public class MainActivity extends BaseActivity { public void onFailure(@Nullable HttpException e) { super.onFailure(e); try { - ErrorHelper.handleErrorWithCustomizedHandler(MainActivity.this, e.response().errorBody().string(), false, new Function1() { + ErrorHelper.handleErrorWithCustomizedHandler(MainActivity.this, e.response().errorBody().string(), false, null, new Function1() { @Override public Boolean invoke(Integer code) { if (code == 404001) { @@ -701,7 +701,7 @@ public class MainActivity extends BaseActivity { public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0 && !mMainWrapperFragment.onHandleBackPressed()) { DownloadEntity downloadEntity = null; - for (DownloadEntity entity : DownloadManager.getInstance().getAllDownloadEntityExcludeSilentUpdate()) { + for (DownloadEntity entity : DownloadManager.getInstance().getAllDownloadEntityExcludeSilentTask()) { if (entity.getStatus().equals(DownloadStatus.done)) { if (PackageUtils.isInstalled(getApplicationContext(), entity.getPackageName()) && (!entity.isPlugin() diff --git a/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java b/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java index 6d1ec9dcd3..2a528c0daa 100644 --- a/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java +++ b/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java @@ -140,6 +140,11 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli } } } + + @Override + public void onDataInit(@NonNull DownloadEntity downloadEntity) { + onDataChanged(downloadEntity); + } }; Runnable runnable = new Runnable() { diff --git a/app/src/main/java/com/gh/gamecenter/SkipActivity.java b/app/src/main/java/com/gh/gamecenter/SkipActivity.java index c98d7e6b82..e815ac95e5 100644 --- a/app/src/main/java/com/gh/gamecenter/SkipActivity.java +++ b/app/src/main/java/com/gh/gamecenter/SkipActivity.java @@ -112,7 +112,7 @@ public class SkipActivity extends BaseActivity { DirectUtils.directToGameDetail(this, path, ENTRANCE_BROWSER, "true".equals(uri.getQueryParameter("auto_download")), to, null); break; case HOST_COLUMN: - DirectUtils.directToSubject(this, path, uri.getQueryParameter(KEY_NAME), ENTRANCE_BROWSER); + DirectUtils.directToSubject(this, path, uri.getQueryParameter(KEY_NAME), ENTRANCE_BROWSER, null); break; case HOST_SUGGESTION: String platform = uri.getQueryParameter(KEY_PLATFORM); diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt index 053aa98dff..f4ec821f01 100644 --- a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt @@ -30,13 +30,14 @@ import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.tracker.TrackerLogger import com.gh.gamecenter.common.utils.DialogHelper import com.gh.gamecenter.common.utils.PackageFlavorHelper +import com.gh.gamecenter.common.utils.PermissionHelper import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.EmptyCallback -import com.gh.gamecenter.core.utils.MtaHelper.onEvent import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.entity.PrivacyPolicyEntity +import com.gh.vspace.VHelper import com.halo.assistant.HaloApp import com.lightgame.download.FileUtils import pub.devrel.easypermissions.AfterPermissionGranted @@ -59,7 +60,8 @@ class SplashScreenActivity : BaseActivity() { private val mPermissions = arrayOf( Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE + Manifest.permission.READ_PHONE_STATE, + PermissionHelper.PERMISSION_GET_INSTALLED_LIST ) override fun onCreate(savedInstanceState: Bundle?) { @@ -178,7 +180,7 @@ class SplashScreenActivity : BaseActivity() { val all = DownloadManager.getInstance().allDownloadEntity for (downloadEntity in all) { if (downloadEntity.packageName == packageName) { - DownloadManager.getInstance().cancel(downloadEntity.url, true, true) + DownloadManager.getInstance().cancel(downloadEntity.url, true, true, false) break } } @@ -282,7 +284,9 @@ class SplashScreenActivity : BaseActivity() { @AfterPermissionGranted(REQUEST_PERMISSION_TAG) private fun checkAndRequestPermission() { if (EasyPermissions.hasPermissions(this, *mPermissions)) { - onEvent("授权情况", "启动授权", "都授权") + // 恢复畅玩数据 + VHelper.recoverVDataIfPossible() + // 检查是否有旧版本光环,有就删掉 AppExecutor.ioExecutor.execute { deleteOutdatedUpdatePackage() } if (mStartMainActivityDirectly) { diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.java b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.java index 44bd6e17d4..08a553588c 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.java +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.Intent; import android.text.TextUtils; import android.view.View; +import android.widget.TextView; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; @@ -23,12 +24,12 @@ import com.gh.common.simulator.SimulatorGameManager; import com.gh.common.util.CheckLoginUtils; import com.gh.common.util.DetailDownloadUtils; import com.gh.common.util.DialogUtils; +import com.gh.common.util.DirectUtils; import com.gh.common.util.DownloadDialogHelper; import com.gh.common.util.EnergyTaskHelper; import com.gh.common.util.LogUtils; import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; -import com.gh.common.util.RealNameHelper; import com.gh.common.util.ReservationHelper; import com.gh.common.view.DownloadProgressBar; import com.gh.download.DownloadManager; @@ -38,10 +39,12 @@ import com.gh.gamecenter.DownloadManagerActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.WebActivity; import com.gh.gamecenter.common.constant.Constants; +import com.gh.gamecenter.common.constant.EntranceConsts; import com.gh.gamecenter.common.utils.DataLogUtils; import com.gh.gamecenter.common.utils.DialogHelper; import com.gh.gamecenter.common.utils.PermissionHelper; import com.gh.gamecenter.core.utils.MtaHelper; +import com.gh.gamecenter.core.utils.PageSwitchDataHelper; import com.gh.gamecenter.core.utils.StringUtils; import com.gh.gamecenter.entity.ApkEntity; import com.gh.gamecenter.entity.GameEntity; @@ -51,6 +54,9 @@ import com.gh.gamecenter.eventbus.EBScroll; import com.gh.gamecenter.gamedetail.GameDetailFragment; import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment; import com.gh.gamecenter.teenagermode.TeenagerModeActivity; +import com.gh.vspace.VDownloadManagerActivity; +import com.gh.vspace.VHelper; +import com.gh.vspace.VSpaceLoadingActivity; import com.lightgame.download.DownloadEntity; import com.lightgame.download.FileUtils; import com.lightgame.utils.Utils; @@ -58,6 +64,7 @@ import com.lightgame.utils.Utils; import org.greenrobot.eventbus.EventBus; import java.io.File; +import java.util.HashMap; /** * Created by khy on 27/06/17. @@ -77,12 +84,14 @@ public class DetailViewHolder { public View downloadBottom; public DownloadProgressBar mDownloadPb; + public TextView mOverlayTv; // 额外的文字 (用于一些含图片的情况) // 注意View的命名 public DetailViewHolder(View view, GameEntity gameEntity, DownloadEntity downloadEntity, boolean isNewsDetail, String entrance, String name, String title, @Nullable ExposureEvent traceEvent) { downloadBottom = view.findViewById(R.id.detail_ll_bottom); mDownloadPb = view.findViewById(R.id.detail_progressbar); + mOverlayTv = view.findViewById(R.id.overlayTv); this.gameEntity = gameEntity; this.downloadEntity = downloadEntity; @@ -96,6 +105,10 @@ public class DetailViewHolder { restoreDialogFragment(); } + public TextView getOverlayTv() { + return mOverlayTv; + } + private void restoreDialogFragment() { DialogFragment gamePermissionDialogFragment = (DialogFragment) ((AppCompatActivity) context).getSupportFragmentManager().findFragmentByTag(GamePermissionDialogFragment.class.getName()); @@ -153,7 +166,9 @@ public class DetailViewHolder { MtaHelper.onEvent("游戏详情_新", "预约", mGameEntity.getName()); break; case LAUNCH_OR_OPEN: - EnergyTaskHelper.postEnergyTask("play_game", mGameEntity.getId(), mGameEntity.getApk().get(0).getPackageName()); + if (!mGameEntity.getApk().isEmpty()) { + EnergyTaskHelper.postEnergyTask("play_game", mGameEntity.getId(), mGameEntity.getApk().get(0).getPackageName()); + } break; } // 由于部分状态不包含在 downloadType 里,所以还是需要手动获取下载按钮文字判断点击时的状态 @@ -194,12 +209,12 @@ public class DetailViewHolder { DataLogUtils.uploadGameLog(mViewHolder.context, mGameEntity.getId(), mGameEntity.getName(), mEntrance); } case PLUGIN: - RealNameHelper.checkIfAuth(v.getContext(), mGameEntity, () -> { + VHelper.validateVSpaceBeforeAction(mViewHolder.context, mGameEntity, true, () -> { GamePermissionDialogFragment.show((AppCompatActivity) mViewHolder.context, mGameEntity, mGameEntity.getInfo(), () -> { PermissionHelper.checkStoragePermissionBeforeAction(mViewHolder.context, () -> { if (mGameEntity.getApk().size() == 1) { ApkEntity apk = mGameEntity.getApk().get(0); - BrowserInstallHelper.showBrowserInstallHintDialog(mViewHolder.context, () -> { + BrowserInstallHelper.showBrowserInstallHintDialog(mViewHolder.context, mGameEntity.isVGame(), () -> { PackageCheckDialogFragment.show((AppCompatActivity) mViewHolder.context, mGameEntity, () -> { DownloadDialogHelper.findAvailableDialogAndShow(mViewHolder.context, mGameEntity, apk, () -> { CertificationDialog.showCertificationDialog(mViewHolder.context, mGameEntity, () -> { @@ -244,6 +259,11 @@ public class DetailViewHolder { return; } + if (mGameEntity.isVGame()) { + VHelper.installOrLaunch(mViewHolder.context, mGameEntity.getApk().get(0).getPackageName()); + return; + } + PackageUtils.launchApplicationByPackageName(mViewHolder.context, mGameEntity.getApk().get(0).getPackageName()); } else { GamePermissionDialogFragment.show((AppCompatActivity) mViewHolder.context, mGameEntity, mGameEntity.getInfo(), () -> { @@ -271,10 +291,16 @@ public class DetailViewHolder { } } + if (mGameEntity.isVGame()) { + VHelper.installOrLaunch(v.getContext(), mGameEntity.getApk().get(0).getPackageName()); + return; + } + PermissionHelper.checkStoragePermissionBeforeAction(mViewHolder.context, () -> { if (mDownloadEntity == null) { mDownloadEntity = DownloadManager.getInstance().getDownloadEntityByUrl(mGameEntity.getApk().get(0).getUrl()); } + if (mDownloadEntity != null) { final String path = mDownloadEntity.getPath(); if (FileUtils.isEmptyFile(path)) { @@ -337,8 +363,23 @@ public class DetailViewHolder { } ); break; + case SPECIAL_DOWNLOAD: + RegionSetting.GameSpecialDownloadInfo info = RegionSettingHelper.getGameSpecialDownloadInfo(mGameEntity.getId()); + if (info != null) { + if (!TextUtils.isEmpty(info.getBbsId())) { + if (!TextUtils.isEmpty(info.getTopId())) { + HashMap map = new HashMap<>(); + map.put(EntranceConsts.KEY_TOP_ID, info.getTopId()); + PageSwitchDataHelper.pushCurrentPageData(map); + } + DirectUtils.directForumDetail(mViewHolder.context, info.getBbsId(), mEntrance); + } + } + break; default: - if (!mGameEntity.getApk().isEmpty()) { + if (mGameEntity.isVGame()) { + mViewHolder.context.startActivity(VDownloadManagerActivity.getIntent(mViewHolder.context, true)); + } else if (!mGameEntity.getApk().isEmpty()) { Intent intent = DownloadManagerActivity.getDownloadMangerIntent(mViewHolder.context, mGameEntity.getApk().get(0).getUrl(), StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])")); @@ -378,19 +419,27 @@ public class DetailViewHolder { ApkEntity apkEntity = mGameEntity.getApk().get(0); String msg = FileUtils.isCanDownload(mViewHolder.context, apkEntity.getSize()); if (TextUtils.isEmpty(msg)) { - DownloadManager.createDownload(mViewHolder.context, - apkEntity, - mGameEntity, - method, - StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])"), - mName + ":" + mTitle, - isSubscribe, - mTraceEvent); + if (mGameEntity.isVGame() && "更新".equals(method)) { + VHelper.updateOrReDownload(mGameEntity); + } else { + DownloadManager.createDownload(mViewHolder.context, + apkEntity, + mGameEntity, + method, + StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])"), + mName + ":" + mTitle, + isSubscribe, + mTraceEvent); + } mViewHolder.mDownloadPb.setProgress(0); mViewHolder.mDownloadPb.setDownloadType("插件化".equals(method) ? DownloadProgressBar.DownloadType.DOWNLOADING_PLUGIN : DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL); DeviceRemindDialog.Companion.showDeviceRemindDialog(mViewHolder.context, mGameEntity); + + if (mGameEntity.isVGame() && mViewHolder.context.getString(R.string.download).equals(method)) { + mViewHolder.context.startActivity(VSpaceLoadingActivity.getIntent(mViewHolder.context, mGameEntity, false)); + } } else { Utils.toast(mViewHolder.context, msg); } diff --git a/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt b/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt index cc7ac99b15..a1514979d0 100644 --- a/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt @@ -9,12 +9,9 @@ import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton -import com.gh.gamecenter.core.utils.TimeElapsedHelper import com.gh.common.exposure.ExposureListener import com.gh.common.exposure.ExposureSource -import com.gh.common.util.* import com.gh.common.util.DialogUtils -import com.gh.gamecenter.common.view.VerticalItemDecoration import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager @@ -24,7 +21,11 @@ import com.gh.gamecenter.baselist.LazyListFragment import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.common.view.VerticalItemDecoration +import com.gh.gamecenter.core.utils.ClickUtils +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.core.utils.TimeElapsedHelper import com.gh.gamecenter.databinding.FragmentAmwayAlBinding import com.gh.gamecenter.databinding.FragmentAmwayBinding import com.gh.gamecenter.entity.RatingComment @@ -64,6 +65,10 @@ class AmwayFragment : LazyListFragment() { showUnzipFailureDialog(downloadEntity) } } + + override fun onDataInit(downloadEntity: DownloadEntity) { + mAdapter?.notifyItemByDownload(downloadEntity) + } } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/gh/gamecenter/baselist/DiffUtilAdapter.kt b/app/src/main/java/com/gh/gamecenter/baselist/DiffUtilAdapter.kt index 45c2b59719..788fe2f025 100644 --- a/app/src/main/java/com/gh/gamecenter/baselist/DiffUtilAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/baselist/DiffUtilAdapter.kt @@ -5,6 +5,8 @@ import android.content.Context import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.common.utils.safelyGetInRelease +import com.gh.gamecenter.common.utils.testChannelOnly +import com.gh.gamecenter.core.utils.ToastUtils import com.lightgame.adapter.BaseRecyclerAdapter abstract class DiffUtilAdapter(context: Context) : @@ -24,55 +26,43 @@ abstract class DiffUtilAdapter(context: Context) : // 这里用新的数组包裹原数据 val updateDataCopy = ArrayList(updateData) - if (mDataList.size > updateData.size) { - mDataList = updateDataCopy - notifyDataSetChanged() - return - } - - ListExecutor.workerExecutor.execute { - val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() { - override fun getOldListSize(): Int { - return mDataList.size - } - - override fun getNewListSize(): Int { - return updateDataCopy.size - } - - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - if (oldItemPosition != newItemPosition - || oldItemPosition >= mDataList.size - || newItemPosition >= updateDataCopy.size - ) { - return false + try { + ListExecutor.workerExecutor.execute { + val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() { + override fun getOldListSize(): Int { + return mDataList.size } - val oldItem = mDataList.safelyGetInRelease(oldItemPosition) - val newItem = updateDataCopy.safelyGetInRelease(newItemPosition) - return areItemsTheSame(oldItem, newItem) - } + override fun getNewListSize(): Int { + return updateDataCopy.size + } - override fun areContentsTheSame( - oldItemPosition: Int, - newItemPosition: Int - ): Boolean { - if (oldItemPosition >= mDataList.size) return false - if (newItemPosition >= updateDataCopy.size) return false + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = mDataList.safelyGetInRelease(oldItemPosition) + val newItem = updateDataCopy.safelyGetInRelease(newItemPosition) + return areItemsTheSame(oldItem, newItem) + } - val oldItem = mDataList.safelyGetInRelease(oldItemPosition) - val newItem = updateDataCopy.safelyGetInRelease(newItemPosition) - return areContentsTheSame(oldItem, newItem) - } - }) - ListExecutor.uiExecutor.execute { - mDataList = ArrayList(updateData) - if (diffResult != null) { + override fun areContentsTheSame( + oldItemPosition: Int, + newItemPosition: Int + ): Boolean { + val oldItem = mDataList.safelyGetInRelease(oldItemPosition) + val newItem = updateDataCopy.safelyGetInRelease(newItemPosition) + return areContentsTheSame(oldItem, newItem) + } + }) + ListExecutor.uiExecutor.execute { + mDataList = updateDataCopy diffResult.dispatchUpdatesTo(this@DiffUtilAdapter) - } else { - notifyDataSetChanged() } } + } catch (e: IndexOutOfBoundsException) { + testChannelOnly { + ToastUtils.toast("DiffUtilAdapter 遇到数组越界异常,请检查") + } + mDataList = updateDataCopy + notifyDataSetChanged() } } diff --git a/app/src/main/java/com/gh/gamecenter/baselist/ListAdapter.java b/app/src/main/java/com/gh/gamecenter/baselist/ListAdapter.java index 9e65c7ab71..a11051f56b 100644 --- a/app/src/main/java/com/gh/gamecenter/baselist/ListAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/baselist/ListAdapter.java @@ -46,6 +46,10 @@ public abstract class ListAdapter extends BaseRecyclerAdapter { return; } + calculateDiff(updateData); + } + + protected void calculateDiff(List updateData) { // 若直接传 updateData 给 DiffUtils 可能因为其它地方操作 updateData 这个列表而出现 ArrayIndexOutOfBounds 异常 // 这里用新的数组包裹原数据 ArrayList updateDataCopy = new ArrayList<>(updateData); diff --git a/app/src/main/java/com/gh/gamecenter/catalog/NewCatalogListFragment.kt b/app/src/main/java/com/gh/gamecenter/catalog/NewCatalogListFragment.kt index 979389be63..0c94c8485a 100644 --- a/app/src/main/java/com/gh/gamecenter/catalog/NewCatalogListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/catalog/NewCatalogListFragment.kt @@ -3,22 +3,24 @@ package com.gh.gamecenter.catalog import android.os.Bundle import android.view.View import com.ethanhua.skeleton.Skeleton -import com.gh.gamecenter.common.constant.Constants import com.gh.common.exposure.ExposureListener import com.gh.common.exposure.ExposureSource import com.gh.common.util.DialogUtils -import com.gh.gamecenter.common.constant.EntranceConsts -import com.gh.gamecenter.common.utils.observeNonNull -import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.common.view.CatalogFilterView import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.utils.observeNonNull import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.databinding.FragmentCatalogListBinding -import com.gh.gamecenter.entity.* +import com.gh.gamecenter.entity.CatalogEntity +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.entity.SubjectSettingEntity import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.lightgame.download.DataWatcher @@ -42,6 +44,10 @@ class NewCatalogListFragment : ListFragment showUnzipFailureDialog(downloadEntity) } } + + override fun onDataInit(downloadEntity: DownloadEntity) { + mAdapter?.notifyItemByDownload(downloadEntity) + } } private var mBinding: FragmentCatalogListBinding? = null diff --git a/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogFragment.kt b/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogFragment.kt index 20093e678d..7c9788fbc6 100644 --- a/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogFragment.kt @@ -32,7 +32,7 @@ class SpecialCatalogFragment : ListFragment(application) { + private val mExposureSourceList: List?) : ListViewModel(application) { private val mApi = RetrofitManager.getInstance().api val basicExposureSource by lazy { arrayListOf().apply { - mExposureSource?.let { add(it) } + if (!mExposureSourceList.isNullOrEmpty()) { + addAll(mExposureSourceList) + } add(ExposureSource("分类", mCatalogTitle)) } } @@ -98,7 +100,7 @@ class SpecialCatalogViewModel(application: Application, class Factory(private val mCatalogId: String, private val mCatalogTitle: String, private val mIsCategoryV2: Boolean, - private val mExposureSource: ExposureSource? + private val mExposureSourceList: List? ) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { return SpecialCatalogViewModel( @@ -106,7 +108,7 @@ class SpecialCatalogViewModel(application: Application, mCatalogId, mCatalogTitle, mIsCategoryV2, - mExposureSource + mExposureSourceList ) as T } } diff --git a/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt b/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt index 0cede00121..15acd24cf0 100644 --- a/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt @@ -4,9 +4,8 @@ import android.os.Bundle import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import com.ethanhua.skeleton.Skeleton -import com.gh.gamecenter.common.constant.Constants import com.gh.common.exposure.ExposureListener -import com.gh.common.util.* +import com.gh.common.util.DialogUtils import com.gh.common.view.ConfigFilterView import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus @@ -14,6 +13,7 @@ import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.observeNonNull import com.gh.gamecenter.common.utils.toColor @@ -45,6 +45,10 @@ class NewCategoryListFragment : ListFragment(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST), EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap ) childFragmentManager @@ -331,7 +331,7 @@ class CategoryV2Fragment : LazyFragment() { EntranceConsts.KEY_CATEGORY_ID to id, EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[selectedCategoryPosition].categoryId, EntranceConsts.KEY_CATEGORY_TITLE to mCategoryTitle, - EntranceConsts.KEY_EXPOSURE_SOURCE to arguments?.getParcelable(EntranceConsts.KEY_EXPOSURE_SOURCE), + EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST), EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap ) childFragmentManager @@ -360,7 +360,7 @@ class CategoryV2Fragment : LazyFragment() { EntranceConsts.KEY_CATEGORY_ID to id, EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[position].categoryId, EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle, - EntranceConsts.KEY_EXPOSURE_SOURCE to arguments?.getParcelable(EntranceConsts.KEY_EXPOSURE_SOURCE), + EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST), EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap ) childFragmentManager @@ -378,7 +378,7 @@ class CategoryV2Fragment : LazyFragment() { EntranceConsts.KEY_IS_CATEGORY_V2 to true, EntranceConsts.KEY_CATALOG_ID to id, EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle, - EntranceConsts.KEY_EXPOSURE_SOURCE to arguments?.getParcelable(EntranceConsts.KEY_EXPOSURE_SOURCE), + EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST), EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap ) childFragmentManager @@ -391,7 +391,7 @@ class CategoryV2Fragment : LazyFragment() { EntranceConsts.KEY_CATEGORY_ID to id, EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[position].categoryId, EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle, - EntranceConsts.KEY_EXPOSURE_SOURCE to arguments?.getParcelable(EntranceConsts.KEY_EXPOSURE_SOURCE), + EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST), EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap ) } @@ -413,7 +413,7 @@ class CategoryV2Fragment : LazyFragment() { EntranceConsts.KEY_CATEGORY_ID to id, EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[position].categoryId, EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle, - EntranceConsts.KEY_EXPOSURE_SOURCE to arguments?.getParcelable(EntranceConsts.KEY_EXPOSURE_SOURCE), + EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST), EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap ) } diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt index 36459ce123..d612c7b9c3 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt @@ -110,7 +110,9 @@ class CategoryV2ListAdapter( val sortSize = mViewModel.sortSize.text val exposureSources = ArrayList() - mViewModel.exposureSource?.let { exposureSources.add(it) } + if (!mViewModel.exposureSourceList.isNullOrEmpty()) { + exposureSources.addAll(mViewModel.exposureSourceList!!) + } exposureSources.add(ExposureSource("分类", categoryTitle)) exposureSources.add(ExposureSource(selectedCategoryName)) exposureSources.add(ExposureSource("二级分类详情", "$selectedSubCatalogName+$sortType+$sortSize")) diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListFragment.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListFragment.kt index c0ba61b88f..a6552429e4 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListFragment.kt @@ -4,9 +4,7 @@ import android.os.Bundle import android.view.View import android.view.ViewGroup import com.ethanhua.skeleton.Skeleton -import com.gh.gamecenter.common.constant.Constants import com.gh.common.exposure.ExposureListener -import com.gh.common.util.* import com.gh.common.util.DialogUtils import com.gh.common.view.CategoryFilterView import com.gh.common.xapk.XapkInstaller @@ -14,6 +12,7 @@ import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.databinding.FragmentCategoryListBinding @@ -47,6 +46,10 @@ class CategoryV2ListFragment : ListFragment showUnzipFailureDialog(downloadEntity) } } + + override fun onDataInit(downloadEntity: DownloadEntity) { + mAdapter?.notifyItemByDownload(downloadEntity) + } } override fun getLayoutId() = 0 @@ -57,7 +60,7 @@ class CategoryV2ListFragment : ListFragment viewModelProvider(CategoryV2ListViewModel.Factory( mCategoryId, mSubCategoryId, - arguments?.getParcelable(EntranceConsts.KEY_EXPOSURE_SOURCE))) + arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST))) override fun provideListAdapter() = mAdapter ?: CategoryV2ListAdapter( diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListViewModel.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListViewModel.kt index 10c19a6b3b..afc2bce771 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListViewModel.kt @@ -20,7 +20,7 @@ import io.reactivex.Single class CategoryV2ListViewModel(application: Application, val categoryId: String, var categoryIds: String, - var exposureSource: ExposureSource?) : ListViewModel(application) { + var exposureSourceList: List?) : ListViewModel(application) { val refresh = MutableLiveData() @@ -85,13 +85,13 @@ class CategoryV2ListViewModel(application: Application, } } - class Factory(val categoryId: String, val categoryIds: String, val exposureSource: ExposureSource?): ViewModelProvider.NewInstanceFactory() { + class Factory(val categoryId: String, val categoryIds: String, val exposureSourceList: List?): ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { return CategoryV2ListViewModel( HaloApp.getInstance().application, categoryId, categoryIds, - exposureSource + exposureSourceList ) as T } } diff --git a/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleViewModel.kt b/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleViewModel.kt index 5e2daac106..d3a958dda0 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/collection/CommunityArticleViewModel.kt @@ -88,7 +88,7 @@ class CommunityArticleViewModel(application: Application) : ListViewModel() { diff --git a/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt b/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt index 6e7d87a6fa..e94e5e0db3 100644 --- a/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt @@ -148,7 +148,7 @@ class DownloadFragment : BaseFragment_TabLayout() { private fun updateDownloadHint() { if (!::mDownloadNumberTv.isInitialized || !isAdded) return - val downloadData = DownloadManager.getInstance().allDownloadEntityExcludeSilentUpdate + val downloadData = DownloadManager.getInstance().allDownloadEntityExcludeSilentTask if (downloadData.size > 0) { mDownloadNumberTv.visibility = View.VISIBLE } else { diff --git a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java index ac1f1aef3a..6d303ed94b 100644 --- a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java +++ b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java @@ -169,12 +169,10 @@ public class GameDownloadFragment extends BaseFragment implements View.OnClickLi if (status.equals(DownloadStatus.downloading) || status.equals(DownloadStatus.waiting) || status.equals(DownloadStatus.subscribe)) { - // 静默更新任务不需要添加 - if (ExtensionsKt.isSilentUpdate(downloadEntity)) { - return; - } - //下载模拟器任务不需要添加 - if (ExtensionsKt.isSimulatorDownload(downloadEntity)) { + // 静默更新任务,下载模拟器任务,畅玩游戏不需要添加 + if (ExtensionsKt.isSilentUpdate(downloadEntity) + || ExtensionsKt.isSimulatorDownload(downloadEntity) + || ExtensionsKt.isVGame(downloadEntity)) { return; } diff --git a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragmentAdapter.java b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragmentAdapter.java index 123dd4d8df..d3de316826 100644 --- a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragmentAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragmentAdapter.java @@ -47,6 +47,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import androidx.annotation.NonNull; import androidx.collection.ArrayMap; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; @@ -57,19 +58,19 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder; */ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { - private LinearLayout mNoDataSkip; + private final LinearLayout mNoDataSkip; - private List downloadingList; - private List doneList; + private final List mDownloadingList; + private final List mDownloadedList; // 1、此处的所有MAP只是对DownloadManager内部Map的引用, // 2、任何对下载器任务的操作,通过DownloadManager处理,由DownloadManager处理之后抛出对应的状态变化事件 // 3、监听下载任务状态变化,刷新界面 // 4、对状态只读不写。 - private ArrayMap locationMap; - private ArrayMap statusMap; - private ArrayMap urlMap; - private ArrayList deleteList; + private final ArrayMap locationMap; + private final ArrayMap statusMap; + private final ArrayMap urlMap; + private final ArrayList deleteList; private String url; @@ -80,21 +81,19 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { mNoDataSkip = textView; -// statusMap = DownloadManager.getInstance().getStatusMap(); -// downloadingList = new ArrayList<>(DownloadManager.getInstance().getDownloadingMap().values()); - statusMap = new ArrayMap<>(); - downloadingList = new ArrayList<>(); + mDownloadingList = new ArrayList<>(); locationMap = new ArrayMap<>(); urlMap = new ArrayMap<>(); deleteList = new ArrayList<>(); - doneList = new ArrayList<>(); + mDownloadedList = new ArrayList<>(); } + @NonNull @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType == 0) { return new DownloadHeadViewHolder(DownloadmanagerItemHeadBinding.inflate(mLayoutInflater, parent, false)); } else { @@ -103,7 +102,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { } @Override - public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { + public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) { if (holder instanceof GameDownloadViewHolder) { final GameDownloadViewHolder viewHolder = (GameDownloadViewHolder) holder; @@ -116,12 +115,12 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { viewHolder.binding.dmItemTvSpeed.setTextColor(ContextCompat.getColor(mContext, R.color.text_9a9a9a)); final DownloadEntity downloadEntity; - if (doneList.size() != 0 && position > 0 && position <= doneList.size()) { - downloadEntity = doneList.get(position - 1); - } else if (doneList.isEmpty()) { - downloadEntity = downloadingList.get(position - 1); + if (mDownloadedList.size() != 0 && position > 0 && position <= mDownloadedList.size()) { + downloadEntity = mDownloadedList.get(position - 1); + } else if (mDownloadedList.isEmpty()) { + downloadEntity = mDownloadingList.get(position - 1); } else { - downloadEntity = downloadingList.get(position - doneList.size() - 2); + downloadEntity = mDownloadingList.get(position - mDownloadedList.size() - 2); } String icon = downloadEntity.getIcon(); String rawIcon = ExtensionsKt.getMetaExtra(downloadEntity, Constants.RAW_GAME_ICON); @@ -172,7 +171,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { viewHolder.binding.dmItemTvStartorpause.setTextColor(Color.WHITE); if (downloadEntity.isPluggable() - && PackagesManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) { + && PackagesManager.isInstalled(downloadEntity.getPackageName())) { viewHolder.binding.dmItemTvStartorpause.setText("安装"); viewHolder.binding.dmItemTvStartorpause.setBackgroundResource(R.drawable.download_button_pluggable_style); } else { @@ -281,7 +280,8 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { if (isSubscribe) { DownloadManager.getInstance().subscribe(downloadEntity); statusMap.put(url, DownloadStatus.subscribe.getStatus()); - notifyItemChanged(doneList.isEmpty() ? 0 : 1 + doneList.size()); + notifyItemChanged(mDownloadedList.isEmpty() ? 0 : 1 + mDownloadedList + .size()); } else { LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams( 0, LinearLayout.LayoutParams.WRAP_CONTENT); @@ -298,7 +298,8 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { viewHolder.binding.dmItemTvStartorpause.setText("暂停"); viewHolder.binding.dmItemTvStartorpause.setTextColor(ContextCompat.getColor(mContext, R.color.theme_font)); statusMap.put(url, DownloadStatus.downloading.getStatus()); - notifyItemChanged(doneList.isEmpty() ? 0 : 1 + doneList.size()); + notifyItemChanged(mDownloadedList.isEmpty() ? 0 : 1 + mDownloadedList + .size()); Message msg = Message.obtain(); msg.what = DownloadConfig.CONTINUE_DOWNLOAD_TASK; @@ -312,7 +313,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { PermissionHelper.checkStoragePermissionBeforeAction(mContext, () -> { final String path = downloadEntity.getPath(); if (downloadEntity.isPluggable() - && PackagesManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) { + && PackagesManager.isInstalled(downloadEntity.getPackageName())) { showPluginDialog(downloadEntity.getPath()); } else { if (FileUtils.isEmptyFile(path)) { @@ -335,7 +336,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { viewHolder.binding.dmItemTvDownloads.setText("已暂停"); viewHolder.binding.dmItemIvDelete.setVisibility(View.VISIBLE); statusMap.put(url, DownloadStatus.pause.getStatus()); - notifyItemChanged(doneList.isEmpty() ? 0 : 1 + doneList.size()); + notifyItemChanged(mDownloadedList.isEmpty() ? 0 : 1 + mDownloadedList.size()); Message msg = Message.obtain(); msg.what = DownloadConfig.PAUSE_DOWNLOAD_TASK; msg.obj = url; @@ -344,7 +345,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { // DownloadManager.getInstance().pause(downloadEntity); break; case "等待": - Utils.toast(mContext, "最多只能同时启动3个下载任务"); + Utils.toast(mContext, "最多只能同时下载三个任务,请稍等"); break; case "启动": PackageUtils.launchApplicationByPackageName(mContext, downloadEntity.getPackageName()); @@ -398,7 +399,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { viewHolder.binding.dmItemHeadTvTask.setTextColor(ContextCompat.getColor(mContext, R.color.text_6c6c6c)); viewHolder.binding.dmItemHeadTvAllstart.setTextColor(ContextCompat.getColor(mContext, R.color.theme_font)); - if (position == 0 && doneList.size() != 0) { + if (position == 0 && mDownloadedList.size() != 0) { viewHolder.binding.dmItemHeadTvTask.setText("已完成"); viewHolder.binding.dmItemHeadTvAllstart.setVisibility(View.GONE); } else { @@ -407,14 +408,14 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { int dlNumber = 0; int wtNumber = 0; - for (DownloadEntity downloadEntity : downloadingList) { + for (DownloadEntity downloadEntity : mDownloadingList) { if (DownloadStatus.downloading.equals(downloadEntity.getStatus())) { dlNumber++; } else if (DownloadStatus.waiting.equals(downloadEntity.getStatus())) { wtNumber++; } } - if ((dlNumber + wtNumber) == downloadingList.size()) { + if ((dlNumber + wtNumber) == mDownloadingList.size()) { viewHolder.binding.dmItemHeadTvAllstart.setText(R.string.download_all_push); viewHolder.binding.dmItemHeadTvAllstart.setTextColor(ContextCompat.getColor(mContext, R.color.btn_gray)); } else { @@ -439,7 +440,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { } public void startAll(DownloadHeadViewHolder viewHolder) { - for (DownloadEntity downloadEntity : downloadingList) { + for (DownloadEntity downloadEntity : mDownloadingList) { DownloadManager.getInstance().put(downloadEntity.getUrl(), System.currentTimeMillis()); Message msg = Message.obtain(); @@ -457,7 +458,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { } public void pauseAll(DownloadHeadViewHolder viewHolder) { - for (DownloadEntity downloadEntity : downloadingList) { + for (DownloadEntity downloadEntity : mDownloadingList) { DownloadManager.getInstance().put(downloadEntity.getUrl(), System.currentTimeMillis()); Message msg = Message.obtain(); @@ -476,7 +477,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { @Override public int getItemViewType(int position) { - if (position == 0 || (doneList.size() > 0 && position == 1 + doneList.size())) { + if (position == 0 || (mDownloadedList.size() > 0 && position == 1 + mDownloadedList.size())) { return 0; } return 1; @@ -484,16 +485,16 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { @Override public int getItemCount() { - if (doneList.isEmpty() && downloadingList.isEmpty()) { + if (mDownloadedList.isEmpty() && mDownloadingList.isEmpty()) { return 0; } - if (doneList.isEmpty()) { - return 1 + downloadingList.size(); + if (mDownloadedList.isEmpty()) { + return 1 + mDownloadingList.size(); } - if (downloadingList.isEmpty()) { - return 1 + doneList.size(); + if (mDownloadingList.isEmpty()) { + return 1 + mDownloadedList.size(); } - return 1 + doneList.size() + 1 + downloadingList.size(); + return 1 + mDownloadedList.size() + 1 + mDownloadingList.size(); } // 显示插件化 @@ -516,62 +517,62 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { return; } boolean isDone = false; - for (DownloadEntity downloadEntity : doneList) { + for (DownloadEntity downloadEntity : mDownloadedList) { if (entry.getUrl().equals(downloadEntity.getUrl())) { isDone = true; break; } } if (isDone) { - if (downloadingList.isEmpty() && doneList.size() == 1) { - doneList.remove(location.intValue()); + if (mDownloadingList.isEmpty() && mDownloadedList.size() == 1) { + mDownloadedList.remove(location.intValue()); locationMap.clear(); notifyDataSetChanged(); EventBus.getDefault().post(new EBDownloadChanged("download", View.GONE, 0)); if (mNoDataSkip.getVisibility() == View.GONE) { mNoDataSkip.setVisibility(View.VISIBLE); } - } else if (doneList.size() == 1) { - doneList.remove(location.intValue()); + } else if (mDownloadedList.size() == 1) { + mDownloadedList.remove(location.intValue()); initLocationMap(); notifyItemRangeRemoved(0, 2); EventBus.getDefault().post(new EBDownloadChanged("download", View.VISIBLE, - downloadingList.size())); + mDownloadingList.size())); } else { - doneList.remove(location.intValue()); + mDownloadedList.remove(location.intValue()); initLocationMap(); notifyItemRemoved(location + 1); EventBus.getDefault().post(new EBDownloadChanged("download", View.VISIBLE, - downloadingList.size())); + mDownloadingList.size())); } } else { - if (doneList.isEmpty() && downloadingList.size() == 1) { - downloadingList.remove(location.intValue()); + if (mDownloadedList.isEmpty() && mDownloadingList.size() == 1) { + mDownloadingList.remove(location.intValue()); locationMap.clear(); notifyDataSetChanged(); EventBus.getDefault().post(new EBDownloadChanged("download", View.GONE, 0)); if (mNoDataSkip.getVisibility() == View.GONE) { mNoDataSkip.setVisibility(View.VISIBLE); } - } else if (downloadingList.size() == 1) { - downloadingList.remove(location.intValue()); + } else if (mDownloadingList.size() == 1) { + mDownloadingList.remove(location.intValue()); initLocationMap(); notifyItemRangeRemoved(getBase(), 2); EventBus.getDefault().post(new EBDownloadChanged("download", View.VISIBLE, - downloadingList.size())); + mDownloadingList.size())); } else { - downloadingList.remove(location.intValue()); + mDownloadingList.remove(location.intValue()); initLocationMap(); notifyDataSetChanged(); EventBus.getDefault().post(new EBDownloadChanged("download", View.VISIBLE, - downloadingList.size())); + mDownloadingList.size())); } } deleteList.add(entry.getUrl()); statusMap.remove(entry.getUrl()); - notifyItemChanged(doneList.isEmpty() ? 0 : 1 + doneList.size()); + notifyItemChanged(mDownloadedList.isEmpty() ? 0 : 1 + mDownloadedList.size()); } // 显示删除提示框 @@ -580,7 +581,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { String msg; if (XapkUnzipStatus.UNZIPPING.name().equals(xapkStatus)) { msg = "游戏正在解压安装哦,确定删除?"; - } else if (doneList.size() != 0 && position <= doneList.size()) { + } else if (mDownloadedList.size() != 0 && position <= mDownloadedList.size()) { msg = "游戏还没安装哦,确定删除?"; } else { msg = "游戏还没下载完,确定删除?"; @@ -596,24 +597,24 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { public void initLocationMap() { locationMap.clear(); - for (int i = 0; i < doneList.size(); i++) { - locationMap.put(doneList.get(i).getUrl(), i); + for (int i = 0; i < mDownloadedList.size(); i++) { + locationMap.put(mDownloadedList.get(i).getUrl(), i); } - for (int i = 0; i < downloadingList.size(); i++) { - locationMap.put(downloadingList.get(i).getUrl(), i); + for (int i = 0; i < mDownloadingList.size(); i++) { + locationMap.put(mDownloadingList.get(i).getUrl(), i); } } public int getBase() { - return doneList.isEmpty() ? 0 : 1 + doneList.size(); + return mDownloadedList.isEmpty() ? 0 : 1 + mDownloadedList.size(); } List getDownloadingList() { - return downloadingList; + return mDownloadingList; } List getDoneList() { - return doneList; + return mDownloadedList; } public ArrayMap getLocationMap() { @@ -641,20 +642,20 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { } void initMap() { - downloadingList.clear(); - doneList.clear(); + mDownloadingList.clear(); + mDownloadedList.clear(); statusMap.clear(); urlMap.clear(); - for (DownloadEntity downloadEntity : DownloadManager.getInstance().getAllDownloadEntityExcludeSilentUpdate()) { + for (DownloadEntity downloadEntity : DownloadManager.getInstance().getAllDownloadEntityExcludeSilentTask()) { statusMap.put(downloadEntity.getUrl(), downloadEntity.getStatus().name()); if (DownloadStatus.done.equals(downloadEntity.getStatus())) { if (!ExtensionsKt.isSimulatorGame(downloadEntity)) { urlMap.put(PackageUtils.getPackageNameByPath(mContext, downloadEntity.getPath()), downloadEntity.getUrl()); - doneList.add(downloadEntity); + mDownloadedList.add(downloadEntity); } } else { - downloadingList.add(downloadEntity); + mDownloadingList.add(downloadEntity); } } // 排序 @@ -667,7 +668,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { return 0; } }; - Collections.sort(downloadingList, comparator); + Collections.sort(mDownloadingList, comparator); comparator = (lhs, rhs) -> { if (rhs.getEnd() > lhs.getEnd()) { @@ -678,7 +679,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { return 0; } }; - Collections.sort(doneList, comparator); + Collections.sort(mDownloadedList, comparator); initLocationMap(); } diff --git a/app/src/main/java/com/gh/gamecenter/download/InstalledGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/download/InstalledGameViewModel.kt index 48a52632df..8e3738f2c0 100644 --- a/app/src/main/java/com/gh/gamecenter/download/InstalledGameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/download/InstalledGameViewModel.kt @@ -9,12 +9,12 @@ import com.gh.common.filter.RegionSettingHelper.shouldThisGameBeFiltered import com.gh.common.util.ApkActiveUtils import com.gh.common.util.GameUtils import com.gh.common.util.PackageUtils -import com.gh.gamecenter.core.utils.ThirdPartyPackageHelper.getGameId import com.gh.download.DownloadManager +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.core.utils.ThirdPartyPackageHelper.getGameId import com.gh.gamecenter.entity.ApkEntity import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.GameInstall -import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers @@ -35,6 +35,9 @@ class InstalledGameViewModel(application: Application) : AndroidViewModel(applic list.add(GameInstall(id = entity.gameId)) } for (gameInstall in list) { + // 畅玩游戏不出现在常规的下载管理里 + if (gameInstall.isSmoothGame) continue + val ghId = PackageUtils.getMetaData(getApplication(), gameInstall.packageName, "gh_id") if (ghId != null && ghId != gameInstall.id) { gameInstall.id = ghId.toString() diff --git a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt index b82a313e66..bb242a8edb 100644 --- a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameViewModel.kt @@ -3,27 +3,31 @@ package com.gh.gamecenter.download import android.app.Application import android.view.View import androidx.lifecycle.* -import com.gh.gamecenter.common.base.BaseSimpleDao -import com.gh.gamecenter.common.constant.Constants import com.gh.common.exposure.ExposureUtils import com.gh.common.exposure.ExposureUtils.logADownloadExposureEvent import com.gh.common.history.HistoryHelper.insertGameEntity -import com.gh.common.util.* -import com.gh.gamecenter.core.utils.GsonUtils.toJson +import com.gh.common.util.ApkActiveUtils +import com.gh.common.util.DataCollectionUtils import com.gh.common.util.PackageInstaller.createDownloadId import com.gh.common.util.PackageInstaller.getDownloadPathWithId +import com.gh.common.util.PackageUtils +import com.gh.common.util.PlatformUtils import com.gh.download.DownloadManager +import com.gh.gamecenter.common.base.BaseSimpleDao +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.common.utils.addMetaExtra import com.gh.gamecenter.common.utils.secondOrNull import com.gh.gamecenter.common.utils.toProperReadableSize import com.gh.gamecenter.common.utils.tryCatchInRelease -import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.core.utils.GsonUtils.toJson +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.core.utils.UrlFilterUtils import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.GameUpdateEntity import com.gh.gamecenter.entity.PluginLocation import com.gh.gamecenter.eventbus.EBDownloadChanged import com.gh.gamecenter.manager.PackagesManager -import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp import com.lightgame.download.DownloadEntity @@ -32,7 +36,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import org.greenrobot.eventbus.EventBus import java.util.* -import kotlin.collections.ArrayList class UpdatableGameViewModel( application: Application, @@ -72,7 +75,7 @@ class UpdatableGameViewModel( // 有闪退日志说这个 update 实体可能为空,实在看不原因 :( if (update == null) continue // 筛选仅下载管理出现的插件化更新 - if (update.isShowPlugin(PluginLocation.only_index)) { + if (update.isShowPlugin(PluginLocation.only_index) && update.downloadStatus != "smooth") { val platform = PlatformUtils.getInstance(getApplication()).getPlatformName(update.platform) if (!platform.isNullOrEmpty() && "官方版" != platform) { @@ -580,6 +583,7 @@ class UpdatableGameViewModel( downloadEntity.addMetaExtra(Constants.RAW_GAME_ICON, update.rawIcon) downloadEntity.addMetaExtra(Constants.GAME_ICON_SUBSCRIPT, update.iconSubscript) downloadEntity.addMetaExtra(Constants.DOWNLOAD_ID, downloadId) + downloadEntity.addMetaExtra(Constants.APK_MD5, update.md5) val platform = PlatformUtils.getInstance(getApplication()).getPlatformName(update.platform) if ("官方版" != platform) { diff --git a/app/src/main/java/com/gh/gamecenter/entity/ApkEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ApkEntity.kt index 1c8db5a4e4..b599df9036 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/ApkEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/ApkEntity.kt @@ -25,6 +25,7 @@ data class ApkEntity(@SerializedName("package") var apkLink: ApkLink? = null, var plugin: String? = "",/*控制是否显示插件化 默认:open,取值有:open/only_index/only_game/close*/ var time: Long? = null, + var md5: String? = null, // 包体的 MD5 用于内型为畅玩的游戏使用用来更新 @SerializedName("platform_name") private var platformName: String = "", val remark: String = "", diff --git a/app/src/main/java/com/gh/gamecenter/entity/AppEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/AppEntity.kt index 9e84ad9f36..70b391b6fd 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/AppEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/AppEntity.kt @@ -1,25 +1,27 @@ package com.gh.gamecenter.entity +import android.os.Parcelable import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize -class AppEntity { - - var version: String? = null +@Parcelize +data class AppEntity( + var version: String? = null, @SerializedName("version_code") - var versionCode: Int = 0 + var versionCode: Int = 0, - var url: String? = null + var url: String? = null, - var size: String? = null + var size: String? = null, - var content: String? = null + var content: String? = null, @SerializedName("force") - var isForce: Boolean = false + var isForce: Boolean = false, @SerializedName("spare_link") - var spareLink: String? = "" + var spareLink: String? = "", /** * NEVER(从不) @@ -28,5 +30,11 @@ class AppEntity { * ONCE_A_DAY(每天一次) * EVERY_TIME_OPEN(每次打开) */ - var alert: String? = null + var alert: String? = null, +) : Parcelable { + fun isAlertEveryTime() = alert == "EVERY_TIME_OPEN" + + fun isAlertOnceADay() = alert == "ONCE_A_DAY" } + + diff --git a/app/src/main/java/com/gh/gamecenter/entity/CommentEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/CommentEntity.kt index 470985fb90..d0b60ec451 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/CommentEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/CommentEntity.kt @@ -30,7 +30,7 @@ data class CommentEntity( var choiceness: Boolean = false,//是否已加精,问题评论用 var images: ArrayList? = arrayListOf(), - // 楼数,本地字段 + // 楼层 var floor: Int = 0, var isExpand: Boolean = false, @SerializedName("attached") // 楼中楼 diff --git a/app/src/main/java/com/gh/gamecenter/entity/ErrorEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ErrorEntity.kt index 82950e6a2f..4ee0c1cd5b 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/ErrorEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/ErrorEntity.kt @@ -24,12 +24,28 @@ data class ErrorEntity(var code: Int? = 0, var answerCount: Int = 0, @SerializedName("follow_count") private var followCount: Int = 0, - val content: String = "" + val content: String = "", + val title: String = "", + val type: String = "", + val link: String = "", + val text: String = "", + @SerializedName("link_community", alternate = ["community"]) + var community: CommunityEntity? = CommunityEntity() ) { // 问题关注数默认是1 fun getFollowCount(): Int { if (followCount > 0) return followCount return 1 } + + fun toLinkEntity(): LinkEntity { + return LinkEntity( + title = title, + link = link, + type = type, + text = text, + community = community + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt index cdfeebcf25..f46300e47e 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameEntity.kt @@ -3,19 +3,21 @@ package com.gh.gamecenter.entity import android.os.Parcelable import android.text.TextUtils import com.gh.common.constant.Config -import com.gh.gamecenter.common.constant.Constants import com.gh.common.exposure.ExposureEvent import com.gh.common.filter.RegionSettingHelper import com.gh.gamecenter.R +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.game.data.GameSubjectData import com.gh.gamecenter.gamedetail.entity.GameInfo import com.gh.gamecenter.gamedetail.entity.ZoneEntity +import com.gh.vspace.VHelper import com.google.gson.annotations.SerializedName import com.halo.assistant.HaloApp import com.lightgame.download.DownloadEntity import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import java.util.* +import kotlin.collections.ArrayList @Parcelize data class GameEntity( @@ -212,7 +214,7 @@ data class GameEntity( private var mH5Link: LinkEntity? = null, val visit: Int = 0, @SerializedName("played_time") - val playedTime: Long = 0, + var playedTime: Long = 0, @SerializedName("played_game_id") val playedGameId: String = "", @SerializedName("mutex_package") @@ -266,8 +268,16 @@ data class GameEntity( @SerializedName("recommend_text") var recommendText: String = "", + // 专题封面图 + @SerializedName("column_image") + var columnImage: String = "", + // 本地字段,使用镜像信息 var useMirrorInfo: Boolean = false, + + // 本地字段,最后打开时间 + var lastPlayedTime: Long = 0, + // 本地字段,曝光用 var displayContent: String = "", var isPlatformRecommend: Boolean = false, @@ -475,12 +485,31 @@ data class GameEntity( fun getApk(): ArrayList { if (shouldUseMirrorInfo()) return mirrorData?.getApk() ?: arrayListOf() - if (apk == null) apk = ArrayList() - if (!Config.isShowPlugin(id)) return getApkNormal() - if (gameLocation == GameLocation.INDEX && apkIndex != null) return apkIndex!! - if (gameLocation == GameLocation.SEARCH && apkSearch != null) return apkSearch!! + var rawApk = apk ?: ArrayList() - return apk!! + if (!Config.isShowPlugin(id)) { + rawApk = getApkNormal() + } else if (gameLocation == GameLocation.INDEX && apkIndex != null) { + rawApk = apkIndex!! + } else if (gameLocation == GameLocation.SEARCH && apkSearch != null) { + rawApk = apkSearch!! + } + + if (isVGame()) { + rawApk.forEach { + it.setPlatform(VHelper.PLATFORM_V) + it.url = VHelper.getVUrl(it.url) + } + } + + return rawApk + } + + /** + * 获取唯一的包名,仅单版本游戏有效,多版本的游戏请不要调用 + */ + fun getUniquePackageName(): String? { + return getApk().firstOrNull()?.packageName } fun getOriginalApk(): ArrayList { @@ -563,6 +592,16 @@ data class GameEntity( && (useMirrorInfo || RegionSettingHelper.shouldThisGameDisplayMirrorInfo(id))) } + fun isVGame(): Boolean { + return if (!VHelper.isVGameOn()) { + false + } else { + downloadStatus == "smooth" + } + } + + fun isSpecialDownload() = RegionSettingHelper.shouldThisGameShowSpecialDownload(id) + fun toSimpleGame(): SimpleGame { val simpleGame = SimpleGame() simpleGame.id = id @@ -818,6 +857,8 @@ data class SimpleGame( //游戏单推荐理由 @SerializedName("recommend_text") var recommendText: String = "", + @SerializedName("download_status") + var downloadStatus: String = "" ) : Parcelable { @IgnoredOnParcel @@ -842,6 +883,7 @@ data class SimpleGame( gameEntity.mirrorData = mirrorData?.toGameEntity() gameEntity.recommendStar = recommendStar gameEntity.recommendText = recommendText + gameEntity.downloadStatus = downloadStatus return gameEntity } } diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameInstall.kt b/app/src/main/java/com/gh/gamecenter/entity/GameInstall.kt index b7ba454688..1d27db820e 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameInstall.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameInstall.kt @@ -14,6 +14,7 @@ data class GameInstall( var isSignByGh: Boolean = false, var installTime: Long = 0, var version: String = "", + var isSmoothGame: Boolean = false, // 是否是畅玩游戏 var tag: Any? = null) { companion object { @@ -28,6 +29,7 @@ data class GameInstall( gameInstall.iconSubScript = game.iconSubscript gameInstall.version = PackageUtils.getVersionNameByPackageName(installedPkgName) ?: "unknown" gameInstall.packageName = installedPkgName + gameInstall.isSmoothGame = game.isVGame() return gameInstall } } diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameUpdateEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/GameUpdateEntity.kt index eead9177dd..579f42a0bf 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameUpdateEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameUpdateEntity.kt @@ -40,7 +40,9 @@ data class GameUpdateEntity( var pluggableCollection: GameCollectionEntity? = null, // 插件化包所在的合集 var format: String = "", var signature: String? = "", - var category: String? = "" + var category: String? = "", + var md5: String? = "", + var downloadStatus: String? = "" ) { fun isShowPlugin(location: PluginLocation): Boolean { diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomeContent.kt b/app/src/main/java/com/gh/gamecenter/entity/HomeContent.kt index cb7ece6001..7a8e58bacf 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HomeContent.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/HomeContent.kt @@ -22,5 +22,12 @@ data class HomeContent( @SerializedName("recommend_text") val recommendText: String = "", @SerializedName("common_collection") - val commonCollection: CommonCollectionEntity? = null + val commonCollection: CommonCollectionEntity? = null, + val image: String = "", + @SerializedName("first_line_recommend") + val firstLineRecommend: String = "", + @SerializedName("second_line_recommend") + val secondLineRecommend: String = "", + @SerializedName("recommend_tag") + val recommendTag: String = "", ) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomeDataEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/HomeDataEntity.kt new file mode 100644 index 0000000000..b440818656 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/HomeDataEntity.kt @@ -0,0 +1,16 @@ +package com.gh.gamecenter.entity + +import com.google.gson.annotations.SerializedName + +data class HomeDataEntity( + @SerializedName("home_tab") + val homeTab: ArrayList = arrayListOf(), + @SerializedName("home_slide") + val homeSlide: ArrayList = arrayListOf(), + @SerializedName("home_recommend") + val homeRecommend: ArrayList = arrayListOf(), + @SerializedName("home_content") + val homeContent: ArrayList = arrayListOf(), + @SerializedName("home_navbar_v2") + val homeNavbarV2: SubjectRecommendEntity? = null +) diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomeSetting.kt b/app/src/main/java/com/gh/gamecenter/entity/HomeSetting.kt index 4371691f53..c09c00259d 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HomeSetting.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/HomeSetting.kt @@ -5,8 +5,12 @@ import com.google.gson.annotations.SerializedName import kotlinx.parcelize.Parcelize @Parcelize -data class HomeSetting(var image: String = "", - @SerializedName("text_color") - val textColor: String = "#FFFFFF", - @SerializedName("placeholder_color") - val placeholderColor: String = "#5C9599") : Parcelable \ No newline at end of file +data class HomeSetting( + var image: String = "", + @SerializedName("text_color") + val textColor: String = "#FFFFFF", + @SerializedName("placeholder_color") + val placeholderColor: String = "#5C9599", + @SerializedName("download_btn_switch") + val downloadBtnSwitch: String = ""//下载按钮是否显示,on 显示 +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/SettingsEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/SettingsEntity.kt index 0195e44f3f..38162da908 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/SettingsEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/SettingsEntity.kt @@ -32,7 +32,9 @@ data class SettingsEntity( @SerializedName("permission_popup_switch") var permissionPopupSwitch: String = "off",//权限引导弹窗开关,on/off @SerializedName("permission_popup_applied_versions") - var permissionPopupAppliedVersions: PermissionPopupAppliedVersions = PermissionPopupAppliedVersions() + var permissionPopupAppliedVersions: PermissionPopupAppliedVersions = PermissionPopupAppliedVersions(), + @SerializedName("game_smooth") + var gameSmooth: String = "off" // 畅玩功能,on/off,默认off ) { fun setCommunityEntrance(communityEntrance: String) { @@ -180,7 +182,7 @@ data class SettingsEntity( @SerializedName("icon_subscript") var iconSubscript: String? = null, @SerializedName("recommend_type") - var recommendType: String? = "none",//none:无、hot: 热门、new: 上新、surge: 飙升 + var recommendType: String? = "none",//none:无、hot: 热门、new: 上新、surge: 飙升、update: 更新 var type: String? = ""//论坛类型 ) { var exposureEvent: ExposureEvent? = null diff --git a/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt index 0017bb9298..5f2b5e2e8f 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt @@ -51,11 +51,25 @@ data class SubjectEntity( var indexRightTopLink: LinkEntity? = null, var remark: String? = null, // 开测表备注 + var image: String = "", + @SerializedName("first_line_recommend") + var firstLineRecommend: String = "", + @SerializedName("second_line_recommend") + var secondLineRecommend: String = "", + @SerializedName("recommend_tag") + var recommendTag: String = "", + @SerializedName("column_name") + var columnName: String = "", + // 专题合集,用于首页专题合集-排行榜样式 var columns: MutableList = mutableListOf(), // 专题背景,用于首页专题合集-排行榜样式 var background: String = "", + // 专题内游戏item是否显示下载按钮(目前只针对横向专题) + @SerializedName("show_download") + var showDownload: Boolean = false, + // 本地字段,用来标记在外部页面中的序号,仅用于曝光记录,具体细节可见 https://git.ghzs.com/pm/halo-app-issues/-/issues/1087 var outerSequence: Int = -1 ) : Parcelable { diff --git a/app/src/main/java/com/gh/gamecenter/entity/VSetting.kt b/app/src/main/java/com/gh/gamecenter/entity/VSetting.kt new file mode 100644 index 0000000000..905ac2573d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/VSetting.kt @@ -0,0 +1,26 @@ +package com.gh.gamecenter.entity + +import com.google.gson.annotations.SerializedName + +class VSetting { + @SerializedName("va") + var va: Va? = null + + data class Va( + @SerializedName("32-bit") + val arch32: VaArch? = null, + @SerializedName("64-bit") + val arch64: VaArch? = null, + ) + + data class VaArch( + val size: String, + @SerializedName("package") + val packageName: String, + @SerializedName("version") + val versionName: String, + @SerializedName("version_code") + val versionCode: Int, + val url: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/eventbus/EBPackage.java b/app/src/main/java/com/gh/gamecenter/eventbus/EBPackage.java deleted file mode 100644 index 79b5736e40..0000000000 --- a/app/src/main/java/com/gh/gamecenter/eventbus/EBPackage.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gh.gamecenter.eventbus; - -public class EBPackage { - - private String type; - private String packageName; - private String versionName; - - public EBPackage(String type, String packageName, String versionName) { - this.type = type; - this.packageName = packageName; - this.versionName = versionName; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getPackageName() { - return packageName; - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } - - public String getVersionName() { - return versionName; - } - - public void setVersionName(String versionName) { - this.versionName = versionName; - } -} diff --git a/app/src/main/java/com/gh/gamecenter/eventbus/EBPackage.kt b/app/src/main/java/com/gh/gamecenter/eventbus/EBPackage.kt new file mode 100644 index 0000000000..b5806fbbd0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/eventbus/EBPackage.kt @@ -0,0 +1,6 @@ +package com.gh.gamecenter.eventbus + +class EBPackage(var type: String, var packageName: String, var versionName: String?) { + var gameId: String? = null +} + diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt index 31cdb894e6..5835bdaf78 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt @@ -269,7 +269,7 @@ class ForumArticleAskListFragment : LazyListFragment> { return when (mPath) { "全部" -> { - RetrofitManager.getInstance().api.getAllForumList(bbsId, UrlFilterUtils.getFilterQuery(sort, "-1"), page) + val data = PageSwitchDataHelper.popLastPageData() + val map = hashMapOf() + if (data != null && data.containsKey(EntranceConsts.KEY_TOP_ID)) { + map["top_id"] = data[EntranceConsts.KEY_TOP_ID] + } + RetrofitManager.getInstance().api.getAllForumList(bbsId, UrlFilterUtils.getFilterQuery(sort, "-1"), page, map) } "精华" -> { RetrofitManager.getInstance().api.getEssenceForumList(bbsId, page) diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt index cc1ab90948..230214d725 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailFragment.kt @@ -739,8 +739,8 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { when (requestCode) { CommunityHomeFragment.ARTICLE_REQUEST_CODE -> { val articleId = data?.getStringExtra("article_id") ?: return - val communityId = data?.getStringExtra("community_id") ?: return - mViewModel?.getArticleData(communityId, articleId) +// val communityId = data?.getStringExtra("community_id") ?: return + mViewModel?.getArticleData(articleId) } CommunityHomeFragment.QUESTION_REQUEST_CODE -> { @@ -798,7 +798,7 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { updateToolbarStyle(mIsToolbarWhite) mBinding.allOrderSfv.run { setContainerBackground(R.drawable.button_round_f5f5f5.toDrawable(requireContext())) - setIndicatorBackground(R.drawable.progressbar_game_collection_primary.toDrawable(requireContext())) + setIndicatorBackground(R.drawable.bg_game_collection_sfv_indicator.toDrawable(requireContext())) setTextColor(ContextCompat.getColorStateList(context, R.color.game_collection_rg_button_selector)) } } diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailViewModel.kt index a12da10a59..532cbd6714 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumDetailViewModel.kt @@ -40,29 +40,29 @@ class ForumDetailViewModel(application: Application, val bbsId: String) : Androi @SuppressLint("CheckResult") fun postForumRead() { mApi.postForumRead(bbsId) - .compose(singleToMain()) - .subscribe(EmptyResponse()) + .compose(singleToMain()) + .subscribe(EmptyResponse()) } fun getForumDetail() { mApi.getForumDetail(bbsId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response() { - override fun onResponse(response: ForumDetailEntity?) { - super.onResponse(response) - forumDetail.postValue(Resource.success(response)) - response?.run { - mForumDao.addForum(convertForumDetailEntityToForumEntity()) - EventBus.getDefault().post(EBForumRecordChange(convertForumDetailEntityToForumEntity())) - } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: ForumDetailEntity?) { + super.onResponse(response) + forumDetail.postValue(Resource.success(response)) + response?.run { + mForumDao.addForum(convertForumDetailEntityToForumEntity()) + EventBus.getDefault().post(EBForumRecordChange(convertForumDetailEntityToForumEntity())) } + } - override fun onFailure(e: HttpException?) { - super.onFailure(e) - forumDetail.postValue(Resource.error(e)) - } - }) + override fun onFailure(e: HttpException?) { + super.onFailure(e) + forumDetail.postValue(Resource.error(e)) + } + }) } fun getModeratorsApplyStatus() { @@ -85,64 +85,64 @@ class ForumDetailViewModel(application: Application, val bbsId: String) : Androi @SuppressLint("CheckResult") fun followForum(onSuccess: () -> Unit) { RetrofitManager.getInstance().api - .followForum(bbsId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: ResponseBody) { - onSuccess.invoke() - } - }) + .followForum(bbsId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + onSuccess.invoke() + } + }) } @SuppressLint("CheckResult") fun unFollowForum(onSuccess: () -> Unit) { RetrofitManager.getInstance().api - .unFollowForum(bbsId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: ResponseBody) { - onSuccess.invoke() - } - }) + .unFollowForum(bbsId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + onSuccess.invoke() + } + }) } - fun getArticleData(communityId: String, articleId: String) { - mApi.getCommunityArticleDetail(communityId, articleId) - .compose(observableToMain()) - .subscribe(object : Response() { - override fun onResponse(response: ArticleDetailEntity?) { - response?.run { - answerLiveData.postValue(convertArticleDetailToAnswer(response)) - } - + fun getArticleData(articleId: String) { + mApi.getCommunityArticleDetail(articleId) + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ArticleDetailEntity?) { + response?.run { + answerLiveData.postValue(convertArticleDetailToAnswer(response)) } - }) + + } + }) } fun getQuestionDetail(questionId: String) { mApi.getQuestionsById(questionId) - .compose(observableToMain()) - .subscribe(object : Response() { - override fun onResponse(response: QuestionsDetailEntity?) { - response?.run { - answerLiveData.postValue(convertQuestionDetailToAnswer(response)) - } + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: QuestionsDetailEntity?) { + response?.run { + answerLiveData.postValue(convertQuestionDetailToAnswer(response)) } - }) + } + }) } fun getVideoDetail(videoId: String) { mApi.getBbsVideoDetail(videoId) - .compose(observableToMain()) - .subscribe(object : Response() { - override fun onResponse(response: ForumVideoEntity?) { - response?.run { - answerLiveData.postValue(convertVideoDetailToAnswer(response)) - } + .compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: ForumVideoEntity?) { + response?.run { + answerLiveData.postValue(convertVideoDetailToAnswer(response)) } - }) + } + }) } fun convertArticleDetailToAnswer(articleDetailEntity: ArticleDetailEntity): AnswerEntity { @@ -159,7 +159,7 @@ class ForumDetailViewModel(application: Application, val bbsId: String) : Androi answerEntity.images = articleDetailEntity.images answerEntity.imagesInfo = articleDetailEntity.imagesInfo answerEntity.videos = articleDetailEntity.videos - answerEntity.status = articleDetailEntity.status ?:"" + answerEntity.status = articleDetailEntity.status ?: "" answerEntity.type = "community_article" return answerEntity @@ -201,12 +201,13 @@ class ForumDetailViewModel(application: Application, val bbsId: String) : Androi answerEntity.time = forumVideoEntity.time.upload forumVideoEntity.user.run { answerEntity.user = UserEntity( - id = id, - name = name, - icon = icon, - auth = auth, - badge = badge, - border = border) + id = id, + name = name, + icon = icon, + auth = auth, + badge = badge, + border = border + ) } answerEntity.type = "video" diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt index 2e3f334b12..20ce0dc489 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt @@ -141,15 +141,15 @@ class CommunityHomeFragment : LazyFragment() { mFragmentList.clear() val tag = "android:switcher:${viewPager.id}:" val forumArticleListFragment = childFragmentManager.findFragmentByTag("${tag}0") - ?: ForumArticleListFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "社区", EntranceConsts.KEY_PATH to "推荐")) + ?: ForumArticleListFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "社区", EntranceConsts.KEY_PATH to "推荐")) mFragmentList.add(forumArticleListFragment) val forumFragment = childFragmentManager.findFragmentByTag("${tag}1") - ?: ForumFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "社区")) + ?: ForumFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "社区")) mFragmentList.add(forumFragment) val activityFragment = childFragmentManager.findFragmentByTag("${tag}2") - ?: ForumActivityFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "活动")) + ?: ForumActivityFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "活动")) mFragmentList.add(activityFragment) viewPager.run { @@ -195,13 +195,25 @@ class CommunityHomeFragment : LazyFragment() { (mTabList[position] as TextView).run { layoutParams.width = (DEFAULT_TAB_TEXT_WIDTH + ((1 - positionOffset) * 4F.dip2px())).roundToInt() textSize = (DEFAULT_TAB_TEXT_SIZE + ((1 - positionOffset) * 4)).roundTo(1) - setTextColor(ColorUtils.blendARGB(TAB_DEFAULT_COLOR.toColor(requireContext()), TAB_SELECTED_COLOR.toColor(requireContext()), 1 - positionOffset)) + setTextColor( + ColorUtils.blendARGB( + TAB_DEFAULT_COLOR.toColor(requireContext()), + TAB_SELECTED_COLOR.toColor(requireContext()), + 1 - positionOffset + ) + ) } if (mTabList[position + 1] is TextView) { (mTabList[position + 1] as TextView).run { layoutParams.width = (DEFAULT_TAB_TEXT_WIDTH + ((positionOffset) * 4F.dip2px())).roundToInt() textSize = (DEFAULT_TAB_TEXT_SIZE + ((positionOffset) * 4)).roundTo(1) - setTextColor(ColorUtils.blendARGB(TAB_DEFAULT_COLOR.toColor(requireContext()), TAB_SELECTED_COLOR.toColor(requireContext()), positionOffset)) + setTextColor( + ColorUtils.blendARGB( + TAB_DEFAULT_COLOR.toColor(requireContext()), + TAB_SELECTED_COLOR.toColor(requireContext()), + positionOffset + ) + ) } } else { (mTabList[position + 1] as TabItemCommunityBinding).run { @@ -211,7 +223,13 @@ class CommunityHomeFragment : LazyFragment() { tabImg.scaleY = (DEFAULT_TAB_IMG_HEIGHT + ((positionOffset) * 4)).roundTo(1) / DEFAULT_TAB_IMG_HEIGHT if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { tabImg.imageTintList = - ColorStateList.valueOf(ColorUtils.blendARGB(TAB_DEFAULT_COLOR.toColor(requireContext()), TAB_SELECTED_COLOR.toColor(requireContext()), positionOffset)) + ColorStateList.valueOf( + ColorUtils.blendARGB( + TAB_DEFAULT_COLOR.toColor(requireContext()), + TAB_SELECTED_COLOR.toColor(requireContext()), + positionOffset + ) + ) } } } @@ -433,8 +451,8 @@ class CommunityHomeFragment : LazyFragment() { when (requestCode) { ARTICLE_REQUEST_CODE -> { val articleId = data?.getStringExtra("article_id") ?: return - val communityId = data?.getStringExtra("community_id") ?: return - mViewModel?.getArticleData(communityId, articleId) +// val communityId = data?.getStringExtra("community_id") ?: return + mViewModel?.getArticleData(articleId) } QUESTION_REQUEST_CODE -> { @@ -531,7 +549,11 @@ class CommunityHomeFragment : LazyFragment() { navigationBg.setBackgroundColor(R.color.transparent.toColor(requireContext())) navigationBg.setImageDrawable(null) } else if (viewPager.currentItem == TAB_RECOMMEND_INDEX) { - navigationBg.setBackgroundColor(if (mNightMode && y > 0) R.color.background_white.toColor(requireContext()) else if (mNightMode && y == 0) R.color.background.toColor(requireContext()) else R.color.transparent.toColor(requireContext())) + navigationBg.setBackgroundColor( + if (mNightMode && y > 0) R.color.background_white.toColor(requireContext()) else if (mNightMode && y == 0) R.color.background.toColor( + requireContext() + ) else R.color.transparent.toColor(requireContext()) + ) navigationBg.setImageDrawable(null) } else if (mNightMode) { navigationBg.setBackgroundColor(R.color.background_white.toColor(requireContext())) @@ -552,7 +574,11 @@ class CommunityHomeFragment : LazyFragment() { } } mBinding?.run { - root.setBackgroundColor(if (viewPager.currentItem == TAB_FORUM_INDEX) R.color.background_white.toColor(requireContext()) else R.color.background.toColor(requireContext())) + root.setBackgroundColor( + if (viewPager.currentItem == TAB_FORUM_INDEX) R.color.background_white.toColor(requireContext()) else R.color.background.toColor( + requireContext() + ) + ) topBg.run { visibleIf(!mNightMode) post { diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeViewModel.kt index 4fa40e2284..3daf5fad05 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeViewModel.kt @@ -19,8 +19,8 @@ class CommunityHomeViewModel(application: Application) : AndroidViewModel(applic private val mApi = RetrofitManager.getInstance().api val articleLiveData = MediatorLiveData() - fun getArticleData(communityId: String, articleId: String) { - mApi.getCommunityArticleDetail(communityId, articleId) + fun getArticleData(articleId: String) { + mApi.getCommunityArticleDetail(articleId) .compose(observableToMain()) .subscribe(object : Response() { override fun onResponse(response: ArticleDetailEntity?) { diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/ForumFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/ForumFragment.kt index b87c82739f..788c5c03be 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/ForumFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/ForumFragment.kt @@ -381,7 +381,7 @@ class ForumFragment: LazyFragment(), SwipeRefreshLayout.OnRefreshListener { cornerRadius = 12F.dip2px().toFloat() setColor(color) } - } else { + } else if (bannerBg.background is GradientDrawable) { (bannerBg.background as GradientDrawable).setColor(color) } } @@ -458,14 +458,6 @@ class ForumFragment: LazyFragment(), SwipeRefreshLayout.OnRefreshListener { otherWelfareContainer.visibility = View.VISIBLE otherWelfareRv.layoutManager = GridLayoutManager(requireContext(), 2) otherWelfareRv.adapter = WelfaresAdapter(requireContext(), welfareLists) - otherWelfareRv.addItemDecoration( - GridSpacingItemColorDecoration( - requireContext(), - 0, - 16, - R.color.transparent - ) - ) otherWelfareRv.addItemDecoration(GridSpacingItemColorDecoration(requireContext(), 8, 8, R.color.transparent)) } } diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/HorizontalForumsAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/HorizontalForumsAdapter.kt index 2351160541..c56f3bc671 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/HorizontalForumsAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/HorizontalForumsAdapter.kt @@ -74,7 +74,7 @@ class HorizontalForumsAdapter( } root.setOnClickListener { - handleItemViewClick(this, forumEntity) + handleItemViewClick(forumEntity) } } } @@ -107,14 +107,12 @@ class HorizontalForumsAdapter( } } - private fun handleItemViewClick(binding: HorizontalForumItemBinding, forumEntity: ForumEntity) { - binding.root.setOnClickListener { - val bbsType = if (forumEntity.type == "official_bbs") "综合论坛" else "游戏论坛" - val event = if (mEntrance.contains("最近浏览")) "click_recent_forum" else "click_following_forum" - val location = if (mEntrance.contains("最近浏览")) "推荐信息流" else "论坛页" - NewLogUtils.logForumClick(event, location, forumEntity.id, bbsType) - mContext.startActivity(ForumDetailActivity.getIntent(mContext, forumEntity.id, mEntrance)) - } + private fun handleItemViewClick(forumEntity: ForumEntity) { + val bbsType = if (forumEntity.type == "official_bbs") "综合论坛" else "游戏论坛" + val event = if (mEntrance.contains("最近浏览")) "click_recent_forum" else "click_following_forum" + val location = if (mEntrance.contains("最近浏览")) "推荐信息流" else "论坛页" + NewLogUtils.logForumClick(event, location, forumEntity.id, bbsType) + mContext.startActivity(ForumDetailActivity.getIntent(mContext, forumEntity.id, mEntrance)) } fun checkResetData(update: List) { diff --git a/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListAdapter.kt index bcca9352cf..dcc4dd7e91 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListAdapter.kt @@ -212,7 +212,7 @@ class ForumContentSearchListAdapter(context: Context, val mListViewModel: ForumC durationTv.goneIf(answer.getPassVideos().isEmpty()) durationTv.text = if (answer.getPassVideos().isNotEmpty()) answer.getPassVideos()[0].duration else "00:00" durationTv.background = GradientDrawable().apply { - setColor(R.color.black_alpha_40.toColor(mContext)) + setColor(R.color.black_alpha_60.toColor(mContext)) cornerRadius = 2F.dip2px().toFloat() } countTv.text = "${answer.count.comment}评论 · ${answer.count.vote}点赞" diff --git a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperFragment.kt index 20f78112d8..620ccd075f 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperFragment.kt @@ -116,7 +116,7 @@ class HomeSearchToolWrapperFragment : SearchToolWrapperFragment() { mBinding?.noConnectionContainer?.reuseNoConnection?.visibility = View.VISIBLE mBinding?.loadingContainer?.reuseLlLoading?.visibility = View.GONE mBinding?.noConnectionContainer?.reuseNoConnection?.setOnClickListener { - mViewModel?.getTabs() + mViewModel?.getHomeContentUnion() mBinding?.noConnectionContainer?.reuseNoConnection?.visibility = View.GONE mBinding?.loadingContainer?.reuseLlLoading?.visibility = View.VISIBLE } diff --git a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt index 31440871ac..098dd02eed 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt @@ -9,6 +9,8 @@ import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.R import com.gh.gamecenter.entity.SubjectRecommendEntity import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.singleToMain +import com.gh.gamecenter.entity.HomeDataEntity import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp import io.reactivex.android.schedulers.AndroidSchedulers @@ -21,49 +23,57 @@ class HomeSearchToolWrapperViewModel(application: Application) : AndroidViewMode var forumTabPairs: ArrayList> = arrayListOf() val tabs = MutableLiveData>() + val homeDataLiveData = MutableLiveData() val error = MutableLiveData() var appBarOffset = 0 init { - getTabs() + getHomeContentUnion() } @SuppressLint("CheckResult") - fun getTabs() { + fun getHomeContentUnion(isRefresh: Boolean = false) { RetrofitManager.getInstance().api - .getHomeTabs(HaloApp.getInstance().channel, BuildConfig.VERSION_NAME) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse>() { - override fun onSuccess(data: ArrayList) { - forumTabPairs.clear() - for ((index, tab) in data.withIndex()) { - if (tab.type == "top_game_comment") { - tab.primaryColor = R.color.amway_primary_color.toColor() - tab.useLightStyle = true - tab.currentSelectColor = tab.primaryColor - } + .getHomeContentUnion(BuildConfig.VERSION_NAME, HaloApp.getInstance().channel) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: HomeDataEntity) { + val homeTab = data.homeTab + if (homeTab.isNotEmpty()) { + forumTabPairs.clear() + for ((index, tab) in homeTab.withIndex()) { + if (tab.type == "top_game_comment") { + tab.primaryColor = R.color.amway_primary_color.toColor() + tab.useLightStyle = true + tab.currentSelectColor = tab.primaryColor + } - if (tab.type == "home") { - tab.useLightStyle = true - } + if (tab.type == "home") { + tab.useLightStyle = true + } - if (tab.type == "bbs") { - forumTabPairs.add(Pair(index, tab.link ?: "")) - } + if (tab.type == "bbs") { + forumTabPairs.add(Pair(index, tab.link ?: "")) + } - if (tab.default) defaultTabPosition = index + if (tab.default) defaultTabPosition = index + } + if (homeTab.size == 0) { + homeTab.add(SubjectRecommendEntity(type = "home")) + } + if (!isRefresh) { + tabs.postValue(homeTab) + } } - if (data.size == 0) { - data.add(SubjectRecommendEntity(type = "home")) - } - tabs.postValue(data) + homeDataLiveData.postValue(data) } override fun onFailure(exception: Exception) { super.onFailure(exception) - error.postValue(exception) + if (!isRefresh) { + error.postValue(exception) + } } }) } diff --git a/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt b/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt index 488be43ddd..d2304afa3f 100644 --- a/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt @@ -10,22 +10,22 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton import com.ethanhua.skeleton.ViewSkeletonScreen -import com.gh.gamecenter.common.base.fragment.LazyFragment -import com.gh.gamecenter.common.constant.Constants import com.gh.common.exposure.ExposureListener import com.gh.common.exposure.ExposureSource -import com.gh.common.util.* -import com.gh.gamecenter.common.view.FixLinearLayoutManager +import com.gh.common.util.DialogUtils import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.LoadStatus +import com.gh.gamecenter.common.base.fragment.LazyFragment +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.goneIf import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.utils.visibleIf +import com.gh.gamecenter.common.view.FixLinearLayoutManager import com.gh.gamecenter.databinding.FragmentGameBinding import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage @@ -58,6 +58,10 @@ class GameFragment : LazyFragment() { showUnzipFailureDialog(downloadEntity) } } + + override fun onDataInit(downloadEntity: DownloadEntity) { + mListAdapter.notifyItemByDownload(downloadEntity) + } } override fun getRealLayoutId() = R.layout.fragment_game diff --git a/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt index a9c76ae845..ddc5408f03 100644 --- a/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt @@ -7,7 +7,6 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout -import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.Config @@ -42,10 +41,12 @@ import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.databinding.* import com.gh.gamecenter.entity.* import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.game.bigimagerecommend.BigImageRecommendViewHolder import com.gh.gamecenter.game.columncollection.GameColumnCollectionViewHolder import com.gh.gamecenter.game.commoncollection.CommonCollectionViewHolder import com.gh.gamecenter.game.commoncollection.detail.CommonCollectionDetailActivity import com.gh.gamecenter.game.data.GameItemData +import com.gh.gamecenter.game.doublecard.DoubleCardListViewHolder import com.gh.gamecenter.game.gallery.GameGallerySlideViewHolder import com.gh.gamecenter.game.gallery.GameGalleryViewHolder import com.gh.gamecenter.game.horizontal.GameHorizontalListViewHolder @@ -114,6 +115,8 @@ class GameFragmentAdapter( if (itemData.blankDivider != null) return ItemViewType.BLANK_DIVIDER if (itemData.rankCollection != null) return ItemViewType.RANK_COLLECTION if (itemData.gameCollection != null) return ItemViewType.GAME_COLLECTION_ITEM + if (itemData.doubleCardColumn != null) return ItemViewType.DOUBLE_CARD_COLUMN + if (itemData.bigImageRecommend != null) return ItemViewType.BIG_IMAGE_RECOMMEND return ItemViewType.LOADING } @@ -206,6 +209,12 @@ class GameFragmentAdapter( ) ) } + ItemViewType.DOUBLE_CARD_COLUMN -> { + DoubleCardListViewHolder(parent.toBinding()) + } + ItemViewType.BIG_IMAGE_RECOMMEND -> { + BigImageRecommendViewHolder(parent.toBinding()) + } else -> GameItemViewHolder(GameItemBinding.bind(mLayoutInflater.inflate(R.layout.game_item, parent, false))) } } @@ -228,6 +237,8 @@ class GameFragmentAdapter( is CommonCollectionViewHolder -> bindCommonCollection(holder, position) is RankCollectionViewHolder -> bindRankCollection(holder, position) is HomeGameCollectionViewHolder -> bindGameCollection(holder, position) + is DoubleCardListViewHolder -> bindGameDoubleCardList(holder, position) + is BigImageRecommendViewHolder -> bindBigImageRecommend(holder, position) } } @@ -389,8 +400,10 @@ class GameFragmentAdapter( val rankCollection = mItemDataList[position].rankCollection val rankCollectionAdapter = holder.bindRankCollection(rankCollection!!) { - NewLogUtils.logColumnCategoryHomeContentClick(it.name ?: "", it.id ?: "", rankCollection.name ?: "", - rankCollection.id ?: "","版块",mViewModel.blockData?.name?:"") + NewLogUtils.logColumnCategoryHomeContentClick( + it.name ?: "", it.id ?: "", rankCollection.name ?: "", + rankCollection.id ?: "", "版块", mViewModel.blockData?.name ?: "" + ) } val exposureEventList = arrayListOf() @@ -518,7 +531,7 @@ class GameFragmentAdapter( val subjectAdapter = holder.bindHorizontalList(subjectEntity!!) if (subjectEntity.type != "game_horizontal") { - holder.binding.horizontalRv.doOnScrolledSpecificDistance(distanceX = DisplayUtils.dip2px(24f), singleTimeEvent = true) { + holder.binding.recyclerView.doOnScrolledSpecificDistance(distanceX = DisplayUtils.dip2px(24f), singleTimeEvent = true) { MtaHelper.onEvent("游戏专题", "滑动", subjectEntity.name) } } @@ -649,7 +662,7 @@ class GameFragmentAdapter( LogUtils.logRecommendClick( "版块:${blockData?.text ?: ""}", - entity.name, entity.type, entity.text,entity.link, clickedPosition + entity.name, entity.type, entity.text, entity.link, clickedPosition ) val entrance = "(推荐入口)" @@ -1105,6 +1118,43 @@ class GameFragmentAdapter( holder.bindGameCollectionList(gameCollectionItemDataList, "版块内容列表") } + private fun bindGameDoubleCardList(holder: DoubleCardListViewHolder, position: Int) { + mItemDataList[position].doubleCardColumn?.data?.run { + val subjectEntity = mItemDataList[position].doubleCardColumn!! + val subjectAdapter = holder.bindDoubleCardList(subjectEntity) + + val exposureEventList = arrayListOf() + runOnIoThread(true) { + for (i in 0 until subjectAdapter.itemCount) { + if (i >= size) break + + get(i).sequence = i + val event = ExposureEvent.createEventWithSourceConcat( + gameEntity = get(i), + basicSource = mBasicExposureSource, + source = listOf(ExposureSource("专题", subjectEntity.name ?: "")) + ) + exposureEventList.add(event) + } + } + mItemDataList[position].exposureEventList = exposureEventList + subjectAdapter.exposureEventList = exposureEventList + } + } + + private fun bindBigImageRecommend(holder: BigImageRecommendViewHolder, position: Int) { + mItemDataList[position].bigImageRecommend?.run { + holder.bindBigImageRecommend(this, "(板块)") { + mViewModel.blockData?.let { blockData -> + NewFlatLogUtils.logBlockGameContentCardClick( + blockData.link ?: "", blockData.name ?: "", + it.type ?: "", it.link ?: "", it.linkText ?: "" + ) + } + } + } + } + override fun getItemCount(): Int { return if (mItemDataList.size > 0) mItemDataList.size + 1 else mItemDataList.size } diff --git a/app/src/main/java/com/gh/gamecenter/game/GameViewModel.kt b/app/src/main/java/com/gh/gamecenter/game/GameViewModel.kt index a74ac8df51..a0f2a37b4a 100644 --- a/app/src/main/java/com/gh/gamecenter/game/GameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/game/GameViewModel.kt @@ -472,7 +472,7 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt var isTopItemShown = false // 轮播图+导航栏+专题入口 - if (blockData == null || blockData?.display?.recommend!! || blockData?.display?.slide!!|| blockData?.display?.navigation!!) { + if (blockData == null || blockData?.display?.recommend!! || blockData?.display?.slide!! || blockData?.display?.navigation!!) { isTopItemShown = true val itemDataTop = GameItemData() itemDataTop.slideList = mSlideList @@ -487,6 +487,19 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt mSmartSubject = null // 防止重复插入 } + val iterator = mSubjectList.iterator() + while (iterator.hasNext()) { + val item = iterator.next() + // 双列卡片专题过滤掉无封面图游戏 + if (item.type == "game_double_card") { + item.data = item.data?.filter { it.columnImage.isNotBlank() }?.toMutableList() + // 游戏数量小于2个不显示专题,所以直接去掉 + if ((item.data?.size ?: 0) < 2) { + iterator.remove() + } + } + } + // 专题 "type": "image/game_vertical/game_horizontal" for ((index, subjectEntity) in mSubjectList.withIndex()) { var containsImageBeforeHead = false // 在专题名上面是否有大图 @@ -552,7 +565,12 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt if (subjectEntity.type == "image" || subjectEntity.type == "image_slide") continue } - if ((subjectEntity.type != "gallery" && subjectEntity.type != "column_collection") + if ((subjectEntity.type != "gallery" + && subjectEntity.type != "column_collection" + && subjectEntity.type != "community_article" + && subjectEntity.type != "question" + && subjectEntity.type != "bbs_video" + && subjectEntity.type != "news") || (subjectEntity.type == "column_collection" && subjectEntity.style != "top") ) { val itemDataHead = GameItemData() @@ -678,6 +696,25 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt continue } + if (subjectEntity.type == "game_double_card") { + val itemDataSubject = GameItemData() + itemDataSubject.doubleCardColumn = subjectEntity + appendAdditionalInfoToSubjectGame(subjectEntity, index) + mItemDataListCache.add(itemDataSubject) + continue + } + + if (subjectEntity.type == "community_article" + || subjectEntity.type == "question" + || subjectEntity.type == "bbs_video" + || subjectEntity.type == "news" + ) { + val itemDataSubject = GameItemData() + itemDataSubject.bigImageRecommend = subjectEntity + mItemDataListCache.add(itemDataSubject) + continue + } + if (!data.isNullOrEmpty()) { for (i in 0 until data.size) { val game = data[i] diff --git a/app/src/main/java/com/gh/gamecenter/game/bigimagerecommend/BigImageRecommendViewHolder.kt b/app/src/main/java/com/gh/gamecenter/game/bigimagerecommend/BigImageRecommendViewHolder.kt new file mode 100644 index 0000000000..724f2cde0a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/game/bigimagerecommend/BigImageRecommendViewHolder.kt @@ -0,0 +1,28 @@ +package com.gh.gamecenter.game.bigimagerecommend + +import com.gh.common.util.DirectUtils +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.databinding.BigImageRecommendItemBinding +import com.gh.gamecenter.entity.LinkEntity +import com.gh.gamecenter.entity.SubjectEntity + +class BigImageRecommendViewHolder(val binding: BigImageRecommendItemBinding) : BaseRecyclerViewHolder(binding.root) { + + fun bindBigImageRecommend(subjectEntity: SubjectEntity, entrance: String, clickCallback: (LinkEntity) -> Unit) { + binding.run { + ImageUtils.display(poster, subjectEntity.image) + recommendTag.goneIf(subjectEntity.recommendTag.isEmpty()) + recommendTextTwo.goneIf(subjectEntity.secondLineRecommend.isEmpty()) + recommendTag.text = subjectEntity.recommendTag + recommendTextOne.text = subjectEntity.firstLineRecommend + recommendTextTwo.text = subjectEntity.secondLineRecommend + root.setOnClickListener { + val linkEntity = LinkEntity(link = subjectEntity.id, type = subjectEntity.type, linkText = subjectEntity.columnName) + clickCallback.invoke(linkEntity) + DirectUtils.directToLinkPage(root.context, linkEntity, entrance, "") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/game/data/GameItemData.kt b/app/src/main/java/com/gh/gamecenter/game/data/GameItemData.kt index 18b9be3ab3..a86c73932a 100644 --- a/app/src/main/java/com/gh/gamecenter/game/data/GameItemData.kt +++ b/app/src/main/java/com/gh/gamecenter/game/data/GameItemData.kt @@ -25,6 +25,10 @@ class GameItemData { var gameCollection: List? = null + var doubleCardColumn: SubjectEntity? = null // 双列卡片专题 + + var bigImageRecommend: SubjectEntity? = null //提问、帖子、视频帖、文章 + var blankDivider: Float? = null // 空白的空间补全item var exposureEvent: ExposureEvent? = null diff --git a/app/src/main/java/com/gh/gamecenter/game/doublecard/DoubleCardListAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/doublecard/DoubleCardListAdapter.kt new file mode 100644 index 0000000000..a71b3b0fb8 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/game/doublecard/DoubleCardListAdapter.kt @@ -0,0 +1,94 @@ +package com.gh.gamecenter.game.doublecard + +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.view.ViewGroup +import com.gh.common.exposure.ExposureEvent +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.databinding.GameDoubleCardItemBinding +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.entity.SubjectEntity +import com.lightgame.adapter.BaseRecyclerAdapter + +class DoubleCardListAdapter( + context: Context, + private var mSubjectEntity: SubjectEntity +) : BaseRecyclerAdapter(context) { + + var exposureEventList: ArrayList? = null + + private var countAndKey: Pair? = null + + init { + var dataIds = "" + mSubjectEntity.data?.forEach { + dataIds += it.id + } + if (dataIds.isNotEmpty()) countAndKey = Pair(mSubjectEntity.data?.size ?: 0, dataIds) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = DoubleCardItemViewHolder(parent.toBinding()) + + override fun getItemCount() = if (mSubjectEntity.data?.isNotEmpty() == true) mSubjectEntity.data!!.size else 0 + + override fun onBindViewHolder(holder: DoubleCardItemViewHolder, position: Int) { + val gameEntity = mSubjectEntity.data!![position] + holder.binding.run { + poster.display(gameEntity.columnImage) + gameName.text = gameEntity.name + brief.text = + if (gameEntity.columnRecommend?.text.isNullOrBlank()) gameEntity.brief + else gameEntity.columnRecommend!!.text + gameSubtitle.run { + goneIf(gameEntity.subtitle.isBlank()) + text = gameEntity.subtitle + if (gameEntity.subtitleStyle != null) { + setTextColor(Color.parseColor("#${gameEntity.subtitleStyle?.color}")) + background = GradientDrawable().apply { + cornerRadius = 2F.dip2px().toFloat() + if (gameEntity.subtitleStyle?.style == "border") { + setColor(Color.TRANSPARENT) + setStroke(0.5F.dip2px(), Color.parseColor("#${gameEntity.subtitleStyle?.background}")) + } else { + shape = GradientDrawable.RECTANGLE + setColor(Color.parseColor("#${gameEntity.subtitleStyle?.background}")) + } + } + } + } + + root.setOnClickListener { + GameDetailActivity.startGameDetailActivity( + mContext, + gameEntity, + StringUtils.buildString("(游戏-专题:", mSubjectEntity.name, "-列表[", (position + 1).toString(), "])"), + traceEvent = exposureEventList?.get(position) + ) + } + } + } + + // notifyDataSetChanged 会出现页面抖动情况 + fun checkResetData(subjectEntity: SubjectEntity) { + var dataIds = "" + subjectEntity.data?.forEach { + dataIds += it.id + } + + mSubjectEntity = subjectEntity + if (countAndKey?.first == subjectEntity.data?.size && countAndKey?.second != dataIds) { // 数量不变,内容发生改变 + notifyItemRangeChanged(0, itemCount) + } else if (countAndKey?.first != subjectEntity.data?.size) { // 数量发生改变 + notifyDataSetChanged() + } + + // 重新刷新数据标识 + countAndKey = Pair(subjectEntity.data?.size ?: 0, dataIds) + } + + inner class DoubleCardItemViewHolder(val binding: GameDoubleCardItemBinding): BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/game/doublecard/DoubleCardListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/game/doublecard/DoubleCardListViewHolder.kt new file mode 100644 index 0000000000..9a6872a9d3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/game/doublecard/DoubleCardListViewHolder.kt @@ -0,0 +1,29 @@ +package com.gh.gamecenter.game.doublecard + +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.GridLayoutManager +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration +import com.gh.gamecenter.databinding.GameDoubleCardListBinding +import com.gh.gamecenter.entity.SubjectEntity + +class DoubleCardListViewHolder(val binding: GameDoubleCardListBinding) : BaseRecyclerViewHolder(binding.root) { + + fun bindDoubleCardList(subject: SubjectEntity): DoubleCardListAdapter { + return binding.doubleCardList.run { + var subjectAdapter = adapter + if (subjectAdapter is DoubleCardListAdapter) { + subjectAdapter.checkResetData(subject) + } else { + (itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false + isNestedScrollingEnabled = false + layoutManager = GridLayoutManager(context, 2) + subjectAdapter = DoubleCardListAdapter(context, subject) + adapter = subjectAdapter + addItemDecoration(GridSpacingItemColorDecoration(context, 8, 8, R.color.transparent)) + } + subjectAdapter + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt index 8b46636ea6..6c8687b98d 100644 --- a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt @@ -5,14 +5,17 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.gh.common.exposure.ExposureEvent import com.gh.common.util.DataCollectionUtils +import com.gh.common.util.DownloadItemUtils import com.gh.common.util.NewLogUtils import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.common.utils.goneIf import com.gh.gamecenter.databinding.GameHorizontalItemBinding import com.gh.gamecenter.entity.SubjectEntity import com.lightgame.adapter.BaseRecyclerAdapter +import com.lightgame.download.DownloadEntity class GameHorizontalAdapter( context: Context, @@ -56,18 +59,27 @@ class GameHorizontalAdapter( } override fun onBindViewHolder(holder: GameHorizontalItemViewHolder, position: Int) { - if (exposureEventList.isNullOrEmpty()) { - val params = holder.binding.root.layoutParams as RecyclerView.LayoutParams - params.width = RecyclerView.LayoutParams.WRAP_CONTENT - holder.binding.root.layoutParams = params - } + val params = holder.binding.root.layoutParams as RecyclerView.LayoutParams + params.width = if (gameName.isNotBlank()) RecyclerView.LayoutParams.WRAP_CONTENT else RecyclerView.LayoutParams.MATCH_PARENT + holder.binding.root.layoutParams = params val gameEntity = mSubjectEntity.data!![position + getIndex()] holder.binding.simpleGameContainer.run { gameIcon.displayGameIcon(gameEntity) GameHorizontalSimpleItemViewHolder.setHorizontalNameAndGravity(gameName, gameEntity.name) + downloadBtn.goneIf(!mSubjectEntity.showDownload) + gameName.maxLines = if (mSubjectEntity.showDownload) 1 else 2 } holder.bindGameHorizontalItem(gameEntity, mSubjectEntity) + var entranceResult = "" + var locationResult = "" + if (exposureEventList.isNullOrEmpty()) { + entranceResult = StringUtils.buildString(entrance, "+(", "游戏详情", "[", gameName, "]:大家都在玩[", (position + 1).toString(), "])") + locationResult = StringUtils.buildString("游戏详情-", gameName, "-大家都在玩", ":", gameEntity.name) + } else { + entranceResult = StringUtils.buildString("(游戏-专题:", mSubjectEntity.name, "-列表[", (position + 1).toString(), "])") + locationResult = StringUtils.buildString("游戏-专题-", mSubjectEntity.name, ":", gameEntity.name) + } holder.itemView.setOnClickListener { if (exposureEventList.isNullOrEmpty()) { DataCollectionUtils.uploadClick(mContext, "大家都在玩", "游戏详情", gameEntity.name) @@ -75,19 +87,63 @@ class GameHorizontalAdapter( MtaHelper.onEvent("游戏详情_新", "大家都在玩", gameName + "+" + gameEntity.name) GameDetailActivity.startGameDetailActivity( - mContext, gameEntity, - StringUtils.buildString(entrance, "+(", "游戏详情", "[", gameName, "]:大家都在玩[", (position + 1).toString(), "])") + mContext, + gameEntity, + entranceResult ) } else { GameDetailActivity.startGameDetailActivity( mContext, gameEntity, - StringUtils.buildString("(游戏-专题:", mSubjectEntity.name, "-列表[", (position + 1).toString(), "])"), + entranceResult, traceEvent = exposureEventList!![position] ) } NewLogUtils.logGameDetailPopularClick(gameName, gameId, "game", gameEntity.name ?: "") } + + if (mSubjectEntity.showDownload) { + DownloadItemUtils.setOnClickListener( + mContext, + holder.binding.simpleGameContainer.downloadBtn, + gameEntity, + position, + this, + entranceResult, + locationResult, + if (position < (exposureEventList?.size ?: 0)) exposureEventList!![position] else null + ) + + DownloadItemUtils.updateDownloadButton( + mContext, + holder.binding.simpleGameContainer.downloadBtn, + gameEntity + ) + } + } + + fun notifyItemByDownload(downloadEntity: DownloadEntity?) { + if (downloadEntity == null) { + notifyDataSetChanged() + } else { + mSubjectEntity.data?.forEachIndexed { position, gameEntity -> + if (downloadEntity.gameId == gameEntity.id) { + notifyItemChanged(position - getIndex()) + return + } + } + } + } + + fun notifyChildItem(packageName: String) { + mSubjectEntity.data?.forEachIndexed { position, gameEntity -> + gameEntity.getApk().forEach { apkEntity -> + if (apkEntity.packageName == packageName) { + notifyItemChanged(position - getIndex()) + return + } + } + } } // notifyDataSetChanged 会出现页面抖动情况 diff --git a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalListViewHolder.kt index ddcf3077a1..3b0a98826b 100644 --- a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalListViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalListViewHolder.kt @@ -12,14 +12,14 @@ class GameHorizontalListViewHolder(val binding: GameHorizontalListBinding) : Bas fun bindHorizontalList(subjectEntity: SubjectEntity): GameHorizontalAdapter { val context = binding.root.context - var subjectAdapter = binding.horizontalRv.adapter + var subjectAdapter = binding.recyclerView.adapter if (subjectAdapter == null) { - binding.horizontalRv.setPadding(5F.dip2px(), 8F.dip2px(), 5F.dip2px(), 8F.dip2px()) - binding.horizontalRv.layoutManager = GridLayoutManager(context, 4) + binding.recyclerView.setPadding(5F.dip2px(), 8F.dip2px(), 5F.dip2px(), 8F.dip2px()) + binding.recyclerView.layoutManager = GridLayoutManager(context, 4) subjectAdapter = GameHorizontalAdapter(context, subjectEntity) - (binding.horizontalRv.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false - binding.horizontalRv.adapter = subjectAdapter - binding.horizontalRv.isNestedScrollingEnabled = false + (binding.recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false + binding.recyclerView.adapter = subjectAdapter + binding.recyclerView.isNestedScrollingEnabled = false } else { (subjectAdapter as GameHorizontalAdapter).checkResetData(subjectEntity) } diff --git a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSlideAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSlideAdapter.kt index 3c2141b251..6173fa362d 100644 --- a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSlideAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSlideAdapter.kt @@ -3,16 +3,15 @@ package com.gh.gamecenter.game.horizontal import android.content.Context import android.view.ViewGroup import com.gh.common.exposure.ExposureEvent +import com.gh.common.util.DownloadItemUtils import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.R -import com.gh.gamecenter.common.utils.dip2px -import com.gh.gamecenter.common.utils.safelyGetInRelease -import com.gh.gamecenter.common.utils.toBinding -import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.* import com.gh.gamecenter.databinding.GameHorizontalItemBinding import com.gh.gamecenter.entity.SubjectEntity import com.lightgame.adapter.BaseRecyclerAdapter +import com.lightgame.download.DownloadEntity class GameHorizontalSlideAdapter( context: Context, @@ -56,6 +55,8 @@ class GameHorizontalSlideAdapter( holder.binding.simpleGameContainer.root.setPadding(8F.dip2px(), 0, 8F.dip2px(), 0) holder.binding.root.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + holder.binding.simpleGameContainer.downloadBtn.goneIf(!mSubjectEntity.showDownload) + holder.binding.simpleGameContainer.gameName.maxLines = if (mSubjectEntity.showDownload) 1 else 2 val gameEntity = mSubjectEntity.data!![position + getIndex()] holder.binding.simpleGameContainer.run { @@ -78,8 +79,51 @@ class GameHorizontalSlideAdapter( ) } } + + if (mSubjectEntity.showDownload) { + DownloadItemUtils.setOnClickListener( + mContext, + holder.binding.simpleGameContainer.downloadBtn, + gameEntity, + position, + this, + StringUtils.buildString("(游戏-专题:", mSubjectEntity.name, "-列表[", (position + 1).toString(), "])"), + StringUtils.buildString("游戏-专题-", mSubjectEntity.name, ":", gameEntity.name), + if (position < (exposureEventList?.size ?: 0)) exposureEventList!![position] else null + ) + + DownloadItemUtils.updateDownloadButton( + mContext, + holder.binding.simpleGameContainer.downloadBtn, + gameEntity + ) + } } + fun notifyItemByDownload(downloadEntity: DownloadEntity?) { + if (downloadEntity == null) { + notifyDataSetChanged() + } else { + mSubjectEntity.data?.forEachIndexed { position, gameEntity -> + if (downloadEntity.gameId == gameEntity.id) { + notifyItemChanged(position - getIndex()) + return + } + } + } + } + fun notifyChildItem(packageName: String) { + mSubjectEntity.data?.forEachIndexed { position, gameEntity -> + gameEntity.getApk().forEach { apkEntity -> + if (apkEntity.packageName == packageName) { + notifyItemChanged(position - getIndex()) + return + } + } + } + } + + // notifyDataSetChanged 会出现页面抖动情况 fun checkResetData(subjectEntity: SubjectEntity) { var dataIds = "" diff --git a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSlideListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSlideListViewHolder.kt index dc13aadd1b..33d39c2006 100644 --- a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSlideListViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSlideListViewHolder.kt @@ -27,17 +27,17 @@ class GameHorizontalSlideListViewHolder(val binding: GameHorizontalListBinding) offsetable: IOffsetable ): GameHorizontalSlideAdapter { val context = binding.root.context - var subjectAdapter = binding.horizontalRv.adapter + var subjectAdapter = binding.recyclerView.adapter binding.fakeRemarkLine.setBackgroundColor(R.color.btn_gray_light.toColor(context)) if (subjectAdapter == null) { - binding.horizontalRv.setPadding(10F.dip2px(), 8F.dip2px(), 10F.dip2px(), 8F.dip2px()) - binding.horizontalRv.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) - binding.horizontalRv.clipToPadding = false + binding.recyclerView.setPadding(10F.dip2px(), 8F.dip2px(), 10F.dip2px(), 8F.dip2px()) + binding.recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) + binding.recyclerView.clipToPadding = false subjectAdapter = GameHorizontalSlideAdapter(context, subjectEntity) - (binding.horizontalRv.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false - binding.horizontalRv.adapter = subjectAdapter - binding.horizontalRv.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING) - binding.horizontalRv.isNestedScrollingEnabled = false + (binding.recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false + binding.recyclerView.adapter = subjectAdapter + binding.recyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING) + binding.recyclerView.isNestedScrollingEnabled = false if (subjectEntity.data?.firstOrNull()?.test?.testTime != null && mLastScrolledPosition == 0) { scrollToSpecificItemForTheFirstTime(subjectEntity) @@ -52,12 +52,12 @@ class GameHorizontalSlideListViewHolder(val binding: GameHorizontalListBinding) if (subjectEntity.data?.firstOrNull()?.test?.testTime != null && mLastScrolledPosition == 0) { scrollToSpecificItemForTheFirstTime(subjectEntity) } else { - binding.horizontalRv.scrollToPosition(0) + binding.recyclerView.scrollToPosition(0) } } else { // 可能会因为上下复用数据变化而出现 IndexOutOfBoundsException 异常,毕竟有局部更新功能... tryCatchInRelease { - binding.horizontalRv.scrollBy(offset, offset) + binding.recyclerView.scrollBy(offset, offset) } } } @@ -77,7 +77,7 @@ class GameHorizontalSlideListViewHolder(val binding: GameHorizontalListBinding) } } - binding.horizontalRv.addOnScrollListener(mScrollListener!!) + binding.recyclerView.addOnScrollListener(mScrollListener!!) } return subjectAdapter @@ -85,7 +85,7 @@ class GameHorizontalSlideListViewHolder(val binding: GameHorizontalListBinding) private fun scrollToSpecificItemForTheFirstTime(subjectEntity: SubjectEntity) { val position = findTheLatestTestGamePosition(subjectEntity) - binding.horizontalRv.scrollToPosition(position) + binding.recyclerView.scrollToPosition(position) } /** @@ -136,7 +136,7 @@ class GameHorizontalSlideListViewHolder(val binding: GameHorizontalListBinding) // 这里显示一条假的线供上层 recyclerView 列表使用,因为写在 item 里面很复杂 binding.root.post { binding.fakeRemarkLine.layoutParams = (binding.fakeRemarkLine.layoutParams as ConstraintLayout.LayoutParams).apply { - topMargin = getTopOfLineContainer(binding.horizontalRv) + 2F.dip2px() // 这里加上的2F是点的边距 + topMargin = getTopOfLineContainer(binding.recyclerView) + 2F.dip2px() // 这里加上的2F是点的边距 } } binding.fakeRemarkLine.visibility = View.VISIBLE diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt index ecf8a068aa..5352a83b4b 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt @@ -87,6 +87,10 @@ class GameCollectionDetailFragment : showUnzipFailureDialog(downloadEntity) } } + + override fun onDataInit(downloadEntity: DownloadEntity) { + mAdapter?.notifyItemByDownload(downloadEntity) + } } override fun getLayoutId() = 0 diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt index 86f2f2cabc..e509b8afbc 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionPosterFragment.kt @@ -5,19 +5,17 @@ import android.os.Build import android.os.Bundle import android.view.View import androidx.annotation.RequiresApi -import com.gh.gamecenter.core.runOnUiThread -import com.gh.common.util.* +import com.gh.common.util.DirectUtils import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.ToolbarFragment import com.gh.gamecenter.common.callback.BiCallback import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.common.utils.ImageUtils -import com.gh.gamecenter.common.utils.BitmapUtils +import com.gh.gamecenter.core.runOnUiThread import com.gh.gamecenter.databinding.FragmentGameCollectionPosterBinding import com.gh.gamecenter.databinding.LayoutGameCollectionTagBinding import com.gh.gamecenter.entity.GamesCollectionDetailEntity import com.gh.gamecenter.manager.UserManager -import com.gh.gamecenter.common.base.fragment.ToolbarFragment class GameCollectionPosterFragment : ToolbarFragment() { @@ -62,9 +60,13 @@ class GameCollectionPosterFragment : ToolbarFragment() { ImageUtils.getBitmap(cover, object : BiCallback { @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1) override fun onFirst(first: Bitmap) { - val blurBitmap = BitmapUtils.getBlurBitmap(requireContext(), first, 16) - runOnUiThread { - posterBg.setImageBitmap(blurBitmap) + try { + val blurBitmap = BitmapUtils.getBlurBitmap(requireContext(), first, 16) + runOnUiThread { + posterBg.setImageBitmap(blurBitmap) + } + } catch (t: Throwable) { + posterBg.setImageBitmap(first) } } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/conversation/GameCollectionCommentConversationFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/conversation/GameCollectionCommentConversationFragment.kt index 25a7f929b3..4cf9cc0d2e 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/conversation/GameCollectionCommentConversationFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/conversation/GameCollectionCommentConversationFragment.kt @@ -274,7 +274,7 @@ class GameCollectionCommentConversationFragment : orderSfv.run { setContainerBackground(R.drawable.button_round_f5f5f5.toDrawable(requireContext())) setIndicatorBackground( - R.drawable.progressbar_game_collection_primary.toDrawable( + R.drawable.bg_game_collection_sfv_indicator.toDrawable( requireContext() ) ) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt index 70c43a1e89..d09138edf5 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/publish/GameCollectionEditActivity.kt @@ -17,6 +17,7 @@ import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.CropImageActivity import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity +import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.PatternUtils @@ -228,7 +229,13 @@ class GameCollectionEditActivity : ToolBarActivity() { } finish() } else { - ErrorHelper.handleError(this, it.exception?.response()?.errorBody()?.string()) + ErrorHelper.handleError(this, it.exception?.response()?.errorBody()?.string(), false, object : ConfirmListener { + override fun onConfirm() { + if (::mMenuPost.isInitialized) { + onMenuItemClick(mMenuPost) + } + } + }) } } mViewModel.detailLiveData.observe(this) { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt index e5b6abda37..88349a4eed 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt @@ -545,7 +545,7 @@ class GameCollectionSquareFragment : LazyListFragment(context) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = GameDetailContentCardContentItemViewHolder(parent.toBinding()) + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is GameDetailContentCardContentItemViewHolder) { + holder.binding.root.setTextColor(if (isHighlightBg) R.color.text_subtitle.toColor(mContext) else R.color.text_subtitleDesc.toColor(mContext)) + if (linkEntity.type == "func_server" && linkEntity.server != null && linkEntity.server?.calendar?.isNotEmpty() == true) { + val calendarList = linkEntity.server!!.calendar + val realPosition = position % calendarList.size + calendarList.safelyGetInRelease(realPosition)?.let { + val serverTime = + if (TimeUtils.isToday(it.getTime())) + it.getFormatTime("今天 HH:mm") + else if (TimeUtils.isTomorrow(it.getTime())) + it.getFormatTime("明天 HH:mm") + else + it.getFormatTime("MM-dd HH:mm") + holder.binding.root.text = "$serverTime ${it.type}" + } + } + } + } + + override fun getItemCount(): Int = Int.MAX_VALUE + + class GameDetailContentCardContentItemViewHolder(var binding: ItemGameDetailContentCardContentBinding) : RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt index be22a37595..0e3564da3b 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt @@ -1,19 +1,22 @@ package com.gh.gamecenter.gamedetail +import android.animation.Animator +import android.animation.ValueAnimator import android.annotation.SuppressLint import android.content.Intent import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color import android.graphics.drawable.GradientDrawable -import android.os.Build -import android.os.Bundle +import android.os.* import android.text.SpannableStringBuilder import android.text.Spanned import android.text.TextUtils import android.view.* +import android.view.animation.AccelerateDecelerateInterpolator import android.widget.FrameLayout import android.widget.ImageView +import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout @@ -21,13 +24,16 @@ import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.core.view.children import androidx.core.widget.TextViewCompat +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.Observer +import androidx.viewpager2.widget.ViewPager2 import com.ethanhua.skeleton.Skeleton import com.ethanhua.skeleton.ViewSkeletonScreen import com.gh.common.constant.Config import com.gh.common.databind.BindingAdapters import com.gh.common.exposure.ExposureEvent +import com.gh.common.exposure.ExposureSource import com.gh.common.repository.ReservationRepository import com.gh.common.simulator.SimulatorGameManager import com.gh.common.util.* @@ -57,8 +63,10 @@ import com.gh.gamecenter.gamedetail.desc.DescFragment import com.gh.gamecenter.gamedetail.dialog.GameBigEventDialog import com.gh.gamecenter.gamedetail.dialog.GameDetailMoreDialog import com.gh.gamecenter.gamedetail.dialog.GameTagsDialog +import com.gh.gamecenter.gamedetail.entity.ContentCardEntity import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity import com.gh.gamecenter.gamedetail.fuli.FuLiFragment +import com.gh.gamecenter.gamedetail.fuli.kaifu.ServersCalendarActivity import com.gh.gamecenter.gamedetail.rating.RatingFragment import com.gh.gamecenter.gamedetail.video.TopVideoView import com.gh.gamecenter.home.video.ScrollCalculatorHelper @@ -84,6 +92,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import retrofit2.HttpException +import java.lang.ref.WeakReference import java.util.* import kotlin.math.abs @@ -116,6 +125,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { private var mRecommendDisposable: Disposable? = null private var mRecommendPopupEntity: RecommendPopupEntity? = null private var mTabClickEvent: Pair = Pair(0L, "") + private var mContentCardServerVp: ViewPager2? = null private lateinit var mBinding: FragmentGamedetailBinding private lateinit var mVideoBinding: PieceGameDetailVideoBinding @@ -127,20 +137,26 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { private lateinit var mPackageViewModel: PackageViewModel private lateinit var mUserViewModel: UserViewModel + private var mShowConcernOnMenu = false + private val mFragmentsList = ArrayList() private val mTabTitleList = ArrayList() + private val mLooperHandle = LooperHandle(this) + private val mServerLooperKey = 123 private val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { if (downloadEntity.gameId == mViewModel.gameId) { - if (downloadEntity.status == DownloadStatus.add || downloadEntity.status == DownloadStatus.done || downloadEntity.status == DownloadStatus.downloading || downloadEntity.status == DownloadStatus.pause || downloadEntity.status == DownloadStatus.redirected) { + if (downloadEntity.status == DownloadStatus.add + || downloadEntity.status == DownloadStatus.done + || downloadEntity.status == DownloadStatus.downloading + || downloadEntity.status == DownloadStatus.pause + || downloadEntity.status == DownloadStatus.redirected) { showInstallHint() SPUtils.setBoolean(Constants.SP_SHOULD_SHOW_GAME_DETAIL_INSTALL_GUIDE, true) } - } - if (mGameEntity != null && mGameEntity!!.getApk().size == 1) { - val url = mGameEntity!!.getApk()[0].url - if (downloadEntity.url == url) { + + if (mGameEntity?.getApk()?.size == 1) { if ("pause" != DownloadManager.getInstance().getStatus(downloadEntity.url)?.status) { mDownloadEntity = downloadEntity DetailDownloadUtils.detailInvalidate(detailViewHolder) @@ -149,43 +165,44 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) } - } - val meta = downloadEntity.getMetaExtra(PageSwitchDataHelper.PAGE_GAME_DETAIL_RECOMMEND) - if (downloadEntity.status == DownloadStatus.add) { - if (downloadEntity.gameId == mViewModel.game?.id) { + + val gameDetailRecommendMeta = downloadEntity.getMetaExtra(PageSwitchDataHelper.PAGE_GAME_DETAIL_RECOMMEND) + if (downloadEntity.status == DownloadStatus.add) { mRecommendBinding.recommendView.postDelayed({ showRecommendView() }, 500) - } - if (meta.isNotEmpty() && mRecommendPopupEntity != null) { - LogUtils.uploadRecommendPopup( - "recommend_pop_download", - mRecommendPopupEntity?.id, - mViewModel.game?.id ?: "", - mViewModel.game?.name ?: "", - mRecommendPopupEntity?.popupDetail?.link?.type, - mRecommendPopupEntity?.popupDetail?.link?.text, - downloadEntity.gameId, - downloadEntity.name - ) - } - } else if (downloadEntity.status == DownloadStatus.done) { - if (downloadEntity.gameId == mViewModel.game?.id) { + if (gameDetailRecommendMeta.isNotEmpty() && mRecommendPopupEntity != null) { + LogUtils.uploadRecommendPopup( + "recommend_pop_download", + mRecommendPopupEntity?.id, + mViewModel.game?.id ?: "", + mViewModel.game?.name ?: "", + mRecommendPopupEntity?.popupDetail?.link?.type, + mRecommendPopupEntity?.popupDetail?.link?.text, + downloadEntity.gameId, + downloadEntity.name + ) + } + } else if (downloadEntity.status == DownloadStatus.done) { hideRecommendView() - } - if (meta.isNotEmpty() && mRecommendPopupEntity != null) { - LogUtils.uploadRecommendPopup( - "recommend_pop_download_complete", - mRecommendPopupEntity?.id, - mViewModel.game?.id ?: "", - mViewModel.game?.name ?: "", - mRecommendPopupEntity?.popupDetail?.link?.type, - mRecommendPopupEntity?.popupDetail?.link?.text, - downloadEntity.gameId, - downloadEntity.name - ) + if (gameDetailRecommendMeta.isNotEmpty() && mRecommendPopupEntity != null) { + LogUtils.uploadRecommendPopup( + "recommend_pop_download_complete", + mRecommendPopupEntity?.id, + mViewModel.game?.id ?: "", + mViewModel.game?.name ?: "", + mRecommendPopupEntity?.popupDetail?.link?.type, + mRecommendPopupEntity?.popupDetail?.link?.text, + downloadEntity.gameId, + downloadEntity.name + ) + } } } } } + + override fun onDataInit(downloadEntity: DownloadEntity) { + onDataChanged(downloadEntity) + } } // 下载按钮ViewHolder @@ -203,8 +220,67 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { mTraceEvent ) - override fun getLayoutId(): Int = 0 + private val contentCardClick: (contentCard: ContentCardEntity) -> Unit = { contentCardEntity -> + NewFlatLogUtils.logGameDetailGameContentCardClick(contentCardEntity.title ?: "", mGameEntity?.name ?: "", mGameEntity?.id ?: "", contentCardEntity.type ?: "", contentCardEntity.link ?: "", contentCardEntity.text ?: "") + when (contentCardEntity.type) { + "func_server" -> { + if (contentCardEntity.server != null) { + requireContext().startActivity( + ServersCalendarActivity.getIntent( + requireContext(), + mViewModel.game!!, contentCardEntity.server!!, + mNewGameDetailEntity?.me + ) + ) + } + } + "func_libao" -> { + mBodyBinding.gamedetailVp.currentItem = 0 + mBodyBinding.gamedetailAppbar.setExpanded(false, true) + val fragment = mFragmentsList[0] + if (fragment is DescFragment && fragment.isAdded) { + fragment.scrollToLibao() + } + } + "func_related_version" -> { + mBodyBinding.gamedetailVp.currentItem = 0 + mBodyBinding.gamedetailAppbar.setExpanded(false, true) + val fragment = mFragmentsList[0] + if (fragment is DescFragment && fragment.isAdded) { + fragment.scrollToRelatedVersion() + } + } + "func_zone" -> { + val gameDetailEntity = mViewModel.gameDetailLiveData.value?.data + if (contentCardEntity.zoneTab && gameDetailEntity?.zone != null && gameDetailEntity.zone!!.style == "link") { + requireContext().startActivity(WebActivity.getIntent(requireContext(), gameDetailEntity.zone!!.link, true)) + } + } + "func_tool_kit" -> { + if (contentCardEntity.toolkit.isNotEmpty()) { + contentCardEntity.toolkit.safelyGetInRelease(0)?.let { + val url = it.url + if (url != null && url.contains(Config.URL_ARTICLE)) { + val newsId = url.substring(url.lastIndexOf("/") + 1, url.length - 5) // 5: ".html" + val intent = NewsDetailActivity.getIntentById(requireContext(), newsId, "游戏详情->内容卡片") + requireContext().startActivity(intent) + } else { + requireContext().startActivity( + WebActivity.getWebByCollectionTools( + requireContext(), + it, + false + ) + ) + } + } + } + } + else -> DirectUtils.directToLinkPage(requireContext(), contentCardEntity.toLinkEntity(), mEntrance, "游戏详情->内容卡片", ExposureEvent.createEvent(null, listOf(ExposureSource("游戏详情", mGameEntity?.id ?: ""), ExposureSource("内容卡片", contentCardEntity.id)))) + } + } + override fun getLayoutId(): Int = 0 override fun getInflatedLayout(): View { return FragmentGamedetailBinding.inflate(LayoutInflater.from(requireContext())).apply { mBinding = this @@ -234,7 +310,9 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { GameDetailMoreDialog.showMoreDialog( requireActivity() as AppCompatActivity, mGameEntity, - mNewGameDetailEntity?.shortId ?: "" + mNewGameDetailEntity?.shortId ?: "", + mShowConcernOnMenu, + mNewGameDetailEntity!!.me.isGameConcerned ) MtaHelper.onEvent("游戏详情_新", "更多按钮", mViewModel.game?.name ?: "") } @@ -253,7 +331,6 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { mSearchMenuItem?.setOnMenuItemClickListener(menuItemClickListener) mDownloadMenuItem?.actionView?.setOnClickListener { MtaHelper.onEvent("游戏详情_新", "下载管理图标", mViewModel.game?.name ?: "") -// MtaHelper.onEvent("下载管理", "下载管理入口", (requireActivity() as BaseActivity).activityNameInChinese) val intent = DownloadManagerActivity.getDownloadMangerIntent(requireContext(), mEntrance) startActivity(intent) } @@ -402,6 +479,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { private fun initGameSubtitle(gameTitleTv: TextView, gameSubtitle: String, gameSubtitleStyle: TagStyleEntity? = null, advanceDownload: Boolean = false) { gameTitleTv.post { + if (!isAdded) return@post gameTitleTv.run { var lastPosX = 0F tryCatchInRelease { @@ -444,7 +522,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { var changeDisplayName = false if ((lineCount == 2 && layout.getEllipsisCount(1) > 0) || (lineCount == 2 && tagView.measuredWidth > remainWidth)) { val textCount = calculateTextCountByWidth(this, 2 * gameTitleTv.width - tagView.measuredWidth) - 1 - if (textCount < text.length) { + if (textCount in text.indices) { displayName = text.substring(0, textCount) + "…" changeDisplayName = true } @@ -475,6 +553,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { } private fun subtractGameNameIfNeeded(textView: TextView, name: CharSequence, tagLayout: FrameLayout) { + if (!isAdded) return textView.run { if (lineCount > 2 || (layout != null && layout.getEllipsisCount(1) > 0)) { val displayName = name.substring(0, name.length - 2) + "…" @@ -539,8 +618,6 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { } mBodyBinding.toolbar.setNavigationOnClickListener { requireActivity().finish() } - mDownloadBinding.ivConcern.visibility = View.VISIBLE - mDownloadBinding.tvConcern.visibility = View.VISIBLE } @Subscribe(threadMode = ThreadMode.MAIN) @@ -577,6 +654,11 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { showAlertDialogIfNeeded(data) initViewPage(data) + + if (data.contentCard.size > 1 && mGameEntity?.shouldUseMirrorInfo() == false) { + initGameContentCard(data.contentCard) + } + updateGameDetailTopArea() val viewHolder = detailViewHolder @@ -603,8 +685,11 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { mBinding.errorToolbarContainer.visibility = View.GONE controlReserveBtn() - mSkeleton?.hide() + showVModeIconAtBottomBarIfNeeded(data.smoothRelatedGame, mGameEntity!!) + showConcernIconAtBottomBarIfAvailable() + + mSkeleton?.hide() } else if (detailResource.status == Status.ERROR) { loadErrorControl(detailResource.exception) } @@ -702,6 +787,9 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { mTabTitleList.add(getString(R.string.game_detail_desc)) data.zone?.let { + data.contentCard.forEach { contentCard -> + if (data.contentCard.size > 1 && contentCard.type == "func_zone") return@let + } if (it.style == "link") {//显示web页面 val webFragment = childFragmentManager.findFragmentByTag("${tag}${INDEX_TRENDES}") ?: WebFragment() val webBundle = Bundle() @@ -960,6 +1048,119 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { } } + private fun initGameContentCard(linkEntityList: List) { + mBodyBinding.toolbarGapView.visibility = View.GONE + mBodyBinding.contentCardContainer.removeAllViews() + mBodyBinding.contentCardContainer.visibility = View.VISIBLE + if (linkEntityList.size >= 3) { + linkEntityList.safelyGetInRelease(0)?.let { + mBodyBinding.contentCardContainer.addView(getLargeContentCardView(it, true), LinearLayout.LayoutParams(0, 56F.dip2px(), 2F)) + } + linkEntityList.safelyGetInRelease(1)?.let { + mBodyBinding.contentCardContainer.addView(getSmallContentCardView(it), LinearLayout.LayoutParams(0, 56F.dip2px(), 1F)) + } + linkEntityList.safelyGetInRelease(2)?.let { + mBodyBinding.contentCardContainer.addView(getSmallContentCardView(it, true), LinearLayout.LayoutParams(0, 56F.dip2px(), 1F)) + } + } else if (linkEntityList.size == 2) { + linkEntityList.safelyGetInRelease(0)?.let { + mBodyBinding.contentCardContainer.addView(getLargeContentCardView(it), LinearLayout.LayoutParams(0, 56F.dip2px(), 1F)) + } + linkEntityList.safelyGetInRelease(1)?.let { + mBodyBinding.contentCardContainer.addView(getLargeContentCardView(it, isLastView = true), LinearLayout.LayoutParams(0, 56F.dip2px(), 1F)) + } + } + } + + private fun getLargeContentCardView(contentCardEntity: ContentCardEntity, isHighlightBg: Boolean = false, isLastView: Boolean = false) = LayoutGameDetailContentCardLargeBinding.inflate(layoutInflater).apply { + when { + isHighlightBg && !isLastView -> root.background = R.drawable.bg_content_card_large_primary.toDrawable(requireContext()) + !isHighlightBg && !isLastView -> root.background = R.drawable.bg_content_card_large.toDrawable(requireContext()) + !isHighlightBg && isLastView -> root.background = R.drawable.bg_content_card_large_right.toDrawable(requireContext()) + } + root.setPadding(if (isLastView) 20F.dip2px() else 12F.dip2px(), 0, if (isLastView) 8F.dip2px() else 12F.dip2px(), 0) + titleTv.text = contentCardEntity.title + ImageUtils.display(iconIv, contentCardEntity.icon) + if (contentCardEntity.des.isNotEmpty() || contentCardEntity.type == "func_libao") { + contentTv.visibility = View.VISIBLE + contentVp.visibility = View.GONE + contentTv.text = if (contentCardEntity.type == "func_libao" && contentCardEntity.des.isEmpty()) "${contentCardEntity.libao?.total}个游戏礼包" else contentCardEntity.des + contentTv.setTextColor(if (isHighlightBg) R.color.text_subtitle.toColor(requireContext()) else R.color.text_subtitleDesc.toColor(requireContext())) + } else if (contentCardEntity.type == "func_server") { + contentTv.visibility = View.GONE + contentVp.visibility = View.VISIBLE + if (contentCardEntity.type == "func_server") mContentCardServerVp = contentVp + contentVp.run { + isUserInputEnabled = false + orientation = ViewPager2.ORIENTATION_VERTICAL + adapter = GameDetailContentCardContentAdapter(requireContext(), contentCardEntity, isHighlightBg) + if (contentCardEntity.type == "func_server") { + val nowTime = System.currentTimeMillis() + var timeDiff = 0L + var closestIndex = 0 + contentCardEntity.server!!.calendar.forEachIndexed { index, serverCalendarEntity -> + val diff = abs(nowTime - serverCalendarEntity.getTime() * 1000L) + if (diff < timeDiff || index == 0) { + timeDiff = diff + closestIndex = index + } + } + currentItem = closestIndex + if (contentCardEntity.server!!.calendar.size > 1) { + startAutoPlay(mServerLooperKey) + } + } + } + } + redDotTv.goneIf(!((contentCardEntity.type == "func_server" && contentCardEntity.server?.total != 0) || contentCardEntity.type == "func_libao")) + if ((contentCardEntity.type == "func_server") && (contentCardEntity.server?.calendar?.isNotEmpty() == true)) redDotTv.text = contentCardEntity.server?.total.toString() + if ((contentCardEntity.type == "func_libao") && (contentCardEntity.libao != null)) redDotTv.text = contentCardEntity.libao?.total.toString() + root.setOnClickListener { + contentCardClick.invoke(contentCardEntity) + } + }.root + + private fun getSmallContentCardView(contentCardEntity: ContentCardEntity, isLastView: Boolean = false) = LayoutGameDetailContentCardSmallBinding.inflate(layoutInflater).apply { + root.background = if (isLastView) R.drawable.bg_content_card_small_right.toDrawable(requireContext()) else R.drawable.bg_content_card_small.toDrawable(requireContext()) + titleTv.text = contentCardEntity.title + ImageUtils.display(iconIv, contentCardEntity.icon) + redDotTv.goneIf(!((contentCardEntity.type == "func_server" && contentCardEntity.server?.total != 0) || contentCardEntity.type == "func_libao")) + redDotTv.layoutParams = (redDotTv.layoutParams as ViewGroup.MarginLayoutParams).apply { setMargins(0, 8F.dip2px(), if (isLastView) 8F.dip2px() else 12F.dip2px(), 0) } + if ((contentCardEntity.type == "func_server") && (contentCardEntity.server?.calendar?.isNotEmpty() == true)) redDotTv.text = contentCardEntity.server?.total.toString() + if ((contentCardEntity.type == "func_libao") && (contentCardEntity.libao != null)) redDotTv.text = contentCardEntity.libao?.total.toString() + root.setOnClickListener { + contentCardClick.invoke(contentCardEntity) + } + }.root + + private fun scrollToNextContent(viewPager: ViewPager2? = null) { + viewPager?.run { + val pxToDrag: Int = height + val animator = ValueAnimator.ofInt(0, pxToDrag) + var previousValue = 0 + animator.addUpdateListener { valueAnimator -> + val currentValue = valueAnimator.animatedValue as Int + val currentPxToDrag = (currentValue - previousValue).toFloat() + fakeDragBy(-currentPxToDrag) + previousValue = currentValue + } + animator.addListener(object : Animator.AnimatorListener { + override fun onAnimationStart(animation: Animator?) { beginFakeDrag() } + override fun onAnimationEnd(animation: Animator?) { endFakeDrag() } + override fun onAnimationCancel(animation: Animator?) { } + override fun onAnimationRepeat(animation: Animator?) { } + }) + animator.interpolator = AccelerateDecelerateInterpolator() + animator.duration = 1000 + animator.start() + } + } + + private fun startAutoPlay(key: Int) { + mLooperHandle.removeMessages(key) + mLooperHandle.sendEmptyMessageDelayed(key, CONTENT_CARD_LOOP_TIME) + } + private fun setUpTopVideo(topVideo: GameDetailEntity.Video) { GSYVideoOptionBuilder() .setIsTouchWigetFull(false) @@ -1609,6 +1810,51 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { } } + /** + * 当其它三个按钮不显示的时候才显示收藏按钮 + */ + private fun showConcernIconAtBottomBarIfAvailable() { + val installSwitchViewIsNotVisible = !mDownloadBinding.ivSwitch.isVisible + val reserveBtnIsNotVisible = !mDownloadBinding.ivReserve.isVisible + val vmodeViewIsNotVisible = !mDownloadBinding.ivVmode.isVisible + + if (installSwitchViewIsNotVisible || reserveBtnIsNotVisible || vmodeViewIsNotVisible) { + mDownloadBinding.ivConcern.visibility = View.VISIBLE + mDownloadBinding.tvConcern.visibility = View.VISIBLE + } else { + mDownloadBinding.ivConcern.visibility = View.GONE + mDownloadBinding.tvConcern.visibility = View.GONE + + mShowConcernOnMenu = true + } + } + + private fun showVModeIconAtBottomBarIfNeeded(simpleGame: SimpleGame?, gameEntity: GameEntity) { + mDownloadBinding.groupVmode.goneIf(simpleGame == null) + simpleGame?.let { + mDownloadBinding.ivVmode.displayGameIcon(simpleGame.getIcon(), simpleGame.iconSubscript) + if (gameEntity.isVGame()) { + mDownloadBinding.tvVmode.text = "下载 >" + mDownloadBinding.ivVmodeBadge.setImageResource(R.drawable.ic_switch_game_download) + } else { + mDownloadBinding.tvVmode.text = "畅玩 >" + mDownloadBinding.ivVmodeBadge.setImageResource(R.drawable.ic_switch_game_smooth) + } + + mDownloadBinding.tvVmode.setOnClickListener { mDownloadBinding.ivVmode.performClick() } + mDownloadBinding.ivVmode.enlargeTouchArea() + mDownloadBinding.ivVmode.setOnClickListener { _ -> + val downloadStatus = when (simpleGame.downloadStatus) { + "smooth" -> "畅玩" + "demo" -> "试玩" + else -> "下载" + } + NewFlatLogUtils.logHaloFunGameDetailJumpClick(downloadStatus, simpleGame.id ?: "") + DirectUtils.directToGameDetail(requireContext(), it.id ?: "", mEntrance) + } + } + } + override fun onSaveInstanceState(outState: Bundle) { mBodyBinding.gamedetailVp.let { outState.putInt(LAST_SELECTED_POSITION, it.currentItem) } super.onSaveInstanceState(outState) @@ -1644,6 +1890,11 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { } mBinding.detailLlBottom.detailLlBottom.setBackgroundColor(R.color.background.toColor(requireContext())) updateToolbarStyle(mBodyBinding.gamedetailThumbSmall.visibility == View.VISIBLE) + mViewModel.gameDetailLiveData.value?.data?.let { + if (it.contentCard.size > 1 && mGameEntity?.shouldUseMirrorInfo() == false) { + initGameContentCard(it.contentCard) + } + } } override fun scrollToTop() { @@ -1668,8 +1919,25 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { const val SCROLL_TO_KAIFU = "scrollToKaiFu" const val EB_SCROLLING = "EB_SCROLLING" const val INITIAL_DELAY = 500L + const val CONTENT_CARD_LOOP_TIME = 3000L private const val SP_OPENED_DIALOG_TIME_PREFIX = "opened_dialog_time_prefix_" private const val LAST_SELECTED_POSITION = "last_selected_position" } + + class LooperHandle(fragment: GameDetailFragment): Handler( + Looper.getMainLooper()) { + private val mWeakReference: WeakReference = WeakReference(fragment) + + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + val fragment = mWeakReference.get() + if (fragment != null) { + if (msg.what == fragment.mServerLooperKey) { + fragment.scrollToNextContent(fragment.mContentCardServerVp) + } + fragment.startAutoPlay(msg.what) + } + } + } } diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt index a86ba4a5fd..a2293e0d3f 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt @@ -9,15 +9,19 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import com.gh.gamecenter.common.constant.Constants import com.gh.common.filter.RegionSettingHelper import com.gh.common.history.HistoryHelper -import com.gh.gamecenter.core.runOnUiThread -import com.gh.common.util.* -import com.gh.gamecenter.core.utils.GsonUtils -import com.gh.gamecenter.core.utils.SPUtils +import com.gh.common.util.ApkActiveUtils +import com.gh.common.util.CheckLoginUtils +import com.gh.common.util.ConcernUtils +import com.gh.common.util.LibaoUtils +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.common.utils.singleToMain import com.gh.gamecenter.common.utils.toRequestBody +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.* import com.gh.gamecenter.gamedetail.entity.BigEvent @@ -26,8 +30,6 @@ import com.gh.gamecenter.gamedetail.entity.DetailEntity import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.mvvm.Resource -import com.gh.gamecenter.common.retrofit.BiResponse -import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp import io.reactivex.android.schedulers.AndroidSchedulers @@ -40,17 +42,11 @@ class GameDetailViewModel(application: Application, var gameId: String?, var game: GameEntity?) : AndroidViewModel(application) { - init { - runOnUiThread { - loadData() - } - } - private val mApi = RetrofitManager.getInstance().api private val mSensitiveApi = RetrofitManager.getInstance().api val concernLiveData = MutableLiveData() - val gameLiveData = MutableLiveData>() + val gameLiveData = MutableLiveData?>() val gameDetailLiveData = MutableLiveData>() val bigEventLiveData = MutableLiveData>() val recommendPopupLiveData = MutableLiveData>() @@ -64,6 +60,10 @@ class GameDetailViewModel(application: Application, var videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) var displayTopVideo: Boolean = false + init { + loadData() + } + fun loadData() { if (RegionSettingHelper.shouldThisGameBeFiltered(gameId)) { gameId = "invalid" @@ -72,7 +72,7 @@ class GameDetailViewModel(application: Application, when { game != null -> { - gameLiveData.value = Resource.success(game) + gameLiveData.postValue(Resource.success(game)) getGameDetailNew() getRecommendPopup(game?.id ?: "") } @@ -115,6 +115,17 @@ class GameDetailViewModel(application: Application, } } + private fun removeLatestServerIfNeeded(data: NewGameDetailEntity) { + var i = 0 + while (i < data.detailEntity.size) { + if (data.detailEntity[i].type == DetailEntity.Type.LATEST_SERVER.value) { + data.detailEntity.removeAt(i) + return + } + i++ + } + } + @SuppressLint("CheckResult") fun getGameDetailNew() { mSensitiveApi.getGameDetailNew(game?.id) @@ -127,6 +138,15 @@ class GameDetailViewModel(application: Application, // 4.4以下设备不显示顶部视频 displayTopVideo = data.topVideo != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT + + data.contentCard.forEach { + // 显示开服内容卡片时,游戏详情-详情tab不显示“最新开服” + if (data.contentCard.size > 1 && it.type == "func_server") { + removeLatestServerIfNeeded(data) + return@forEach + } + } + gameDetailLiveData.postValue(Resource.success(data)) if (CheckLoginUtils.isLogin()) { getUserRelatedInfo(data) diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt index c44e3fdb9c..ca146dc5ce 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt @@ -26,15 +26,14 @@ import com.gh.common.databind.BindingAdapters import com.gh.common.exposure.ExposureEvent import com.gh.common.exposure.ExposureManager import com.gh.common.exposure.ExposureSource +import com.gh.common.util.* import com.gh.common.util.DialogUtils -import com.gh.common.util.DirectUtils import com.gh.common.util.LogUtils import com.gh.common.util.NewLogUtils import com.gh.gamecenter.GameNewsActivity import com.gh.gamecenter.R import com.gh.gamecenter.SuggestionActivity import com.gh.gamecenter.adapter.viewholder.FooterViewHolder -import com.gh.gamecenter.common.base.activity.BaseActivity import com.gh.gamecenter.common.callback.OnListClickListener import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.utils.* @@ -964,8 +963,11 @@ class DescAdapter( } } - class GameDetailCustomColumnViewHolder(var binding: GamedetailItemCustomColumnBinding, handler: Handler) : - ExposureViewHolder(binding.root, handler) + inner class GameDetailCustomColumnViewHolder(var binding: GamedetailItemCustomColumnBinding, handler: Handler) : ExposureViewHolder(binding.root, handler) { + override fun exposureLog() { + NewFlatLogUtils.logGameDetailColumnOrderingView(binding.titleTv.text.toString(), mViewModel.game?.name ?: "", mViewModel.game?.id ?: "") + } + } class GameDetailRelatedVersionViewHolder(var binding: GameGalleryListBinding, handler: Handler) : ExposureViewHolder(binding.root, handler) class GameVideoGalleryViewHolder(var binding: GameGalleryListBinding, handler: Handler) : ExposureViewHolder(binding.root, handler) diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescFragment.kt index 63941da7c3..679fa5e95f 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescFragment.kt @@ -270,6 +270,18 @@ class DescFragment : BaseFragment(), IScrollable { } } + fun scrollToRelatedVersion() { + if (mViewModel.getRelatedVersionPosition() != -1) { + mLayoutManager?.scrollToPositionWithOffset(mViewModel.getRelatedVersionPosition(), 0) + } + } + + fun scrollToLibao() { + if (mViewModel.getDetailLibaoPosition() != -1) { + mLayoutManager?.scrollToPositionWithOffset(mViewModel.getDetailLibaoPosition(), 0) + } + } + override fun onNightModeChange() { super.onNightModeChange() mBinding.recyclerview.setBackgroundColor(R.color.background.toColor(requireContext())) diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescViewModel.kt index 4381d820e7..b2eb183847 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescViewModel.kt @@ -31,6 +31,7 @@ import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import okhttp3.ResponseBody +import org.json.JSONObject import retrofit2.HttpException class DescViewModel(application: Application, @@ -42,6 +43,8 @@ class DescViewModel(application: Application, private var mGameInfoPosition = 0 private var mLibaoPosition = -1 private var mServerPosition = -1 + private var mDetailLibaoPosition = -1 + private var mRelatedVersionPosition = -1 private var mGameInfo: GameInfo? = null var list = MutableLiveData>() @@ -215,6 +218,16 @@ class DescViewModel(application: Application, super.onResponse(response) ToastUtils.showToast("感谢您的反馈信息,我们将尽快处理~") } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + e?.response()?.errorBody()?.let { + val content = JSONObject(it.string()) + if (content.getInt("code") == 403208) { + ToastUtils.showToast("您已经提交过反馈信息,我们将尽快处理~") + } + } + } }) } @@ -349,6 +362,15 @@ class DescViewModel(application: Application, } } + for ((index, entity) in detailEntityList.withIndex()) { + if (entity.libao != null) { + mDetailLibaoPosition = index + } + if (entity.relatedVersion != null) { + mRelatedVersionPosition = index + } + } + if (containsFirstTimeExpandCustomColumnTags) { SPUtils.setBoolean(Constants.SP_HAS_EXPANDED_GAME_DETAIL_TAGS, true) } @@ -364,6 +386,10 @@ class DescViewModel(application: Application, fun getGameInfoPosition() = mGameInfoPosition + fun getDetailLibaoPosition() = mDetailLibaoPosition + + fun getRelatedVersionPosition() = mRelatedVersionPosition + fun getGameInfo() = mGameInfo /** diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/GameDetailMoreDialog.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/GameDetailMoreDialog.kt index b306030f1d..b8a220f590 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/GameDetailMoreDialog.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/GameDetailMoreDialog.kt @@ -14,28 +14,37 @@ import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity import com.gh.gamecenter.common.base.fragment.BaseDraggableDialogFragment import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.utils.ShareUtils -import com.gh.gamecenter.common.utils.toColor -import com.gh.gamecenter.common.utils.toDrawable +import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.databinding.DialogGameDetailMoreBinding import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.gamedetail.GameDetailViewModel class GameDetailMoreDialog : BaseDraggableDialogFragment() { private lateinit var binding: DialogGameDetailMoreBinding - private var mGameEntity: GameEntity? = null private var mShortId = "" + private var mGameEntity: GameEntity? = null + private var mShowConcernIcon = false + private var mIsConcerned = false + private val mViewModel: GameDetailViewModel by lazy { viewModelProviderFromParent() } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requireArguments().run { mGameEntity = getParcelable(KEY_GAME) mShortId = getString(KEY_SHORT_ID) ?: "" + mShowConcernIcon = getBoolean(KEY_SHOW_CONCERN_ICON) + mIsConcerned = getBoolean(KEY_IS_CONCERNED) } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { binding = DialogGameDetailMoreBinding.inflate(inflater, container, false) return binding.root } @@ -46,6 +55,13 @@ class GameDetailMoreDialog : BaseDraggableDialogFragment() { binding.gameIconView.displayGameIcon(it) binding.gameNameTv.text = it.name } + + updateConcernView() + mViewModel.concernLiveData.observeNonNull(this) { response -> + mIsConcerned = response.isConcern + updateConcernView() + } + binding.shareWechatTv.setOnClickListener { getShareUtils().wechatShare() MtaHelper.onEvent("内容分享", "微信好友", mGameEntity?.name) @@ -122,6 +138,41 @@ class GameDetailMoreDialog : BaseDraggableDialogFragment() { return requireContext().getString(R.string.share_game_url, mShortId) } + private fun updateConcernView() { + if (mShowConcernIcon) { + binding.concernTv.visibility = View.VISIBLE + if (mIsConcerned) { + binding.concernTv.text = "取消关注" + binding.concernTv.setCompoundDrawablesWithIntrinsicBounds( + null, + R.drawable.ic_gamedetail_menu_followed.toDrawable(), + null, + null + ) + binding.concernTv.setOnClickListener { + ifLogin("游戏详情-[关注]") { + DialogHelper.showCancelDialog(requireContext(), { + mViewModel.concernCommand(false) + }) + } + } + } else { + binding.concernTv.text = "关注游戏" + binding.concernTv.setCompoundDrawablesWithIntrinsicBounds( + null, + R.drawable.ic_gamedetail_menu_follow.toDrawable(), + null, + null + ) + binding.concernTv.setOnClickListener { + ifLogin("游戏详情-[关注]") { + mViewModel.concernCommand(true) + } + } + } + } + } + override fun getRootView(): View = binding.root override fun getDragCloseView(): View = binding.dragClose @@ -156,16 +207,26 @@ class GameDetailMoreDialog : BaseDraggableDialogFragment() { } companion object { + const val KEY_SHOW_CONCERN_ICON = "displayed_concern_icon" + const val KEY_IS_CONCERNED = "concerned" private const val KEY_GAME = "game" private const val KEY_SHORT_ID = "short_id" @JvmStatic - fun showMoreDialog(activity: AppCompatActivity, gameEntity: GameEntity?, shortId: String) { + fun showMoreDialog( + activity: AppCompatActivity, + gameEntity: GameEntity?, + shortId: String, + showConcernIcon: Boolean = false, + isConcerned: Boolean = false, + ) { if (gameEntity == null) return GameDetailMoreDialog().apply { arguments = Bundle().apply { putParcelable(KEY_GAME, gameEntity) putString(KEY_SHORT_ID, shortId) + putBoolean(KEY_SHOW_CONCERN_ICON, showConcernIcon) + putBoolean(KEY_IS_CONCERNED, isConcerned) } }.show(activity.supportFragmentManager, GameDetailMoreDialog::class.java.name) } diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/entity/ContentCardEntity.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/entity/ContentCardEntity.kt new file mode 100644 index 0000000000..a2c0be817c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/entity/ContentCardEntity.kt @@ -0,0 +1,71 @@ +package com.gh.gamecenter.gamedetail.entity + +import androidx.annotation.Keep +import com.gh.gamecenter.entity.* +import com.google.gson.annotations.SerializedName + +@Keep +data class ContentCardEntity( + @SerializedName("_id") + var id: String = "", + var name: String? = "", // tag + var title: String? = "", + var image: String? = "", + @SerializedName("target", alternate = ["link", "link_id"]) + var link: String? = "", + @SerializedName("type", alternate = ["link_type"]) + var type: String? = "", + var icon: String = "", + @SerializedName("game_icon") + var gameIcon: String? = "", + @SerializedName("game_icon_subscript") + var gameIconSubscript: String? = "", + var text: String? = "", + @SerializedName("link_text") + var linkText: String? = "",//游戏详情弹窗,兼容旧版本用 + var value: String? = "", + @SerializedName("community_id") + var communityId: String? = "", + @SerializedName("link_community", alternate = ["community"]) + var community: CommunityEntity? = CommunityEntity(), + var display: Display? = null, // 板块 + @SerializedName("close_button") + var closeButton: String = "open",//用户判断h5游戏关闭按钮是否显示,hide(隐藏)、open(开启) + @SerializedName("button_link") + var buttonLink: Boolean = false, + @SerializedName("activity_id") + var activityId: String = "", + var style: String = "", + + var des: String = "", + var server: GameDetailServer? = null, + var libao: LibaoEntity? = null, + @SerializedName("zone_tab") + var zoneTab: Boolean = false, + @SerializedName("related_version") + var relatedVersion: Boolean = false, + var toolkit: ArrayList = ArrayList() +) { + fun toLinkEntity(): LinkEntity { + return LinkEntity( + name = name, + title = title, + image = image, + link = link, + type = type, + icon = icon, + gameIcon = gameIcon, + gameIconSubscript = gameIconSubscript, + text = text, + linkText = linkText, + value = value, + communityId = communityId, + community = community, + display = display, + closeButton = closeButton, + buttonLink = buttonLink, + activityId = activityId, + style = style + ) + } +} diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/entity/NewGameDetailEntity.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/entity/NewGameDetailEntity.kt index 1bdd9cb435..8cdd272a4f 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/entity/NewGameDetailEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/entity/NewGameDetailEntity.kt @@ -45,7 +45,12 @@ data class NewGameDetailEntity( @SerializedName("bbs_tab") var bbsTab: LinkEntity? = null, @SerializedName("certification_tag") - var certificateTag: Screenshot? = null + var certificateTag: Screenshot? = null, + @SerializedName("content_card") + var contentCard: ArrayList = ArrayList(), + + @SerializedName("smooth_relation_game") + var smoothRelatedGame: SimpleGame? = null, ) @Keep diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/rating/RatingReplyActivity.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/rating/RatingReplyActivity.kt index d895cb4cfa..fd783ab392 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/rating/RatingReplyActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/rating/RatingReplyActivity.kt @@ -12,12 +12,13 @@ import androidx.core.widget.doOnTextChanged import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.RecyclerView import com.gh.common.exposure.ExposureManager -import com.gh.common.util.* import com.gh.common.util.NewLogUtils +import com.gh.common.util.SyncDataBetweenPageHelper import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListActivity import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment +import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.VerticalItemDecoration @@ -65,6 +66,10 @@ class RatingReplyActivity : ListActivity Unit) { + fun replyComment(replyId: String?, content: String, successCallback: () -> Unit, realNameConfirmListener: ConfirmListener) { processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true)) val json = json { @@ -224,7 +225,7 @@ class RatingReplyViewModel( false ) ) - ErrorHelper.handleError(getApplication(), e?.response()?.errorBody()?.string()) + ErrorHelper.handleError(getApplication(), e?.response()?.errorBody()?.string(), false, realNameConfirmListener) } }) } diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/rating/edit/RatingEditActivity.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/rating/edit/RatingEditActivity.kt index 2ec81b16a8..1a2676c9cb 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/rating/edit/RatingEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/rating/edit/RatingEditActivity.kt @@ -348,7 +348,11 @@ class RatingEditActivity : ToolBarActivity(), KeyboardHeightObserver { }, TrackableEntity(event = "游戏评论跳转", key = "意见反馈弹窗") ) } - else -> ErrorHelper.handleError(this@RatingEditActivity, errorString) + else -> ErrorHelper.handleError(this@RatingEditActivity, errorString, false, object : ConfirmListener { + override fun onConfirm() { + postGameComment(again = false) + } + }) } } }) diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt index 307101cd83..757dd31b6c 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt @@ -11,20 +11,20 @@ import android.widget.SeekBar import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager -import com.gh.gamecenter.common.observer.MuteCallback -import com.gh.gamecenter.common.observer.VolumeObserver -import com.gh.gamecenter.core.runOnIoThread -import com.gh.gamecenter.core.runOnUiThread -import com.gh.common.util.* +import com.gh.common.util.LogUtils import com.gh.download.cache.ExoCacheManager import com.gh.gamecenter.R -import com.gh.gamecenter.core.utils.StringUtils -import com.gh.gamecenter.common.utils.debounceActionWithInterval -import com.gh.gamecenter.common.utils.rxTimer +import com.gh.gamecenter.common.observer.MuteCallback +import com.gh.gamecenter.common.observer.VolumeObserver import com.gh.gamecenter.common.utils.ImageUtils import com.gh.gamecenter.common.utils.NetworkUtils +import com.gh.gamecenter.common.utils.debounceActionWithInterval +import com.gh.gamecenter.common.utils.rxTimer +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.runOnUiThread import com.gh.gamecenter.core.utils.MD5Utils import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.entity.GameDetailEntity import com.gh.gamecenter.gamedetail.GameDetailViewModel import com.gh.gamecenter.home.video.ScrollCalculatorHelper @@ -41,7 +41,7 @@ import java.util.* class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : StandardGSYVideoPlayer(context, attrs) { private var mMuteCallback: MuteCallback - private var mVolumeObserver: VolumeObserver + private var mVolumeObserver: VolumeObserver? = null var gameName = "" var video: GameDetailEntity.Video? = null @@ -84,7 +84,9 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS } } - mVolumeObserver = VolumeObserver(mMuteCallback) + if (!isInEditMode) { + mVolumeObserver = VolumeObserver(mMuteCallback) + } setBackFromFullScreenListener { if (it.id == R.id.fullscreen) { @@ -115,14 +117,14 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS fun observeVolume(fragment: Fragment?) { fragment?.context?.applicationContext?.contentResolver?.registerContentObserver( - android.provider.Settings.System.CONTENT_URI, true, mVolumeObserver + android.provider.Settings.System.CONTENT_URI, true, mVolumeObserver!! ) fragment?.fragmentManager?.registerFragmentLifecycleCallbacks( object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { if (f === fragment) { - fragment.context?.applicationContext?.contentResolver?.unregisterContentObserver(mVolumeObserver) + fragment.context?.applicationContext?.contentResolver?.unregisterContentObserver(mVolumeObserver!!) fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this) } } diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt index f9e8ce0ec0..22d2b22161 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt @@ -16,10 +16,7 @@ import com.gh.gamecenter.baselist.LoadStatus import com.gh.gamecenter.common.base.fragment.LazyFragment import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts -import com.gh.gamecenter.common.utils.goneIf -import com.gh.gamecenter.common.utils.observeNonNull -import com.gh.gamecenter.common.utils.safelyGetInRelease -import com.gh.gamecenter.common.utils.viewModelProvider +import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.OffsetLinearLayoutManager import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.utils.MD5Utils @@ -31,6 +28,7 @@ import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.eventbus.EBReuse import com.gh.gamecenter.eventbus.EBUISwitch import com.gh.gamecenter.fragment.HomeSearchToolWrapperFragment +import com.gh.gamecenter.fragment.HomeSearchToolWrapperViewModel import com.gh.gamecenter.fragment.MainWrapperFragment import com.gh.gamecenter.game.gallery.GameGallerySlideViewHolder import com.gh.gamecenter.home.slide.HomeSlideListAdapter @@ -43,6 +41,7 @@ import org.greenrobot.eventbus.ThreadMode class HomeFragment : LazyFragment() { private lateinit var mViewModel: HomeViewModel + private lateinit var mHomeSearchViewModel: HomeSearchToolWrapperViewModel private lateinit var mBinding: FragmentMainHomeBinding private lateinit var mLayoutManager: LinearLayoutManager private lateinit var mListAdapter: HomeFragmentAdapter @@ -59,14 +58,18 @@ class HomeFragment : LazyFragment() { showUnzipFailureDialog(downloadEntity) } } + + override fun onDataInit(downloadEntity: DownloadEntity) { + mListAdapter.notifyItemByDownload(downloadEntity) + } } override fun getRealLayoutId() = R.layout.fragment_main_home override fun onFragmentFirstVisible() { mViewModel = viewModelProvider() + mHomeSearchViewModel = viewModelProviderFromParent() mViewModel.homeOnlyWithoutOtherTab = arguments?.getInt(EntranceConsts.KEY_TAB_COUNT) == 1 - super.onFragmentFirstVisible() mViewModel.itemDataList.observeNonNull(this, callback = { @@ -82,10 +85,8 @@ class HomeFragment : LazyFragment() { reuseLoading.root.goneIf(loadStatus != LoadStatus.INIT_LOADING) } mListAdapter.setLoadStatus(it) - mBinding.reuseNoConnection.root.visibility = if (it == LoadStatus.INIT_FAILED) View.VISIBLE else View.GONE - mBinding.reuseLoading.root.visibility = if (it == LoadStatus.INIT_LOADING) View.VISIBLE else View.GONE if (it == LoadStatus.INIT_LOADED) { - AppExecutor.uiExecutor.executeWithDelay(Runnable { + AppExecutor.uiExecutor.executeWithDelay({ scroll() mScrollCalculatorHelper.onScrollStateChanged( mBinding.gameList, @@ -94,6 +95,9 @@ class HomeFragment : LazyFragment() { }, 100) } }) + mHomeSearchViewModel.homeDataLiveData.observe(this){ + mViewModel.initData(it) + } mScrollCalculatorHelper = ScrollCalculatorHelper(R.id.autoVideoView, 0) } @@ -108,13 +112,8 @@ class HomeFragment : LazyFragment() { R.color.theme ) ) - val loadStatus = LoadStatus.LIST_LOADED mBinding.run { BindingAdapters.isRefreshing(gameRefresh, LoadStatus.LIST_LOADED) - gameRefresh.goneIf(loadStatus == LoadStatus.INIT_FAILED) - gameList.goneIf(loadStatus == LoadStatus.INIT_LOADING) - reuseNoConnection.root.goneIf(loadStatus != LoadStatus.INIT_FAILED) - reuseLoading.root.goneIf(loadStatus != LoadStatus.INIT_LOADING) } mAutomaticLayoutManager = OffsetLinearLayoutManager(requireContext()) mLayoutManager = mAutomaticLayoutManager @@ -157,12 +156,12 @@ class HomeFragment : LazyFragment() { mBinding.gameRefresh.setOnRefreshListener { MtaHelper.onEvent("首页_新", "刷新") mViewModel.loadStatus.postValue(LoadStatus.LIST_LOADING) - mViewModel.initData() + mHomeSearchViewModel.getHomeContentUnion(true) } mBinding.reuseNoConnection.root.setOnClickListener { mViewModel.loadStatus.postValue(LoadStatus.INIT_LOADING) - mViewModel.initData() + mHomeSearchViewModel.getHomeContentUnion(true) } } @@ -188,7 +187,6 @@ class HomeFragment : LazyFragment() { override fun onFragmentResume() { super.onFragmentResume() resumeVideo() - if (isEverPause) mListAdapter.notifyDataSetChanged() super.onResume() DownloadManager.getInstance().addObserver(dataWatcher) @@ -284,6 +282,7 @@ class HomeFragment : LazyFragment() { for (gameAndPosition in data) { mListAdapter.notifyChildItem(gameAndPosition.position, busFour.packageName) } + mViewModel.refreshRecentVGameIfNeeded() } @Subscribe(threadMode = ThreadMode.MAIN) @@ -291,6 +290,7 @@ class HomeFragment : LazyFragment() { if (::mListAdapter.isInitialized && "Refresh" == reuse.type) { mListAdapter.notifyDataSetChanged() } + mViewModel.refreshRecentVGameIfNeeded() } @Subscribe(threadMode = ThreadMode.MAIN) diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeFragmentAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/HomeFragmentAdapter.kt index e56f00bd62..b0a546a3d0 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeFragmentAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeFragmentAdapter.kt @@ -6,12 +6,10 @@ import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.gh.gamecenter.common.constant.ItemViewType import com.gh.common.exposure.ExposureEvent import com.gh.common.exposure.ExposureSource import com.gh.common.exposure.IExposable -import com.gh.gamecenter.core.runOnIoThread -import com.gh.common.util.* +import com.gh.common.util.DirectUtils import com.gh.common.util.NewLogUtils import com.gh.gamecenter.AboutActivity import com.gh.gamecenter.GameDetailActivity @@ -20,12 +18,16 @@ import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.adapter.viewholder.ReuseViewHolder import com.gh.gamecenter.baselist.DiffUtilAdapter import com.gh.gamecenter.baselist.LoadStatus +import com.gh.gamecenter.common.constant.ItemViewType import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.MtaHelper -import com.gh.gamecenter.databinding.* +import com.gh.gamecenter.databinding.HomeDividerItemBinding import com.gh.gamecenter.entity.AmwayCommentEntity import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.game.GameAndPosition +import com.gh.gamecenter.game.horizontal.GameHorizontalAdapter +import com.gh.gamecenter.game.horizontal.GameHorizontalSlideAdapter import com.gh.gamecenter.game.rank.RankCollectionAdapter import com.gh.gamecenter.game.vertical.GameVerticalAdapter import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity @@ -34,6 +36,9 @@ import com.gh.gamecenter.home.gamecollection.HomeGameCollectionViewHolder import com.gh.gamecenter.home.slide.HomeSlideListAdapter import com.gh.gamecenter.home.slide.HomeSlideListViewHolder import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel +import com.gh.vspace.HomeRecentVGameAdapter +import com.gh.vspace.HomeRecentVGameViewHolder +import com.gh.vspace.VHelper import com.halo.assistant.fragment.game.GamePluginAdapter import com.lightgame.download.DownloadEntity import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder @@ -64,6 +69,7 @@ class HomeFragmentAdapter( if (oldItem?.pluginList != null && newItem?.pluginList != null) return true if (oldItem?.verticalSlide != null && newItem?.verticalSlide != null) return true if (oldItem?.horizontalColumn != null && newItem?.horizontalColumn != null) return true + if (oldItem?.recentVGame != null && newItem?.recentVGame != null) return true if (oldItem?.columnHead != null && newItem?.columnHead != null) return true if (oldItem?.horizontalSlide != null && newItem?.horizontalSlide != null) return true return super.areItemsTheSame(oldItem, newItem) @@ -76,6 +82,7 @@ class HomeFragmentAdapter( if (oldItem?.game?.id != newItem?.game?.id) return false if (oldItem?.verticalSlide != null && newItem?.verticalSlide != null) return false if (oldItem?.horizontalColumn != null && newItem?.horizontalColumn != null) return false + if (oldItem?.recentVGame != null && newItem?.recentVGame != null) return false if (oldItem?.columnHead != null && newItem?.columnHead != null) return false if (oldItem?.horizontalSlide != null && newItem?.horizontalSlide != null) return false return super.areContentsTheSame(oldItem, newItem) @@ -100,6 +107,7 @@ class HomeFragmentAdapter( if (itemData.attachGame != null) return GAME_ITEM if (itemData.amway != null) return AMWAY_ITEM if (itemData.gameCollection != null) return GAME_COLLECTION_ITEM + if (itemData.recentVGame != null) return RECENT_V_GAME if (itemData.lineDivider != null) return DIVIDER_ITEM if (itemData.unknownData != null) return UNKNOWN_ITEM @@ -109,30 +117,13 @@ class HomeFragmentAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val view: View return when (viewType) { - SLIDE_ITEM -> { - view = mLayoutInflater.inflate(R.layout.home_slide_list, parent, false) - HomeSlideListViewHolder(HomeSlideListBinding.bind(view), callback) - } - RECOMMENDS_ITEM -> { - view = mLayoutInflater.inflate(R.layout.home_recommend_item, parent, false) - HomeRecommendItemViewHolder(HomeRecommendItemBinding.bind(view)) - } - GAME_ITEM -> { - view = mLayoutInflater.inflate(R.layout.home_game_item, parent, false) - HomeGameItemViewHolder(HomeGameItemBinding.bind(view)) - } - AMWAY_ITEM -> { - view = mLayoutInflater.inflate(R.layout.home_amway_list, parent, false) - HomeAmwayListViewHolder(HomeAmwayListBinding.bind(view)) - } - GAME_COLLECTION_ITEM -> { - view = mLayoutInflater.inflate(R.layout.home_game_collection_item, parent, false) - HomeGameCollectionViewHolder(HomeGameCollectionItemBinding.bind(view)) - } - DIVIDER_ITEM -> { - view = mLayoutInflater.inflate(R.layout.home_divider_item, parent, false) - HomeDividerViewHolder(HomeDividerItemBinding.bind(view)) - } + SLIDE_ITEM -> HomeSlideListViewHolder(parent.toBinding(), callback) + RECOMMENDS_ITEM -> HomeRecommendItemViewHolder(parent.toBinding()) + GAME_ITEM -> HomeGameItemViewHolder(parent.toBinding()) + AMWAY_ITEM -> HomeAmwayListViewHolder(parent.toBinding()) + GAME_COLLECTION_ITEM -> HomeGameCollectionViewHolder(parent.toBinding()) + DIVIDER_ITEM -> HomeDividerViewHolder(parent.toBinding()) + RECENT_V_GAME -> HomeRecentVGameViewHolder(parent.toBinding()) FOOTER_ITEM -> { view = mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false) FooterViewHolder(view) @@ -169,6 +160,7 @@ class HomeFragmentAdapter( is ReuseViewHolder -> bindUnknown(holder) is HomeDividerViewHolder -> holder.bindView(mDataList[position].lineDivider ?: 1F) is HomeGameCollectionViewHolder -> bindGameCollection(holder, position) + is HomeRecentVGameViewHolder -> bindRecentVGame(holder, position) else -> mLegacyHomeFragmentAdapterAssistant.bindLegacyViewHolder( holder, @@ -259,8 +251,7 @@ class HomeFragmentAdapter( }, basicSource = mBasicExposureSource, source = gameCollectionSource - ) - ) + )) } gameCollectionItemData.exposureEventList = gameExposureList exposureList.addAll(gameExposureList) @@ -270,12 +261,33 @@ class HomeFragmentAdapter( holder.bindGameCollectionList(gameCollectionItemDataList, "首页内容列表") } + private fun bindRecentVGame(holder: HomeRecentVGameViewHolder, position: Int) { + val homeItemData = mDataList[position] + + val exposureEventList = arrayListOf() + runOnIoThread(true) { + homeItemData.recentVGame?.forEach { + val event = ExposureEvent.createEventWithSourceConcat( + gameEntity = VHelper.toGameEntity(it.downloadEntity), + basicSource = mBasicExposureSource, + source = listOf(ExposureSource("最近在玩", "")) + ) + exposureEventList.add(event) + } + } + homeItemData.exposureEventList = exposureEventList + + if (!homeItemData.recentVGame.isNullOrEmpty()) { + holder.bindView(homeItemData.recentVGame!!) + } + } + private fun bindAttachGame(holder: HomeGameItemViewHolder, position: Int) { val homeItemData = mDataList[position] val game = homeItemData.attachGame?.linkGame!! val displayContent = homeItemData.attachGame?.displayContent ?: "" game.displayContent = displayContent - holder.bindGame(game) + holder.bindGame(homeItemData, this, position) runOnIoThread(true) { homeItemData.exposureEvent = ExposureEvent.createEventWithSourceConcat( @@ -391,10 +403,13 @@ class HomeFragmentAdapter( val entryMap = gameAndPosition.entity.getEntryMap() entryMap[download.platform] = download } - if (getItemViewType(gameAndPosition.position) == ItemViewType.VERTICAL_SLIDE_ITEM || - getItemViewType(gameAndPosition.position) == ItemViewType.GAME_PLUGIN || - getItemViewType(gameAndPosition.position) == SLIDE_ITEM || - getItemViewType(gameAndPosition.position) == ItemViewType.RANK_COLLECTION + if (getItemViewType(gameAndPosition.position) == ItemViewType.VERTICAL_SLIDE_ITEM + || getItemViewType(gameAndPosition.position) == ItemViewType.GAME_PLUGIN + || getItemViewType(gameAndPosition.position) == SLIDE_ITEM + || getItemViewType(gameAndPosition.position) == ItemViewType.RANK_COLLECTION + || getItemViewType(gameAndPosition.position) == RECENT_V_GAME + || getItemViewType(gameAndPosition.position) == ItemViewType.GAME_SUBJECT + || getItemViewType(gameAndPosition.position) == ItemViewType.GAME_SUBJECT_SLIDE ) { val view = layoutManager.findViewByPosition(gameAndPosition.position) val recyclerView = view?.findViewById(R.id.recycler_view) @@ -403,6 +418,9 @@ class HomeFragmentAdapter( is GamePluginAdapter -> adapter.notifyItemByDownload(download) is HomeSlideListAdapter -> adapter.notifyItemByDownload(download) is RankCollectionAdapter -> adapter.notifyItemByDownload(download) + is GameHorizontalAdapter -> adapter.notifyItemByDownload(download) + is GameHorizontalSlideAdapter -> adapter.notifyItemByDownload(download) + is HomeRecentVGameAdapter -> adapter.notifyItemByDownload(download) } } else { notifyItemChanged(gameAndPosition.position) @@ -421,16 +439,20 @@ class HomeFragmentAdapter( } fun notifyChildItem(position: Int, packageName: String) { - if (getItemViewType(position) == ItemViewType.VERTICAL_SLIDE_ITEM || - getItemViewType(position) == ItemViewType.GAME_PLUGIN || - getItemViewType(position) == SLIDE_ITEM || - getItemViewType(position) == ItemViewType.RANK_COLLECTION + if (getItemViewType(position) == ItemViewType.VERTICAL_SLIDE_ITEM + || getItemViewType(position) == ItemViewType.GAME_PLUGIN + || getItemViewType(position) == SLIDE_ITEM + || getItemViewType(position) == ItemViewType.RANK_COLLECTION + || getItemViewType(position) == ItemViewType.GAME_SUBJECT + || getItemViewType(position) == ItemViewType.GAME_SUBJECT_SLIDE ) { val view = layoutManager.findViewByPosition(position) val recyclerView = view?.findViewById(R.id.recycler_view) - when (recyclerView?.adapter) { - is RankCollectionAdapter -> (recyclerView.adapter as RankCollectionAdapter).notifyChildItem() - is GameVerticalAdapter -> (recyclerView.adapter as GameVerticalAdapter).notifyChildItem(packageName) + when (val adapter = recyclerView?.adapter) { + is RankCollectionAdapter -> adapter.notifyChildItem() + is GameVerticalAdapter -> adapter.notifyChildItem(packageName) + is GameHorizontalAdapter -> adapter.notifyChildItem(packageName) + is GameHorizontalSlideAdapter -> adapter.notifyChildItem(packageName) else -> recyclerView?.adapter?.notifyDataSetChanged() } } else { @@ -440,7 +462,7 @@ class HomeFragmentAdapter( fun getGameEntityByPackage(packageName: String): List { val positionList = ArrayList() - val positionMap = viewModel.positionAndPackageMap + val positionMap = viewModel.positionAndPackageMap.value ?: return positionList for (key in positionMap.keys) { if (key.contains(packageName)) { val position = positionMap[key]!! @@ -465,7 +487,9 @@ class HomeFragmentAdapter( } val attachGame = itemData.attachGame?.linkGame - if (attachGame != null) { + val recentVGame = itemData.recentVGame + + if (attachGame != null || recentVGame != null) { positionList.add(GameAndPosition(attachGame, position)) continue } @@ -510,5 +534,6 @@ class HomeFragmentAdapter( const val UNKNOWN_ITEM: Int = 111 const val COMMON_ITEM: Int = 115 const val GAME_COLLECTION_ITEM: Int = 116 + const val RECENT_V_GAME: Int = 117 } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeGameItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/HomeGameItemViewHolder.kt index e902dc5892..a163d4af31 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeGameItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeGameItemViewHolder.kt @@ -4,23 +4,29 @@ import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.graphics.drawable.GradientDrawable import android.view.View +import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet -import com.gh.gamecenter.common.base.BaseRecyclerViewHolder -import com.gh.gamecenter.core.utils.RandomUtils +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.DownloadItemUtils import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.RandomUtils import com.gh.gamecenter.databinding.HomeGameItemBinding -import com.gh.gamecenter.entity.GameEntity class HomeGameItemViewHolder(val binding: HomeGameItemBinding) : BaseRecyclerViewHolder(binding.root) { - fun bindGame(game: GameEntity) { + fun bindGame(homeItemData: HomeItemData, adapter: RecyclerView.Adapter, position: Int) { + val game = homeItemData.attachGame?.linkGame!! binding.gameIcon.displayGameIcon(game) binding.gameName.text = game.name binding.gameName.setTextColor(R.color.text_title.toColor(binding.root.context)) binding.gameBrief.setTextColor(R.color.text_title.toColor(binding.root.context)) - binding.gameRating.goneIf(!(game.showComment && game.commentCount >= 3 && game.star >= 7)) binding.gameRating.text = game.star.toString() + binding.gameRating2.text = game.star.toString() + val drawable = R.drawable.home_game_rating.toDrawable(binding.root.context) + drawable?.setBounds(0, 0, 12F.dip2px(), 12F.dip2px()) + binding.gameRating.setCompoundDrawables(drawable, null, null, null) ImageUtils.display(binding.gameImage, game.homeSetting.image) binding.gameTags.postDelayed({ binding.gameTags.visibility = if (game.tagStyle.isNotEmpty()) { @@ -47,8 +53,8 @@ class HomeGameItemViewHolder(val binding: HomeGameItemBinding) : BaseRecyclerVie } } } - binding.gameTags.post { - binding.gameSubtitleTv.maxWidth = binding.gameTags.width + 24F.dip2px() + binding.ratingAndTagContainer.post { + binding.gameSubtitleTv.maxWidth = binding.ratingAndTagContainer.width + 32F.dip2px() } } if (game.advanceDownload) { @@ -61,7 +67,12 @@ class HomeGameItemViewHolder(val binding: HomeGameItemBinding) : BaseRecyclerVie } ConstraintSet().apply { clone(binding.root) - connect(R.id.game_name, ConstraintSet.END, if (game.serverLabel != null && !game.advanceDownload) R.id.recent_played_tag else R.id.gameSubtitleTv, ConstraintSet.START) + connect( + R.id.game_name, + ConstraintSet.END, + if (game.serverLabel != null && !game.advanceDownload) R.id.recent_played_tag else R.id.gameSubtitleTv, + ConstraintSet.START + ) }.applyTo(binding.root) val hierarchy = binding.gameImage.hierarchy @@ -70,5 +81,33 @@ class HomeGameItemViewHolder(val binding: HomeGameItemBinding) : BaseRecyclerVie } catch (ignore: Throwable) { hierarchy.setPlaceholderImage(RandomUtils.getRandomPlaceholderColor()) } + + DownloadItemUtils.setOnClickListener( + binding.root.context, + binding.downloadBtn, + game, + position, + adapter, + "新首页", + "", + homeItemData.exposureEvent + ) + DownloadItemUtils.updateDownloadButton(binding.root.context, binding.downloadBtn, game) + binding.downloadBtn.goneIf(game.homeSetting.downloadBtnSwitch != "on") + binding.gameRating.goneIf(!(game.showComment && game.commentCount >= 3 && game.star >= 7 && game.homeSetting.downloadBtnSwitch == "on")) + binding.gameRating2.goneIf(!(game.showComment && game.commentCount >= 3 && game.star >= 7 && game.homeSetting.downloadBtnSwitch != "on")) + ConstraintSet().apply { + clone(binding.gameSubtitleTv.parent as ConstraintLayout) + clear(binding.gameSubtitleTv.id, ConstraintSet.END) + if (binding.downloadBtn.visibility == View.VISIBLE) { + connect(binding.gameSubtitleTv.id, ConstraintSet.END, binding.downloadBtn.id, ConstraintSet.START) + } else if (binding.gameRating2.visibility == View.VISIBLE) { + connect(binding.gameSubtitleTv.id, ConstraintSet.END, binding.gameRating2.id, ConstraintSet.START) + } else { + connect(binding.gameSubtitleTv.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) + } + setMargin(binding.gameSubtitleTv.id, ConstraintSet.END, 8F.dip2px()) + applyTo(binding.gameSubtitleTv.parent as ConstraintLayout) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeItemData.kt b/app/src/main/java/com/gh/gamecenter/home/HomeItemData.kt index 6c35cae520..89717b960a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeItemData.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeItemData.kt @@ -1,12 +1,17 @@ package com.gh.gamecenter.home -import com.gh.gamecenter.entity.* +import com.gh.gamecenter.entity.AmwayCommentEntity +import com.gh.gamecenter.entity.HomeContent +import com.gh.gamecenter.entity.HomeRecommend +import com.gh.gamecenter.entity.HomeSlide import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData +import com.gh.vspace.VGameItemData data class HomeItemData(var slides: List? = null, var recommends: List? = null, var amway: List? = null, var gameCollection: List? = null, var attachGame: HomeContent? = null, + var recentVGame: ArrayList? = null, var lineDivider: Float? = null, var unknownData: Any? = null) : LegacyHomeItemData() \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeViewModel.kt b/app/src/main/java/com/gh/gamecenter/home/HomeViewModel.kt index 531e7c418f..12c17cd528 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeViewModel.kt @@ -15,8 +15,10 @@ import com.gh.common.util.HomePluggableHelper import com.gh.download.DownloadManager import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.baselist.LoadStatus +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.RandomUtils import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.entity.* @@ -24,8 +26,11 @@ import com.gh.gamecenter.game.rank.RankCollectionAdapter import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData import com.gh.gamecenter.packagehelper.PackageRepository import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.vspace.VGameItemData +import com.gh.vspace.VHelper import com.halo.assistant.HaloApp import com.halo.assistant.fragment.SettingsFragment +import com.lightgame.download.DownloadEntity import com.lightgame.utils.Utils import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -44,6 +49,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { private var mHomeContents = ArrayList() private var mPluginList: List? = null // 插件化 private var mSmartSubject: SubjectEntity? = null // 智能推荐专题 + private var mVGameList: ArrayList? = null // 最近的畅玩游戏 private var mContentPage = 1 // 专题分页 private var mIsLoading = false @@ -56,32 +62,136 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { var homeOnlyWithoutOtherTab = false var itemDataList: MediatorLiveData> = MediatorLiveData() - var positionAndPackageMap = HashMap() // key: packageName + position, value: position + private var mPositionAndPackageMap = HashMap() // key: packageName + position, value: position + var positionAndPackageMap = MutableLiveData>() val loadStatus = MutableLiveData() init { itemDataList.addSource(PackageRepository.gameUpdateLiveData) { initPlugin(it) + refreshRecentVGameIfNeeded() + } + itemDataList.addSource(DownloadManager.getInstance().downloadEntityLiveData) { + refreshRecentVGameIfNeeded() } loadStatus.postValue(LoadStatus.INIT_LOADING) - initData() } - fun initData() { + fun initData(homeDataEntity: HomeDataEntity?) { mHomeSlides = arrayListOf() mHomeRecommends = arrayListOf() mHomeContents = arrayListOf() mSubjectGameIdList = hashSetOf() + homeDataEntity?.let { + mHomeSlides.addAll(it.homeSlide) + mHomeRecommends.addAll(it.homeRecommend) + mHomeContents.addAll(it.homeContent) + transformationItemData() + mContentPage = 2 + loadStatus.postValue(LoadStatus.INIT_LOADED) + } + // 触发列表刷新行为时亦刷新内存中的备用游戏库列表 GameSubstituteRepositoryHelper.refreshRepositoryFromLocal() - getHomeSlides() if (SPUtils.getBoolean(SettingsFragment.PERSONAL_RECOMMEND_SP_KEY, true)) { getSmartColumn() } + + refreshRecentVGameIfNeeded() + } + + fun refreshRecentVGameIfNeeded() { + if (VHelper.isVGameOn() + && SPUtils.getBoolean(Constants.SP_HOME_VGAME_AREA_ENABLED, true) + ) { + val entityList = getSortedVEntityList() + + if (entityList.isNotEmpty()) { + mVGameList = ArrayList(entityList) + + if (mHomeSlides.isNotEmpty() || mHomeRecommends.isNotEmpty()) { + transformationItemData() + } + } else { + mVGameList?.clear() + transformationItemData() + } + } else { + mVGameList?.clear() + transformationItemData() + } + } + + /** + * 获取排好序的最近在玩列表 + * + * 首位:固定展示最近一次点击过下载\启动的游戏 + * 后续位置-优先级:完成下载但未启动过的游戏 > 完成下载且已启动过的游戏 + * 完成下载但未启动过的游戏:按照点击下载的时间倒序排列 + * 完成下载且已启动过的游戏:按照游戏的畅玩时长从左(长)向右(短)排列 + */ + private fun getSortedVEntityList(): List { + val rawDownloadEntityList = DownloadManager.getInstance().allVDownloadTaskSnapshots + val rawInstalledEntityList = VHelper.getAllVGameSnapshots() + val rawEntityList = arrayListOf() + + var fixedTopEntity: VGameItemData? = null + val unplayedEntityList = arrayListOf() + val playedEntityList = arrayListOf() + val sortedEntityList = arrayListOf() + + val distinctIdSet = hashSetOf() + // 将下载任务的实体放进待排序列表里 + for (rawDownloadEntity in rawDownloadEntityList) { + distinctIdSet.add(rawDownloadEntity.gameId) + rawEntityList.add(rawDownloadEntity) + } + // 将已安装任务的实体放进待排序列表里 + for (rawInstalledEntity in rawInstalledEntityList) { + if (distinctIdSet.contains(rawInstalledEntity.downloadEntity.gameId)) { + continue + } + rawEntityList.add(rawInstalledEntity.downloadEntity) + } + distinctIdSet.clear() + + for (rawEntity in rawEntityList) { + val lastPlayedTime = VHelper.getLastPlayedTime(rawEntity) + val latestModifiedTime = maxOf(rawEntity.start, VHelper.getLastPlayedTime(rawEntity)) + + if (fixedTopEntity == null) { + fixedTopEntity = VGameItemData.from(rawEntity) + } else { + val fixedTopLatestModifiedTime = maxOf(fixedTopEntity.downloadEntity.start, VHelper.getLastPlayedTime(fixedTopEntity.downloadEntity)) + if (latestModifiedTime > fixedTopLatestModifiedTime) { + fixedTopEntity = VGameItemData.from(rawEntity) + } + } + + if (lastPlayedTime == 0L) { + unplayedEntityList.add(VGameItemData.from(rawEntity)) + } else { + playedEntityList.add(VGameItemData.from(rawEntity)) + } + } + + fixedTopEntity?.let { + sortedEntityList.add(it) + unplayedEntityList.removeAll { entity -> + entity.downloadEntity.packageName == fixedTopEntity.downloadEntity.packageName + } + playedEntityList.removeAll { entity -> + entity.downloadEntity.packageName == fixedTopEntity.downloadEntity.packageName + } + } + sortedEntityList.addAll(unplayedEntityList.sortedByDescending { it.downloadEntity.start }) + sortedEntityList.addAll(playedEntityList.sortedByDescending { VHelper.getTotalPlayedTime(it.downloadEntity.packageName) }) + + return sortedEntityList.take(8) } private fun initPlugin(updateList: List?) { @@ -101,7 +211,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { } // 下载7天排序 - list.sortWith(Comparator { o1, o2 -> o2.download - o1.download }) + list.sortWith { o1, o2 -> o2.download - o1.download } mPluginList = list if (mHomeSlides.isNotEmpty() || mHomeRecommends.isNotEmpty()) { @@ -109,40 +219,6 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { } } - @SuppressLint("CheckResult") - private fun getHomeSlides() { - mApi.getHomeSlides(HaloApp.getInstance().channel, BuildConfig.VERSION_NAME) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse>() { - override fun onSuccess(data: List) { - mHomeSlides.addAll(data) - getHomeRecommends() - } - - override fun onFailure(exception: Exception) { - getHomeRecommends() - } - }) - } - - @SuppressLint("CheckResult") - private fun getHomeRecommends() { - mApi.getHomeRecommends(HaloApp.getInstance().channel, BuildConfig.VERSION_NAME) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse>() { - override fun onSuccess(data: List) { - mHomeRecommends.addAll(data) - getHomeContent(true) - } - - override fun onFailure(exception: Exception) { - getHomeContent(true) - } - }) - } - @SuppressLint("CheckResult") fun getHomeContent(initData: Boolean) { if (mIsLoading && !initData || loadStatus.value == LoadStatus.LIST_OVER) return @@ -241,7 +317,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { private fun initRandomGame(subjectId: String, sourceList: List?) { var rawList: MutableList? = null for (entity in mHomeContents) { - if (entity.linkColumn?.id == subjectId) rawList = entity.linkColumn?.data + if (entity.linkColumn?.id == subjectId) rawList = entity.linkColumn.data } if (rawList == null) return @@ -273,185 +349,231 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { } fun transformationItemData() { - mSnapshotItemList.clear() + runOnIoThread(true) { + mSnapshotItemList.clear() - // 是否使用带特别高的带分割线的 item - var useUltraHeightDivider = false - var shouldShowDivider = false + // 是否使用带特别高的带分割线的 item + var useUltraHeightDivider = false + var shouldShowDivider = false - val iterator = mHomeContents.iterator() - while (iterator.hasNext()) { - val item = iterator.next() - // 裁剪排行榜数据数量 - if (item.linkType == "column_collection" && item.linkColumn?.style == "top") { - item.linkColumn.columns.let { columns -> - for (column in columns) { - column.data = ArrayList(column.data?.take(RankCollectionAdapter.MAX_RANK_ITEM_COUNT) ?: listOf()) - } - } - } - } - - if (mSmartSubject != null && mHomeContents.size > mSmartSubject!!.sort) { - val element = HomeContent( - linkType = "smart_subject", - linkId = mSmartSubject?.id ?: "", - linkText = mSmartSubject?.name ?: "", - linkColumn = mSmartSubject - ) - mHomeContents.add(mSmartSubject!!.sort, element) - mSmartSubject = null // 防止重复插入 - } - - if (mHomeSlides.isNotEmpty()) { - val slideItem = HomeItemData() - slideItem.slides = mHomeSlides - mSnapshotItemList.add(slideItem) - } - - if (mHomeRecommends.isNotEmpty()) { - val recommend = HomeItemData() - recommend.recommends = mHomeRecommends - mSnapshotItemList.add(recommend) - } - - // 插件化 - if (mPluginList != null && mPluginList!!.isNotEmpty()) { - val plugin = HomeItemData() - plugin.pluginList = mPluginList - mSnapshotItemList.add(plugin) - - for (entity in mPluginList!!) { - addGamePositionAndPackage(entity) - } - } - - for (i in 0 until mHomeContents.size) { - val homeContent = mHomeContents[i] - val linkType = homeContent.linkType - val linkStyle = homeContent.linkColumn?.style ?: "" - - if (i + 1 < mHomeContents.size) { - val nextItem = mHomeContents[i + 1] - shouldShowDivider = nextItem.linkType == "game" || nextItem.linkType == "video" - } - - if (linkType == "game" || linkType == "video") { - val attachGame = HomeItemData() - attachGame.blockPosition = i - attachGame.attachGame = homeContent - attachGame.attachGame?.linkGame?.outerSequence = attachGame.blockPosition - attachGame.attachGame?.linkGame?.sequence = attachGame.blockPosition - mSnapshotItemList.add(attachGame) - } else if (linkType == "top_game_comment") { - val head = HomeItemData() - head.columnHead = SubjectEntity(type = linkType, name = "安利墙") - mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData) - mSnapshotItemList.add(head) - - val amway = HomeItemData() - amway.blockPosition = i - amway.amway = homeContent.linkTopGameComment?.apply { - for ((index, amwayEntity) in this.withIndex()) { - amwayEntity.game.sequence = index - amwayEntity.game.outerSequence = i - } - } - mSnapshotItemList.add(amway) - } else if (linkType == "column" - || linkType == "column_collection" && linkStyle != "top" - || linkType == "smart_subject" - || linkType == "column_test" - ) { - - homeContent.linkColumn?.data = RegionSettingHelper.filterGame(homeContent.linkColumn?.data) - - homeContent.linkColumn?.let { subjectEntity -> - subjectEntity.data?.let { data -> - // 这个 for 循环主要功能是用来标识替换已安装的游戏 - for (game in data) { - mSubjectGameIdList.add(game.id) - // 应用专题是否显示游戏名后缀的配置 - game.shouldShowNameSuffix = subjectEntity.showSuffix - } - - subjectEntity.relatedColumnId?.let { - GameSubstituteRepositoryHelper.replaceGames(data, mSubjectGameIdList, it, mShouldLogReplaceEvent) - mShouldLogReplaceEvent = false + val iterator = mHomeContents.iterator() + while (iterator.hasNext()) { + val item = iterator.next() + // 裁剪排行榜数据数量 + if (item.linkType == "column_collection" && item.linkColumn?.style == "top") { + item.linkColumn.columns.let { columns -> + for (column in columns) { + column.data = ArrayList(column.data?.take(RankCollectionAdapter.MAX_RANK_ITEM_COUNT) ?: listOf()) } } } - // 仅普通纵向专题需要特别高的分割线 - useUltraHeightDivider = homeContent.linkColumn?.type == "game_vertical" + // 双列卡片专题过滤掉无封面图游戏 + if (item.linkType == "column" && item.linkColumn?.type == "game_double_card") { + item.linkColumn.data = item.linkColumn.data?.filter { it.columnImage.isNotBlank() }?.toMutableList() + // 游戏数量小于2个不显示专题,所以直接去掉 + if ((item.linkColumn.data?.size ?: 0) < 2) { + iterator.remove() + } + } + } - LegacyHomeSubjectTransformer.transform( - mSnapshotItemList as ArrayList, - homeContent.linkColumn, - i, - { HomeItemData() }, - { addGamePositionAndPackage(it) } + if (mSmartSubject != null && mHomeContents.size > mSmartSubject!!.sort) { + val element = HomeContent( + linkType = "smart_subject", + linkId = mSmartSubject?.id ?: "", + linkText = mSmartSubject?.name ?: "", + linkColumn = mSmartSubject ) - } else if (linkType == "column_collection" && linkStyle == "top") { - val rankItem = HomeItemData().apply { - blockPosition = i - rankCollection = homeContent.linkColumn - } - mSnapshotItemList.add(rankItem) - appendAdditionalInfoToRankSubjectGame(homeContent.linkColumn, i) - } else if (linkType == "common_collection") { - val commonCollectionItem = HomeItemData() - val subjectEntity = SubjectEntity().apply { - type = homeContent.linkType - id = homeContent.linkId - name = homeContent.linkText - style = homeContent.commonCollection?.style - commonCollectionList = homeContent.commonCollection?.collectionList - } - val head = HomeItemData() - head.columnHead = subjectEntity - mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData) - mSnapshotItemList.add(head) - commonCollectionItem.commonCollection = subjectEntity - commonCollectionItem.blockPosition = i - mSnapshotItemList.add(commonCollectionItem) - } else if (linkType == "game_list_collection") { - val head = HomeItemData() - head.columnHead = SubjectEntity(type = linkType, name = homeContent.linkText) - mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData) - mSnapshotItemList.add(head) + mHomeContents.add(mSmartSubject!!.sort, element) + mSmartSubject = null // 防止重复插入 + } - val gameCollection = HomeItemData() - gameCollection.blockPosition = i - val itemDataList = arrayListOf().apply { - if (!homeContent.linkGameCollection.isNullOrEmpty()) { - var position = 0 - for (item in homeContent.linkGameCollection) { - add(GameCollectionListItemData(gameCollectionItem = item, gameStartPosition = position)) - position += if (item.count?.game!! > 2) 3 else if (item.games?.size == 0) 0 else item.count?.game ?: 0 + if (mHomeSlides.isNotEmpty()) { + val slideItem = HomeItemData() + slideItem.slides = mHomeSlides + mSnapshotItemList.add(slideItem) + } + + if (mHomeRecommends.isNotEmpty()) { + val recommend = HomeItemData() + recommend.recommends = mHomeRecommends + mSnapshotItemList.add(recommend) + } + + // 最近在玩的畅玩游戏 + if (mVGameList != null && !mVGameList.isNullOrEmpty()) { + val item = HomeItemData() + item.recentVGame = mVGameList + mSnapshotItemList.add(item) + + for (entity in mVGameList!!) { + val packageName = entity.downloadEntity.packageName + mPositionAndPackageMap[packageName + (mSnapshotItemList.size - 1)] = mSnapshotItemList.size - 1 + } + } + + // 插件化 + if (mPluginList != null && mPluginList!!.isNotEmpty()) { + val plugin = HomeItemData() + plugin.pluginList = mPluginList + mSnapshotItemList.add(plugin) + + for (entity in mPluginList!!) { + addGamePositionAndPackage(entity) + } + } + + for (i in 0 until mHomeContents.size) { + val homeContent = mHomeContents[i] + val linkType = homeContent.linkType + val linkStyle = homeContent.linkColumn?.style ?: "" + + if (i + 1 < mHomeContents.size) { + val nextItem = mHomeContents[i + 1] + shouldShowDivider = nextItem.linkType == "game" || nextItem.linkType == "video" + } + + if (linkType == "game" || linkType == "video") { + val attachGame = HomeItemData() + attachGame.blockPosition = i + attachGame.attachGame = homeContent + attachGame.attachGame?.linkGame?.outerSequence = attachGame.blockPosition + attachGame.attachGame?.linkGame?.sequence = attachGame.blockPosition + mSnapshotItemList.add(attachGame) + homeContent.linkGame?.let { + addGamePositionAndPackage(it) + } + } else if (linkType == "top_game_comment") { + val head = HomeItemData() + head.columnHead = SubjectEntity(type = linkType, name = "安利墙") + mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData) + mSnapshotItemList.add(head) + + val amway = HomeItemData() + amway.blockPosition = i + amway.amway = homeContent.linkTopGameComment?.apply { + for ((index, amwayEntity) in this.withIndex()) { + amwayEntity.game.sequence = index + amwayEntity.game.outerSequence = i } } - } - gameCollection.gameCollection = itemDataList - mSnapshotItemList.add(gameCollection) - } else { - val unknown = HomeItemData() - unknown.blockPosition = i + 1 - unknown.unknownData = "" - mSnapshotItemList.add(unknown) - } + mSnapshotItemList.add(amway) + } else if (linkType == "column" + || linkType == "column_collection" && linkStyle != "top" + || linkType == "smart_subject" + || linkType == "column_test" + ) { - if (i != 0 && shouldShowDivider) { - if (useUltraHeightDivider) { - useUltraHeightDivider = false - mSnapshotItemList.add(HomeItemData(lineDivider = ULTRA_HEIGHT_DIVIDER)) + homeContent.linkColumn?.data = RegionSettingHelper.filterGame(homeContent.linkColumn?.data) + + homeContent.linkColumn?.let { subjectEntity -> + subjectEntity.data?.let { data -> + // 这个 for 循环主要功能是用来标识替换已安装的游戏 + for (game in data) { + mSubjectGameIdList.add(game.id) + // 应用专题是否显示游戏名后缀的配置 + game.shouldShowNameSuffix = subjectEntity.showSuffix + } + + subjectEntity.relatedColumnId?.let { + GameSubstituteRepositoryHelper.replaceGames(data, mSubjectGameIdList, it, mShouldLogReplaceEvent) + mShouldLogReplaceEvent = false + } + } + } + + // 仅普通纵向专题需要特别高的分割线 + useUltraHeightDivider = homeContent.linkColumn?.type == "game_vertical" + + LegacyHomeSubjectTransformer.transform( + mSnapshotItemList as ArrayList, + homeContent.linkColumn, + i, + { HomeItemData() }, + { addGamePositionAndPackage(it) } + ) + } else if (linkType == "column_collection" && linkStyle == "top") { + val rankItem = HomeItemData().apply { + blockPosition = i + rankCollection = homeContent.linkColumn + } + mSnapshotItemList.add(rankItem) + appendAdditionalInfoToRankSubjectGame(homeContent.linkColumn, i) + } else if (linkType == "common_collection") { + val commonCollectionItem = HomeItemData() + val subjectEntity = SubjectEntity().apply { + type = homeContent.linkType + id = homeContent.linkId + name = homeContent.linkText + style = homeContent.commonCollection?.style + commonCollectionList = homeContent.commonCollection?.collectionList + } + val head = HomeItemData() + head.columnHead = subjectEntity + mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData) + mSnapshotItemList.add(head) + commonCollectionItem.commonCollection = subjectEntity + commonCollectionItem.blockPosition = i + mSnapshotItemList.add(commonCollectionItem) + } else if (linkType == "game_list_collection") { + val head = HomeItemData() + head.columnHead = SubjectEntity(type = linkType, name = homeContent.linkText) + mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData) + mSnapshotItemList.add(head) + + val gameCollection = HomeItemData() + gameCollection.blockPosition = i + val itemDataList = arrayListOf().apply { + if (!homeContent.linkGameCollection.isNullOrEmpty()) { + var position = 0 + for (item in homeContent.linkGameCollection) { + add(GameCollectionListItemData(gameCollectionItem = item, gameStartPosition = position)) + position += if (item.count?.game!! > 2) 3 else if (item.games?.size == 0) 0 else item.count?.game ?: 0 + } + } + } + gameCollection.gameCollection = itemDataList + mSnapshotItemList.add(gameCollection) + } else if (linkType == "community_article" + || linkType == "question" + || linkType == "bbs_video" + || linkType == "news" + ) { + val homeItemData = HomeItemData().apply { + blockPosition = i + bigImageRecommend = SubjectEntity( + id = homeContent.linkId, + type = homeContent.linkType, + image = homeContent.image, + firstLineRecommend = homeContent.firstLineRecommend, + secondLineRecommend = homeContent.secondLineRecommend, + recommendTag = homeContent.recommendTag, + columnName = homeContent.linkText + ) + } + mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData) + mSnapshotItemList.add(homeItemData) } else { - mSnapshotItemList.add(HomeItemData(lineDivider = DEFAULT_DIVIDER)) + val unknown = HomeItemData() + unknown.blockPosition = i + 1 + unknown.unknownData = "" + mSnapshotItemList.add(unknown) + } + + if (i != 0 && shouldShowDivider) { + if (useUltraHeightDivider) { + useUltraHeightDivider = false + mSnapshotItemList.add(HomeItemData(lineDivider = ULTRA_HEIGHT_DIVIDER)) + } else { + mSnapshotItemList.add(HomeItemData(lineDivider = DEFAULT_DIVIDER)) + } + shouldShowDivider = false } - shouldShowDivider = false } + positionAndPackageMap.postValue(HashMap(mPositionAndPackageMap)) + itemDataList.postValue(ArrayList(mSnapshotItemList)) } - itemDataList.postValue(mSnapshotItemList) } private fun addGamePositionAndPackage(game: GameEntity) { @@ -459,7 +581,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { for (apkEntity in game.getApk()) { packages += apkEntity.packageName } - positionAndPackageMap[packages + (mSnapshotItemList.size - 1)] = mSnapshotItemList.size - 1 + mPositionAndPackageMap[packages + (mSnapshotItemList.size - 1)] = mSnapshotItemList.size - 1 game.gameLocation = GameEntity.GameLocation.INDEX game.setEntryMap(DownloadManager.getInstance().getEntryMap(game.name)) } diff --git a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt index bd5ec69a75..043923d254 100644 --- a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt +++ b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt @@ -31,9 +31,11 @@ import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.LinkEntity import com.gh.gamecenter.game.GameAndPosition import com.gh.gamecenter.game.GameItemViewHolder +import com.gh.gamecenter.game.bigimagerecommend.BigImageRecommendViewHolder import com.gh.gamecenter.game.columncollection.GameColumnCollectionViewHolder import com.gh.gamecenter.game.commoncollection.CommonCollectionViewHolder import com.gh.gamecenter.game.commoncollection.detail.CommonCollectionDetailActivity +import com.gh.gamecenter.game.doublecard.DoubleCardListViewHolder import com.gh.gamecenter.game.gallery.GameGallerySlideViewHolder import com.gh.gamecenter.game.gallery.GameGalleryViewHolder import com.gh.gamecenter.game.horizontal.GameHorizontalListViewHolder @@ -81,6 +83,8 @@ class LegacyHomeFragmentAdapterAssistant( if (itemData.blankDivider != null) return ItemViewType.BLANK_DIVIDER if (itemData.commonCollection != null) return ItemViewType.COMMON_LINK_COLLECTION if (itemData.rankCollection != null) return ItemViewType.RANK_COLLECTION + if (itemData.doubleCardColumn != null) return ItemViewType.DOUBLE_CARD_COLUMN + if (itemData.bigImageRecommend != null) return ItemViewType.BIG_IMAGE_RECOMMEND return 0 } @@ -103,6 +107,8 @@ class LegacyHomeFragmentAdapterAssistant( ItemViewType.BLANK_DIVIDER -> BlankDividerViewHolder(parent.toBinding()) ItemViewType.COMMON_LINK_COLLECTION -> CommonCollectionViewHolder(parent.toBinding()) ItemViewType.RANK_COLLECTION -> RankCollectionViewHolder(parent.toBinding()) + ItemViewType.DOUBLE_CARD_COLUMN -> DoubleCardListViewHolder(parent.toBinding()) + ItemViewType.BIG_IMAGE_RECOMMEND -> BigImageRecommendViewHolder(parent.toBinding()) else -> throw NullPointerException() } @@ -131,6 +137,8 @@ class LegacyHomeFragmentAdapterAssistant( ) is CommonCollectionViewHolder -> bindCommonCollection(holder, item) is RankCollectionViewHolder -> bindRankCollection(holder, item) + is DoubleCardListViewHolder -> bindGameDoubleCardList(holder, item) + is BigImageRecommendViewHolder -> bindBigImageRecommend(holder, item) } } @@ -158,7 +166,29 @@ class LegacyHomeFragmentAdapterAssistant( val horizontalColumn = itemData.horizontalColumn if (horizontalColumn != null) { - positionList.add(GameAndPosition(null, position)) + for (i in horizontalColumn.data!!.indices) { + val entity = horizontalColumn.data!![i] + if (!entity.image.isNullOrEmpty()) continue + for (apkEntity in entity.getApk()) { + if (apkEntity.packageName == packageName) { + positionList.add(GameAndPosition(entity, position)) + } + } + } + return + } + + val horizontalSlide = itemData.horizontalSlide + if (horizontalSlide != null) { + for (i in horizontalSlide.data!!.indices) { + val entity = horizontalSlide.data!![i] + if (!entity.image.isNullOrEmpty()) continue + for (apkEntity in entity.getApk()) { + if (apkEntity.packageName == packageName) { + positionList.add(GameAndPosition(entity, position)) + } + } + } return } @@ -372,7 +402,7 @@ class LegacyHomeFragmentAdapterAssistant( val subjectAdapter = holder.bindHorizontalList(subjectEntity!!) if (mOuterType == OuterType.AMWAY) { - holder.binding.horizontalRv.isNestedScrollingEnabled = false + holder.binding.recyclerView.isNestedScrollingEnabled = false } val exposureEventList = arrayListOf() @@ -404,7 +434,7 @@ class LegacyHomeFragmentAdapterAssistant( val subjectAdapter = holder.bindHorizontalSlideList(subjectEntity!!, this) if (mOuterType == OuterType.AMWAY) { - holder.binding.horizontalRv.isNestedScrollingEnabled = false + holder.binding.recyclerView.isNestedScrollingEnabled = false } val exposureEventList = arrayListOf() @@ -434,7 +464,7 @@ class LegacyHomeFragmentAdapterAssistant( } if (subjectEntity.type != "game_horizontal") { - holder.binding.horizontalRv.doOnScrolledSpecificDistance(distanceX = DisplayUtils.dip2px(24f), singleTimeEvent = true) { + holder.binding.recyclerView.doOnScrolledSpecificDistance(distanceX = DisplayUtils.dip2px(24f), singleTimeEvent = true) { if (mOuterType == OuterType.NEW_HOME) { MtaHelper.onEvent("游戏专题", "新首页专题滑动", "内容" + item.blockPosition + "_" + subjectEntity.name) } else { @@ -839,6 +869,38 @@ class LegacyHomeFragmentAdapterAssistant( } } + private fun bindGameDoubleCardList(holder: DoubleCardListViewHolder, item: LegacyHomeItemData) { + item.doubleCardColumn?.data?.run { + val subjectEntity = item.doubleCardColumn!! + val subjectAdapter = holder.bindDoubleCardList(subjectEntity) + + val exposureEventList = arrayListOf() + runOnIoThread(true) { + for (i in 0 until subjectAdapter.itemCount) { + if (i >= size) break + + get(i).sequence = i + val event = ExposureEvent.createEventWithSourceConcat( + gameEntity = get(i), + basicSource = mBasicExposureSources, + source = listOf(ExposureSource("专题", subjectEntity.name ?: "")) + ) + exposureEventList.add(event) + } + } + item.exposureEventList = exposureEventList + subjectAdapter.exposureEventList = exposureEventList + } + } + + private fun bindBigImageRecommend(holder: BigImageRecommendViewHolder, item: LegacyHomeItemData) { + item.bigImageRecommend?.run { + holder.bindBigImageRecommend(this, "新首页") { + NewFlatLogUtils.logHomeGameContentCardClick(it.type ?: "", it.link ?: "", it.linkText ?: "") + } + } + } + private fun setPageSwitchData() { PageSwitchDataHelper.pushCurrentPageData( hashMapOf( diff --git a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeItemData.kt b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeItemData.kt index 7a1bcb222e..5b96ea2ad1 100644 --- a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeItemData.kt +++ b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeItemData.kt @@ -28,5 +28,7 @@ open class LegacyHomeItemData( var columnCollection: SubjectEntity? = null, var commonCollection: SubjectEntity? = null, var rankCollection: SubjectEntity? = null, // 排行榜样式专题合集 + var doubleCardColumn: SubjectEntity? = null, // 双列卡片专题 + var bigImageRecommend: SubjectEntity? = null, //提问、帖子、视频帖、文章 var blankDivider: Float? = null // 空白填充内容 ) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeSubjectTransformer.kt b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeSubjectTransformer.kt index 9b11983b2a..3f1203dd1f 100644 --- a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeSubjectTransformer.kt +++ b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeSubjectTransformer.kt @@ -115,13 +115,32 @@ object LegacyHomeSubjectTransformer { val itemHorizontalSlide = newItemInstance() itemHorizontalSlide.blockPosition = blockPosition + 1 itemHorizontalSlide.horizontalSlide = subjectEntity - appendAdditionalInfoToSubjectGame(subjectEntity, blockPosition) - if (subjectEntity.indexRightTop == "all") { subjectEntity.indexRightTopLink = LinkEntity(text = subjectEntity.name, link = subjectEntity.id, type = "column_test") } - itemList.add(itemHorizontalSlide) + + if (!subjectEntity.showDownload) { + appendAdditionalInfoToSubjectGame(subjectEntity, blockPosition) + } else { + for (i in 0 until data.size) { + val game = data[i] + if (!game.image.isNullOrEmpty()) continue + game.subjectData = GameSubjectData( + id = subjectEntity.id, + name = subjectEntity.name, + tag = subjectEntity.tag, + position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, + isOrder = subjectEntity.isOrder, + briefStyle = subjectEntity.briefStyle, + isShowSuffix = subjectEntity.showSuffix + ) + game.outerSequence = blockPosition + game.sequence = i + + addGamePositionAndPackage(game) + } + } return } @@ -129,8 +148,29 @@ object LegacyHomeSubjectTransformer { val itemDataSubject = newItemInstance() itemDataSubject.blockPosition = blockPosition + 1 itemDataSubject.horizontalColumn = subjectEntity - appendAdditionalInfoToSubjectGame(subjectEntity, blockPosition) itemList.add(itemDataSubject) + + if (!subjectEntity.showDownload) { + appendAdditionalInfoToSubjectGame(subjectEntity, blockPosition) + } else { + for (i in 0 until data.size) { + val game = data[i] + if (!game.image.isNullOrEmpty()) continue + game.subjectData = GameSubjectData( + id = subjectEntity.id, + name = subjectEntity.name, + tag = subjectEntity.tag, + position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, + isOrder = subjectEntity.isOrder, + briefStyle = subjectEntity.briefStyle, + isShowSuffix = subjectEntity.showSuffix + ) + game.outerSequence = blockPosition + game.sequence = i + + addGamePositionAndPackage(game) + } + } return } @@ -166,6 +206,15 @@ object LegacyHomeSubjectTransformer { return } + if (subjectEntity.type == "game_double_card") { + val itemDataSubject = newItemInstance() + itemDataSubject.blockPosition = blockPosition + 1 + itemDataSubject.doubleCardColumn = subjectEntity + appendAdditionalInfoToSubjectGame(subjectEntity, blockPosition) + itemList.add(itemDataSubject) + return + } + for (i in 0 until data.size) { val game = data[i] game.sequence = i diff --git a/app/src/main/java/com/gh/gamecenter/manager/DataCollectionManager.java b/app/src/main/java/com/gh/gamecenter/manager/DataCollectionManager.java index d9fef20e7c..613262c8b8 100644 --- a/app/src/main/java/com/gh/gamecenter/manager/DataCollectionManager.java +++ b/app/src/main/java/com/gh/gamecenter/manager/DataCollectionManager.java @@ -36,7 +36,7 @@ public class DataCollectionManager { } public static void onEvent(Context context, String type, Map map, boolean isUpload) { - AppExecutor.getLogExecutor().execute(() -> { + AppExecutor.getIoExecutor().execute(() -> { map.put("createdOn", Utils.getTime(context)); if (isUpload) { DataCollectionManager.getInstance().realTimeUpload(type, map); @@ -183,7 +183,7 @@ public class DataCollectionManager { * 统计点击数据 */ public void statClickData() { - AppExecutor.getLogExecutor().execute(() -> { + AppExecutor.getIoExecutor().execute(() -> { List list = dao.getClickData(); if (list != null && !list.isEmpty()) { List ids = new ArrayList<>(); @@ -207,7 +207,7 @@ public class DataCollectionManager { } public static void onEvent(Context context, String type, Map map) { - AppExecutor.getLogExecutor().execute(() -> { + AppExecutor.getIoExecutor().execute(() -> { map.put("createdOn", Utils.getTime(context)); if ("news".equals(type) || "download".equals(type) || "search".equals(type)) { DataCollectionManager.getInstance().realTimeUpload(type, map); diff --git a/app/src/main/java/com/gh/gamecenter/manager/PackagesManager.kt b/app/src/main/java/com/gh/gamecenter/manager/PackagesManager.kt index ede57e2cb8..95bc5e6402 100644 --- a/app/src/main/java/com/gh/gamecenter/manager/PackagesManager.kt +++ b/app/src/main/java/com/gh/gamecenter/manager/PackagesManager.kt @@ -2,11 +2,11 @@ package com.gh.gamecenter.manager import android.text.TextUtils import com.gh.common.constant.Config +import com.gh.common.util.PackageUtils import com.gh.gamecenter.entity.GameInstall import com.gh.gamecenter.entity.GameUpdateEntity +import com.halo.assistant.HaloApp import java.util.* -import kotlin.collections.ArrayList -import kotlin.collections.HashMap /** * todo 整理部分与[PackageUtils]冲突的方法 @@ -109,7 +109,7 @@ object PackagesManager { // TODO 检查为什么 4.3.0 以后 arrayList.contains 也会触发 outOfBounds 异常,而以前不会,因为 kotlin 版本更新? // 先简单改成 SynchronizedList - return installedPkgList.contains(packageName) + return installedPkgList.contains(packageName) && PackageUtils.isInstalled(HaloApp.getInstance(), packageName) } /** diff --git a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java b/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java index 488465bb49..0b0403e01c 100644 --- a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java +++ b/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java @@ -144,7 +144,7 @@ public class UpdateManager { } if (DownloadStatus.done.equals(downloadEntity.getStatus())) { - DownloadManager.getInstance().cancel(downloadEntity.getUrl(), false, true); + DownloadManager.getInstance().cancel(downloadEntity.getUrl(), false, true, false); if (downloadDialog != null) { try { downloadDialog.dismiss(); @@ -517,7 +517,7 @@ public class UpdateManager { } downloadEntity.setPackageName(mApplicationContext.getPackageName()); - DownloadManager.getInstance().cancel(appEntity.getUrl(), true, true); + DownloadManager.getInstance().cancel(appEntity.getUrl(), true, true, false); DownloadManager.getInstance().pauseAll(); AppExecutor.getUiExecutor().executeWithDelay(() -> { diff --git a/app/src/main/java/com/gh/gamecenter/manager/UserManager.java b/app/src/main/java/com/gh/gamecenter/manager/UserManager.java index 051a08c94e..e2b4e6bc15 100644 --- a/app/src/main/java/com/gh/gamecenter/manager/UserManager.java +++ b/app/src/main/java/com/gh/gamecenter/manager/UserManager.java @@ -7,10 +7,14 @@ import android.text.TextUtils; import androidx.annotation.Nullable; +import com.gh.common.repository.ReservationRepository; +import com.gh.common.util.ErrorHelper; import com.gh.gamecenter.common.base.activity.BaseActivity; import com.gh.gamecenter.common.constant.Constants; +import com.gh.gamecenter.common.eventbus.EBShowDialog; import com.gh.gamecenter.common.exposure.meta.MetaUtil; -import com.gh.common.repository.ReservationRepository; +import com.gh.gamecenter.common.retrofit.BiResponse; +import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.common.utils.DeviceUtils; import com.gh.gamecenter.common.utils.EnvHelper; import com.gh.gamecenter.core.utils.GsonUtils; @@ -19,9 +23,6 @@ import com.gh.gamecenter.entity.CommunityEntity; import com.gh.gamecenter.entity.LoginTokenEntity; import com.gh.gamecenter.entity.TokenEntity; import com.gh.gamecenter.entity.UserInfoEntity; -import com.gh.gamecenter.common.eventbus.EBShowDialog; -import com.gh.gamecenter.common.retrofit.BiResponse; -import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.retrofit.RetrofitManager; import com.gh.gamecenter.retrofit.service.ApiService; import com.gh.gamecenter.user.UserRepository; @@ -187,7 +188,7 @@ public class UserManager { public void onFailure(HttpException e) { super.onFailure(e); String errorMessage = "null"; - if (e != null && (e.code() == 401 || e.code() == 400)) { + if (e != null && (e.code() == 401 || e.code() == 403 || e.code() == 400)) { int code = -1; try { errorMessage = e.response().errorBody().string(); @@ -200,6 +201,11 @@ public class UserManager { if (code == 400401 || code == 400802) { // 自动注销 UserRepository.getInstance().logout(); } + + if (code == 403401) { + UserRepository.getInstance().logout(); + ErrorHelper.handleLoginError(HaloApp.getInstance().getApplicationContext(), e); + } } catch (Exception e1) { e1.printStackTrace(); } diff --git a/app/src/main/java/com/gh/gamecenter/message/MessageDetailFragment.java b/app/src/main/java/com/gh/gamecenter/message/MessageDetailFragment.java index 77e0f634ee..cba0cf2ac1 100644 --- a/app/src/main/java/com/gh/gamecenter/message/MessageDetailFragment.java +++ b/app/src/main/java/com/gh/gamecenter/message/MessageDetailFragment.java @@ -21,6 +21,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.gh.common.constant.Config; import com.gh.common.util.CheckLoginUtils; import com.gh.common.util.DialogUtils; +import com.gh.gamecenter.common.callback.ConfirmListener; import com.gh.gamecenter.core.utils.DisplayUtils; import com.gh.gamecenter.common.constant.EntranceConsts; import com.gh.common.util.ErrorHelper; @@ -399,7 +400,7 @@ public class MessageDetailFragment extends ToolbarFragment implements OnCommentC e1.printStackTrace(); } } - ErrorHelper.handleError(requireContext(), errorString, false); + ErrorHelper.handleError(requireContext(), errorString, false, () -> mBinding.pieceCommentTypingContainer.answerCommentSendBtn.performClick()); } }); }); diff --git a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameAdapter.kt index 6ea5d76125..7010c26614 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameAdapter.kt @@ -33,7 +33,7 @@ import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.* import com.gh.gamecenter.databinding.ItemPlayedGameBinding -import com.gh.gamecenter.databinding.ItemUsageStatsBinding +import com.gh.gamecenter.databinding.ViewSimpleToggleBinding import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.eventbus.EBDownloadStatus import com.lightgame.download.DownloadEntity @@ -340,7 +340,7 @@ open class PlayedGameAdapter( } } - class UsageStatsViewHolder(var binding: ItemUsageStatsBinding) : RecyclerView.ViewHolder(binding.root) + class UsageStatsViewHolder(var binding: ViewSimpleToggleBinding) : RecyclerView.ViewHolder(binding.root) fun resetListData() { if (!hasHeader()) EnergyTaskHelper.postEnergyTask("open_game_time") diff --git a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt index 072ed61789..56025baaf3 100644 --- a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt +++ b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt @@ -3,23 +3,25 @@ package com.gh.gamecenter.packagehelper import android.annotation.SuppressLint import android.text.TextUtils import androidx.lifecycle.MutableLiveData -import com.gh.gamecenter.common.exposure.meta.MetaUtil import com.gh.common.filter.RegionSettingHelper -import com.gh.gamecenter.common.loghub.LoghubUtils -import com.gh.common.util.* +import com.gh.common.util.ApkActiveUtils +import com.gh.common.util.GameUtils +import com.gh.common.util.PackageUtils import com.gh.gamecenter.R -import com.gh.gamecenter.core.runOnIoThread -import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.common.exposure.meta.MetaUtil +import com.gh.gamecenter.common.loghub.LoghubUtils +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.retrofit.ObservableUtil +import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.common.utils.secondOrNull import com.gh.gamecenter.common.utils.tryCatchInRelease +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.entity.* import com.gh.gamecenter.manager.PackagesManager import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.packagehelper.PackageRepository.gameInstalled import com.gh.gamecenter.packagehelper.PackageRepository.gameUpdate -import com.gh.gamecenter.common.retrofit.BiResponse -import com.gh.gamecenter.common.retrofit.ObservableUtil -import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp import com.lightgame.utils.Utils @@ -53,6 +55,9 @@ object PackageRepository { val gameUpdateLiveData = MutableLiveData>() val gameInstalledLiveData = MutableLiveData>() + val installedGameListLiveData = MutableLiveData>() // 已安装的游戏列表 (信息比 GameInstall 更全) + + private val mInstalledGameList = Collections.synchronizedList(ArrayList()) val gameInstalled = Collections.synchronizedList(ArrayList()) val gameUpdate = ArrayList() @@ -61,11 +66,11 @@ object PackageRepository { /** * 预留方法,如果想手动初始化可以调用 */ - // TODO 在获取到页面数据以后刷新 @JvmStatic fun initData() { runOnIoThread { if (gameInstalled.isNotEmpty()) gameInstalled.clear() + if (mInstalledGameList.isNotEmpty()) mInstalledGameList.clear() if (gameUpdate.isNotEmpty()) gameUpdate.clear() if (mInstalledPkgList.isNotEmpty()) mInstalledPkgList.clear() @@ -169,11 +174,14 @@ object PackageRepository { * 如果有数据变动会调用[gameUpdateLiveData] [gameInstalledLiveData]实现相关页面刷新 * * @param filteredList 已安装的游戏包名集合 (仅已收录部分) + * @param onWorkerThreadOnly 是否在工作线程执行 + * @param matchSmoothGameOnly 是否仅匹配畅玩游戏 */ @SuppressLint("CheckResult") private fun loadInstalledGameDigestAndNotifyData( filteredList: ArrayList, - onWorkerThreadOnly: Boolean = false + onWorkerThreadOnly: Boolean = false, + matchSmoothGameOnly: Boolean = false, ) { var isNotifyUpdate = false val maxPageCount = (filteredList.size / PAGE_SIZE) + 1 @@ -207,13 +215,16 @@ object PackageRepository { } for (game in validGames) { + // 仅匹配畅玩游戏时,非畅玩游戏直接跳过 + if (matchSmoothGameOnly && !game.isVGame()) { + continue + } + if (gh_id == null || gh_id == game.id) { gameInstalled.add( - GameInstall.transformGameInstall( - game, - pkgName - ) + GameInstall.transformGameInstall(game, pkgName) ) + mInstalledGameList.add(game) val isCanPluggable = checkGamePlugin(game, pkgName) val isCanUpdate = checkGameUpdate(game) addCurrentlyInstalledVersionIfValid(game) @@ -247,7 +258,12 @@ object PackageRepository { addUpdateOrPluggable(updateEntity) } return true + } else if (game.isVGame()) { + // 畅玩游戏移除更新,避免死循环更新 + removeUpdate(game.id, false) + return true } + return false } @@ -340,11 +356,22 @@ object PackageRepository { if (!isExist) gameUpdate.add(data) } + /** + * 移除更新 + * @param gameId 游戏 ID + */ + fun removeUpdate(gameId: String, notifyUpdate: Boolean) { + gameUpdate.removeAll { it.id == gameId } + if (notifyUpdate) { + notifyGameUpdateData() + } + } + /** * 新增已安装的游戏 * @param pkgName 已安装的游戏包名 */ - fun installedGame(pkgName: String) { + fun addInstalledGame(pkgName: String) { mInstalledPkgList.add(pkgName) notifyInstallPkgData() @@ -355,11 +382,24 @@ object PackageRepository { } } + /** + * 批量新增已安装的游戏数量 + * @param pkgNameList 数组列表 + */ + fun addInstalledGames(pkgNameList: ArrayList, isVGameOnly: Boolean = false) { + mInstalledPkgList.addAll(pkgNameList) + notifyInstallPkgData() + + updateFilterPackage(pkgNameList) { + loadInstalledGameDigestAndNotifyData(pkgNameList, true, isVGameOnly) + } + } + /** * 新增卸载游戏 * @param pkgName 已安装的游戏包名 */ - fun uninstalledGame(pkgName: String) { + fun addUninstalledGame(pkgName: String) { // TODO 检查为什么会有两个相同的包名添加到 mInstalledPkgList 里 mInstalledPkgList.removeAll { it == pkgName } // 尝试从临时的当前版本列表里移除已卸载的条目 @@ -389,8 +429,10 @@ object PackageRepository { var i = 0 while (i < gameInstalled.size) { val game = gameInstalled[i] + val gameEntity = mInstalledGameList[i] if (game.packageName == pkgName) { gameInstalled.remove(game) + mInstalledGameList.remove(gameEntity) } else { i++ } @@ -401,6 +443,7 @@ object PackageRepository { private fun notifyGameInstallData() { PackagesManager.initGameInstall(ArrayList(gameInstalled)) gameInstalledLiveData.postValue(ArrayList(gameInstalled)) + installedGameListLiveData.postValue(ArrayList(mInstalledGameList)) } private fun notifyGameUpdateData() { diff --git a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageViewModel.kt b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageViewModel.kt index 8acf514491..fa72d37602 100644 --- a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageViewModel.kt @@ -65,7 +65,7 @@ class PackageViewModel(application: Application, * 只在收到安装广播时调用,不建议手动调用 */ fun addInstalledGame(pkgName: String?) { - if (!TextUtils.isEmpty(pkgName)) mRepository.installedGame(pkgName!!) + if (!TextUtils.isEmpty(pkgName)) mRepository.addInstalledGame(pkgName!!) } /** @@ -73,7 +73,7 @@ class PackageViewModel(application: Application, * 只在收到卸载广播时调用,不建议手动调用 */ fun addUninstalledGame(pkgName: String?) { - if (!TextUtils.isEmpty(pkgName)) mRepository.uninstalledGame(pkgName!!) + if (!TextUtils.isEmpty(pkgName)) mRepository.addUninstalledGame(pkgName!!) } /** diff --git a/app/src/main/java/com/gh/gamecenter/personal/PersonalFragment.kt b/app/src/main/java/com/gh/gamecenter/personal/PersonalFragment.kt index f5604262ef..33064af33c 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/PersonalFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/PersonalFragment.kt @@ -222,7 +222,7 @@ class PersonalFragment : BaseLazyFragment() { .decorView .findViewById(android.R.id.content) .findViewById(BaseActivity.ID_ROOT_INDICATOR) - indicator.setOnClickListener { + indicator?.setOnClickListener { requireContext().startActivity( getIntent(requireContext()) ) diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt index 9146c4feee..a20dc06167 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt @@ -1,10 +1,12 @@ package com.gh.gamecenter.personalhome +import android.annotation.SuppressLint import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.os.Build import android.os.Bundle import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View import android.widget.CheckedTextView import android.widget.LinearLayout @@ -44,6 +46,7 @@ import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener import com.halo.assistant.HaloApp import com.lightgame.utils.Utils +import io.reactivex.disposables.Disposable import kotlin.math.abs class UserHomeFragment : ToolbarFragment() { @@ -661,30 +664,13 @@ class UserHomeFragment : ToolbarFragment() { } } -// userBadge.setOnClickListener { -// MtaHelper.onEvent("个人主页详情", "个人主页详情", if (mUserHomeViewModel.userId == UserManager.getInstance().userId) "我的徽章" else "TA的徽章") -// MtaHelper.onEvent("进入徽章墙_用户记录", if (mUserHomeViewModel.userId == UserManager.getInstance().userId) "个人主页-我的徽章" else "个人主页-Ta的徽章", -// "${mUserHomeViewModel.userInfo.value?.name}(${mUserHomeViewModel.userId})") -// MtaHelper.onEvent("徽章中心", "进入徽章中心", if (mUserHomeViewModel.userId == UserManager.getInstance().userId) "个人主页-我的徽章" else "个人主页-Ta的徽章") -// directToBadgeWall(requireContext(), -// mUserHomeViewModel.userId, mUserHomeViewModel.userInfo.value?.name -// ?: "", mUserHomeViewModel.userInfo.value?.icon ?: "") -// } + userName.setOnLongClickListener { + userName.text.toString().copyTextAndToast("用户昵称已复制~") + return@setOnLongClickListener true + } } } -// private fun updateUserBadge(badges: List) { -// mHomeBinding?.userBadgeList?.removeAllViews() -// for (badge in badges.take(6)) { -// val badgeView = SimpleDraweeView(context) -// val params = LinearLayout.LayoutParams(24F.dip2px(), 24F.dip2px()) -// params.setMargins(2F.dip2px(), 0, 2F.dip2px(), 0) -// badgeView.layoutParams = params -// ImageUtils.display(badgeView, badge.icon) -// mHomeBinding?.userBadgeList?.addView(badgeView) -// } -// } - override fun onClick(v: View) { super.onClick(v) when (v.id) { diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/background/BackgroundPreviewFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/background/BackgroundPreviewFragment.kt index 1096c623ec..4bdf9ab1de 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/background/BackgroundPreviewFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/background/BackgroundPreviewFragment.kt @@ -11,10 +11,12 @@ import android.view.View import androidx.annotation.RequiresApi import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat -import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment -import com.gh.common.util.* +import com.gh.common.util.EnergyTaskHelper +import com.gh.common.util.GhMatisseFilter import com.gh.gamecenter.CropImageActivity import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.* @@ -22,7 +24,6 @@ import com.gh.gamecenter.databinding.FragmentBackgroundPreviewBinding import com.gh.gamecenter.entity.BackgroundImageEntity import com.gh.gamecenter.entity.ErrorEntity import com.gh.gamecenter.manager.UserManager -import com.gh.gamecenter.common.base.fragment.ToolbarFragment import com.gh.gamecenter.user.UserViewModel import com.halo.assistant.HaloApp import com.zhihu.matisse.Matisse @@ -43,8 +44,6 @@ class BackgroundPreviewFragment : ToolbarFragment() { private var mLocalPath: String = "" private var backgroundImageEntity: BackgroundImageEntity? = null//如果为空,则是本地选择的 - private var mIsTheFirstTimeUserChangeBlurriness = true // 用户是否进入页面来第一次更改模糊度 - private lateinit var mUserViewModel: UserViewModel override fun getLayoutId(): Int = 0 @@ -176,10 +175,6 @@ class BackgroundPreviewFragment : ToolbarFragment() { }, { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { changeAmbiguity() - if (mIsTheFirstTimeUserChangeBlurriness) { - SentryHelper.onEvent("USER_CHANGE_BLURRINESS", "isEmulator", "${HaloApp.getInstance().isEmulator}") - mIsTheFirstTimeUserChangeBlurriness = false - } } else { ToastUtils.showToast("系统版本太低") } @@ -214,13 +209,18 @@ class BackgroundPreviewFragment : ToolbarFragment() { private fun changeAmbiguity() { if (mOriginBitmap == null) return val progress = mBinding.blurSeek.progress - mTempBitmap = if (progress == 0) { - Bitmap.createBitmap(mOriginBitmap!!) - } else { - BitmapUtils.getBlurBitmap(requireContext(), mOriginBitmap, progress) + + try { + mTempBitmap = if (progress == 0) { + Bitmap.createBitmap(mOriginBitmap!!) + } else { + BitmapUtils.getBlurBitmap(requireContext(), mOriginBitmap, progress) + } + mBinding.mineGhIv.setImageBitmap(mTempBitmap) + mBinding.personalHomeIv.setImageBitmap(mTempBitmap) + } catch (t: Throwable) { + ToastUtils.toast("您的设备暂不支持调整模糊度") } - mBinding.mineGhIv.setImageBitmap(mTempBitmap) - mBinding.personalHomeIv.setImageBitmap(mTempBitmap) } private fun changeCommitButton() { diff --git a/app/src/main/java/com/gh/gamecenter/provider/GhContentProvider.kt b/app/src/main/java/com/gh/gamecenter/provider/GhContentProvider.kt index 91129dcc8b..006c56e97a 100644 --- a/app/src/main/java/com/gh/gamecenter/provider/GhContentProvider.kt +++ b/app/src/main/java/com/gh/gamecenter/provider/GhContentProvider.kt @@ -28,6 +28,7 @@ class GhContentProvider : ContentProvider() { } private fun initProviderSqliteHelper(context: Context?){ + val helper: SQLiteOpenHelper = object : SQLiteOpenHelper(context, CERTIFICATION_DATABASE_NAME, null, 1) { override fun onCreate(db: SQLiteDatabase) { @@ -94,22 +95,22 @@ class GhContentProvider : ContentProvider() { - fun localInsert(context: Context,values: ContentValues?){ - initProviderSqliteHelper(context) - try { - values?.put(KEY_PRIMARY_KEY, 1) - // 如果已存在则直接替换 - mSqLiteDatabase?.insertWithOnConflict( - CERTIFICATION_TABLE_NAME, - null, - values, - SQLiteDatabase.CONFLICT_REPLACE - ) - - } catch (e: SQLiteFullException) { - Utils.toast(context,"数据库内存已满,无法同步") - } - } +// fun localInsert(context: Context,values: ContentValues?){ +// initProviderSqliteHelper(context) +// try { +// values?.put(KEY_PRIMARY_KEY, 1) +// // 如果已存在则直接替换 +// mSqLiteDatabase?.insertWithOnConflict( +// CERTIFICATION_TABLE_NAME, +// null, +// values, +// SQLiteDatabase.CONFLICT_REPLACE +// ) +// +// } catch (e: SQLiteFullException) { +// Utils.toast(context,"数据库内存已满,无法同步") +// } +// } override fun insert(uri: Uri, values: ContentValues?): Uri? { val context = context ?: return null diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt index 42c5ff3e9d..2e84e8ca0d 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/BaseAnswerOrArticleItemViewHolder.kt @@ -60,28 +60,38 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH when (entity.type) { "community_article" -> { val communityId = if (!entity.communityId.isNullOrEmpty()) entity.communityId - ?: "" else entity.bbs.id - val intent = ArticleDetailActivity.getCommentIntent(itemView.context, - CommunityEntity(communityId, entity.communityName ?: ""), - entity.id ?: "", entrance, "") + ?: "" else entity.bbs.id + val intent = ArticleDetailActivity.getCommentIntent( + itemView.context, + CommunityEntity(communityId, entity.communityName ?: ""), + entity.id ?: "", entrance, "" + ) itemView.context.startActivity(intent) } "video" -> { val communityId = if (!entity.communityId.isNullOrEmpty()) entity.communityId ?: "" else entity.bbs.id - itemView.context.startActivity(ForumVideoDetailActivity.getIntent(itemView.context, entity.id - ?: "", communityId, true)) + itemView.context.startActivity( + ForumVideoDetailActivity.getIntent( + itemView.context, entity.id + ?: "", communityId, true + ) + ) } "answer" -> { - val intent = CommentActivity.getAnswerCommentIntent(itemView.context, - entity.id ?: "", - entity.count.comment, - false) + val intent = CommentActivity.getAnswerCommentIntent( + itemView.context, + entity.id ?: "", + entity.count.comment, + false + ) itemView.context.startActivity(intent) } else -> { - val intent = NewQuestionDetailActivity.getCommentIntent(itemView.context, entity.id - ?: "", entrance, "") + val intent = NewQuestionDetailActivity.getCommentIntent( + itemView.context, entity.id + ?: "", entrance, "" + ) itemView.context.startActivity(intent) } } @@ -156,10 +166,15 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH id = entity.id, title = entity.title, images = entity.images, - description = entity.brief) - it.context.startActivity(QuestionsInviteActivity.getIntent(it.context, - questionsDetailEntity, - entrance)) + description = entity.brief + ) + it.context.startActivity( + QuestionsInviteActivity.getIntent( + it.context, + questionsDetailEntity, + entrance + ) + ) } } @@ -173,7 +188,16 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH "video" -> "视频帖评论" else -> "提问帖评论" } - NewLogUtils.logRecommendFeedContentClick("click_for_you_comment", contentType, id, (position + 1), bbs.id, bbsType, user.id ?: "", commentType) + NewLogUtils.logRecommendFeedContentClick( + "click_for_you_comment", + contentType, + id, + (position + 1), + bbs.id, + bbsType, + user.id ?: "", + commentType + ) } } @@ -192,10 +216,12 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH else -> { val communityId = if (entity.community.id.isNotEmpty()) entity.community.id else UserManager.getInstance().community.id - val intent = ArticleDetailActivity.getCommentIntent(itemView.context, - CommunityEntity(communityId, entity.community.name), - entity.id, - entrance, "") + val intent = ArticleDetailActivity.getCommentIntent( + itemView.context, + CommunityEntity(communityId, entity.community.name), + entity.id, + entrance, "" + ) itemView.context.startActivity(intent) MtaHelper.onEvent(getEventId(entrance), getKey(entrance), "评论图标") } @@ -209,7 +235,15 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH CheckLoginUtils.checkLogin(itemView.context, entrance) { if (entrance == "社区+(推荐)" && position != null) { entity.run { - NewLogUtils.logRecommendFeedContentClick("click_for_you_like", contentType, id, position + 1, bbs.id, bbsType, user.id ?: "") + NewLogUtils.logRecommendFeedContentClick( + "click_for_you_like", + contentType, + id, + position + 1, + bbs.id, + bbsType, + user.id ?: "" + ) } } @@ -281,75 +315,72 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH } if (entity.type == "video") { RetrofitManager.getInstance() - .api.voteVideo(entity.id) - .subscribeOn(Schedulers.io()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: ResponseBody) { - //Utils.toast(getApplication(), "已点赞") - EnergyTaskHelper.postEnergyTask("vote_video", entity.id) - } + .api.voteVideo(entity.id) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + //Utils.toast(getApplication(), "已点赞") + EnergyTaskHelper.postEnergyTask("vote_video", entity.id) + } - override fun onFailure(exception: Exception) { - super.onFailure(exception) - entity.count.vote = entity.count.vote - 1 - entity.me.isVoted = false - voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" - setVoteAndCommentStyle(entity) - } - }) + override fun onFailure(exception: Exception) { + super.onFailure(exception) + entity.count.vote = entity.count.vote - 1 + entity.me.isVoted = false + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + setVoteAndCommentStyle(entity) + } + }) } else { val voteObservable = if (entity.type == "community_article") { - val communityId = if (entity.articleCommunityId.isNotEmpty()) entity.articleCommunityId - else entity.bbs.id - RetrofitManager.getInstance().api - .postCommunityArticleVote(communityId, entity.id) + RetrofitManager.getInstance().api.postCommunityArticleVote(entity.id) } else { RetrofitManager.getInstance().api - .postVoteQuestionComment(entity.questions.id, entity.id) - .map { GsonUtils.fromJson(it.string(), VoteEntity::class.java) } + .postVoteQuestionComment(entity.questions.id, entity.id) + .map { GsonUtils.fromJson(it.string(), VoteEntity::class.java) } } voteObservable - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response() { - override fun onResponse(response: VoteEntity?) { - if (entity.type == "community_article") { - entity.me.isCommunityArticleVote = true - EnergyTaskHelper.postEnergyTask("vote_community_article", entity.id) - } else { - entity.me.isAnswerVoted = true - EnergyTaskHelper.postEnergyTask("vote_answer", entity.id) - } - ToastUtils.showToast("已赞同") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: VoteEntity?) { + if (entity.type == "community_article") { + entity.me.isCommunityArticleVote = true + EnergyTaskHelper.postEnergyTask("vote_community_article", entity.id) + } else { + entity.me.isAnswerVoted = true + EnergyTaskHelper.postEnergyTask("vote_answer", entity.id) } + ToastUtils.showToast("已赞同") + } - override fun onFailure(e: HttpException?) { - ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { - if (403008 == it) { - Utils.toast(itemView.context, R.string.ask_vote_hint) - true - } else if (403036 == it) { - Utils.toast(itemView.context, R.string.ask_vote_limit_hint) - true - } else if (404001 == it) { - Utils.toast(itemView.context, "内容可能已被删除") - entity.active = false - setVoteAndCommentStyle(entity) - true + override fun onFailure(e: HttpException?) { + ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { + if (403008 == it) { + Utils.toast(itemView.context, R.string.ask_vote_hint) + true + } else if (403036 == it) { + Utils.toast(itemView.context, R.string.ask_vote_limit_hint) + true + } else if (404001 == it) { + Utils.toast(itemView.context, "内容可能已被删除") + entity.active = false + setVoteAndCommentStyle(entity) + true + } else { + entity.count.vote = entity.count.vote - 1 + if (entity.type == "community_article") { + entity.me.isCommunityArticleVote = true } else { - entity.count.vote = entity.count.vote - 1 - if (entity.type == "community_article") { - entity.me.isCommunityArticleVote = true - } else { - entity.me.isAnswerVoted = true - } - voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" - setVoteAndCommentStyle(entity) - false + entity.me.isAnswerVoted = true } + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + setVoteAndCommentStyle(entity) + false } } - }) + } + }) } entity.count.vote = entity.count.vote + 1 voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" @@ -366,127 +397,32 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH if (entity.type == "video") { RetrofitManager.getInstance() - .api.undoVoteVideo(entity.id) - .subscribeOn(Schedulers.io()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: ResponseBody) { - Utils.toast(itemView.context, "取消点赞") - } + .api.undoVoteVideo(entity.id) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: ResponseBody) { + Utils.toast(itemView.context, "取消点赞") + } - override fun onFailure(exception: Exception) { - super.onFailure(exception) - entity.count.vote = entity.count.vote + 1 - entity.me.isVoted = true - voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" - setVoteAndCommentStyle(entity) - } - }) + override fun onFailure(exception: Exception) { + super.onFailure(exception) + entity.count.vote = entity.count.vote + 1 + entity.me.isVoted = true + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + setVoteAndCommentStyle(entity) + } + }) } else { val unVoteObservable = if (entity.type == "community_article") { entity.me.isCommunityArticleVote = false - - val communityId = if (entity.articleCommunityId.isNotEmpty()) entity.articleCommunityId - else entity.bbs.id - RetrofitManager.getInstance().api - .postCommunityArticleUnVote(communityId, entity.id) + RetrofitManager.getInstance().api.postCommunityArticleUnVote(entity.id) } else { entity.me.isAnswerVoted = false RetrofitManager.getInstance().api - .postAnswerUnvote(entity.id) + .postAnswerUnvote(entity.id) } unVoteObservable - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response() { - override fun onResponse(response: VoteEntity?) { - Utils.toast(itemView.context, "取消赞同") - } - - override fun onFailure(e: HttpException?) { - ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { - if (403008 == it) { - Utils.toast(itemView.context, R.string.ask_vote_hint) - true - } else if (403036 == it) { - Utils.toast(itemView.context, R.string.ask_vote_limit_hint) - true - } else if (404001 == it) { - Utils.toast(itemView.context, "内容可能已被删除") - entity.active = false - setVoteAndCommentStyle(entity) - true - } else { - entity.count.vote = entity.count.vote + 1 - if (entity.type == "community_article") { - entity.me.isCommunityArticleVote = true - } else { - entity.me.isAnswerVoted = true - } - voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" - setVoteAndCommentStyle(entity) - false - } - } - } - }) - } - } - - fun voteArticle(entity: ArticleEntity) { - entity.count.vote = entity.count.vote + 1 - voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" - playVoteAnimation() - - val communityId = if (entity.community.id.isEmpty()) entity.bbs.id - else entity.community.id - RetrofitManager.getInstance().api - .postCommunityArticleVote(communityId, entity.id) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response() { - override fun onResponse(response: VoteEntity?) { - entity.me.isCommunityArticleVote = true - ToastUtils.showToast("已赞同") - EnergyTaskHelper.postEnergyTask("vote_community_article", entity.id) - } - - override fun onFailure(e: HttpException?) { - ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { - if (403008 == it) { - Utils.toast(itemView.context, R.string.ask_vote_hint) - true - } else if (403036 == it) { - Utils.toast(itemView.context, R.string.ask_vote_limit_hint) - true - } else if (404001 == it) { - Utils.toast(itemView.context, "内容可能已被删除") - entity.active = false - setVoteAndCommentStyle(entity.transformAnswerEntity()) - true - } else { - entity.count.vote = entity.count.vote - 1 - entity.me.isCommunityArticleVote = false - voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" - setVoteAndCommentStyle(entity.transformAnswerEntity()) - false - } - } - } - }) - } - - fun cancelArticleVote(entity: ArticleEntity) { - entity.count.vote = entity.count.vote - 1 - entity.me.isCommunityArticleVote = false - voteIcon.isChecked = false - voteCount.setTextColor(R.color.text_subtitleDesc.toColor()) - voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" - - val communityId = if (entity.community.id.isEmpty()) entity.bbs.id - else entity.community.id - RetrofitManager.getInstance().api - .postCommunityArticleUnVote(communityId, entity.id) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response() { @@ -505,36 +441,123 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH } else if (404001 == it) { Utils.toast(itemView.context, "内容可能已被删除") entity.active = false - setVoteAndCommentStyle(entity.transformAnswerEntity()) + setVoteAndCommentStyle(entity) true } else { entity.count.vote = entity.count.vote + 1 - entity.me.isCommunityArticleVote = true + if (entity.type == "community_article") { + entity.me.isCommunityArticleVote = true + } else { + entity.me.isAnswerVoted = true + } voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" - setVoteAndCommentStyle(entity.transformAnswerEntity()) + setVoteAndCommentStyle(entity) false } } } }) + } + } + + fun voteArticle(entity: ArticleEntity) { + entity.count.vote = entity.count.vote + 1 + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + playVoteAnimation() + + RetrofitManager.getInstance().api + .postCommunityArticleVote(entity.id) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: VoteEntity?) { + entity.me.isCommunityArticleVote = true + ToastUtils.showToast("已赞同") + EnergyTaskHelper.postEnergyTask("vote_community_article", entity.id) + } + + override fun onFailure(e: HttpException?) { + ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { + if (403008 == it) { + Utils.toast(itemView.context, R.string.ask_vote_hint) + true + } else if (403036 == it) { + Utils.toast(itemView.context, R.string.ask_vote_limit_hint) + true + } else if (404001 == it) { + Utils.toast(itemView.context, "内容可能已被删除") + entity.active = false + setVoteAndCommentStyle(entity.transformAnswerEntity()) + true + } else { + entity.count.vote = entity.count.vote - 1 + entity.me.isCommunityArticleVote = false + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + setVoteAndCommentStyle(entity.transformAnswerEntity()) + false + } + } + } + }) + } + + fun cancelArticleVote(entity: ArticleEntity) { + entity.count.vote = entity.count.vote - 1 + entity.me.isCommunityArticleVote = false + voteIcon.isChecked = false + voteCount.setTextColor(R.color.text_subtitleDesc.toColor()) + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + + RetrofitManager.getInstance().api + .postCommunityArticleUnVote(entity.id) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: VoteEntity?) { + Utils.toast(itemView.context, "取消赞同") + } + + override fun onFailure(e: HttpException?) { + ErrorHelper.handleErrorWithCustomizedHandler(itemView.context, e?.response()?.errorBody()?.string(), false) { + if (403008 == it) { + Utils.toast(itemView.context, R.string.ask_vote_hint) + true + } else if (403036 == it) { + Utils.toast(itemView.context, R.string.ask_vote_limit_hint) + true + } else if (404001 == it) { + Utils.toast(itemView.context, "内容可能已被删除") + entity.active = false + setVoteAndCommentStyle(entity.transformAnswerEntity()) + true + } else { + entity.count.vote = entity.count.vote + 1 + entity.me.isCommunityArticleVote = true + voteCount.text = if (entity.count.vote > 0) entity.count.vote.toString() else "赞同" + setVoteAndCommentStyle(entity.transformAnswerEntity()) + false + } + } + } + }) } fun followUser(entity: AnswerEntity, callback: EmptyCallback?) { RetrofitManager.getInstance().api - .postFollowing(entity.user.id) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response() { - override fun onResponse(response: ResponseBody?) { - super.onResponse(response) - callback?.onCallback() - } + .postFollowing(entity.user.id) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + callback?.onCallback() + } - override fun onFailure(e: HttpException?) { - super.onFailure(e) - Utils.toast(itemView.context, R.string.loading_failed_hint) - } - }) + override fun onFailure(e: HttpException?) { + super.onFailure(e) + Utils.toast(itemView.context, R.string.loading_failed_hint) + } + }) } private fun playVoteAnimation() { diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt index 9b7fb9d457..8eedb1e820 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/detail/AnswerDetailFragment.kt @@ -1029,7 +1029,7 @@ open class AnswerDetailFragment : ToolbarFragment() { mQuestionBinding.videoStatus.visibleIf(video != null && video.status != "pass") mQuestionBinding.videoDuration.visibleIf(video?.status == "pass") if (video?.status == "pass") { - mQuestionBinding.videoDuration.background = DrawableView.getOvalDrawable(R.color.black_alpha_80, 999f) + mQuestionBinding.videoDuration.background = DrawableView.getOvalDrawable(R.color.black_alpha_50, 999f) mQuestionBinding.videoDuration.text = video.duration } else { val status = if (!question.me.isContentOwner && !question.me.isModerator && video?.status == "fail") { diff --git a/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditActivity.kt index e75f5de0ec..c067aabcf0 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/answer/edit/AnswerEditActivity.kt @@ -293,7 +293,7 @@ class AnswerEditActivity : BaseRichEditorActivity(), Keyboa mBinding.videoStatus.visibleIf(video != null && video.status != "pass") mBinding.videoDuration.visibleIf(video?.status == "pass") if (video?.status == "pass") { - mBinding.videoDuration.background = DrawableView.getOvalDrawable(R.color.black_alpha_80, 999f) + mBinding.videoDuration.background = DrawableView.getOvalDrawable(R.color.black_alpha_50, 999f) mBinding.videoDuration.text = video.duration } else { val status = if (!question.me.isContentOwner && !question.me.isModerator && video?.status == "fail") { diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt index 92822f1224..e2672d90ef 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt @@ -676,10 +676,7 @@ class ArticleDetailFragment : BaseCommentFragment() { @@ -89,7 +88,7 @@ class ArticleDetailViewModel( topItemData = CommentItemData(articleDetail = response) commentCount = response?.count?.comment ?: 0 loadResultLiveData.postValue(LoadResult.SUCCESS) - mergeListData(mListLiveData.value, displayFloor = true) + mergeListData(mListLiveData.value) NewLogUtils.logForumContentBrowser(articleId, "bbs_article", recommendId) } @@ -105,7 +104,7 @@ class ArticleDetailViewModel( } fun cancelLikeArticle() { - mApi.postCommunityArticleUnVote(communityId, articleId) + mApi.postCommunityArticleUnVote(articleId) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response() { @@ -126,7 +125,7 @@ class ArticleDetailViewModel( } fun likeArticle() { - mApi.postCommunityArticleVote(communityId, articleId) + mApi.postCommunityArticleVote(articleId) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response() { @@ -225,17 +224,9 @@ class ArticleDetailViewModel( fun collectionCommand(isCollection: Boolean, callback: (isFollow: Boolean) -> Unit) { val observable = if (isCollection) { - mApi.postCommunityArticleFavorites( - UserManager.getInstance().userId, - communityId, - articleId - ) + mApi.postCommunityArticleFavorites(articleId) } else { - mApi.deleteCommunityArticleFavorites( - UserManager.getInstance().userId, - communityId, - articleId - ) + mApi.deleteCommunityArticleFavorites(articleId) } observable .subscribeOn(Schedulers.io()) @@ -309,8 +300,8 @@ class ArticleDetailViewModel( }) } - fun doHighlightThisArticle(communityId: String, articleId: String?) { - mApi.highlightCommunityArticle(communityId, articleId) + fun doHighlightThisArticle(articleId: String?) { + mApi.highlightCommunityArticle(articleId) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response() { @@ -326,8 +317,8 @@ class ArticleDetailViewModel( }) } - fun cancelHighlightThisArticle(communityId: String, articleId: String){ - mApi.cancelHighlightCommunityArticle(communityId, articleId) + fun cancelHighlightThisArticle(articleId: String) { + mApi.cancelHighlightCommunityArticle(articleId) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response() { @@ -359,8 +350,8 @@ class ArticleDetailViewModel( }) } - fun doHideThisArticle(communityId: String, articleId: String?) { - mApi.hideCommunityArticle(communityId, articleId) + fun doHideThisArticle(articleId: String?) { + mApi.hideCommunityArticle(articleId) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Response() { @@ -377,12 +368,11 @@ class ArticleDetailViewModel( } fun modifyArticleActivityTag( - communityId: String, articleId: String, label: ActivityLabelEntity? ) { val body = json { "tag_activity_id" to label?.id }.toRequestBody() - mApi.modifyArticleActivityTag(communityId, articleId, body) + mApi.modifyArticleActivityTag(articleId, body) .compose(observableToMain()) .subscribe(object : Response() { override fun onResponse(response: ResponseBody?) { diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentViewModel.kt index e8e920496d..e8cc4ef967 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/comment/ArticleDetailCommentViewModel.kt @@ -31,13 +31,13 @@ class ArticleDetailCommentViewModel(application: Application, } override fun provideDataSingle(page: Int): Single> { - return RetrofitManager.getInstance().api.getCommunityArticleCommentReply(communityId, articleId, commentId, currentSortType.value, page, mapOf()) + return RetrofitManager.getInstance().api.getCommunityArticleCommentReply(commentId, currentSortType.value, page, mapOf()) } @SuppressLint("CheckResult") fun getComment() { RetrofitManager.getInstance().api - .getCommunityArticleComment(communityId, articleId, commentId) + .getCommunityArticleComment(commentId) .subscribeOn(Schedulers.io()) .subscribe(object : BiResponse() { @SuppressLint("CheckResult") diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditActivity.kt index c784489c61..f6b9ae2520 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/edit/ArticleEditActivity.kt @@ -243,7 +243,13 @@ class ArticleEditActivity : BaseRichEditorActivity(), Keyb }) mViewModel.error.observeNonNull(this) { - ErrorHelper.handleError(this, it) + ErrorHelper.handleError(this, it, false, object : ConfirmListener { + override fun onConfirm() { + if (::mMenuPost.isInitialized) { + onMenuItemClick(mMenuPost) + } + } + }) } when { diff --git a/app/src/main/java/com/gh/gamecenter/qa/column/AskColumnRepository.java b/app/src/main/java/com/gh/gamecenter/qa/column/AskColumnRepository.java index a350eeb5fb..70e2f83131 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/column/AskColumnRepository.java +++ b/app/src/main/java/com/gh/gamecenter/qa/column/AskColumnRepository.java @@ -126,7 +126,7 @@ public class AskColumnRepository { } catch (Exception e1) { e1.printStackTrace(); } - ErrorHelper.handleError(mContext, errorString, false); + ErrorHelper.handleError(mContext, errorString, false, null); } }); diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt index c765106154..577d747583 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentFragment.kt @@ -29,6 +29,7 @@ import com.gh.gamecenter.R import com.gh.gamecenter.adapter.OnCommentCallBackListener import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.* import com.gh.gamecenter.databinding.ItemCommentEditImageBinding @@ -201,7 +202,11 @@ open class NewCommentFragment : ListFragment e1.printStackTrace() } - ErrorHelper.handleError(requireContext(), errorString, false) + ErrorHelper.handleError(requireContext(), errorString, false, object : ConfirmListener { + override fun onConfirm() { + commentSendBtn.performClick() + } + }) } else -> { mSendingDialog?.dismiss() diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt index 228d125dc6..a19d1766ec 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/NewCommentViewModel.kt @@ -51,12 +51,12 @@ open class NewCommentViewModel( val postCommentLiveData: LiveData> get() = mPostCommentLiveData - val deleteVideoCommentLiveData = MutableLiveData() + val deleteVideoCommentLiveData = MutableLiveData() val pictureList = ArrayList() val pictureLiveData = MutableLiveData>() init { - commentDraftDao = AppDatabase.getInstance().commentDraftDao() + commentDraftDao = AppDatabase.getInstance().commentDraftDao() } override fun provideDataObservable(page: Int): Observable>? { @@ -66,8 +66,8 @@ open class NewCommentViewModel( CommentType.ANSWER -> api.getAnswerCommentList(answerId, page) CommentType.ANSWER_CONVERSATION -> api.getAnswerCommentConversationList(answerId, commentId, page) - CommentType.COMMUNITY_ARTICLE -> api.getCommunityArticleCommentList(communityId, articleId, "time.create:1", page, mapOf()) - CommentType.COMMUNITY_ARTICLE_CONVERSATION -> api.getCommunityArticleCommentConversation(communityId, articleId, commentId, page) + CommentType.COMMUNITY_ARTICLE -> api.getCommunityArticleCommentList(articleId, "time.create:1", page, mapOf()) + CommentType.COMMUNITY_ARTICLE_CONVERSATION -> api.getCommunityArticleCommentConversation(commentId, page) CommentType.VIDEO -> api.getVideoCommentList(videoId, page, mapOf()) CommentType.VIDEO_CONVERSATION -> api.getVideoCommentConversationList(videoId, commentId, page) @@ -141,9 +141,9 @@ open class NewCommentViewModel( CommentType.COMMUNITY_ARTICLE, CommentType.COMMUNITY_ARTICLE_CONVERSATION -> { if (commentEntity == null) { - api.postCommentToCommunityArticle(communityId, articleId, body) + api.postCommentToCommunityArticle(articleId, body) } else { - api.postReplyToCommunityArticleComment(communityId, articleId, commentEntity.id, body) + api.postReplyToCommunityArticleComment(commentEntity.id, body) } } @@ -317,7 +317,7 @@ open class NewCommentViewModel( @SuppressLint("CheckResult") fun deleteVideoComment(entity: CommentEntity) { RetrofitManager.getInstance().api - .deleteVideoComment(videoId, entity.id) + .deleteVideoComment(entity.id) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : BiResponse() { diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt index 0d23da7052..b29b982c18 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentAdapter.kt @@ -266,7 +266,7 @@ abstract class BaseCommentAdapter( commentHintCountTv.setTextColor(R.color.text_subtitleDesc.toColor(mContext)) orderSfv.run { setContainerBackground(R.drawable.button_round_f5f5f5.toDrawable(mContext)) - setIndicatorBackground(R.drawable.progressbar_game_collection_primary.toDrawable(mContext)) + setIndicatorBackground(R.drawable.bg_game_collection_sfv_indicator.toDrawable(mContext)) setTextColor(ContextCompat.getColorStateList(mContext, R.color.game_collection_rg_button_selector)) } diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentViewModel.kt index dbf33ad7b9..c14fcfffe0 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/base/BaseCommentViewModel.kt @@ -94,7 +94,6 @@ abstract class BaseCommentViewModel( fun mergeListData( commentList: List?, - displayFloor: Boolean = false, hasFilter: Boolean = true, ) { topItemData?.let { @@ -116,21 +115,13 @@ abstract class BaseCommentViewModel( } else if (commentList.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_FAILED) { add(CommentItemData(errorConnection = true)) } else { - //从第二楼开始 - var floor = 2 commentList?.forEachIndexed { index, entity -> - if (displayFloor) { - entity.floor = floor - } // 没有 me 会导致不能跨页面更新点赞 if (entity.me == null) { entity.me = MeEntity() } handleTopComment(index, entity) add(CommentItemData(commentNormal = entity)) - if (displayFloor) { - floor++ - } } add(CommentItemData(footer = true)) } @@ -166,7 +157,6 @@ abstract class BaseCommentViewModel( PostCommentUtils.likeComment( null, articleId, - communityId, videoId, questionId, comment.id, @@ -264,14 +254,14 @@ abstract class BaseCommentViewModel( if (entity.me?.isModerator == true) { mApi.moderatorsHideVideoComment(communityId, videoId, entity.id).toObservable() } else { - mApi.deleteVideoComment(videoId, entity.id).toObservable() + mApi.deleteVideoComment(entity.id).toObservable() } } questionId.isNotEmpty() -> { mApi.deleteQuestionComment(questionId, entity.id).toObservable() } articleId.isNotEmpty() -> { - mApi.hideCommunityArticleComment(communityId, articleId, entity.id) + mApi.hideCommunityArticleComment(entity.id) } else -> null } ?: return @@ -314,26 +304,26 @@ abstract class BaseCommentViewModel( } when { articleId.isNotEmpty() -> { - mApi.postArticleCommentTop(communityId, articleId, commentId, map) + mApi.postArticleCommentTop(commentId, map) } questionId.isNotEmpty() -> { mApi.postQuestionCommentTop(questionId, commentId, map) } videoId.isNotEmpty() -> { - mApi.postVideoCommentTop(videoId, commentId, map) + mApi.postVideoCommentTop(commentId, map) } else -> null } } else { when { articleId.isNotEmpty() -> { - mApi.postArticleCommentUnTop(communityId, articleId, commentId) + mApi.postArticleCommentUnTop(commentId) } questionId.isNotEmpty() -> { mApi.postQuestionCommentUnTop(questionId, commentId) } videoId.isNotEmpty() -> { - mApi.postVideoCommentUnTop(videoId, commentId) + mApi.postVideoCommentUnTop(commentId) } else -> null } diff --git a/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt index 822410cecd..a273a670f6 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/comment/conversation/CommentConversationViewModel.kt @@ -36,8 +36,6 @@ class CommentConversationViewModel( return when { articleId.isNotEmpty() -> { mApi.getCommunityArticleCommentReply( - communityId, - articleId, commentId, currentSortType.value, page, @@ -61,10 +59,10 @@ class CommentConversationViewModel( fun getComment() { val single = when { articleId.isNotEmpty() -> { - mApi.getCommunityArticleComment(communityId, articleId, commentId) + mApi.getCommunityArticleComment(commentId) } videoId.isNotEmpty() -> { - mApi.getCommunityVideoComment(videoId, commentId) + mApi.getCommunityVideoComment(commentId) } questionId.isNotEmpty() -> { mApi.getCommunityQuestionComment(questionId, commentId) diff --git a/app/src/main/java/com/gh/gamecenter/qa/editor/VideoAlbumsSpanner.kt b/app/src/main/java/com/gh/gamecenter/qa/editor/VideoAlbumsSpanner.kt index f033d07d5a..83d6df9f11 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/editor/VideoAlbumsSpanner.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/editor/VideoAlbumsSpanner.kt @@ -60,7 +60,7 @@ class VideoAlbumsSpanner(val context: Context) { //添加半透明maskView val maskView = View(context) val maskViewParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, popupHeight) - maskView.background = ColorDrawable(ContextCompat.getColor(context, R.color.black_alpha_60)) + maskView.background = ColorDrawable(ContextCompat.getColor(context, R.color.black_alpha_40)) maskView.layoutParams = maskViewParams parentContainer.addView(maskView, 0) maskView.setOnClickListener { diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/detail/AnswerViewHolder.java b/app/src/main/java/com/gh/gamecenter/qa/questions/detail/AnswerViewHolder.java index 1fd5026fc4..f2dd0dd312 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/detail/AnswerViewHolder.java +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/detail/AnswerViewHolder.java @@ -154,7 +154,7 @@ public class AnswerViewHolder extends BaseRecyclerViewHolder { if (videos.size() > 0) { CommunityVideoEntity videoEntity = videos.get(0); ImageUtils.display(binding.askAnswerItemImg, videoEntity.getPoster()); - binding.askAnswerItemVideoDuration.setBackground(DrawableView.getOvalDrawable(R.color.black_alpha_80, 999F)); + binding.askAnswerItemVideoDuration.setBackground(DrawableView.getOvalDrawable(R.color.black_alpha_50, 999F)); binding.askAnswerItemVideoDuration.setText(videoEntity.getDuration()); binding.askAnswerItemVideoDuration.setVisibility(View.VISIBLE); binding.askAnswerItemVideoPlay.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditActivity.kt index fc9095ab5c..f5f5efef63 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/edit/QuestionEditActivity.kt @@ -19,6 +19,7 @@ import androidx.core.widget.addTextChangedListener import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import com.gh.base.BaseRichEditorActivity +import com.gh.common.util.ErrorHelper import com.gh.common.util.NewLogUtils import com.gh.gamecenter.common.utils.NotificationHelper import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment @@ -388,6 +389,14 @@ class QuestionEditActivity : BaseRichEditorActivity(), extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true) ) return@Observer + } else if (errorCode == 403209) { + ErrorHelper.handleError(this, string, false, object : ConfirmListener { + override fun onConfirm() { + if (::mMenuPost.isInitialized) { + onMenuItemClick(mMenuPost) + } + } + }) } } toast(R.string.post_failure_hint) diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailViewModel.kt index 6d17108941..78b8dfd901 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailViewModel.kt @@ -70,7 +70,7 @@ class NewQuestionDetailViewModel( topItemData = CommentItemData(questionDetail = response) commentCount = response?.count?.answer ?: 0 loadResultLiveData.postValue(LoadResult.SUCCESS) - mergeListData(mListLiveData.value, displayFloor = true) + mergeListData(mListLiveData.value) NewLogUtils.logForumContentBrowser(questionId, "bbs_question", recommendId) } @@ -96,7 +96,7 @@ class NewQuestionDetailViewModel( } override fun mergeResultLiveData() { - mResultLiveData.addSource(mListLiveData) { mergeListData(it, displayFloor = true) } + mResultLiveData.addSource(mListLiveData) { mergeListData(it) } } override fun hideCommentSuccess() { diff --git a/app/src/main/java/com/gh/gamecenter/qa/tags/AskQuestionsNewRepository.java b/app/src/main/java/com/gh/gamecenter/qa/tags/AskQuestionsNewRepository.java index ec4c42f1cd..f16af22f09 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/tags/AskQuestionsNewRepository.java +++ b/app/src/main/java/com/gh/gamecenter/qa/tags/AskQuestionsNewRepository.java @@ -132,7 +132,7 @@ public class AskQuestionsNewRepository { } catch (Exception e1) { e1.printStackTrace(); } - ErrorHelper.handleError(mContext, errorString, false); + ErrorHelper.handleError(mContext, errorString, false, null); } }); diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt index d3293b1f98..645e791ff0 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt @@ -81,6 +81,10 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { override fun onDataChanged(downloadEntity: DownloadEntity) { setDownloadButton(mForumVideoEntity?.game) } + + override fun onDataInit(downloadEntity: DownloadEntity) { + onDataChanged(downloadEntity) + } } override fun getLayoutId(): Int = 0 @@ -696,12 +700,9 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { "取消", { if (isHighlight) { - mViewModel.doHighlightThisVideo(mForumVideoEntity?.bbsId ?: "", mVideoId) + mViewModel.doHighlightThisVideo(mVideoId) } else { - mViewModel.cancelHighlightThisVideo( - mForumVideoEntity?.bbsId - ?: "", mVideoId - ) + mViewModel.cancelHighlightThisVideo(mVideoId) } }, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true) ) diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailViewModel.kt index dd739ed0d5..5447a94053 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailViewModel.kt @@ -103,8 +103,8 @@ class ForumVideoDetailViewModel( //版主加精 - fun doHighlightThisVideo(bbsId: String, videoId: String) { - mApi.highlightCommunityVideo(bbsId, videoId) + fun doHighlightThisVideo(videoId: String) { + mApi.highlightCommunityVideo(videoId) .compose(observableToMain()) .subscribe(object : Response() { override fun onResponse(response: ResponseBody?) { @@ -120,8 +120,8 @@ class ForumVideoDetailViewModel( } //版主取消精选 - fun cancelHighlightThisVideo(bbsId: String, videoId: String) { - mApi.cancelHighlightCommunityVideo(bbsId, videoId) + fun cancelHighlightThisVideo(videoId: String) { + mApi.cancelHighlightCommunityVideo(videoId) .compose(observableToMain()) .subscribe(object : Response() { override fun onResponse(response: ResponseBody?) { diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentViewModel.kt index d40121b8e6..3170ff58cb 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/comment/VideoCommentViewModel.kt @@ -39,12 +39,9 @@ class VideoCommentViewModel( } else if (list.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_FAILED) { itemDataList.add(CommentItemData(errorConnection = true)) } else { - var floor = 1 list.forEachIndexed { index, entity -> - entity.floor = floor handleTopComment(index, entity) itemDataList.add(CommentItemData(commentNormal = entity)) - floor++ } itemDataList.add(CommentItemData(footer = true)) } diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishFragment.kt index 77dbbf8739..34f94fd356 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/video/publish/VideoPublishFragment.kt @@ -29,6 +29,7 @@ import com.gh.gamecenter.entity.* import com.gh.gamecenter.manager.UserManager import com.gh.gamecenter.mvvm.Status import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.entity.NotificationUgc import com.gh.gamecenter.qa.BbsType import com.gh.gamecenter.qa.dialog.ChooseActivityDialogFragment @@ -300,7 +301,15 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver { } else if (it.status == Status.ERROR) { ErrorHelper.handleError( requireContext(), - it.exception?.response()?.errorBody()?.string() + it.exception?.response()?.errorBody()?.string(), + false, + object : ConfirmListener { + override fun onConfirm() { + if (::mMenuPost.isInitialized) { + onMenuItemClick(mMenuPost) + } + } + } ) } } diff --git a/app/src/main/java/com/gh/gamecenter/receiver/DownloadReceiver.java b/app/src/main/java/com/gh/gamecenter/receiver/DownloadReceiver.java index d92eb6f11d..0a158c70b3 100644 --- a/app/src/main/java/com/gh/gamecenter/receiver/DownloadReceiver.java +++ b/app/src/main/java/com/gh/gamecenter/receiver/DownloadReceiver.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.Intent; import com.gh.common.util.DirectUtils; +import com.gh.common.util.DownloadNotificationHelper; import com.gh.gamecenter.common.utils.ExtensionsKt; /** @@ -18,7 +19,11 @@ public class DownloadReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ExtensionsKt.doOnMainProcessOnly(context, () -> { - DirectUtils.directToDownloadManager(context, ENTRANCE_DOWNLOAD); + if (DownloadNotificationHelper.ACTION_VDOWNLOAD.equals(intent.getAction())) { + DirectUtils.directToVGameDownload(context, true); + } else { + DirectUtils.directToDownloadManager(context, ENTRANCE_DOWNLOAD); + } }); } } diff --git a/app/src/main/java/com/gh/gamecenter/receiver/InstallAndUninstallReceiver.java b/app/src/main/java/com/gh/gamecenter/receiver/InstallAndUninstallReceiver.java index d32a7e7735..86fd57a50c 100644 --- a/app/src/main/java/com/gh/gamecenter/receiver/InstallAndUninstallReceiver.java +++ b/app/src/main/java/com/gh/gamecenter/receiver/InstallAndUninstallReceiver.java @@ -6,7 +6,6 @@ import android.content.Intent; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.core.AppExecutor; -import com.gh.common.util.DataUtils; import com.gh.common.util.InstallUtils; import com.gh.common.util.PackageHelper; import com.gh.common.util.PackageUtils; diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java b/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java index ac685b35e9..f9da389367 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java @@ -4,6 +4,7 @@ import android.content.Context; import com.gh.common.constant.Config; import com.gh.gamecenter.common.retrofit.BaseRetrofitManager; import com.gh.gamecenter.retrofit.service.ApiService; +import com.gh.gamecenter.retrofit.service.VApiService; import com.halo.assistant.HaloApp; import okhttp3.OkHttpClient; @@ -16,6 +17,7 @@ public class RetrofitManager extends BaseRetrofitManager { private final ApiService mApiService; private final ApiService mNewApiService; private final ApiService mUploadApiService; + private final VApiService mVApiService; private RetrofitManager() { Context context = HaloApp.getInstance().getApplicationContext(); @@ -23,6 +25,7 @@ public class RetrofitManager extends BaseRetrofitManager { mApiService = provideService(okHttpNormalConfig, Config.API_HOST, ApiService.class); mNewApiService = provideService(okHttpNormalConfig, Config.NEW_API_HOST, ApiService.class); mUploadApiService = provideService(getOkHttpConfig(context, UPLOAD_CALL_TIME_OUT, 1), Config.API_HOST, ApiService.class); + mVApiService = provideService(okHttpNormalConfig, Config.VAPI_HOST, VApiService.class); } public static RetrofitManager getInstance() { @@ -41,6 +44,8 @@ public class RetrofitManager extends BaseRetrofitManager { return mUploadApiService; } + public VApiService getVApi() { return mVApiService; } + private static class SingletonHolder { private static final RetrofitManager INSTANCE = new RetrofitManager(); } diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 1a6154a056..5d97c564eb 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -1299,8 +1299,8 @@ public interface ApiService { */ @GET("users/{user_id}/histories") Single> getPersonalHistoryCount(@Path("user_id") String userId, - @Query("channel") String channel, - @Query("filter") String filter); + @Query("channel") String channel, + @Query("filter") String filter); /** * 检测游戏是否开启社区,用在游戏详情页的答案列表。如果有可用答案,将会返回社区ID和专题ID @@ -1413,20 +1413,20 @@ public interface ApiService { /** * 获取社区文章详情 */ - @GET("communities/{community_id}/articles/{article_id}?view=detail") - Observable getCommunityArticleDetail(@Path("community_id") String communityId, @Path("article_id") String articleId); + @GET("communities/articles/{article_id}?view=detail") + Observable getCommunityArticleDetail(@Path("article_id") String articleId); /** * 点赞社区文章 */ - @POST("communities/{community_id}/articles/{article_id}:vote") - Observable postCommunityArticleVote(@Path("community_id") String communityId, @Path("article_id") String articleId); + @POST("communities/articles/{article_id}:vote") + Observable postCommunityArticleVote(@Path("article_id") String articleId); /** * 取消点赞社区文章 */ - @POST("communities/{community_id}/articles/{article_id}:unvote") - Observable postCommunityArticleUnVote(@Path("community_id") String communityId, @Path("article_id") String articleId); + @POST("communities/articles/{article_id}:unvote") + Observable postCommunityArticleUnVote(@Path("article_id") String articleId); /** * 踩社区文章 @@ -1461,25 +1461,20 @@ public interface ApiService { /** * 收藏社区文章 */ - @POST("users/{user_id}/favorites/communities/{community_id}/articles/{article_id}") - Observable postCommunityArticleFavorites(@Path("user_id") String userId, - @Path("community_id") String communityId, - @Path("article_id") String articleId); + @POST("users/favorites/communities/articles/{article_id}") + Observable postCommunityArticleFavorites(@Path("article_id") String articleId); /** * 取消收藏社区文章 */ - @DELETE("users/{user_id}/favorites/communities/{community_id}/articles/{article_id}") - Observable deleteCommunityArticleFavorites(@Path("user_id") String userId, - @Path("community_id") String communityId, - @Path("article_id") String articleId); + @DELETE("users/favorites/communities/articles/{article_id}") + Observable deleteCommunityArticleFavorites(@Path("article_id") String articleId); /** * 获取社区文章评论列表.可以分页 */ - @GET("communities/{community_id}/articles/{article_id}/comments") - Observable> getCommunityArticleCommentList(@Path("community_id") String communityId, - @Path("article_id") String articleId, + @GET("communities/articles/{article_id}/comments") + Observable> getCommunityArticleCommentList(@Path("article_id") String articleId, @Query("sort") String type, @Query("page") int page, @QueryMap Map params); @@ -1487,27 +1482,21 @@ public interface ApiService { /** * 获取社区文章评论的对话列表. */ - @GET("communities/{community_id}/articles/{article_id}/comments/{comment_id}/trace") - Observable> getCommunityArticleCommentConversation(@Path("community_id") String communityId, - @Path("article_id") String articleId, - @Path("comment_id") String commentId, + @GET("communities/articles/comments/{comment_id}/trace") + Observable> getCommunityArticleCommentConversation(@Path("comment_id") String commentId, @Query("page") int page); /** * 获取单条社区文章评论 */ - @GET("communities/{community_id}/articles/{article_id}/comments/{comment_id}") - Single getCommunityArticleComment(@Path("community_id") String communityId, - @Path("article_id") String articleId, - @Path("comment_id") String commentId); + @GET("communities/articles/comments/{comment_id}") + Single getCommunityArticleComment(@Path("comment_id") String commentId); /** * 获取社区文章评论回复 */ - @GET("communities/{community_id}/articles/{article_id}/comments/{comment_id}/replies") - Single> getCommunityArticleCommentReply(@Path("community_id") String communityId, - @Path("article_id") String articleId, - @Path("comment_id") String commentId, + @GET("communities/articles/comments/{comment_id}/replies") + Single> getCommunityArticleCommentReply(@Path("comment_id") String commentId, @Query("sort") String sort, @Query("page") int page, @QueryMap Map params); @@ -1515,35 +1504,28 @@ public interface ApiService { /** * 评论社区文章评论 */ - @POST("communities/{community_id}/articles/{article_id}/comments") - Observable postCommentToCommunityArticle(@Path("community_id") String communityId, - @Path("article_id") String articleId, + @POST("communities/articles/{article_id}/comments") + Observable postCommentToCommunityArticle(@Path("article_id") String articleId, @Body RequestBody body); /** * 在评论列表回复某一条评论 */ - @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:reply") - Observable postReplyToCommunityArticleComment(@Path("community_id") String communityId, - @Path("article_id") String articleId, - @Path("comment_id") String commentId, + @POST("communities/articles/comments/{comment_id}:reply") + Observable postReplyToCommunityArticleComment(@Path("comment_id") String commentId, @Body RequestBody body); /** * 对社区文章评论的点赞 */ - @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:vote") - Observable postVoteCommunityArticleComment(@Path("community_id") String communityId, - @Path("article_id") String articled, - @Path("comment_id") String commentId); + @POST("communities/articles/comments/{comment_id}:vote") + Observable postVoteCommunityArticleComment(@Path("comment_id") String commentId); /** * 投诉社区文章评论 */ - @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:report") - Observable postCommunityArticleCommentReport(@Path("community_id") String communityId, - @Path("article_id") String articleId, - @Path("comment_id") String commentId, + @POST("communities/articles/comments/{comment_id}:report") + Observable postCommunityArticleCommentReport(@Path("comment_id") String commentId, @Body RequestBody reportData); /** @@ -1738,33 +1720,33 @@ public interface ApiService { /** * 加精社区文章 */ - @POST("communities/{community_id}/articles/{article_id}:choiceness") - Observable highlightCommunityArticle(@Path("community_id") String communityId, @Path("article_id") String articleId); + @POST("communities/articles/{article_id}:moderator_choiceness") + Observable highlightCommunityArticle(@Path("article_id") String articleId); /** * 取消加精社区文章 */ - @POST("communities/{bbs_id}/articles/{article_id}:cancel_choiceness") - Observable cancelHighlightCommunityArticle(@Path("bbs_id") String bbsId, @Path("article_id") String articleId); + @POST("communities/articles/{article_id}:cancel_choiceness") + Observable cancelHighlightCommunityArticle(@Path("article_id") String articleId); /** * 隐藏社区文章 */ - @POST("communities/{community_id}/articles/{article_id}:hide") - Observable hideCommunityArticle(@Path("community_id") String communityId, @Path("article_id") String articleId); + @POST("communities/articles/{article_id}:hide") + Observable hideCommunityArticle(@Path("article_id") String articleId); /** * 置顶社区文章评论 */ - @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:set-top") - Observable highlightCommunityArticleComment(@Path("community_id") String communityId, @Path("article_id") String articleId, @Path("comment_id") String commendId); + @POST("communities/articles/comments/{comment_id}:set-top") + Observable highlightCommunityArticleComment(@Path("comment_id") String commendId); /** * 隐藏社区文章评论 */ - @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:hide") - Observable hideCommunityArticleComment(@Path("community_id") String communityId, @Path("article_id") String articleId, @Path("comment_id") String commendId); + @POST("communities/articles/comments/{comment_id}:hide") + Observable hideCommunityArticleComment(@Path("comment_id") String commendId); /** * 设置回答详情的的评论区域是否开启 @@ -2550,8 +2532,8 @@ public interface ApiService { /** * 删除视频评论 */ - @POST("videos/{video_id}/comments/{comment_id}:inactivate") - Single deleteVideoComment(@Path("video_id") String videoId, @Path("comment_id") String commentId); + @POST("videos/comments/{comment_id}:inactivate") + Single deleteVideoComment(@Path("comment_id") String commentId); /** * 版主删除视频评论 @@ -2672,7 +2654,7 @@ public interface ApiService { * 获取论坛全部Tab内容(社区文章+问题) */ @GET("bbses/{bbs_id}/contents") - Observable> getAllForumList(@Path("bbs_id") String bbsId, @Query("sort") String sort, @Query("page") int page); + Observable> getAllForumList(@Path("bbs_id") String bbsId, @Query("sort") String sort, @Query("page") int page, @QueryMap Map params); /** * 获取论坛精华Tab内容(社区文章) @@ -2815,14 +2797,14 @@ public interface ApiService { /** * 置顶评论 */ - @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:set-top") - Observable postArticleCommentTop(@Path("community_id") String communityId, @Path("article_id") String articleId, @Path("comment_id") String commentId, @QueryMap Map params); + @POST("communities/articles/comments/{comment_id}:set-top") + Observable postArticleCommentTop(@Path("comment_id") String commentId, @QueryMap Map params); /** * 取消置顶评论 */ - @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:unset-top") - Observable postArticleCommentUnTop(@Path("community_id") String communityId, @Path("article_id") String articleId, @Path("comment_id") String commentId); + @POST("communities/articles/comments/{comment_id}:unset-top") + Observable postArticleCommentUnTop(@Path("comment_id") String commentId); /** * 获取推荐的论坛 @@ -3172,8 +3154,8 @@ public interface ApiService { /** * 获取单条视频评论 */ - @GET("videos/{video_id}/comments/{comment_id}") - Single getCommunityVideoComment(@Path("video_id") String videoId, @Path("comment_id") String commentId); + @GET("videos/comments/{comment_id}") + Single getCommunityVideoComment(@Path("comment_id") String commentId); /** * 获取视频评论回复 @@ -3188,14 +3170,14 @@ public interface ApiService { /** * 加精视频贴 */ - @POST("bbses/{bbs_id}/videos/{video_id}:choiceness") - Observable highlightCommunityVideo(@Path("bbs_id") String bbsId, @Path("video_id") String videoId); + @POST("bbses/videos/{video_id}:moderator_choiceness") + Observable highlightCommunityVideo(@Path("video_id") String videoId); /** * 取消加精视频贴 */ - @POST("bbses/{bbs_id}/videos/{video_id}:cancel_choiceness") - Observable cancelHighlightCommunityVideo(@Path("bbs_id") String bbsId, @Path("video_id") String videoId); + @POST("bbses/videos/{video_id}:cancel_choiceness") + Observable cancelHighlightCommunityVideo(@Path("video_id") String videoId); /** * 申请加精视频贴 @@ -3212,8 +3194,8 @@ public interface ApiService { /** * 版主修改社区文章活动标签 */ - @POST("communities/{community_id}/articles/{article_id}/activity_tags") - Observable modifyArticleActivityTag(@Path("community_id") String communityId, @Path("article_id") String articleId, @Body RequestBody body); + @POST("communities/articles/{article_id}/activity_tags") + Observable modifyArticleActivityTag(@Path("article_id") String articleId, @Body RequestBody body); /** * 版主修改视频帖活动标签 @@ -3528,20 +3510,20 @@ public interface ApiService { /** * 取消帖子评论点赞 */ - @POST("communities/{community_id}/articles/{article_id}/comments/{comment_id}:unvote") - Observable postUnVoteArticleComment(@Path("community_id") String communityId, @Path("article_id") String articleId, @Path("comment_id") String commentId); + @POST("communities/articles/comments/{comment_id}:unvote") + Observable postUnVoteArticleComment(@Path("comment_id") String commentId); /** * 置顶视频评论 */ - @POST("videos/{video_id}/comments/{comment_id}:set-top") - Observable postVideoCommentTop(@Path("video_id") String videoId, @Path("comment_id") String commentId, @QueryMap Map params); + @POST("videos/comments/{comment_id}:set-top") + Observable postVideoCommentTop(@Path("comment_id") String commentId, @QueryMap Map params); /** * 取消视频置顶评论 */ - @POST("videos/{video_id}/comments/{comment_id}:unset-top") - Observable postVideoCommentUnTop(@Path("video_id") String videoId, @Path("comment_id") String commentId); + @POST("videos/comments/{comment_id}:unset-top") + Observable postVideoCommentUnTop(@Path("comment_id") String commentId); /** * 求版本上传apk后回调 @@ -3561,4 +3543,10 @@ public interface ApiService { @POST("./certification:sync") Observable postSyncCertification(@Body RequestBody body); + /** + * 整合首页接口 + */ + @GET("home/union") + Single getHomeContentUnion(@Query("version") String version, @Query("channel") String channel); + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/VApiService.kt b/app/src/main/java/com/gh/gamecenter/retrofit/service/VApiService.kt new file mode 100644 index 0000000000..f0693ff570 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/VApiService.kt @@ -0,0 +1,27 @@ +package com.gh.gamecenter.retrofit.service + +import com.gh.gamecenter.entity.AppEntity +import com.gh.gamecenter.entity.VSetting +import io.reactivex.Single +import retrofit2.http.GET +import retrofit2.http.Query + +interface VApiService { + + /** + * 获取对应包名匹配的更新 + */ + @GET("upgrade") + fun getPackageUpdate( + @Query("version") version: String?, + @Query("version_code") code: Int, + @Query("package") packageName: String? + ): Single + + /** + * 获取设置 + */ + @GET("setting") + fun getSettings(@Query("version") version: String?): Single + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchDefaultFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchDefaultFragment.kt index 406c7cc29d..fd392fd692 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchDefaultFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchDefaultFragment.kt @@ -93,6 +93,12 @@ open class SearchDefaultFragment : BaseFragment() { mBinding.hotTagFlexContainer.setLimitHeight(mFlexMaxHeight) createFlexContent(mBinding.hotTagFlex, getTagListString(), clickListener = { val tag = mHotTagList!![it] + NewFlatLogUtils.logSearchHotTagClick( + tag.name ?: "", + tag.type ?: "", + tag.link ?: "", + tag.text ?: "" + ) DataLogUtils.uploadHotTagLog(context, tag.name) PageSwitchDataHelper.pushCurrentPageData( hashMapOf( diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchDefaultHotAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchDefaultHotAdapter.kt index 51ba8e32c6..a07cc54971 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchDefaultHotAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchDefaultHotAdapter.kt @@ -43,17 +43,11 @@ class SearchDefaultHotAdapter( val drawableId = mContext.resources.getIdentifier(drawableRes, "drawable", mContext.packageName) index.setImageDrawable(drawableId.toDrawable()) } - val textColor = when (position) { - 0 -> R.color.text_ff5151.toColor(mContext) - 1 -> R.color.text_F67722.toColor(mContext) - 2 -> R.color.text_ffbf00.toColor(mContext) - else -> R.color.text_title.toColor(mContext) - } - name.setTextColor(textColor) val labelIcon = when (hotSearch.recommendType) { "hot" -> R.drawable.ic_search_hot "new" -> R.drawable.ic_search_new - "surge", "rise" -> R.drawable.ic_search_surge + "surge", "rise" -> R.drawable.ic_search_rise + "update" -> R.drawable.ic_search_update else -> -1 } if (labelIcon != -1) { diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt index e0ba7a870d..c06507e942 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt @@ -8,7 +8,6 @@ import android.text.method.LinkMovementMethod import android.text.style.ClickableSpan import android.view.View import androidx.core.content.ContextCompat -import com.gh.gamecenter.common.constant.ItemViewType import com.gh.common.exposure.ExposureListener import com.gh.common.util.* import com.gh.common.xapk.XapkInstaller @@ -21,6 +20,7 @@ import com.gh.gamecenter.SuggestionActivity import com.gh.gamecenter.baselist.ListFragment import com.gh.gamecenter.baselist.LoadType import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.constant.ItemViewType import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.databinding.FragmentSearchResultBinding @@ -52,6 +52,10 @@ class SearchGameIndexFragment : ListFragment -DisplayUtils.dip2px(50F) && mAdapter?.isLoadOver() == true) { + return + } + } mBinding.containerMenuOpen.visibility = View.VISIBLE mBinding.seekGameBtn.visibility = View.VISIBLE mBinding.seekFunctionBtn.visibility = View.VISIBLE @@ -134,7 +145,7 @@ class SearchGameResultFragment : ListFragment -DisplayUtils.dip2px(50f) && mAdapter?.isLoadOver() == true) { + if (child != null && recyclerView.bottom - child.bottom > -DisplayUtils.dip2px(50F) && mAdapter?.isLoadOver() == true) { mBinding.containerMenuClose.visibility = View.GONE } else { mBinding.containerMenuClose.visibility = View.VISIBLE diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultViewModel.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultViewModel.kt index 968a073975..d0cb654afd 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultViewModel.kt @@ -5,6 +5,8 @@ import android.app.Application import com.gh.common.constant.Config import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.baselist.LoadParams +import com.gh.gamecenter.baselist.LoadStatus import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.SearchSubjectEntity import com.gh.gamecenter.retrofit.RetrofitManager @@ -33,18 +35,54 @@ class SearchGameResultViewModel(application: Application) : ListViewModel 0) it.location - 1 else 0, + if (it.location <= 0 || it.location >= itemDataList.size) 0 else it.location - 1, SearchItemData(subject = it) ) } + // 处理初始化列表且游戏列表size为0的情况 + if (mPage == 1 && list.isEmpty()) { + mLoadStatusLiveData.value = if (itemDataList.isEmpty()) LoadStatus.INIT_EMPTY else LoadStatus.INIT_OVER + } mResultLiveData.postValue(itemDataList) }, { it.printStackTrace() + if (mPage == 1 && list.isEmpty()) { + mLoadStatusLiveData.value = if (itemDataList.isEmpty()) LoadStatus.INIT_EMPTY else LoadStatus.INIT_OVER + } mResultLiveData.postValue(itemDataList) }) } } + override fun loadStatusControl(size: Int) { + if (mCurLoadParams.loadOffset == LoadParams.DEFAULT_OFFSET) { // 初始化列表 + if (size == 0) { +// 初始化列表size为0情况放到mergeResultLiveData方法处理 + } else if (size == REQUEST_FAILURE_SIZE) { + mLoadStatusLiveData.setValue(LoadStatus.INIT_FAILED) + } else if (size < mOverLimitSize) { // 避免一个屏幕出现两次分页 + mLoadStatusLiveData.setValue(LoadStatus.INIT_OVER) + } else { + mLoadStatusLiveData.setValue(LoadStatus.INIT_OVER) + } + } else { + if (size == REQUEST_FAILURE_SIZE) { + mLoadStatusLiveData.setValue(LoadStatus.LIST_FAILED) + } else if (size == 0) { + mLoadStatusLiveData.setValue(LoadStatus.LIST_OVER) + } else { + mLoadStatusLiveData.setValue(LoadStatus.LIST_LOADED) + } + } + + if (size == REQUEST_FAILURE_SIZE) { + mRetryParams = mCurLoadParams + } else { + mRetryParams = null + mCurLoadParams.setLoadOffset(mCurLoadParams.getLoadOffset() + 1) // 页数 + 1 + } + } + override fun provideDataObservable(page: Int): Observable> { // 可能会有特殊字符,需要 encode 处理 val encodedKey = URLEncoder.encode(key, "utf-8") diff --git a/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt b/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt index da8822d23e..b7a0d9a30e 100644 --- a/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/setting/GameDownloadSettingFragment.kt @@ -17,6 +17,7 @@ import com.gh.gamecenter.CleanApkActivity import com.gh.gamecenter.R import com.gh.gamecenter.databinding.FragmentGameDownloadSettingBinding import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.utils.setSwitchAnimation import com.gh.gamecenter.common.utils.toColor import java.io.File @@ -107,8 +108,6 @@ class GameDownloadSettingFragment: ToolbarFragment() { } } - private fun LottieAnimationView.setSwitchAnimation(turnOff: Boolean) = setAnimation(if (turnOff) "lottie/switch_turnoff.json" else "lottie/switch_turnon.json") - private fun startFilePath(dirPath: String) { val intent = Intent(Intent.ACTION_GET_CONTENT) val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { @@ -129,7 +128,12 @@ class GameDownloadSettingFragment: ToolbarFragment() { override fun onNightModeChange() { super.onNightModeChange() - mBinding?.root?.setBackgroundColor(R.color.background.toColor(requireContext())) + mBinding?.run { + root.setBackgroundColor(R.color.background.toColor(requireContext())) + autoInstallItem.switchLottie.setSwitchAnimation(SPUtils.getBoolean(AUTO_INSTALL_SP_KEY, true)) + concernGameItem.switchLottie.setSwitchAnimation(SPUtils.getBoolean(CONCERN_GAME_SP_KEY, true)) + trafficItem.switchLottie.setSwitchAnimation(SPUtils.getBoolean(getTrafficDownloadHintKey(), false)) + } } companion object { diff --git a/app/src/main/java/com/gh/gamecenter/setting/VideoSettingActivity.kt b/app/src/main/java/com/gh/gamecenter/setting/VideoSettingActivity.kt index 42406841a2..0c6a84200c 100644 --- a/app/src/main/java/com/gh/gamecenter/setting/VideoSettingActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/setting/VideoSettingActivity.kt @@ -2,6 +2,7 @@ package com.gh.gamecenter.setting import android.content.Context import android.content.Intent import android.os.Bundle +import android.view.View import com.gh.gamecenter.common.base.activity.ToolBarActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.constant.EntranceConsts @@ -32,4 +33,8 @@ class VideoSettingActivity : ToolBarActivity() { super.onNightModeChange() updateStatusBarColor(R.color.background_white, R.color.background_white) } + + override fun updateStaticViewBackground(view: View?) { + updateStaticView(view, listOf(R.id.selectedIv)) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/setting/VideoSettingFragment.kt b/app/src/main/java/com/gh/gamecenter/setting/VideoSettingFragment.kt index 068551b625..442e60c54d 100644 --- a/app/src/main/java/com/gh/gamecenter/setting/VideoSettingFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/setting/VideoSettingFragment.kt @@ -2,17 +2,20 @@ package com.gh.gamecenter.setting import android.os.Bundle import android.view.View -import com.airbnb.lottie.LottieAnimationView -import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.R -import com.gh.gamecenter.databinding.FragmentVideoSettingBinding import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.utils.setSwitchAnimation import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.toDrawable +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.databinding.FragmentVideoSettingBinding class VideoSettingFragment: ToolbarFragment() { private var mBinding: FragmentVideoSettingBinding? = null + private var mContentVideoOptionSelected = "" + private var mHomeOrDetailVideoOptionSelected = "" override fun getLayoutId() = 0 @@ -29,6 +32,7 @@ class VideoSettingFragment: ToolbarFragment() { mBinding?.homeOrDetailVideoTitle?.root?.text = "首页/游戏详情页视频" mBinding?.contentVideoOptionAllItem?.run { titleTv.text = getString(R.string.all_network_auto_play) + selectedIv.visibility = View.VISIBLE root.setOnClickListener { if (!isCurrentContentStatus(VIDEO_OPTION_ALL)) { setContentVideoOption(VIDEO_OPTION_ALL) @@ -38,6 +42,7 @@ class VideoSettingFragment: ToolbarFragment() { } mBinding?.contentVideoOptionWifiItem?.run { titleTv.text = getString(R.string.only_wifi_auto_play) + selectedIv.visibility = View.VISIBLE root.setOnClickListener { if (!isCurrentContentStatus(VIDEO_OPTION_WIFI)) { setContentVideoOption(VIDEO_OPTION_WIFI) @@ -47,6 +52,7 @@ class VideoSettingFragment: ToolbarFragment() { } mBinding?.contentVideoOptionCloseItem?.run { titleTv.text = getString(R.string.close_auto_play) + selectedIv.visibility = View.VISIBLE root.setOnClickListener { if (!isCurrentContentStatus(VIDEO_OPTION_CLOSE)) { setContentVideoOption(VIDEO_OPTION_CLOSE) @@ -56,6 +62,7 @@ class VideoSettingFragment: ToolbarFragment() { } mBinding?.homeOrDetailVideoOptionAllItem?.run { titleTv.text = getString(R.string.all_network_auto_play) + selectedIv.visibility = View.VISIBLE root.setOnClickListener { if (!isCurrentHomeOrDetailStatus(VIDEO_OPTION_ALL)) { setHomeOrDetailVideoOption(VIDEO_OPTION_ALL) @@ -65,6 +72,7 @@ class VideoSettingFragment: ToolbarFragment() { } mBinding?.homeOrDetailVideoOptionWifiItem?.run { titleTv.text = getString(R.string.only_wifi_auto_play) + selectedIv.visibility = View.VISIBLE root.setOnClickListener { if (!isCurrentHomeOrDetailStatus(VIDEO_OPTION_WIFI)) { setHomeOrDetailVideoOption(VIDEO_OPTION_WIFI) @@ -74,6 +82,7 @@ class VideoSettingFragment: ToolbarFragment() { } mBinding?.homeOrDetailVideoOptionCloseItem?.run { titleTv.text = getString(R.string.close_auto_play) + selectedIv.visibility = View.VISIBLE root.setOnClickListener { if (!isCurrentHomeOrDetailStatus(VIDEO_OPTION_CLOSE)) { setHomeOrDetailVideoOption(VIDEO_OPTION_CLOSE) @@ -96,11 +105,11 @@ class VideoSettingFragment: ToolbarFragment() { } } - private fun LottieAnimationView.setSwitchAnimation(turnOff: Boolean) = setAnimation(if (turnOff) "lottie/switch_turnoff.json" else "lottie/switch_turnon.json") - private fun initStatus() { - setContentVideoOption(SPUtils.getString(SP_CONTENT_VIDEO_OPTION, VIDEO_OPTION_WIFI) ?: VIDEO_OPTION_WIFI) - setHomeOrDetailVideoOption(SPUtils.getString(SP_HOME_OR_DETAIL_VIDEO_OPTION, VIDEO_OPTION_WIFI) ?: VIDEO_OPTION_WIFI) + mContentVideoOptionSelected = SPUtils.getString(SP_CONTENT_VIDEO_OPTION, VIDEO_OPTION_WIFI) ?: VIDEO_OPTION_WIFI + mHomeOrDetailVideoOptionSelected = SPUtils.getString(SP_HOME_OR_DETAIL_VIDEO_OPTION, VIDEO_OPTION_WIFI) ?: VIDEO_OPTION_WIFI + setContentVideoOption(mContentVideoOptionSelected) + setHomeOrDetailVideoOption(mHomeOrDetailVideoOptionSelected) mBinding?.muteItem?.switchLottie?.setSwitchAnimation(SPUtils.getBoolean(SP_VIDEO_PLAY_MUTE, true)) } @@ -108,21 +117,21 @@ class VideoSettingFragment: ToolbarFragment() { mBinding?.run { when (status) { VIDEO_OPTION_ALL -> { - contentVideoOptionAllItem.selectedIv.visibility = View.VISIBLE - contentVideoOptionWifiItem.selectedIv.visibility = View.GONE - contentVideoOptionCloseItem.selectedIv.visibility = View.GONE + contentVideoOptionAllItem.selectedIv.setImageDrawable(R.drawable.ic_video_setting_select.toDrawable(requireContext())) + contentVideoOptionWifiItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) + contentVideoOptionCloseItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) } VIDEO_OPTION_WIFI -> { - contentVideoOptionAllItem.selectedIv.visibility = View.GONE - contentVideoOptionWifiItem.selectedIv.visibility = View.VISIBLE - contentVideoOptionCloseItem.selectedIv.visibility = View.GONE + contentVideoOptionAllItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) + contentVideoOptionWifiItem.selectedIv.setImageDrawable(R.drawable.ic_video_setting_select.toDrawable(requireContext())) + contentVideoOptionCloseItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) } VIDEO_OPTION_CLOSE -> { - contentVideoOptionAllItem.selectedIv.visibility = View.GONE - contentVideoOptionWifiItem.selectedIv.visibility = View.GONE - contentVideoOptionCloseItem.selectedIv.visibility = View.VISIBLE + contentVideoOptionAllItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) + contentVideoOptionWifiItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) + contentVideoOptionCloseItem.selectedIv.setImageDrawable(R.drawable.ic_video_setting_select.toDrawable(requireContext())) } } } @@ -132,21 +141,21 @@ class VideoSettingFragment: ToolbarFragment() { mBinding?.run { when (status) { VIDEO_OPTION_ALL -> { - homeOrDetailVideoOptionAllItem.selectedIv.visibility = View.VISIBLE - homeOrDetailVideoOptionWifiItem.selectedIv.visibility = View.GONE - homeOrDetailVideoOptionCloseItem.selectedIv.visibility = View.GONE + homeOrDetailVideoOptionAllItem.selectedIv.setImageDrawable(R.drawable.ic_video_setting_select.toDrawable(requireContext())) + homeOrDetailVideoOptionWifiItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) + homeOrDetailVideoOptionCloseItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) } VIDEO_OPTION_WIFI -> { - homeOrDetailVideoOptionAllItem.selectedIv.visibility = View.GONE - homeOrDetailVideoOptionWifiItem.selectedIv.visibility = View.VISIBLE - homeOrDetailVideoOptionCloseItem.selectedIv.visibility = View.GONE + homeOrDetailVideoOptionAllItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) + homeOrDetailVideoOptionWifiItem.selectedIv.setImageDrawable(R.drawable.ic_video_setting_select.toDrawable(requireContext())) + homeOrDetailVideoOptionCloseItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) } VIDEO_OPTION_CLOSE -> { - homeOrDetailVideoOptionAllItem.selectedIv.visibility = View.GONE - homeOrDetailVideoOptionWifiItem.selectedIv.visibility = View.GONE - homeOrDetailVideoOptionCloseItem.selectedIv.visibility = View.VISIBLE + homeOrDetailVideoOptionAllItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) + homeOrDetailVideoOptionWifiItem.selectedIv.setImageDrawable(R.drawable.ic_selector_default.toDrawable(requireContext())) + homeOrDetailVideoOptionCloseItem.selectedIv.setImageDrawable(R.drawable.ic_video_setting_select.toDrawable(requireContext())) } } } @@ -163,6 +172,9 @@ class VideoSettingFragment: ToolbarFragment() { override fun onNightModeChange() { super.onNightModeChange() mBinding?.root?.setBackgroundColor(R.color.background.toColor(requireContext())) + mBinding?.muteItem?.switchLottie?.setSwitchAnimation(SPUtils.getBoolean(SP_VIDEO_PLAY_MUTE, true)) + setContentVideoOption(mContentVideoOptionSelected) + setHomeOrDetailVideoOption(mHomeOrDetailVideoOptionSelected) } companion object { diff --git a/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListFragment.kt b/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListFragment.kt index 274dbe57e5..6b7038e7c6 100644 --- a/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListFragment.kt @@ -2,21 +2,20 @@ package com.gh.gamecenter.simulatorgame import android.os.Bundle import android.view.View -import com.gh.gamecenter.core.AppExecutor +import com.gh.download.DownloadManager +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.baselist.LoadType import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.safelyGetInRelease import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.common.utils.viewModelProviderFromParent -import com.gh.download.DownloadManager -import com.gh.gamecenter.baselist.ListFragment -import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.SimulatorEntity import com.gh.gamecenter.game.GameAndPosition import com.halo.assistant.HaloApp import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity -import java.util.* class SimulatorGameListFragment : ListFragment() { @@ -36,6 +35,10 @@ class SimulatorGameListFragment : ListFragment { diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt index e2943488dc..a65df62988 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt @@ -312,7 +312,9 @@ class SubjectAdapter( private fun generateExposureEvent(gameEntity: GameEntity, subjectStyle: String, isFilterOn: Boolean): ExposureEvent { val exposureSourceList = arrayListOf() - mViewModel.exposureSource?.let { exposureSourceList.add(it) } + if (!mViewModel.exposureSourceList.isNullOrEmpty()) { + exposureSourceList.addAll(mViewModel.exposureSourceList!!) + } if (mIsColumnCollection) { exposureSourceList.add(ExposureSource("排行榜")) exposureSourceList.add(ExposureSource("专题", subjectData.subjectName.toString())) diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt index 7fabb2fdb1..10d1a0fbf2 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt @@ -4,19 +4,19 @@ import android.view.View import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton -import com.gh.gamecenter.common.constant.Constants import com.gh.common.exposure.ExposureListener -import com.gh.common.util.* -import com.gh.gamecenter.common.view.SpacingItemDecoration +import com.gh.common.util.DialogUtils import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.LazyListFragment import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.utils.viewModelProviderFromParent +import com.gh.gamecenter.common.view.SpacingItemDecoration import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.SubjectData @@ -50,6 +50,10 @@ class SubjectListFragment : LazyListFragment() showUnzipFailureDialog(downloadEntity) } } + + override fun onDataInit(downloadEntity: DownloadEntity) { + mAdapter?.notifyItemByDownload(downloadEntity) + } } override fun getStubLayoutId() = R.layout.fragment_list_base_skeleton_stub @@ -77,7 +81,7 @@ class SubjectListFragment : LazyListFragment() val factory = SubjectListViewModel.Factory( HaloApp.getInstance().application, arguments?.getParcelable(EntranceConsts.KEY_SUBJECT_DATA)!!, - arguments?.getParcelable(EntranceConsts.KEY_EXPOSURE_SOURCE)) + arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST)) if (requireContext() is SubjectActivity) { requireActivity().intent.getParcelableExtra(EntranceConsts.KEY_SUBJECT_DATA)?.let { val f = SubjectViewModel.Factory(HaloApp.getInstance().application, it) @@ -151,8 +155,6 @@ class SubjectListFragment : LazyListFragment() } override fun onResume() { - if (isEverPause && mAdapter != null) mAdapter?.notifyDataSetChanged() - super.onResume() } diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt index 9016c9b951..f74d606fcb 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt @@ -23,7 +23,7 @@ import retrofit2.HttpException class SubjectListViewModel(application: Application, var subjectData: SubjectData, - var exposureSource: ExposureSource?) : ListViewModel(application) { + var exposureSourceList: List?) : ListViewModel(application) { // 供专题类型为 rows 时统计用 var selectedLabelList = arrayListOf() @@ -104,9 +104,9 @@ class SubjectListViewModel(application: Application, } } - class Factory(private val mApplication: Application, private val subjectData: SubjectData, private val exposureSource: ExposureSource? = null) : ViewModelProvider.NewInstanceFactory() { + class Factory(private val mApplication: Application, private val subjectData: SubjectData, private val exposureSourceList: List? = null) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { - return SubjectListViewModel(mApplication, subjectData, exposureSource) as T + return SubjectListViewModel(mApplication, subjectData, exposureSourceList) as T } } diff --git a/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt b/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt index b12721769c..e581c5090a 100644 --- a/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt @@ -4,13 +4,9 @@ import android.os.Bundle import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import com.ethanhua.skeleton.Skeleton -import com.gh.gamecenter.common.constant.Constants import com.gh.common.exposure.ExposureListener import com.gh.common.exposure.ExposureSource import com.gh.common.util.DialogUtils -import com.gh.gamecenter.common.utils.observeNonNull -import com.gh.gamecenter.common.utils.toColor -import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.common.view.ConfigFilterView import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus @@ -19,6 +15,10 @@ import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListAdapter import com.gh.gamecenter.baselist.ListFragment import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.utils.observeNonNull +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.databinding.FragmentTagsBinding import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.entity.SubjectSettingEntity @@ -46,6 +46,10 @@ class TagsListFragment : ListFragment() { showUnzipFailureDialog(downloadEntity) } } + + override fun onDataInit(downloadEntity: DownloadEntity) { + mAdapter?.notifyItemByDownload(downloadEntity) + } } private lateinit var mViewModel: TagsListViewModel diff --git a/app/src/main/java/com/gh/vspace/HomeRecentVGameViewHolder.kt b/app/src/main/java/com/gh/vspace/HomeRecentVGameViewHolder.kt new file mode 100644 index 0000000000..3d1e2a82a9 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/HomeRecentVGameViewHolder.kt @@ -0,0 +1,207 @@ +package com.gh.vspace + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnScrollListener +import com.gh.common.util.NewFlatLogUtils +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.DiffUtilAdapter +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.databinding.ItemHomeRecentVgameBinding +import com.gh.gamecenter.databinding.ItemHomeVgameBinding +import com.gh.gamecenter.manager.PackagesManager +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus + +class HomeRecentVGameViewHolder(var binding: ItemHomeRecentVgameBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bindView(entityList: ArrayList) { + if (binding.recyclerView.adapter == null) { + val layoutManager = + LinearLayoutManager(binding.root.context, RecyclerView.HORIZONTAL, false) + + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.itemAnimator = null + binding.recyclerView.adapter = HomeRecentVGameAdapter(binding.root.context) + binding.recyclerView.addItemDecoration( + GridSpacingItemColorDecoration(binding.root.context, 4, 0, R.color.transparent) + ) + + binding.recyclerView.addOnScrollListener(object : OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + val scrollToEnd = + layoutManager.findLastCompletelyVisibleItemPosition() == (binding.recyclerView.adapter!!.itemCount - 1) + binding.divider.visibleIf(scrollToEnd) + } + }) + + binding.moreTv.setOnClickListener { + NewFlatLogUtils.logHaloFunManageShow("最近在玩") + binding.root.context.startActivity( + VDownloadManagerActivity.getIntent( + binding.root.context, + switchToDownloadingTab = false, + isFromHomeRecent = true + ) + ) + } + + binding.vspaceIv.setOnClickListener { + NewFlatLogUtils.logHaloFunEvent("halo_fun_manage_square_entrance_click") + VHelper.startVSpaceSquare(it.context) + } + } + + (binding.recyclerView.adapter as? HomeRecentVGameAdapter)?.submitList(entityList) + } +} + +class HomeRecentVGameAdapter(context: Context) : DiffUtilAdapter(context) { + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): HomeRecentVGameItemViewHolder { + return HomeRecentVGameItemViewHolder(parent.toBinding()) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as HomeRecentVGameItemViewHolder).bindView(mDataList[position]) + } + + override fun getItemCount(): Int { + return mDataList.size + } + + fun notifyItemByDownload(downloadEntity: DownloadEntity?) { + if (downloadEntity == null) { + notifyDataSetChanged() + } else { + for (position in mDataList.indices) { + if (downloadEntity.name == mDataList[position].downloadEntity.name) { + mDataList[position].downloadEntity = downloadEntity + notifyItemChanged(position) + } + } + } + } + + override fun areItemsTheSame(oldItem: VGameItemData?, newItem: VGameItemData?): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: VGameItemData?, newItem: VGameItemData?): Boolean { + // VGameItemData 里除了 downloadEntity 以外的内容仅作是否内容发生变更的判断, + // 并不应用到实际状态当中,最后还是得从 downloadEntity 里实时获取判断, + // 别问,问就是下载管理跟实际列表更新两边很难扯在一起 + + return oldItem?.downloadEntity == newItem?.downloadEntity + && oldItem?.shouldShowControlTv == newItem?.shouldShowControlTv + && oldItem?.shouldShowDot == newItem?.shouldShowDot + && oldItem?.shouldShowMask == newItem?.shouldShowMask + && oldItem?.shouldShowUpdate == newItem?.shouldShowUpdate + && oldItem?.shouldShowProgressBar == newItem?.shouldShowProgressBar + } + + class HomeRecentVGameItemViewHolder(private var mBinding: ItemHomeVgameBinding) : + RecyclerView.ViewHolder(mBinding.root) { + + fun bindView(entity: VGameItemData) { + if (mBinding.gameIconIv.getTag(R.string.app_name) != entity.downloadEntity.packageName) { + mBinding.gameIconIv.displayGameIcon( + entity.downloadEntity.getMetaExtra(Constants.RAW_GAME_ICON), + entity.downloadEntity.getMetaExtra(Constants.GAME_ICON_SUBSCRIPT) + ) + mBinding.gameIconIv.setTag(R.string.app_name, entity.downloadEntity.packageName) + } + + updateViewByStatus(mBinding, entity) + } + + private fun updateViewByStatus(binding: ItemHomeVgameBinding, itemData: VGameItemData) { + val downloadEntity = itemData.downloadEntity + + itemData.refresh() + + when (downloadEntity.status) { + DownloadStatus.done -> { + if (itemData.shouldShowUpdate) { + binding.root.setOnClickListener { + PackagesManager.getUpdateList() + .firstOrNull { it.id == downloadEntity.gameId } + ?.let { + VHelper.updateOrReDownload(downloadEntity, it) + } + } + } else { + binding.root.setOnClickListener { + VHelper.installOrLaunch( + binding.root.context, + downloadEntity.packageName + ) + } + } + } + DownloadStatus.subscribe, + DownloadStatus.waiting, + DownloadStatus.pause -> { + binding.progressBar.progressDrawable = + R.drawable.bg_home_vgame_progress_inactive.toDrawable() + + if (downloadEntity.status == DownloadStatus.waiting) { + binding.root.setOnClickListener { + ToastUtils.toast("最多只能同时下载三个任务,请稍等") + } + } else { + binding.root.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, false) + } + } + } + DownloadStatus.redirected, + DownloadStatus.downloading -> { + binding.progressBar.progressDrawable = + R.drawable.bg_home_vgame_progress_active.toDrawable() + binding.root.setOnClickListener { + DownloadManager.getInstance().pause(downloadEntity.url) + } + } + + DownloadStatus.cancel, + DownloadStatus.timeout, + DownloadStatus.neterror, + DownloadStatus.hijack, + DownloadStatus.uncertificated, + DownloadStatus.unqualified, + DownloadStatus.notfound, + DownloadStatus.unavailable, + DownloadStatus.overflow -> { + binding.root.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, false) + } + } + else -> { + binding.root.setOnClickListener { + ToastUtils.showToast(downloadEntity.status.toString()) + } + } + } + + mBinding.maskView.goneIf(!itemData.shouldShowMask) + mBinding.controlTv.goneIf(!itemData.shouldShowControlTv) + mBinding.progressBar.goneIf(!itemData.shouldShowProgressBar) + mBinding.dotView.goneIf(!itemData.shouldShowDot) + mBinding.updateHintIv.goneIf(!itemData.shouldShowUpdate) + + mBinding.controlTv.text = itemData.controlText + mBinding.progressBar.progress = downloadEntity.percent.toInt() + } + } +} diff --git a/app/src/main/java/com/gh/vspace/VBackupHelper.kt b/app/src/main/java/com/gh/vspace/VBackupHelper.kt new file mode 100644 index 0000000000..ebd2d1269d --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VBackupHelper.kt @@ -0,0 +1,128 @@ +package com.gh.vspace + +import android.content.Context +import android.os.Build +import android.os.Environment +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.utils.MD5Utils +import com.gh.vspace.db.VGameDatabase +import com.lightgame.utils.Utils +import net.lingala.zip4j.ZipFile +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.nio.channels.FileChannel +import java.nio.file.Files + +object VBackupHelper { + + private const val BACKUP_FOLDER = "/gh-files/vspace_backup" + + fun recoverValidData(context: Context) { + runOnIoThread { + recoverFromBackupDb(context) + } + } + + private fun recoverFromBackupDb(context: Context) { + try { + val backupDir = File(Environment.getExternalStorageDirectory().absolutePath + BACKUP_FOLDER) + val backupSubDir = File(Environment.getExternalStorageDirectory().absolutePath + BACKUP_FOLDER + "/files") + val databaseDir: File? = context.getDatabasePath(VGameDatabase.DATABASE).parentFile + + if (backupDir.canWrite() && databaseDir != null) { + backupDir.listFiles()?.forEach { + if (it.name.endsWith(".zip")) { + if (MD5Utils.calculateMD5(it) == it.nameWithoutExtension) { + val zipFile = ZipFile(it) + zipFile.extractAll(backupDir.absolutePath) + } else { + Utils.log("备份的压缩包 MD5 变了,可能损坏,中止还原") + return + } + } + it.delete() + } + + backupSubDir.listFiles()?.forEach { + if (it.name.endsWith(".bak")) { + val src: FileChannel = FileInputStream(it).channel + val dst: FileChannel = + FileOutputStream(File(databaseDir.absolutePath + "/${it.name.removeSuffix(".bak")}")).channel + dst.transferFrom(src, 0, src.size()) + src.close() + dst.close() + + it.delete() + + Utils.log("Recover ${it.name} successful!") + } + } + + backupDir.deleteRecursively() + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + /** + * 备份现有数据库到 SD 卡 + */ + fun backupDBToExternalStorage(context: Context) { + val backupDir = File(Environment.getExternalStorageDirectory().absolutePath + BACKUP_FOLDER) + val backupSubDir = File(Environment.getExternalStorageDirectory().absolutePath + BACKUP_FOLDER + "/files") + val databaseDir: File? = context.getDatabasePath(VGameDatabase.DATABASE).parentFile + + backupDir.deleteRecursively() + + backupDir.mkdir() + backupSubDir.mkdir() + + try { + if (backupSubDir.canWrite() && databaseDir != null) { + databaseDir.listFiles()?.forEach { + if (it.name.contains(VGameDatabase.DATABASE)) { + val src: FileChannel = FileInputStream(it).channel + val dst: FileChannel = + FileOutputStream(File(backupSubDir.absolutePath + "/${it.name}" + ".bak")).channel + dst.transferFrom(src, 0, src.size()) + src.close() + dst.close() + + Utils.log("Backup ${it.name} successful!") + } + } + } + + val backupZip = ZipFile(backupDir.absolutePath + "/" + backupSubDir.name + ".zip").apply { addFolder(backupSubDir) } + + backupSubDir.deleteRecursively() + + val fileName = MD5Utils.calculateMD5(backupZip.file) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Files.move(backupZip.file.toPath(), File(backupDir.absolutePath +"/" + fileName + ".zip").toPath()) + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun removeAllDatabase(context: Context) { + try { + val rootDir: File = Environment.getExternalStorageDirectory() + if (rootDir.canWrite()) { + val backupDBPath = String.format("/gh-files/%s.bak", VGameDatabase.DATABASE) + val externalDB = File(rootDir, backupDBPath) + + externalDB.delete() + context.deleteDatabase(VGameDatabase.DATABASE) + + Utils.log("Import Download DB Successful!") + } + } catch (e: Exception) { + e.printStackTrace() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerActivity.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerActivity.kt new file mode 100644 index 0000000000..4b8f8f1ffc --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerActivity.kt @@ -0,0 +1,44 @@ +package com.gh.vspace + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.activity.ToolBarActivity +import com.gh.gamecenter.common.constant.EntranceConsts + +class VDownloadManagerActivity: ToolBarActivity() { + + private var mFragment : VDownloadManagerWrapperFragment? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState == null) { + mFragment = VDownloadManagerWrapperFragment() + mFragment?.arguments = intent.extras + supportFragmentManager.beginTransaction().replace(R.id.placeholder, mFragment!!).commitAllowingStateLoss() + updateTargetFragment(mFragment) + } + } + + override fun getLayoutId() = R.layout.activity_vdownload_manager + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + + mFragment?.onNewIntent(intent) + } + + companion object { + @JvmStatic + @JvmOverloads + fun getIntent(context: Context, switchToDownloadingTab: Boolean = false, isFromHomeRecent: Boolean = false): Intent { + val intent = Intent(context, VDownloadManagerActivity::class.java) + intent.putExtra(EntranceConsts.KEY_POSITION, if (switchToDownloadingTab) 1 else 0) + intent.putExtra(EntranceConsts.KEY_IS_FROM_HOME_RECENT, isFromHomeRecent) + return intent + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt new file mode 100644 index 0000000000..c3e115ed94 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt @@ -0,0 +1,374 @@ +package com.gh.vspace + +import android.content.Context +import android.view.Gravity +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.PopupWindow +import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.databind.BindingAdapters +import com.gh.common.exposure.ExposureEvent +import com.gh.common.exposure.ExposureSource +import com.gh.common.exposure.IExposable +import com.gh.common.util.NewFlatLogUtils +import com.gh.common.view.DownloadProgressBar +import com.gh.download.DownloadManager +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.FooterViewHolder +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.ItemViewType +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.databinding.ItemVgameDownloadManagerBinding +import com.gh.gamecenter.databinding.PopupHistoryOptionBinding +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.history.ManageOption +import com.gh.gamecenter.manager.PackagesManager +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus +import com.lightgame.utils.Utils +import org.json.JSONArray + +class VDownloadManagerAdapter( + context: Context, + private val mViewModel: VDownloadManagerViewModel, +) : ListAdapter(context), IExposable { + + private val mBasicExposureSource by lazy { + if (mViewModel.isFromHomeRecent) listOf(ExposureSource("新首页"), ExposureSource("最近在玩")) + else listOf() + } + + private var mPopWindow: PopupWindow? = null + private var mCurrentOption = ManageOption.OPTION_MANAGER + private var mPopupBinding: PopupHistoryOptionBinding? = null + + var selectItems = arrayListOf() + + private val mPositionAndPackageMap = HashMap() + + override fun setListData(updateData: MutableList?) { + mPositionAndPackageMap.clear() + // 记录游戏位置 + if (updateData != null) { + for (i in 0 until updateData.size) { + val gameEntity = updateData[i] + var packages = gameEntity.id + for (apkEntity in gameEntity.getApk()) { + packages += apkEntity.packageName + } + mPositionAndPackageMap[packages + i] = i + } + } + + if (updateData == null) { + mEntityList = ArrayList() + notifyDataSetChanged() + return + } + + calculateDiff(updateData) + } + + override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean { + return oldItem != null && oldItem.id == newItem?.id + } + + override fun areContentsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean { + return oldItem?.id == newItem?.id + } + + override fun getItemViewType(position: Int) = ItemViewType.GAME_NORMAL + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return VGameItemViewHolder(parent.toBinding()) + } + + override fun getItemCount(): Int = + if (mEntityList == null || mEntityList.isEmpty()) 0 else mEntityList.size + + fun changeOption(option: ManageOption) { + mCurrentOption = option + when (mCurrentOption) { + ManageOption.OPTION_MANAGER -> { + selectItems.clear() + mPopWindow?.dismiss() + mPopWindow = null + } + else -> { + if (mPopWindow == null || mPopWindow?.isShowing == false) { + showOptionWindow() + } + } + } + notifyItemRangeChanged(0, mEntityList.size) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is VGameItemViewHolder -> { + val gameEntity = mEntityList[position] + + if (mViewModel.isTypeDownloaded()) { + runOnIoThread(true) { + gameEntity.exposureEvent = ExposureEvent.createEventWithSourceConcat( + gameEntity = gameEntity, + basicSource = mBasicExposureSource, + source = listOf(ExposureSource("畅玩游戏管理")) + ) + } + } + + BindingAdapters.setGameName(holder.binding.nameTv, gameEntity, false, true) + holder.binding.iconIv.displayGameIcon(gameEntity) + + holder.binding.descTv.goneIf(!mViewModel.isTypeDownloaded() || gameEntity.des?.isEmpty() == true) + holder.binding.descTv.text = gameEntity.des + + (holder.binding.selectIv.layoutParams as ConstraintLayout.LayoutParams).apply { + width = 20F.dip2px() + height = 20F.dip2px() + holder.binding.selectIv.layoutParams = this + } + holder.binding.selectIv.setImageDrawable(R.drawable.selector_ic_history.toDrawable()) + holder.binding.selectIv.goneIf(mCurrentOption == ManageOption.OPTION_MANAGER) + holder.binding.selectIv.isChecked = selectItems.contains(gameEntity.id) + + holder.itemView.setOnClickListener { + if (mCurrentOption == ManageOption.OPTION_MANAGER) { + GameDetailActivity.startGameDetailActivity( + mContext, gameEntity.id, "(畅玩游戏管理)" + ) + } else { + if (selectItems.contains(gameEntity.id)) { + selectItems.remove(gameEntity.id) + } else { + selectItems.add(gameEntity.id) + } + checkSelectItems() + notifyItemChanged(position) + } + } + + updateDownloadBtn(mContext, holder.binding.downloadBtn, gameEntity) + + holder.itemView.setOnLongClickListener { + DialogHelper.showDialog( + holder.binding.root.context, + "删除游戏", + "单机类游戏被删除将可能导致本地存档、充值数据丢失,请确认后操作(网游类游戏删除不会影响游戏存档和充值数据)", + "再等等", + "删除", + {}, + { + runOnIoThread { + val apk = gameEntity.getApk().firstOrNull() + mViewModel.removeItem(apk?.url, apk?.packageName) + + AppExecutor.uiExecutor.executeWithDelay({ + mViewModel.load(LoadType.REFRESH) + }, 200) + } + }, + uiModificationCallback = { + it.cancelTv.setTextColor(R.color.theme_red.toColor(it.root.context)) + it.confirmTv.setTextColor(R.color.text_subtitle.toColor(it.root.context)) + }, + extraConfig = DialogHelper.Config(centerTitle = true) + ) + return@setOnLongClickListener false + } + } + is FooterViewHolder -> { + holder.initItemPadding() + holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) + } + } + } + + private fun showOptionWindow() { + mPopupBinding = PopupHistoryOptionBinding.inflate(LayoutInflater.from(mContext)) + mPopupBinding?.root?.isFocusable = true + mPopupBinding?.root?.isFocusableInTouchMode = true + + mPopWindow = + PopupWindow(mPopupBinding?.root, LinearLayout.LayoutParams.MATCH_PARENT, 56F.dip2px()) + mPopWindow?.showAtLocation( + (mContext as AppCompatActivity).window.decorView, Gravity.BOTTOM, 0, 0 + ) + + mPopupBinding?.itemDelete?.setOnClickListener { + NewFlatLogUtils.logHaloFunEvent("halo_fun_manage_game_delete_dialog_show") + mViewModel.pauseItems(ArrayList(selectItems)) + DialogHelper.showDialog( + mContext, + "删除游戏", + "单机类游戏被删除将可能导致本地存档、充值数据丢失,请确认后操作(网游类游戏删除不会影响游戏存档和充值数据)", + "再等等", + "删除", + { + NewFlatLogUtils.logHaloFunManageGameDeleteDialogClick( + "再看看", JSONArray(selectItems) + ) + }, + { + NewFlatLogUtils.logHaloFunManageGameDeleteDialogClick( + "删除", JSONArray(selectItems) + ) + + val selectItemCopy = ArrayList(selectItems) + + selectItems.clear() + checkSelectItems() + changeOption(ManageOption.OPTION_MANAGER) + + runOnIoThread { + mViewModel.removeItems(selectItemCopy) + AppExecutor.uiExecutor.executeWithDelay({ + mViewModel.load(LoadType.REFRESH) + }, 200) + } + }, + uiModificationCallback = { + it.cancelTv.setTextColor(R.color.theme_red.toColor(it.root.context)) + it.confirmTv.setTextColor(R.color.text_subtitle.toColor(it.root.context)) + }, + extraConfig = DialogHelper.Config(centerTitle = true) + ) + } + mPopupBinding?.checkAllCb?.setOnClickListener { + if (mPopupBinding?.checkAllCb?.isChecked == true) { + selectItems.clear() + selectItems.addAll(mEntityList.map { it.id }.toList()) + } else { + selectItems.clear() + } + checkSelectItems() + notifyItemRangeChanged(0, mEntityList.size) + } + checkSelectItems() + } + + private fun checkSelectItems() { + mPopupBinding?.run { + selectNumTv.text = if (selectItems.isEmpty()) "" else "(${selectItems.size})" + itemDelete.background = + if (selectItems.isEmpty()) R.drawable.bg_shape_f5_radius_999.toDrawable() else R.drawable.download_button_normal_style.toDrawable() + itemDelete.setTextColor(if (selectItems.isEmpty()) R.color.text_body.toColor() else R.color.white.toColor()) + itemDelete.isEnabled = selectItems.isNotEmpty() + checkAllCb.isChecked = selectItems.size == mEntityList.size + } + } + + fun notifyItemByDownload(download: DownloadEntity) { + for (key in mPositionAndPackageMap.keys) { + if (key.contains(download.packageName) && key.contains(download.gameId)) { + val position = mPositionAndPackageMap[key] + if (position != null && mEntityList != null && position < mEntityList.size) { + mEntityList[position].getEntryMap()[download.platform] = download + notifyItemChanged(position) + } + } + } + } + + private fun updateDownloadBtn( + context: Context, downloadBtn: DownloadProgressBar, gameEntity: GameEntity + ) { + // 青少年模式显示查看 + if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) { + downloadBtn.text = "查看" + return + } + + downloadBtn.goneIf(mCurrentOption != ManageOption.OPTION_MANAGER) + + val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity) + ?: VHelper.getDownloadEntitySnapshot(gameEntity.id, gameEntity.getUniquePackageName()) + + if (downloadEntity != null) { + val status = downloadEntity.status + var btnText = context.getText(R.string.downloading) + var backgroundType = DownloadProgressBar.DownloadType.NORMAL + + downloadBtn.apply { + when (status) { + DownloadStatus.downloading -> { + btnText = "${downloadEntity.percent}%" + backgroundType = DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL + progress = (downloadEntity.percent * 10).toInt() + setOnClickListener { + DownloadManager.getInstance().pause(downloadEntity.url) + } + } + DownloadStatus.waiting -> { + btnText = context.getString(R.string.waiting) + setOnClickListener { Utils.toast(mContext, "最多只能同时下载三个任务,请稍等"); } + } + DownloadStatus.pause, + DownloadStatus.timeout, + DownloadStatus.neterror, + DownloadStatus.subscribe, + DownloadStatus.overflow -> { + btnText = context.getString(R.string.resume) + setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, true) + } + } + DownloadStatus.done -> { + if (PackagesManager.isCanUpdate( + gameEntity.id, + gameEntity.getApk().firstOrNull()?.packageName + ) + ) { + btnText = context.getString(R.string.update) + setOnClickListener { + PackagesManager.getUpdateList() + .firstOrNull { it.id == downloadEntity.gameId }?.let { + VHelper.updateOrReDownload(downloadEntity, it) + } + } + } else { + btnText = context.getString(R.string.launch) + setOnClickListener { + CurrentActivityHolder.getCurrentActivity()?.let { + VHelper.installOrLaunch(it, downloadEntity.packageName) + } + } + } + } + else -> { + // do nothing + } + } + + text = btnText.toString() + downloadType = backgroundType + } + } else { + downloadBtn.downloadType = DownloadProgressBar.DownloadType.NORMAL + downloadBtn.text = "下载" + downloadBtn.setOnClickListener { + ToastUtils.toast("不应该出现状态为'下载'的按钮") + } + } + } + + override fun getEventByPosition(pos: Int) = mEntityList[pos].exposureEvent + + override fun getEventListByPosition(pos: Int): List? = null + + class VGameItemViewHolder(var binding: ItemVgameDownloadManagerBinding) : + RecyclerView.ViewHolder(binding.root) + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerFragment.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerFragment.kt new file mode 100644 index 0000000000..e3a1b0b5c1 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerFragment.kt @@ -0,0 +1,118 @@ +package com.gh.vspace + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.exposure.ExposureListener +import com.gh.common.util.NewFlatLogUtils +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.baselist.ListAdapter +import com.gh.gamecenter.baselist.ListFragment +import com.gh.gamecenter.baselist.LoadType +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.databinding.FragmentVdownloadManagerBinding +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.history.IBatchDelete +import com.gh.gamecenter.history.ManageOption +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity + +class VDownloadManagerFragment : + ListFragment(), IBatchDelete { + + private val mAdapter by lazy { + VDownloadManagerAdapter( + requireContext(), + provideListViewModel() + ) + } + private val mViewModel: VDownloadManagerViewModel by lazy { viewModelProvider() } + private val mBinding by lazy { FragmentVdownloadManagerBinding.inflate(layoutInflater) } + private val mExposureListener by lazy { ExposureListener(this, mAdapter) } + + private val dataWatcher: DataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + mAdapter.notifyItemByDownload(downloadEntity) + } + + override fun onDataInit(downloadEntity: DownloadEntity) { + mAdapter.notifyItemByDownload(downloadEntity) + } + } + + override fun provideListAdapter(): ListAdapter<*> = mAdapter + override fun provideListViewModel(): VDownloadManagerViewModel = mViewModel + + override fun getLayoutId() = 0 + override fun getInflatedLayout() = mBinding.root + + override fun onCreate(savedInstanceState: Bundle?) { + mViewModel.type = arguments?.get(VDownloadManagerViewModel.TYPE) as String + mViewModel.isFromHomeRecent = + arguments?.getBoolean(EntranceConsts.KEY_IS_FROM_HOME_RECENT) ?: false + + super.onCreate(savedInstanceState) + } + + override fun onResume() { + super.onResume() + DownloadManager.getInstance().addObserver(dataWatcher) + } + + override fun onPause() { + super.onPause() + DownloadManager.getInstance().removeObserver(dataWatcher) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mCachedView.setBackgroundColor(R.color.background_white.toColor(requireContext())) + + mBinding.headerContainer.run { + goneIf(mViewModel.type != VDownloadManagerViewModel.TYPE_DOWNLOADED) + switchIv.enlargeTouchArea() + hintTv.text = "首页展示“最近在玩”板块" + switchIv.isChecked = SPUtils.getBoolean(Constants.SP_HOME_VGAME_AREA_ENABLED, true) + switchIv.setOnClickListener { + if (lottieView.isAnimating) return@setOnClickListener + val status = switchIv.isChecked + lottieView.setSwitchAnimation(status) + lottieView.doOnAnimationEnd { + switchIv.isChecked = !status + } + lottieView.playAnimation() + SPUtils.setBoolean(Constants.SP_HOME_VGAME_AREA_ENABLED, !status) + NewFlatLogUtils.logHaloFunManageRecentGameSwitch(!status) + + DownloadManager.getInstance().notifyDownloadLiveDataChanged() + } + } + + if (mViewModel.isTypeDownloaded()) { + mListRv.addOnScrollListener(mExposureListener) + } + + VHelper.vGameLiveData.observe(viewLifecycleOwner) { + onLoadRefresh() + } + } + + override fun changeOption(option: ManageOption) { + mAdapter.changeOption(option) + } + + override fun getItemDecoration(): RecyclerView.ItemDecoration? = null + + override fun shouldLoadMore() = false + + override fun isAutomaticLoad(): Boolean = false + + override fun onLoadRefresh() { + mReuseNoData?.visibility = View.GONE + mListViewModel.load(LoadType.REFRESH) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerViewModel.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerViewModel.kt new file mode 100644 index 0000000000..001fa5a809 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerViewModel.kt @@ -0,0 +1,159 @@ +package com.gh.vspace + +import android.app.Application +import androidx.annotation.WorkerThread +import com.gh.download.DownloadManager +import com.gh.download.PackageObserver +import com.gh.gamecenter.baselist.ListViewModel +import com.gh.gamecenter.common.utils.toProperReadableSize +import com.gh.gamecenter.core.runOnUiThread +import com.gh.gamecenter.core.utils.NumberUtils +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.eventbus.EBPackage +import com.lightgame.download.DownloadStatus +import io.reactivex.Observable +import io.reactivex.Single +import org.greenrobot.eventbus.EventBus + +class VDownloadManagerViewModel(application: Application) : + ListViewModel(application) { + + var type = "" + var isFromHomeRecent = false + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { + mResultLiveData.postValue(it) + } + } + + override fun provideDataObservable(page: Int): Observable>? = null + + override fun provideDataSingle(page: Int): Single> { + return if (type == TYPE_DOWNLOADED) { + provideDownloadedSingle() + } else { + provideDownloadingSingle() + } + } + + private fun provideDownloadedSingle(): Single> { + return Single.create { emitter -> + val vGameList = VHelper.getAllVGame().sortedByDescending { it.downloadEntity.start } + val gameList = arrayListOf() + + // 为空直接返回 + if (vGameList.isEmpty()) { + emitter.onSuccess(gameList) + return@create + } + + for (vGame in vGameList) { + // 过滤部分不需要的 + if (DownloadManager.getInstance().allVDownloadTaskSnapshots.any { + it.gameId == vGame.downloadEntity.gameId && it.status != DownloadStatus.done + }) { + continue + } + + val gameEntity = VHelper.toGameEntity(vGame.downloadEntity) + if (gameEntity.playedTime != 0L) { + gameEntity.des = + "已畅玩${NumberUtils.transSimpleUsageTime(gameEntity.playedTime / 1000)}" + } else { + var occupiedSpace = + VHelper.getAppOccupiedSpace(vGame.downloadEntity.packageName) + if (occupiedSpace == 0L) { + occupiedSpace = vGame.downloadEntity.size + } + + gameEntity.des = "已占用 ${occupiedSpace.toProperReadableSize()}" + } + + gameList.add(gameEntity) + } + + emitter.onSuccess(gameList) + } + } + + private fun provideDownloadingSingle(): Single> { + return Single.create { emitter -> + val downloadList = DownloadManager.getInstance().allVDownloadTaskSnapshots + + val gameList = arrayListOf() + + for (downloadEntity in downloadList) { + // 过滤下载完成的部分 + if (downloadEntity.status == DownloadStatus.done && VHelper.isInstalled(downloadEntity.packageName)) { + continue + } + gameList.add(VHelper.toGameEntity(downloadEntity)) + } + + emitter.onSuccess(gameList) + } + } + + @WorkerThread + fun removeItem(url: String?, packageName: String?) { + DownloadManager.getInstance().pause(url) + DownloadManager.getInstance().cancel(url) + + VHelper.uninstall(packageName ?: "") + + runOnUiThread { + val event = EBPackage("卸载", packageName ?: "", "unknown") + EventBus.getDefault().post(event) + PackageObserver.onPackageChanged(event) + + ToastUtils.toast("已删除 1 款游戏") + } + } + + @WorkerThread + fun removeItems(idList: ArrayList) { + val size = idList.size.coerceAtMost(99) + val packageList = arrayListOf() + + for (id in idList) { + val apkEntity = + mResultLiveData.value?.firstOrNull { id == it.id }?.getApk()?.firstOrNull() + DownloadManager.getInstance().pause(apkEntity?.url) + DownloadManager.getInstance().cancel(apkEntity?.url) + packageList.add(apkEntity?.packageName ?: "") + + VHelper.uninstall(apkEntity?.packageName) + } + + runOnUiThread { + for (packageName in packageList) { + val event = EBPackage("卸载", packageName, "unknown") + EventBus.getDefault().post(event) + PackageObserver.onPackageChanged(event) + } + + ToastUtils.toast("已删除 $size 款游戏") + } + } + + fun pauseItems(idList: ArrayList) { + for (id in idList) { + val apkEntity = + mResultLiveData.value?.firstOrNull { id == it.id }?.getApk()?.firstOrNull() + DownloadManager.getInstance().pause(apkEntity?.url) + } + } + + fun isTypeDownloaded(): Boolean { + return type == TYPE_DOWNLOADED + } + + companion object { + const val TYPE = "type" + const val TYPE_DOWNLOADED = "type_downloaded" + const val TYPE_DOWNLOADING = "type_downloading" + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperFragment.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperFragment.kt new file mode 100644 index 0000000000..3fbe0d9d0a --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperFragment.kt @@ -0,0 +1,119 @@ +package com.gh.vspace + +import android.content.Intent +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import android.widget.TextView +import androidx.fragment.app.Fragment +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.BaseLazyTabFragment +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.utils.viewModelProvider +import com.gh.gamecenter.databinding.FragmentVdownloadManagerWrapperBinding +import com.gh.gamecenter.history.IBatchDelete +import com.gh.gamecenter.history.ManageOption + +class VDownloadManagerWrapperFragment: BaseLazyTabFragment() { + + private var mLastPosition = 0 + private var mManageMenu: MenuItem? = null + private val mViewModel: VDownloadManagerWrapperViewModel by lazy { viewModelProvider() } + private val mBinding by lazy { FragmentVdownloadManagerWrapperBinding.inflate(layoutInflater) } + + override fun getLayoutId() = 0 + override fun getInflatedLayout() = mBinding.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setNavigationTitle("畅玩游戏管理") + + initMenu(R.menu.menu_manage) + mBinding.bottomContainer.setOnClickListener { + VHelper.startVSpaceSquare(requireContext()) + } + + arguments?.getInt(EntranceConsts.KEY_POSITION)?.let { + mViewPager.post { mViewPager.currentItem = it } + } + + mManageMenu = getItemMenu(R.id.layout_menu_manage) + mManageMenu?.actionView?.setOnClickListener { + when (mViewModel.currentOptionLiveData.value) { + ManageOption.OPTION_MANAGER -> { + mViewModel.currentOptionLiveData.value = ManageOption.OPTION_CANCEL_SELECT + } + ManageOption.OPTION_CANCEL_SELECT -> { + mViewModel.currentOptionLiveData.value = ManageOption.OPTION_MANAGER + } + else -> { + // do nothing + } + } + changeOption() + } + changeMenuTextByOption() + } + + override fun initFragmentList(fragments: MutableList) { + fragments.add(VDownloadManagerFragment().with(Bundle().apply { + putString(VDownloadManagerViewModel.TYPE, VDownloadManagerViewModel.TYPE_DOWNLOADED) + putBoolean(EntranceConsts.KEY_IS_FROM_HOME_RECENT, arguments?.getBoolean(EntranceConsts.KEY_IS_FROM_HOME_RECENT) ?: false) + })) + fragments.add(VDownloadManagerFragment().with(Bundle().apply { + putString(VDownloadManagerViewModel.TYPE, VDownloadManagerViewModel.TYPE_DOWNLOADING) + })) + } + + override fun initTabTitleList(tabTitleList: MutableList) { + tabTitleList.add("在玩") + tabTitleList.add("下载") + } + + fun onNewIntent(intent: Intent?) { + childFragmentManager.fragments.forEach { + if (it.isAdded && it is VDownloadManagerFragment) { + it.onLoadRefresh() + } + } + + intent?.getIntExtra(EntranceConsts.KEY_POSITION, 0)?.let { + mViewPager.post { mViewPager.currentItem = it } + } + } + + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + mViewModel.currentOptionLiveData.value = ManageOption.OPTION_MANAGER + changeOption() + mLastPosition = position + } + + private fun changeMenuTextByOption() { + (mManageMenu?.actionView as? TextView)?.apply { + text = when (mViewModel.currentOptionLiveData.value) { + ManageOption.OPTION_MANAGER -> "管理" + ManageOption.OPTION_CANCEL_SELECT -> "取消" + else -> "" + } + } + } + + fun changeOption() { + val fragments = childFragmentManager.fragments + mViewModel.currentOptionLiveData.value?.let { + fragments.forEachIndexed { index, fragment -> + if (fragment is IBatchDelete) { + if (index == mViewPager.currentItem) { + fragment.changeOption(it) + } else if (index == mLastPosition) { + fragment.changeOption(ManageOption.OPTION_MANAGER) + } + } + } + } + changeMenuTextByOption() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperViewModel.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperViewModel.kt new file mode 100644 index 0000000000..c97dffdb0b --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperViewModel.kt @@ -0,0 +1,19 @@ +package com.gh.vspace + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.history.ManageOption + +class VDownloadManagerWrapperViewModel : ViewModel() { + + init { + runOnIoThread { + // 进入的时候再连接一遍服务,尽量保证能用 + VHelper.connectService(true) + } + } + + var currentOptionLiveData = MutableLiveData(ManageOption.OPTION_MANAGER) + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VFeedbackDialogFragment.kt b/app/src/main/java/com/gh/vspace/VFeedbackDialogFragment.kt new file mode 100644 index 0000000000..8912c3b0fc --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VFeedbackDialogFragment.kt @@ -0,0 +1,224 @@ +package com.gh.vspace + +import android.os.Build +import android.os.Bundle +import android.os.Parcelable +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.widget.doOnTextChanged +import androidx.lifecycle.ViewModel +import com.gh.common.util.NewFlatLogUtils +import com.gh.common.util.PackageUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.BaseDialogFragment +import com.gh.gamecenter.common.exposure.meta.MetaUtil +import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.databinding.DialogVgameFeedbackBinding +import com.gh.gamecenter.databinding.ItemVfeedbackOptionBinding +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import com.lightgame.utils.Utils +import com.lightgame.utils.toast.ToastHelper +import com.walkud.rom.checker.RomIdentifier +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import kotlinx.parcelize.Parcelize +import okhttp3.ResponseBody +import org.json.JSONArray + +class VFeedbackDialogFragment : BaseDialogFragment() { + + private var mGame: GameEntity? = null + private var mTagList: List? = null + private val mBinding by lazy { DialogVgameFeedbackBinding.inflate(layoutInflater) } + private val mViewModel: VFeedbackViewModel by lazy { viewModelProvider() } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return mBinding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + mTagList = arguments?.getParcelableArrayList(KEY_TAG_LIST) + + mGame = arguments?.getParcelable(KEY_GAME) + + if (mGame == null) { + Utils.log("遇到内部异常") + dismissAllowingStateLoss() + } + + mGame!!.let { + mBinding.gameIconIv.displayGameIcon(it) + mBinding.gameNameTv.text = it.name + } + mBinding.closeIv.enlargeTouchArea() + mBinding.closeIv.setOnClickListener { + dismissAllowingStateLoss() + } + mBinding.feedbackEt.doOnTextChanged { _, _, _, count -> + if (count == 500) { + ToastHelper.showToast(requireContext(), "最多输入500个字") + } + + updateSubmitButton() + } + mBinding.dontShowAgainTv.enlargeTouchArea() + mBinding.dontShowAgainTv.setOnClickListener { + VFeedbackSuppressedSimpleDao().add(mGame?.getUniquePackageName() ?: "") + dismissAllowingStateLoss() + } + mBinding.submitTv.setOnClickListener { + mViewModel.postFeedback(mGame!!.id, mBinding.feedbackEt.text.toString(), getSelectedTagString()) + NewFlatLogUtils.logHaloFunGameExitDialogSubmitClick(mBinding.feedbackEt.text.toString(), JSONArray(getSelectedTagString())) + dismissAllowingStateLoss() + } + checkLabel() + + updateSubmitButton() + } + + private fun getSelectedTagString(): ArrayList { + val selectedTagList = arrayListOf() + mTagList?.forEachIndexed { index, tag -> + if (tag.checked) { + selectedTagList.add(mTagList!![index].tagName) + } + } + return selectedTagList + } + + override fun onStart() { + super.onStart() + dialog?.window?.attributes?.gravity = Gravity.BOTTOM + val width = DisplayUtils.getScreenWidth() + val height = ViewGroup.LayoutParams.WRAP_CONTENT + dialog?.window?.setLayout(width, height) + dialog?.setCanceledOnTouchOutside(true) + } + + private fun addChildToFlexboxLayout(feedbackTag: FeedbackTag) { + val binding = ItemVfeedbackOptionBinding.inflate(layoutInflater) + binding.feedbackTagTv.width = (DisplayUtils.getScreenWidth() - 58F.dip2px()) / 2 + binding.feedbackTagTv.text = feedbackTag.tagName + if (feedbackTag.checked) { + binding.feedbackTagTv.setTextColor(R.color.theme_font.toColor(requireContext())) + binding.feedbackTagTv.setBackgroundResource(R.drawable.bg_vfeedback_label_selected) + } else { + binding.feedbackTagTv.setTextColor(R.color.text_subtitle.toColor(requireContext())) + binding.feedbackTagTv.setBackgroundResource(R.drawable.bg_vfeedback_label) + } + binding.root.setOnClickListener { + feedbackTag.checked = !feedbackTag.checked + checkLabel() + } + mBinding.feedbackFlexbox.addView(binding.root) + } + + private fun updateSubmitButton() { + val isTagSelected = mTagList?.any { it.checked } ?: false + + if (mBinding.feedbackEt.text.isNotEmpty() || isTagSelected) { + mBinding.submitTv.isEnabled = true + mBinding.submitTv.alpha = 1F + } else { + mBinding.submitTv.isEnabled = false + mBinding.submitTv.alpha = 0.6F + } + } + + private fun checkLabel() { + mBinding.feedbackFlexbox.removeAllViews() + for (tag in mTagList!!) { + addChildToFlexboxLayout(tag) + } + updateSubmitButton() + } + + companion object { + private const val KEY_GAME = "game" + private const val KEY_TAG_LIST = "tag_list" + + @JvmStatic + fun show( + activity: AppCompatActivity, + game: GameEntity? + ) { + NewFlatLogUtils.logHaloFunEvent("halo_fun_game_exit_dialog_show") + VFeedbackDialogFragment().apply { + arguments = Bundle().apply { + putParcelable(KEY_GAME, game) + putParcelableArrayList( + KEY_TAG_LIST, + arrayListOf( + FeedbackTag(tagName = "游戏卡顿", tagType = "game_lag"), + FeedbackTag(tagName = "游戏闪退", tagType = "game_crash"), + FeedbackTag(tagName = "游戏不好玩", tagType = "game_not_fun"), + FeedbackTag(tagName = "广告太多了", tagType = "to_many_ad"), + FeedbackTag(tagName = "游戏需要更新", tagType = "game_need_update"), + FeedbackTag(tagName = "需要实名认证", tagType = "need_name_verification"), + FeedbackTag(tagName = "需要登录", tagType = "need_login"), + FeedbackTag(tagName = "登录不了", tagType = "can_not_login") + ) + ) + } + }.show(activity.supportFragmentManager, VFeedbackDialogFragment::class.java.simpleName) + } + } + + @Parcelize + data class FeedbackTag( + val tagName: String = "", + val tagType: String = "", // 对应后台的 key + var checked: Boolean = false + ) : Parcelable + + class VFeedbackViewModel : ViewModel() { + + fun postFeedback(gameId: String, message: String, tags: ArrayList) { + val json = json { + "from" to "" + "ghversion" to PackageUtils.getGhVersionName() + "channel" to HaloApp.getInstance().channel + "type" to Build.MODEL + "sdk" to Build.VERSION.SDK_INT.toString() + "version" to Build.VERSION.RELEASE + "source" to HaloApp.getInstance().application.getString(R.string.app_name) + "jnfj" to MetaUtil.getBase64EncodedIMEI() + "manufacturer" to Build.MANUFACTURER + "rom" to RomIdentifier.getRom().name + " " + RomIdentifier.getRom().versionName + + "suggestion_type" to "畅玩问题" + "game_id" to gameId + "message" to message + "tags" to JSONArray(tags) + } + + val requestBody = json.toRequestBody() + RetrofitManager.getInstance().api.postSuggestion(requestBody) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { + super.onResponse(response) + ToastUtils.showToast("感谢您的反馈信息,我们将尽快处理~") + } + }) + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VFeedbackSuppressedSimpleDao.kt b/app/src/main/java/com/gh/vspace/VFeedbackSuppressedSimpleDao.kt new file mode 100644 index 0000000000..c175e20618 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VFeedbackSuppressedSimpleDao.kt @@ -0,0 +1,11 @@ +package com.gh.vspace + +import com.gh.gamecenter.common.base.BaseSimpleDao + +class VFeedbackSuppressedSimpleDao : BaseSimpleDao() { + + override fun getSPKey(): String { + return "v_feedback_suppressed" + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VGameItemData.kt b/app/src/main/java/com/gh/vspace/VGameItemData.kt new file mode 100644 index 0000000000..2b97be3b77 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VGameItemData.kt @@ -0,0 +1,77 @@ +package com.gh.vspace + +import com.gh.gamecenter.manager.PackagesManager +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus + +data class VGameItemData( + var downloadEntity: DownloadEntity, + + var shouldShowMask: Boolean = false, + var shouldShowProgressBar: Boolean = false, + var shouldShowUpdate: Boolean = false, + var shouldShowControlTv: Boolean = false, + var shouldShowDot: Boolean = false, + var controlText: String = "继续" +) { + fun refresh() { + if (downloadEntity.status == DownloadStatus.done + && VHelper.getLastPlayedTime(downloadEntity) == 0L + ) { + shouldShowDot = true + } + + if (PackagesManager.isCanUpdate( + downloadEntity.gameId, + downloadEntity.packageName + ) + ) { + shouldShowUpdate = true + } + + when (downloadEntity.status) { + DownloadStatus.done -> { + controlText = "" + } + DownloadStatus.pause, + DownloadStatus.subscribe, + DownloadStatus.waiting -> { + shouldShowMask = true + shouldShowProgressBar = true + shouldShowControlTv = true + + controlText = if (downloadEntity.status == DownloadStatus.waiting) { + "等待中" + } else { + "继续" + } + } + DownloadStatus.downloading -> { + shouldShowMask = true + shouldShowProgressBar = true + controlText = "" + } + + DownloadStatus.cancel, + DownloadStatus.timeout, + DownloadStatus.neterror, + DownloadStatus.hijack, + DownloadStatus.uncertificated, + DownloadStatus.unqualified, + DownloadStatus.notfound, + DownloadStatus.unavailable, + DownloadStatus.overflow -> { + controlText = "重试" + } + else -> { + // do nothing + } + } + } + + companion object { + fun from(downloadEntity: DownloadEntity): VGameItemData { + return VGameItemData(downloadEntity).also { it.refresh() } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VHelper.kt b/app/src/main/java/com/gh/vspace/VHelper.kt new file mode 100644 index 0000000000..1157de9359 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VHelper.kt @@ -0,0 +1,871 @@ +package com.gh.vspace + +import android.Manifest +import android.annotation.SuppressLint +import android.content.ContentValues +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.text.TextUtils +import android.view.View +import androidx.annotation.WorkerThread +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.gh.common.constant.Config +import com.gh.common.exposure.ExposureUtils +import com.gh.common.history.HistoryHelper +import com.gh.common.util.DataCollectionUtils +import com.gh.common.util.DirectUtils +import com.gh.common.util.NewFlatLogUtils +import com.gh.common.util.PackageUtils +import com.gh.download.DownloadManager +import com.gh.download.PackageObserver +import com.gh.gamecenter.R +import com.gh.gamecenter.SplashScreenActivity +import com.gh.gamecenter.common.BuildConfig +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.utils.EmptyCallback +import com.gh.gamecenter.core.utils.GsonUtils +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.entity.* +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.eventbus.EBReuse +import com.gh.gamecenter.manager.PackagesManager +import com.gh.gamecenter.packagehelper.PackageRepository +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.vspace.db.VGameDatabase +import com.gh.vspace.db.VGameEntity +import com.halo.assistant.HaloApp +import com.lg.vspace.VirtualAppManager +import com.lg.vspace.remote.BinderPool +import com.lg.vspace.remote.listener.RemoteConnectListener +import com.lg.vspace.remote.model.AppInstallerInfo +import com.lightgame.download.DownloadEntity +import com.lightgame.utils.AppManager +import com.lightgame.utils.Utils +import io.reactivex.schedulers.Schedulers +import org.greenrobot.eventbus.EventBus +import java.io.File +import java.util.* + +object VHelper { + + private const val LOG_TAG = "VSPACE" + + private const val KEY_LAST_PLAYED_TIME = "last_played_time" + private const val KEY_LAST_ALERT_UPDATE_URL = "last_alert_update_url" + private const val KEY_TOTAL_PLAYED_TIME = "total_played_time" + + const val DEFAULT_VSPACE_PACKAGENAME = "com.lg.vspace" + const val PLATFORM_V = "畅玩版" + + private val mDelegateManager by lazy { VirtualAppManager.get() } + private var mInstalledInfoList: List = arrayListOf() + private val mInstallationLiveData by lazy { MutableLiveData() } + private var mLastSuccessfullyLaunchedGame: Pair? = null + + private val mVGameDao by lazy { + VGameDatabase.buildDatabase(HaloApp.getInstance().application).vGameDao() + } + private var mVGameSnapshotList = arrayListOf() + + private var mUpdateEntity: AppEntity? = null + + private var mIsInitialized = false // 是否已初始化 + + // 当前正卡在安装中的 VA 游戏,避免重复调用安装 + private val mInstallingVaPathSet by lazy { Collections.synchronizedSet(hashSetOf()) } + + val vGameLiveData by lazy { mVGameDao.getAllLiveData() } + + private val mPackageObserver by lazy { + PackageObserver.PackageChangeListener { + val vaConfig = Config.getVSettingEntity()?.va ?: return@PackageChangeListener + val isVSpace = + it.packageName == vaConfig.arch32?.packageName || it.packageName == vaConfig.arch64?.packageName + + if (!isVSpace) return@PackageChangeListener + + if (it.type == "安装") { + // 即时调用大概率调不起来,我也不知道为什么,只能延迟大法了 + AppExecutor.uiExecutor.executeWithDelay({ + connectService() + }, 500) + } else if (it.type == "卸载") { + // 执行卸载逻辑 + } + } + } + + @SuppressLint("CheckResult") + @JvmStatic + fun init(context: Context) { + if (isVGameOn() && !mIsInitialized) { + mIsInitialized = true + + val config = Config.getVSettingEntity()?.va + if (config?.arch64 != null + && PackageUtils.isInstalled(context, config.arch64.packageName)) { + connectService(true) + + // 检查畅玩助手组件更新 + checkVSpaceUpdate(config.arch64) + } + PackageObserver.registerPackageChangeChangeListener(mPackageObserver) + + vGameLiveData.observeForever { + mVGameSnapshotList = ArrayList(it) + } + } + } + + /** + * 连接服务 + */ + fun connectService(shouldCheckUpdate: Boolean = false, callbackClosure: (() -> Unit)? = null) { + mDelegateManager.connectService(object : RemoteConnectListener { + override fun onServiceConnectionSuccessed() { + Utils.log(LOG_TAG, "V 服务连接成功") + + mInstalledInfoList = getInstalledList() + + if (shouldCheckUpdate) { + checkUpdateViaPackageRepository() + } + + callbackClosure?.invoke() + } + + override fun onServiceConnectionFailed(failCode: Int) { + if (failCode == BinderPool.CONNECT_STATE_NOT_INSTALLED) { + Utils.log(LOG_TAG, "未安装畅玩助手") + } + Utils.log(LOG_TAG, "V 服务连接失败") + } + }) + } + + /** + * 获取数据库里所有的畅玩游戏 + * + * (查询数据库,请在工作线程调用) + */ + @WorkerThread + fun getAllVGame(): List { + return mVGameDao.getAll() + } + + fun getAllVGameSnapshots(): ArrayList { + return ArrayList(mVGameSnapshotList) + } + + /** + * 获取内存里的畅玩游戏快照 + * + * @param gameId 游戏 ID + * @param packageName 需要获取的畅玩游戏的包名 + * + */ + @JvmStatic + fun getVGameSnapshot(gameId: String?, packageName: String?): VGameEntity? { + return mVGameSnapshotList.find { + it.packageName == packageName && (gameId == null || it.downloadEntity.gameId == gameId) + } + } + + private fun updateInstalledList() { + mInstalledInfoList = try { + mDelegateManager.installedGamesInfo + } catch (e: RuntimeException) { + Utils.log(LOG_TAG, "获取安装列表信息失败") + mInstalledInfoList + } + } + + /** + * 是否在 V 空间里已安装此包名游戏 + */ + @JvmStatic + fun isInstalled(packageName: String?): Boolean { + Utils.log(LOG_TAG, "检查 $packageName 是否在已安装列表 $mInstalledInfoList 里") + return mInstalledInfoList.any { it.packageName == packageName } + } + + /** + * 在执行 callback 前先检查组件是否已安装,是否可下载 + */ + @JvmStatic + fun validateVSpaceBeforeAction( + context: Context, + gameEntity: GameEntity?, + requireStoragePermission: Boolean, + callback: EmptyCallback + ) { + validateVSpaceBeforeAction(context, gameEntity, requireStoragePermission) { + callback.onCallback() + } + } + + /**** + * 启动成功,五秒内退出才显示反馈弹框 + */ + fun showFeedbackDialogIfLastSuccessfulLaunchedGameExitUnexpectedly(activity: AppCompatActivity) { + if (isVGameOn()) { + val time = mLastSuccessfullyLaunchedGame?.first + val packageName = mLastSuccessfullyLaunchedGame?.second + if (activity !is SplashScreenActivity + && time != null + && packageName != null + && System.currentTimeMillis() - time < 5000 + && !VFeedbackSuppressedSimpleDao().contains(packageName) + ) { + getVGameSnapshot(null, packageName)?.let { + VFeedbackDialogFragment.show(activity, toGameEntity(it.downloadEntity)) + mLastSuccessfullyLaunchedGame = null + } + } + } + } + + /** + * 获取游戏占用的空间 + */ + fun getAppOccupiedSpace(packageName: String): Long { + return try { + mDelegateManager.getAppOccupiedSpace(packageName) + } catch (e: Exception) { + e.printStackTrace() + 0 + } + } + + /** + * 在执行 callback 前先检查组件是否已安装,是否可下载 + */ + fun validateVSpaceBeforeAction( + context: Context, + gameEntity: GameEntity?, + requireStoragePermission: Boolean, + callback: () -> Unit + ) { + // 仅下载类型为畅玩的类型才执行判断 + if (gameEntity == null || gameEntity.isVGame()) { + if (showDialogIfVSpaceIsNeeded(context)) { + return + } + + val vaConfig64 = Config.getVSettingEntity()?.va?.arch64 + val installedSpaceVersionCode = + PackageUtils.getVersionCodeByPackageName(vaConfig64?.packageName) + + // 检查更新 + val containsUpdate = shouldShowVSpaceUpdate(mUpdateEntity, installedSpaceVersionCode) + + if (containsUpdate) { + val dialogType = if (mUpdateEntity!!.isAlertEveryTime()) "强制更新" else "提示更新" + NewFlatLogUtils.logHaloFunEvent("halo_fun_update_dialog_show") + SPUtils.setString( + KEY_LAST_ALERT_UPDATE_URL, + mUpdateEntity!!.url + mUpdateEntity!!.alert + ) + DialogHelper.showDialog( + context = context, + title = "服务工具更新提示", + content = mUpdateEntity!!.content.toString(), + cancelText = "立即更新", + confirmText = "继续游戏", + cancelClickCallback = { + NewFlatLogUtils.logHaloFunUpdateDialogClick(dialogType, "立即更新") + VSpaceDialogFragment.showDownloadDialog( + context, + getVSpaceDownloadEntity(true), + autoDownload = true, + isUpdate = true + ) + }, + confirmClickCallback = { + NewFlatLogUtils.logHaloFunUpdateDialogClick(dialogType, "继续游戏") + callback.invoke() + }, + extraConfig = DialogHelper.Config(centerTitle = true), + uiModificationCallback = { + if (mUpdateEntity!!.isAlertEveryTime()) { + it.confirmTv.visibility = View.GONE + it.cancelTv.setTextColor(R.color.theme_font.toColor(context)) + } + + it.confirmTv.setTextColor(R.color.text_subtitle.toColor(context)) + } + ) + return + } + + if (requireStoragePermission) { + checkStoragePermissionBeforeAction(context) { + callback.invoke() + } + } else { + callback.invoke() + } + } else { + callback.invoke() + } + } + + /** + * 获取已安装的包名列表 + */ + private fun getInstalledPackageList(): ArrayList { + val installedList = mInstalledInfoList + val installedPackageList = arrayListOf() + + for (info in installedList) { + info.packageName?.let { + installedPackageList.add(info.packageName) + } + } + + Utils.log(LOG_TAG, "已安装包名列表$installedPackageList") + + return installedPackageList + } + + /** + * 获取已安装列表信息 (每次调用都会生成一份新的副本,放心使用) + */ + fun getInstalledList(): ArrayList { + var list: List = arrayListOf() + + try { + list = mDelegateManager.installedGamesInfo + + Utils.log(LOG_TAG, "已安装应用数量${list.size}" + list) + } catch (e: Exception) { + Utils.log(LOG_TAG, "获取已安装应用数量异常 ${e.localizedMessage}") + } + + return ArrayList(list) + } + + /** + * 检查存储权限,若已授予直接执行后续逻辑 + */ + private fun checkStoragePermissionBeforeAction(context: Context, callback: () -> Unit) { + val checkClosure: () -> Unit = { + try { + val isStoragePermissionGranted = + mDelegateManager.checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE) + if (isStoragePermissionGranted) { + callback.invoke() + } else { + NewFlatLogUtils.logHaloFunEvent("halo_fun_access_dialog_show") + context.startActivity(mDelegateManager.requestPermissionIntent) + } + } catch (e: RuntimeException) { + e.printStackTrace() + Utils.log(LOG_TAG, "检查存储权限失败 ${e.localizedMessage}") + } + } + + if (mDelegateManager.isConnectAidlInterface) { + checkClosure.invoke() + } else { + connectService(false, checkClosure) + } + } + + /** + * 安装新应用 + */ + fun install( + context: Context, + downloadEntity: DownloadEntity, + isManualInstall: Boolean = false + ) { + Utils.log(LOG_TAG, "尝试安装新应用 ${downloadEntity.path}") + + if (showDialogIfVSpaceIsNeeded(context)) return + + val installClosure: () -> Unit = { + checkStoragePermissionBeforeAction(context) { + if (isManualInstall) { + val gameEntity = toGameEntity(downloadEntity) + context.startActivity( + VSpaceLoadingActivity.getIntent( + context, + gameEntity, + true + ) + ) + } + + // 正在安装中,忽略重复调用 + if (mInstallingVaPathSet.contains(downloadEntity.path)) return@checkStoragePermissionBeforeAction + + // 安装过程会比较漫长,所以得放在工作线程运行 + runOnIoThread { + try { + mInstallingVaPathSet.add(downloadEntity.path) + + val result = VirtualAppManager.get().installGame(downloadEntity.path) + + // 去掉更新标记 + downloadEntity.isUpdate = false + mVGameDao.insert(VGameEntity.from(downloadEntity)) + + if (result.status == 0) { + updateInstalledList() + PackageObserver.onPackageChanged( + EBPackage( + "安装", + result.packageName, + "unknown" + ).also { it.gameId = downloadEntity.gameId } + ) + insertInstalledGameToProvider(downloadEntity) + } else { + ToastUtils.toast("安装出现异常, ${result.status}") + } + + Utils.log(LOG_TAG, "安装新应用结果 -> " + result.status) + + mInstallingVaPathSet.remove(downloadEntity.path) + + mInstallationLiveData.postValue(result.packageName) + + VBackupHelper.backupDBToExternalStorage(HaloApp.getInstance()) + } catch (e: Exception) { + ToastUtils.toast(e.localizedMessage ?: "") + } + } + } + } + + if (mDelegateManager.isConnectAidlInterface) { + installClosure.invoke() + } else { + connectService(false, installClosure) + } + } + + private fun insertInstalledGameToProvider(downloadEntity: DownloadEntity) { + val values = ContentValues() + val packageName = downloadEntity.packageName + if (packageName.isEmpty()) { + return + } + //主键 + values.put("package_name", packageName) + values.put("url", downloadEntity.url) + values.put("name", downloadEntity.name) + values.put("size", downloadEntity.size) + values.put("meta", GsonUtils.toJson(downloadEntity.meta)) + val type = if (PackageFlavorHelper.IS_TEST_FLAVOR) "test_flavor" else "" + values.put("type", type) + val uri = Uri.parse("content://com.lg.core.provider/download_game") + HaloApp.getInstance().contentResolver.insert(uri, values) + } + + /** + * 安装或启动应用 + */ + @JvmStatic + fun installOrLaunch(context: Context, packageName: String) { + Utils.log(LOG_TAG, "检测是需要安装还是启动 $packageName") + + validateVSpaceBeforeAction(context, null, false) { + if (isInstalled(packageName)) { + launch(context, packageName) + } else { + checkStoragePermissionBeforeAction(context) { + // 这里重新检查一遍是否已安装,因为有可能因为上一次更新列表失败而出现重新下载的情况 + if (isInstalled(packageName)) { + launch(context, packageName) + return@checkStoragePermissionBeforeAction + } + + // 检查下载管理是否有下载实体,有实体表明未安装成功 + val downloadEntity = DownloadManager.getInstance() + .getDownloadEntitySnapshotByPackageName(packageName) + ?: getDownloadEntitySnapshot(gameId = null, packageName = packageName) + + if (downloadEntity != null) { + val downloadedFile = File(downloadEntity.path) + if (downloadedFile.exists() + && downloadedFile.length() == downloadEntity.size + ) { + install(context, downloadEntity, true) + } else { + // 重新下载 + updateOrReDownload(downloadEntity, null) + } + } else { + ToastUtils.toast("该游戏已损坏,请重新下载") + } + } + } + } + } + + /** + * 启动应用 + */ + fun launch(context: Context, packageName: String) { + Utils.log(LOG_TAG, "打开应用 $packageName") + + try { + val intent = mDelegateManager.getStartGameIntent(packageName) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + + mLastSuccessfullyLaunchedGame = Pair(System.currentTimeMillis(), packageName) + + runOnIoThread { + updateVGamePlayedTime(packageName, System.currentTimeMillis(), getPlayedTime(packageName)) + VBackupHelper.backupDBToExternalStorage(HaloApp.getInstance()) + } + } catch (e: Exception) { + ToastUtils.toast(e.localizedMessage ?: "") + } + } + + /** + * 卸载应用 + */ + fun uninstall(packageName: String?) { + Utils.log(LOG_TAG, "卸载游戏 $packageName") + + if (packageName.isNullOrBlank()) return + + mVGameDao.delete(packageName) + + if (mVGameDao.getAll().isEmpty()) { + VBackupHelper.removeAllDatabase(HaloApp.getInstance()) + } else { + VBackupHelper.backupDBToExternalStorage(HaloApp.getInstance()) + } + + val uninstallClosure: () -> Unit = { + try { + val result = VirtualAppManager.get().uninstallGame(packageName) + if (result) { + updateInstalledList() + } + + Utils.log(LOG_TAG, "卸载应用结果 -> $result") + } catch (e: Exception) { + ToastUtils.toast(e.localizedMessage ?: "") + } + } + + if (mDelegateManager.isConnectAidlInterface) { + uninstallClosure.invoke() + } else { + connectService(false, uninstallClosure) + } + } + + /** + * 获取游戏最后打开的时间 + * @return 最后打开的时间戳 + */ + fun getLastPlayedTime(downloadEntity: DownloadEntity): Long { + val lastPlayedTimeString = downloadEntity.getMetaExtra(KEY_LAST_PLAYED_TIME) + return if (TextUtils.isEmpty(lastPlayedTimeString)) { + 0 + } else { + lastPlayedTimeString.toLong() + } + } + + /** + * 获取游戏的游玩时长 + */ + fun getTotalPlayedTime(packageName: String): Long { + return mInstalledInfoList.firstOrNull { + it.packageName == packageName + }?.appTotalPlayTime ?: 0L + } + + /** + * 根据游戏 ID 和包名获取下载快照 + */ + @JvmStatic + fun getDownloadEntitySnapshot(gameId: String?, packageName: String?): DownloadEntity? { + return getVGameSnapshot(gameId, packageName)?.downloadEntity + } + + /** + * 更新数据库重中的畅玩游戏 + */ + private fun updateVGamePlayedTime(packageName: String, lastPlayedTime: Long, totalPlayedTime: Long) { + getVGameSnapshot(null, packageName)?.let { + it.downloadEntity.addMetaExtra(KEY_LAST_PLAYED_TIME, totalPlayedTime.toString()) + it.downloadEntity.addMetaExtra(KEY_TOTAL_PLAYED_TIME, totalPlayedTime.toString()) + mVGameDao.insert(it) + } + + // 更新首页排序 + EventBus.getDefault().post(EBReuse("vgame")) + } + + fun getInstallationLiveData(): LiveData { + return mInstallationLiveData + } + + fun showFloatingWindow(packageName: String) { + val vDownloadList = DownloadManager.getInstance().allVDownloadTaskSnapshots + vDownloadList.firstOrNull { + it.packageName == packageName + }?.let { + val topActivity = AppManager.getInstance().currentActivity() ?: return + if (topActivity.isFinishing || topActivity is VSpaceLoadingActivity) return + + VLoadCompleteWindowHelper.showFloatingWindow(topActivity, toGameEntity(it)) + } + } + + /** + * 畅玩空间是否已安装 + * 如果已安装或配置为空返回 true + * 未安装的情况下会弹弹窗 + */ + private fun showDialogIfVSpaceIsNeeded(context: Context): Boolean { + Utils.log(LOG_TAG, "检测是否已安装畅玩空间") + + val vaConfig = Config.getVSettingEntity()?.va + + if (vaConfig == null) { + ToastUtils.toast("畅玩空间暂未上线") + return true + } + + // TODO 检测 32 位 + if (!PackageUtils.isInstalled(context, vaConfig.arch64?.packageName)) { + VSpaceDialogFragment.showDownloadDialog(context, getVSpaceDownloadEntity(false)) + Utils.log(LOG_TAG, "显示下载畅玩空间弹窗") + return true + } + + return false + } + + private fun getVSpaceDownloadEntity(forUpdate: Boolean): AppEntity { + if (!forUpdate) { + val appEntity = AppEntity() + val vaConfig64 = Config.getVSettingEntity()?.va?.arch64 + appEntity.versionCode = vaConfig64?.versionCode ?: 0 + appEntity.version = vaConfig64?.versionName + appEntity.url = vaConfig64?.url + + return appEntity + } else { + return mUpdateEntity!! + } + } + + /** + * 更新或重下载 + */ + @JvmStatic + fun updateOrReDownload(gameEntity: GameEntity) { + PackagesManager.getUpdateList().firstOrNull { it.id == gameEntity.id } + ?.let { updateEntity -> + getVGameSnapshot(gameEntity.id, updateEntity.packageName) + ?.let { vGame -> + updateOrReDownload(vGame.downloadEntity, updateEntity) + } + } + } + + /** + * 更新或重下载 + * + * @param originDownloadEntity 旧的下载实体 + * @param updateEntity 更新内容,当 updateEntity 为空时重新下载 + */ + fun updateOrReDownload( + originDownloadEntity: DownloadEntity, + updateEntity: GameUpdateEntity? = null + ) { + Utils.log(LOG_TAG, "更新应用${originDownloadEntity.packageName}") + + if (updateEntity != null) { + originDownloadEntity.url = getVUrl(updateEntity.url) + originDownloadEntity.gameId = updateEntity.id + originDownloadEntity.name = updateEntity.name + originDownloadEntity.eTag = updateEntity.etag + originDownloadEntity.icon = updateEntity.icon + originDownloadEntity.size = 0 + originDownloadEntity.platform = updateEntity.platform + originDownloadEntity.packageName = updateEntity.packageName + originDownloadEntity.versionName = updateEntity.version + originDownloadEntity.isUpdate = true + originDownloadEntity.addMetaExtra(Constants.RAW_GAME_ICON, updateEntity.rawIcon) + originDownloadEntity.addMetaExtra( + Constants.GAME_ICON_SUBSCRIPT, + updateEntity.iconSubscript + ) + originDownloadEntity.addMetaExtra(Constants.APK_MD5, updateEntity.md5) + + PackageRepository.removeUpdate(updateEntity.id, true) + } + + originDownloadEntity.progress = 0 + originDownloadEntity.finalRedirectedUrl = "" + originDownloadEntity.percent = 0.0 + + // 确定下载类型 + val downloadType = + if (updateEntity == null) ExposureUtils.DownloadType.FUN_DOWNLOAD else ExposureUtils.DownloadType.FUN_UPDATE + val gameEntity = GameEntity(originDownloadEntity.gameId, originDownloadEntity.name) + gameEntity.gameVersion = originDownloadEntity.versionName ?: "" + + val event = ExposureUtils.logADownloadExposureEvent( + gameEntity, + originDownloadEntity.platform, + null, + downloadType + ) + + originDownloadEntity.exposureTrace = GsonUtils.toJson(event) + + HistoryHelper.insertGameEntity(gameEntity) + DownloadManager.getInstance().add(originDownloadEntity) + + // 收集下载数据 + DataCollectionUtils.uploadDownload(HaloApp.getInstance(), originDownloadEntity, "开始") + } + + fun toGameEntity(downloadEntity: DownloadEntity): GameEntity { + val lastPlayedTimeString = downloadEntity.getMetaExtra(KEY_LAST_PLAYED_TIME) + + return GameEntity(id = downloadEntity.gameId, name = downloadEntity.name).apply { + setApk( + arrayListOf( + ApkEntity( + packageName = downloadEntity.packageName, + url = downloadEntity.url, + platform = downloadEntity.platform + ) + ) + ) + icon = downloadEntity.icon + rawIcon = downloadEntity.getMetaExtra(Constants.RAW_GAME_ICON) + iconSubscript = downloadEntity.getMetaExtra(Constants.GAME_ICON_SUBSCRIPT) + lastPlayedTime = if (lastPlayedTimeString == "") 0L else lastPlayedTimeString.toLong() + playedTime = getPlayedTime(downloadEntity.packageName, downloadEntity) + downloadStatus = "smooth" + setEntryMap(DownloadManager.getInstance().getEntryMap(name)) + } + } + + private fun getPlayedTime(packageName: String, downloadEntity: DownloadEntity? = null): Long { + var playedTime = + mInstalledInfoList.firstOrNull { it.packageName == packageName }?.appTotalPlayTime ?: 0L + + val cachedPlayedTimeString = downloadEntity?.getMetaExtra(KEY_TOTAL_PLAYED_TIME) + if (playedTime == 0L && !cachedPlayedTimeString.isNullOrEmpty()) { + playedTime = cachedPlayedTimeString.toLong() + } + + return playedTime + } + + @SuppressLint("CheckResult") + private fun checkVSpaceUpdate(config: VSetting.VaArch) { + val installedVersionName = + PackageUtils.getVersionNameByPackageName(config.packageName) + val installedVersionCode = + PackageUtils.getVersionCodeByPackageName(config.packageName) + + RetrofitManager.getInstance() + .vApi + .getPackageUpdate( + installedVersionName, + installedVersionCode, + config.packageName + ) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: AppEntity) { + mUpdateEntity = data + } + }) + } + + /** + * 通过 PackageRepository 检查更新 + */ + private fun checkUpdateViaPackageRepository() { + val rawInstalledPackageList = getInstalledPackageList() + val validInstalledPackageList = arrayListOf() + for (packageName in rawInstalledPackageList) { + if (getVGameSnapshot(null, packageName) != null) { + validInstalledPackageList.add(packageName) + } + } + PackageRepository.addInstalledGames(validInstalledPackageList, true) + } + + /** + * 是否需要显示畅玩助手更新 + */ + private fun shouldShowVSpaceUpdate( + updateEntity: AppEntity?, + installedSpaceVersionCode: Int + ): Boolean { + if (updateEntity == null) return false + + val hasNewerVersion = installedSpaceVersionCode < updateEntity.versionCode + + if (!hasNewerVersion) return false + + if (updateEntity.isAlertEveryTime()) return true + + if (updateEntity.isAlertOnceADay() + && SPUtils.getString(KEY_LAST_ALERT_UPDATE_URL) != (mUpdateEntity!!.url + mUpdateEntity!!.alert) + ) { + return true + } + + return false + } + + fun startVSpaceSquare(context: Context) { + val entity = SubjectRecommendEntity() + entity.link = "62fc8047b07e0c0bb63058c2" + entity.name = "畅玩广场" + entity.text = "畅玩广场" + DirectUtils.directToBlock(context, entity, "") + } + + @JvmStatic + fun recoverVDataIfPossible() { + VBackupHelper.recoverValidData(HaloApp.getInstance()) + } + + fun getVUrl(originUrl: String?): String { + if (originUrl?.endsWith("type=v") == false) { + return if (originUrl.contains("?")) { + "$originUrl&type=v" + } else { + "$originUrl?type=v" + } + } + return originUrl ?: "" + } + + @JvmStatic + fun isVGameOn() = BuildConfig.IS_VGAME_ON + && Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 + && Config.isVGameEnabled() + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VLoadCompleteWindowHelper.kt b/app/src/main/java/com/gh/vspace/VLoadCompleteWindowHelper.kt new file mode 100644 index 0000000000..dfe4b2f4ad --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VLoadCompleteWindowHelper.kt @@ -0,0 +1,268 @@ +package com.gh.vspace + +import android.animation.* +import android.app.Activity +import android.content.Context +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.view.animation.AccelerateDecelerateInterpolator +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.get +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.gh.common.util.NewFlatLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.core.utils.ClickUtils +import com.gh.gamecenter.core.utils.doOnEnd +import com.gh.gamecenter.databinding.ItemFloatGameLoadCompleteBinding +import com.gh.gamecenter.databinding.LayoutFloatLoadCompleteBinding +import com.gh.gamecenter.entity.GameEntity +import com.lightgame.adapter.BaseRecyclerAdapter +import com.lzf.easyfloat.EasyFloat +import com.lzf.easyfloat.enums.SidePattern +import com.lzf.easyfloat.interfaces.OnFloatAnimator +import java.lang.ref.WeakReference + +object VLoadCompleteWindowHelper { + private const val LOAD_COMPLETE_WINDOW = "load_complete_window" + private const val LOOP_TIME = 2000L + private const val LOOP_DURATION = 200L + private const val AUTO_DISMISS_TIME = 3000L + + private val mDismissTask = Runnable { EasyFloat.dismiss(LOAD_COMPLETE_WINDOW) } + + fun showFloatingWindow(activity: Activity, gameEntity: GameEntity) { + showFloatingWindow(activity, listOf(gameEntity)) + } + + fun showFloatingWindow(activity: Activity, gameEntityList: List = emptyList()) { + val floatingView = EasyFloat.getFloatView(LOAD_COMPLETE_WINDOW) + if (floatingView != null) { + if (!EasyFloat.isShow(LOAD_COMPLETE_WINDOW)) EasyFloat.show(LOAD_COMPLETE_WINDOW) + floatingView.findViewById(R.id.container)?.let { + val binding = LayoutFloatLoadCompleteBinding.bind(it) + binding.viewPager.run { + val adapter = adapter + var isLastItem = false + if (adapter is GameLoadCompleteAdapter) { + if (currentItem == adapter.gameEntityList.size - 1) isLastItem = true + adapter.submitList(gameEntityList) + } + removeCallbacks(mDismissTask) + if (isLastItem) { + val mLoopTask = object : Runnable { + private val reference: WeakReference = WeakReference(binding.viewPager) + + override fun run() { + val vp: ViewPager2? = reference.get() + if (vp != null) { + val count = binding.viewPager.adapter?.itemCount ?: 0 + if (count == 0) return + val next = vp.currentItem + 1 + if (next < count) { + vp.setCurrentItem(next, LOOP_DURATION) + vp.postDelayed(this, LOOP_TIME) + } else { + vp.postDelayed(mDismissTask, AUTO_DISMISS_TIME) + } + } + } + } + post(mLoopTask) + } + } + } + return + } + + EasyFloat.with(activity) + .setLayout(R.layout.layout_float_load_complete) + .setTag(LOAD_COMPLETE_WINDOW) + .setDragEnable(false) + .setMatchParent(widthMatch = true, heightMatch = false) + .setGravity(Gravity.BOTTOM, 0, (-62F).dip2px()) + .setAnimator(LoadCompleteFloatAnimator()) + .registerCallback { + createResult { _, _, view -> + view?.findViewById(R.id.container)?.let { + initFloatingView(it, activity, gameEntityList) + } + } + } + .show() + } + + private fun initFloatingView(view: ConstraintLayout, activity: Activity, entityList: List) { + val mBinding = LayoutFloatLoadCompleteBinding.bind(view) + val mAdapter = GameLoadCompleteAdapter(activity, entityList) + val mLoopTask = object : Runnable { + private val reference: WeakReference = WeakReference(mBinding.viewPager) + + override fun run() { + val vp: ViewPager2? = reference.get() + if (vp != null) { + val count = mBinding.viewPager.adapter?.itemCount ?: 0 + if (count == 0) return + val next = vp.currentItem + 1 + if (next < count) { + vp.setCurrentItem(next, LOOP_DURATION) + vp.postDelayed(this, LOOP_TIME) + } else { + vp.postDelayed(mDismissTask, AUTO_DISMISS_TIME) + } + } + } + } + mBinding.viewPager.run { + adapter = mAdapter + isUserInputEnabled = false + orientation = ViewPager2.ORIENTATION_VERTICAL + offscreenPageLimit = entityList.size + registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + super.onPageScrolled(position, positionOffset, positionOffsetPixels) + val layoutManager = (get(0) as RecyclerView).layoutManager as LinearLayoutManager + layoutManager.findViewByPosition(position)?.alpha = 1 - positionOffset + layoutManager.findViewByPosition(position + 1)?.alpha = positionOffset + } + }) + if (entityList.size > 1) { + postDelayed(mLoopTask, LOOP_TIME) + } else { + postDelayed(mDismissTask, AUTO_DISMISS_TIME) + } + } + mBinding.closeIv.setOnClickListener { + if (!ClickUtils.isFastDoubleClick(it.id)) { + NewFlatLogUtils.logHaloFunDownloadCompleteTipClick("关闭", mAdapter.gameEntityList[mBinding.viewPager.currentItem].id) + mBinding.viewPager.removeCallbacks(mLoopTask) + mBinding.viewPager.removeCallbacks(mDismissTask) + EasyFloat.dismiss(LOAD_COMPLETE_WINDOW) + } + } + mBinding.launchTv.setOnClickListener { + NewFlatLogUtils.logHaloFunDownloadCompleteTipClick("启动", mAdapter.gameEntityList[mBinding.viewPager.currentItem].id) + VHelper.installOrLaunch(activity, mAdapter.gameEntityList[mBinding.viewPager.currentItem].getApk()[0].packageName) + } + } +} + +class LoadCompleteFloatAnimator : OnFloatAnimator { + override fun enterAnim( + view: View, + params: WindowManager.LayoutParams, + windowManager: WindowManager, + sidePattern: SidePattern + ): Animator = getEnterAnimator(view) + + override fun exitAnim( + view: View, + params: WindowManager.LayoutParams, + windowManager: WindowManager, + sidePattern: SidePattern + ): Animator = getExitAnimator(view) + + private fun getEnterAnimator(view: View): Animator { + val alphaAnimator = ObjectAnimator.ofFloat(view, "alpha", 0F, 1F).apply { + duration = DURATION + interpolator = AccelerateDecelerateInterpolator() + } + val scaleXAnimator = ObjectAnimator.ofFloat(view, "scaleX", 0.8F, 1F).apply { + duration = DURATION + interpolator = AccelerateDecelerateInterpolator() + } + val scaleYAnimator = ObjectAnimator.ofFloat(view, "scaleY", 0.8F, 1F).apply { + duration = DURATION + interpolator = AccelerateDecelerateInterpolator() + } + view.pivotX = view.width / 2F + view.pivotY = view.height / 2F + return AnimatorSet().apply { playTogether(alphaAnimator, scaleXAnimator, scaleYAnimator) } + } + + private fun getExitAnimator(view: View): Animator { + val alphaAnimator = ObjectAnimator.ofFloat(view, "alpha", 1F, 0F).apply { + duration = DURATION + interpolator = AccelerateDecelerateInterpolator() + } + val scaleXAnimator = ObjectAnimator.ofFloat(view, "scaleX", 1F, 0.8F).apply { + duration = DURATION + interpolator = AccelerateDecelerateInterpolator() + } + val scaleYAnimator = ObjectAnimator.ofFloat(view, "scaleY", 1F, 0.8F).apply { + duration = DURATION + interpolator = AccelerateDecelerateInterpolator() + } + view.pivotX = view.width / 2F + view.pivotY = view.height / 2F + return AnimatorSet().apply { + playTogether(alphaAnimator, scaleXAnimator, scaleYAnimator) + doOnEnd { + view.visibility = View.GONE + } + } + } + + companion object { + const val DURATION = 200L + } +} + +class GameLoadCompleteAdapter(context: Context, var gameEntityList: List = emptyList()): BaseRecyclerAdapter(context) { + + fun submitList(entityList: List) { + if (entityList.isNotEmpty()) { + gameEntityList = arrayListOf().apply { + addAll(gameEntityList) + addAll(entityList) + } + notifyDataSetChanged() + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = GameLoadCompleteViewHolder(parent.toBinding()) + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is GameLoadCompleteViewHolder) { + val gameEntity = gameEntityList[position] + holder.binding.gameIconIv.displayGameIcon(gameEntity) + holder.binding.gameNameTv.text = gameEntity.name + } + } + + override fun getItemCount() = gameEntityList.size + + class GameLoadCompleteViewHolder(val binding: ItemFloatGameLoadCompleteBinding): RecyclerView.ViewHolder(binding.root) + +} + +private fun ViewPager2.setCurrentItem( + item: Int, + duration: Long, + interpolator: TimeInterpolator = AccelerateDecelerateInterpolator(), + pagePxHeight: Int = height +) { + val pxToDrag: Int = pagePxHeight * (item - currentItem) + val animator = ValueAnimator.ofInt(0, pxToDrag) + var previousValue = 0 + animator.addUpdateListener { valueAnimator -> + val currentValue = valueAnimator.animatedValue as Int + val currentPxToDrag = (currentValue - previousValue).toFloat() + fakeDragBy(-currentPxToDrag) + previousValue = currentValue + } + animator.addListener(object : Animator.AnimatorListener { + override fun onAnimationStart(animation: Animator?) { beginFakeDrag() } + override fun onAnimationEnd(animation: Animator?) { endFakeDrag() } + override fun onAnimationCancel(animation: Animator?) { } + override fun onAnimationRepeat(animation: Animator?) { } + }) + animator.interpolator = interpolator + animator.duration = duration + animator.start() +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VSpaceDialogFragment.kt b/app/src/main/java/com/gh/vspace/VSpaceDialogFragment.kt new file mode 100644 index 0000000000..d97d8dffe0 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VSpaceDialogFragment.kt @@ -0,0 +1,287 @@ +package com.gh.vspace + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Bundle +import android.text.SpannableStringBuilder +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.text.bold +import androidx.core.text.color +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.gh.common.exposure.ExposureUtils +import com.gh.common.exposure.ExposureUtils.DownloadType.DOWNLOAD +import com.gh.common.exposure.ExposureUtils.DownloadType.UPDATE +import com.gh.common.util.DataCollectionUtils +import com.gh.common.util.DirectUtils +import com.gh.common.util.NewFlatLogUtils +import com.gh.common.util.PackageInstaller +import com.gh.common.view.DownloadProgressBar +import com.gh.download.DownloadManager +import com.gh.download.PackageObserver +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.BaseDraggableDialogFragment +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.utils.addMetaExtra +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.toJson +import com.gh.gamecenter.common.utils.viewModelProvider +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.databinding.DialogVspaceBinding +import com.gh.gamecenter.entity.AppEntity +import com.gh.gamecenter.entity.GameEntity +import com.gh.gamecenter.eventbus.EBPackage +import com.halo.assistant.HaloApp +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadConfig +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus.* +import com.lightgame.utils.AppManager +import splitties.bundle.put + +class VSpaceDialogFragment : BaseDraggableDialogFragment() { + + private var mAppEntity: AppEntity? = null + private val mDownloadUrl by lazy { mAppEntity?.url ?: "" } + private val mBinding by lazy { DialogVspaceBinding.inflate(layoutInflater) } + private val mDataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + if (downloadEntity.url == mDownloadUrl && isAdded) { + updateDownloadButton(downloadEntity) + } + } + + override fun onDataInit(downloadEntity: DownloadEntity) { + onDataChanged(downloadEntity) + } + } + + override fun getRootView(): View = mBinding.root + override fun getDragCloseView(): View = mBinding.dragClose + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + mAppEntity = arguments?.get(KEY_ENTITY) as AppEntity + } + + @SuppressLint("ClickableViewAccessibility") + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return mBinding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val mViewModel = viewModelProvider() + mViewModel.packageLiveData.observe(this) { + if (it.packageName == "com.lg.vspace") { + dismissAllowingStateLoss() + } + } + + val spanBuilder = SpannableStringBuilder() + .append("畅玩是一种更灵活、轻便的游戏方式\n") + .append("下载") + .color(R.color.text_subtitle.toColor(requireContext())) { bold { append(" 畅玩助手服务组件 ") } } + .append("即可快速畅玩游戏~") + + mBinding.downloadBtn.text = "下载畅玩助手服务组件" + mBinding.downloadBtn.downloadType = DownloadProgressBar.DownloadType.NORMAL + mBinding.descTv.text = spanBuilder + mBinding.privacyPolicyTv.setOnClickListener { + NewFlatLogUtils.logHaloFunEvent("halo_fun_download_dialog_privacy_click") + DirectUtils.directToWebView(requireContext(), Constants.SMOOTH_GAME_PRIVACY_POLICY_ADDRESS) + } + + val downloadSnapshot = DownloadManager.getInstance() + .getDownloadEntitySnapshotByPackageName(VHelper.DEFAULT_VSPACE_PACKAGENAME) + + mBinding.downloadBtn.setOnClickListener { + NewFlatLogUtils.logHaloFunEvent("halo_fun_download_dialog_download_click") + + val name = "畅玩助手V" + mAppEntity?.version + val downloadId = PackageInstaller.createDownloadId(name) + + val downloadEntity = DownloadEntity() + downloadEntity.url = mDownloadUrl + downloadEntity.name = name + downloadEntity.platform = "官方版" + downloadEntity.gameId = "62bd412bbbf04747cd3de539" + downloadEntity.path = PackageInstaller.getDownloadPathWithId(downloadId, "apk") + downloadEntity.packageName = VHelper.DEFAULT_VSPACE_PACKAGENAME + downloadEntity.addMetaExtra(DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200") + + // 确定下载类型 + val downloadType = + if (arguments?.getBoolean(KEY_IS_UPDATE) == true) UPDATE else DOWNLOAD + val gameEntity = GameEntity(downloadEntity.gameId, downloadEntity.name) + gameEntity.gameVersion = mAppEntity?.version ?: "" + + val event = ExposureUtils.logADownloadExposureEvent( + gameEntity, + downloadEntity.platform, + null, + downloadType + ) + + downloadEntity.exposureTrace = event.toJson() + + AppExecutor.uiExecutor.executeWithDelay({ + DownloadManager.getInstance().cancel(mDownloadUrl) + DownloadManager.getInstance().add(downloadEntity) + }, 200) + + // 收集下载数据 + DataCollectionUtils.uploadDownload(HaloApp.getInstance(), downloadEntity, "开始") + } + + if (arguments?.getBoolean(KEY_AUTO_DOWNLOAD) == true && downloadSnapshot == null) { + mBinding.downloadBtn.performClick() + } + } + + override fun onStart() { + super.onStart() + DownloadManager.getInstance().addObserver(mDataWatcher) + } + + override fun onStop() { + super.onStop() + DownloadManager.getInstance().removeObserver(mDataWatcher) + } + + private fun updateDownloadButton(downloadEntity: DownloadEntity) { + val downloadBtn = mBinding.downloadBtn + downloadBtn.progress = (downloadEntity.progress * 10).toInt() + when (downloadEntity.status) { + downloading -> { + downloadBtn.setText(R.string.pause) + downloadBtn.progress = (downloadEntity.percent * 10).toInt() + downloadBtn.downloadType = DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL + downloadBtn.setOnClickListener { + DownloadManager.getInstance().pause(mDownloadUrl) + } + } + + pause -> { + downloadBtn.setText(R.string.resume) + downloadBtn.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, true) + } + } + + overflow, + timeout, + neterror, + waiting, + subscribe -> { + downloadBtn.setText(R.string.waiting) + downloadBtn.downloadType = DownloadProgressBar.DownloadType.DOWNLOADING_NORMAL + downloadBtn.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, false) + } + } + done -> { + downloadBtn.setText(R.string.install) + downloadBtn.downloadType = DownloadProgressBar.DownloadType.INSTALL_NORMAL + downloadBtn.setOnClickListener { + PackageInstaller.install(requireContext(), downloadEntity) + } + } + + cancel, + hijack, + notfound, + uncertificated, + unqualified -> { + downloadBtn.text = "下载畅玩助手服务组件" + downloadBtn.downloadType = DownloadProgressBar.DownloadType.NORMAL + downloadBtn.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, true) + } + } + else -> { + // do nothing + } + } + } + + companion object { + const val KEY_ENTITY = "entity" + const val KEY_AUTO_DOWNLOAD = "auto_download" + const val KEY_IS_UPDATE = "is_update" + + @JvmStatic + fun showDownloadDialog( + context: Context?, + appEntity: AppEntity, + autoDownload: Boolean = false, + isUpdate: Boolean = false + ) { + val fragmentActivity: FragmentActivity = if (context is FragmentActivity) { + context + } else { + val currentActivity = AppManager.getInstance().currentActivity() + if (currentActivity is FragmentActivity) { + currentActivity + } else { + throw IllegalStateException("current activity context must be FragmentActivity") + } + } + + // 防止重复弹出 + if (hasDialogDisplayedInCurrentActivity(fragmentActivity)) return + + NewFlatLogUtils.logHaloFunEvent("halo_fun_download_dialog_show") + + val downloadDialog = VSpaceDialogFragment().apply { + arguments = Bundle().apply { + put(KEY_ENTITY, appEntity) + put(KEY_AUTO_DOWNLOAD, autoDownload) + put(KEY_IS_UPDATE, isUpdate) + } + } + downloadDialog.show( + fragmentActivity.supportFragmentManager, + VSpaceDialogFragment::class.java.name + ) + } + + private fun hasDialogDisplayedInCurrentActivity(fragmentActivity: FragmentActivity): Boolean { + val fragments: List = fragmentActivity.supportFragmentManager.fragments + fragments.forEach { fragment -> + if (fragment is VSpaceDialogFragment) return true + } + return false + } + } + + internal class VSpaceDialogViewModel : ViewModel() { + val packageLiveData = MutableLiveData() + private val mPackageChangedListener by lazy { + PackageObserver.PackageChangeListener { + packageLiveData.postValue(it) + } + } + + init { + PackageObserver.registerPackageChangeChangeListener(mPackageChangedListener) + } + + override fun onCleared() { + super.onCleared() + + PackageObserver.unregisterPackageChangeChangeListener(mPackageChangedListener) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VSpaceLoadingActivity.kt b/app/src/main/java/com/gh/vspace/VSpaceLoadingActivity.kt new file mode 100644 index 0000000000..d01f2739e7 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VSpaceLoadingActivity.kt @@ -0,0 +1,52 @@ +package com.gh.vspace + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.activity.BaseActivity +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.entity.GameEntity + +class VSpaceLoadingActivity : BaseActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + DisplayUtils.transparentStatusBar(this) + DisplayUtils.setLightStatusBar(this, true) + + if (savedInstanceState == null) { + resetFragment() + } + } + + override fun getLayoutId() = R.layout.activity_shell + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + + resetFragment() + } + + private fun resetFragment() { + val fragment = VSpaceLoadingFragment().apply { arguments = intent?.extras } + supportFragmentManager.beginTransaction() + .replace(R.id.placeholder, fragment, VSpaceLoadingFragment::class.java.name) + .commitAllowingStateLoss() + } + + companion object { + const val IS_INSTALLATION = "is_installation" + + @JvmStatic + fun getIntent(context: Context, game: GameEntity, isInstall: Boolean): Intent { + val intent = Intent(context, VSpaceLoadingActivity::class.java) + intent.putExtra(EntranceConsts.KEY_DATA, game) + intent.putExtra(IS_INSTALLATION, isInstall) + return intent + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VSpaceLoadingFragment.kt b/app/src/main/java/com/gh/vspace/VSpaceLoadingFragment.kt new file mode 100644 index 0000000000..fc6cfea806 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/VSpaceLoadingFragment.kt @@ -0,0 +1,136 @@ +package com.gh.vspace + +import android.animation.ValueAnimator +import android.os.Bundle +import android.view.View +import android.view.animation.DecelerateInterpolator +import com.gh.download.DownloadManager +import com.gh.gamecenter.common.base.fragment.BaseFragment +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.databinding.FragmentVspaceLoadingBinding +import com.gh.gamecenter.entity.GameEntity +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus.* + +class VSpaceLoadingFragment : BaseFragment() { + + private var mIsInstallation = false + private var mGame: GameEntity? = null + private val mBinding: FragmentVspaceLoadingBinding by lazy { + FragmentVspaceLoadingBinding.inflate( + layoutInflater + ) + } + private val mDataWatcher by lazy { + object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity?) { + if (downloadEntity == null) return + if (downloadEntity.gameId != mGame?.id) return + + when (downloadEntity.status) { + add, + download, + downloading -> { + mBinding.progressBar.progress = downloadEntity.percent.toInt() + mBinding.progressTv.text = "加载中...${downloadEntity.percent}%" + } + + done -> { + mBinding.progressBar.progress = 100 + mBinding.progressTv.text = "启动中...${downloadEntity.percent}%" + } + + pause, + delete, + timeout, + hijack, + notfound, + neterror, + overflow -> requireActivity().finish() + unqualified -> { + ToastUtils.toast("暂不支持未成年人下载") + requireActivity().finish() + } + uncertificated -> { + ToastUtils.toast("未实名,暂不支持下载") + requireActivity().finish() + } + unavailable -> { + ToastUtils.toast("该游戏未接入防沉迷系统,暂不支持下载") + requireActivity().finish() + } + + else -> { + // do nothing + } + } + } + } + } + + override fun getLayoutId() = 0 + override fun getInflatedLayout() = mBinding.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + mGame = arguments?.get(EntranceConsts.KEY_DATA) as GameEntity? + mIsInstallation = arguments?.get(VSpaceLoadingActivity.IS_INSTALLATION) as Boolean + mGame?.let { initView(it, mBinding) } + + // 安装状态显示虚假的进度动画 + if (mIsInstallation) { + showFakeInstallationProgress(mBinding) + } + + VHelper.getInstallationLiveData().observe(viewLifecycleOwner) { + val currentPackageName = mGame?.getApk()?.firstOrNull()?.packageName + if (it == currentPackageName) { + VHelper.launch(requireActivity(), currentPackageName!!) + requireActivity().finish() + } + } + } + + private fun initView(gameEntity: GameEntity, binding: FragmentVspaceLoadingBinding) { + binding.gameIconIv.displayGameIcon(gameEntity) + binding.hideLoadingContainer.setOnClickListener { requireActivity().finish() } + binding.nameTv.text = gameEntity.name + } + + private fun showFakeInstallationProgress(binding: FragmentVspaceLoadingBinding) { + ValueAnimator.ofInt(0, 99).apply { + duration = 5000L + interpolator = DecelerateInterpolator() + addUpdateListener { + if (!isAdded) { + removeAllUpdateListeners() + return@addUpdateListener + } + + val progress = it.animatedValue as Int + binding.progressBar.progress = progress + binding.progressTv.text = "启动中${progress}%" + } + }.start() + } + + override fun onResume() { + super.onResume() + + if (!mIsInstallation) { + DownloadManager.getInstance().addObserver(mDataWatcher) + } + } + + override fun onPause() { + super.onPause() + + if (!mIsInstallation) { + DownloadManager.getInstance().removeObserver(mDataWatcher) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/db/VGameConverter.kt b/app/src/main/java/com/gh/vspace/db/VGameConverter.kt new file mode 100644 index 0000000000..25c1608785 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/db/VGameConverter.kt @@ -0,0 +1,19 @@ +package com.gh.vspace.db + +import androidx.room.TypeConverter +import com.gh.gamecenter.core.utils.GsonUtils +import com.lightgame.download.DownloadEntity + +class VGameConverter { + + @TypeConverter + fun convertDownload2String(any: DownloadEntity): String { + return GsonUtils.toJson(any) + } + + @TypeConverter + fun convertString2DownloadEntity(string: String): DownloadEntity { + return GsonUtils.fromJson(string, DownloadEntity::class.java) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/db/VGameDao.kt b/app/src/main/java/com/gh/vspace/db/VGameDao.kt new file mode 100644 index 0000000000..fb79e7cbbc --- /dev/null +++ b/app/src/main/java/com/gh/vspace/db/VGameDao.kt @@ -0,0 +1,27 @@ +package com.gh.vspace.db + +import androidx.lifecycle.LiveData +import androidx.room.* + +@Dao +interface VGameDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insert(game: VGameEntity) + + @Query("SELECT * FROM v_game") + fun getAll(): List + + @Query("SELECT * FROM v_game") + fun getAllLiveData(): LiveData> + + @Delete + fun delete(gameList: List) + + @Delete + fun delete(game: VGameEntity) + + @Query("DELETE FROM v_game WHERE packageName = :packageName") + fun delete(packageName: String) + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/db/VGameDatabase.kt b/app/src/main/java/com/gh/vspace/db/VGameDatabase.kt new file mode 100644 index 0000000000..f3b93d67f6 --- /dev/null +++ b/app/src/main/java/com/gh/vspace/db/VGameDatabase.kt @@ -0,0 +1,25 @@ +package com.gh.vspace.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters + +@TypeConverters(VGameConverter::class) +@Database(entities = [VGameEntity::class], version = 1, exportSchema = false) +abstract class VGameDatabase : RoomDatabase() { + + abstract fun vGameDao(): VGameDao + + companion object { + const val DATABASE = "v_game_database.db" + + fun buildDatabase(context: Context): VGameDatabase { + return Room.databaseBuilder(context, VGameDatabase::class.java, DATABASE) + .fallbackToDestructiveMigration() + .build() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/db/VGameEntity.kt b/app/src/main/java/com/gh/vspace/db/VGameEntity.kt new file mode 100644 index 0000000000..f7b067b01d --- /dev/null +++ b/app/src/main/java/com/gh/vspace/db/VGameEntity.kt @@ -0,0 +1,21 @@ +package com.gh.vspace.db + +import android.os.Parcelable +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.lightgame.download.DownloadEntity +import kotlinx.parcelize.Parcelize + +@Parcelize +@Entity(tableName = "v_game") +data class VGameEntity( + @PrimaryKey + var packageName: String, + var downloadEntity: DownloadEntity, +) : Parcelable { + companion object { + fun from(downloadEntity: DownloadEntity): VGameEntity { + return VGameEntity(downloadEntity.packageName, downloadEntity) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/halo/assistant/HaloApp.java b/app/src/main/java/com/halo/assistant/HaloApp.java index deef8cae70..9ef89f9f38 100644 --- a/app/src/main/java/com/halo/assistant/HaloApp.java +++ b/app/src/main/java/com/halo/assistant/HaloApp.java @@ -63,6 +63,7 @@ import com.gh.gamecenter.receiver.InstallAndUninstallReceiver; import com.gh.gamecenter.receiver.InstallReceiver; import com.gh.gamecenter.receiver.NetworkStateReceiver; import com.gh.gamecenter.user.UserRepository; +import com.gh.vspace.VHelper; import com.github.piasy.biv.BigImageViewer; import com.github.piasy.biv.loader.fresco.FrescoImageLoader; import com.lightgame.utils.Utils; @@ -196,7 +197,7 @@ public class HaloApp extends MultiDexApplication implements Configuration.Provid AppExecutor.getIoExecutor().execute(() -> { initDataHelper(); - Tracker.init(this); + ExtensionsKt.doOnMainProcessOnly(this, () -> Tracker.init(this)); initFresco(); @@ -273,8 +274,11 @@ public class HaloApp extends MultiDexApplication implements Configuration.Provid FixedRateJobHelper.begin(); RegionSettingHelper.getRegionSetting(); - - ExtensionsKt.doOnMainProcessOnly(this, PackageRepository::initData); + + ExtensionsKt.doOnMainProcessOnly(this, () -> { + retrieveVGameInfoIfNeeded(); + PackageRepository.initData(); + }); checkIfDeviceIsEmulator(); @@ -311,12 +315,7 @@ public class HaloApp extends MultiDexApplication implements Configuration.Provid } private void initDataHelper() { - AppExecutor.getIoExecutor().execute(() -> { - ExecutorService logExecutor = AppExecutor.getLogExecutor(); - ExposureManager.init(logExecutor); - LoghubUtils.init(this, logExecutor); - VideoRecordUtils.init(this, logExecutor); - }); + VideoRecordUtils.init(this, AppExecutor.getIoExecutor()); } // todo 动态注册和静态注册并行的话会触发多次回调 @@ -325,6 +324,7 @@ public class HaloApp extends MultiDexApplication implements Configuration.Provid DownloadReceiver downloadReceiver = new DownloadReceiver(); IntentFilter downloadFilter = new IntentFilter(); downloadFilter.addAction(DownloadNotificationHelper.ACTION_DOWNLOAD); + downloadFilter.addAction(DownloadNotificationHelper.ACTION_VDOWNLOAD); this.registerReceiver(downloadReceiver, downloadFilter); InstallReceiver installReceiver = new InstallReceiver(); @@ -436,6 +436,10 @@ public class HaloApp extends MultiDexApplication implements Configuration.Provid } } + private void retrieveVGameInfoIfNeeded() { + VHelper.init(this); + } + public boolean isEmulator() { return mIsEmulator; } diff --git a/app/src/main/java/com/halo/assistant/TinkerApp.java b/app/src/main/java/com/halo/assistant/TinkerApp.java deleted file mode 100644 index 6fd7c23640..0000000000 --- a/app/src/main/java/com/halo/assistant/TinkerApp.java +++ /dev/null @@ -1,19 +0,0 @@ -//package com.halo.assistant; -// -//import com.tencent.tinker.loader.app.TinkerApplication; -//import com.tencent.tinker.loader.shareutil.ShareConstants; -// -///** -// * 这个类不要加任何应用逻辑,有意义的东西都放在{@link HaloApp} -// * -// * @author CsHeng -// * @Date 13/09/2017 -// * @Time 6:12 PM -// */ -//public class TinkerApp extends TinkerApplication { -// -// public TinkerApp() { -// super(ShareConstants.TINKER_ENABLE_ALL, "com.halo.assistant.HaloApp", "com.tencent.tinker.loader.TinkerLoader", false); -// } -// -//} diff --git a/app/src/main/java/com/halo/assistant/TinkerAppLike.java b/app/src/main/java/com/halo/assistant/TinkerAppLike.java deleted file mode 100644 index 921b052a31..0000000000 --- a/app/src/main/java/com/halo/assistant/TinkerAppLike.java +++ /dev/null @@ -1,49 +0,0 @@ -//package com.halo.assistant; -// -//import android.annotation.TargetApi; -//import android.app.Application; -//import android.content.Context; -//import android.content.Intent; -//import android.os.Build; -// -//import androidx.multidex.MultiDex; -// -//import com.tencent.tinker.loader.app.DefaultApplicationLike; -// -//public class TinkerAppLike extends DefaultApplicationLike { -// -// public TinkerAppLike(Application application, int tinkerFlags, -// boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, -// long applicationStartMillisTime, Intent tinkerResultIntent) { -// super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent); -// } -// -// @Override -// public void onCreate() { -// super.onCreate(); -// -//// if (HaloApp.isUserAcceptPrivacyPolicy(getApplication())) { -//// Bugly.init(getApplication(), Config.BUGLY_APPID, BuildConfig.DEBUG); -//// } -// } -// -// -// @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) -// @Override -// public void onBaseContextAttached(Context base) { -// super.onBaseContextAttached(base); -// // you must install multiDex whatever tinker is installed! -// MultiDex.install(base); -// -// // 安装tinker -//// if (HaloApp.isUserAcceptPrivacyPolicy(base)) { -//// Beta.installTinker(this); -//// } -// } -// -// @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) -// public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callbacks) { -// getApplication().registerActivityLifecycleCallbacks(callbacks); -// } -// -//} \ No newline at end of file diff --git a/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.kt b/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.kt index 97cf15fd4c..84059104ac 100644 --- a/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.kt +++ b/app/src/main/java/com/halo/assistant/fragment/SettingsFragment.kt @@ -179,13 +179,11 @@ class SettingsFragment : ToolbarFragment() { } if (UsageStatsHelper.checkForPermission() && SPUtils.getBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, true)) { mUsageStatus = true - mBinding.usageStatsItem.switchIv.isChecked = true EnergyTaskHelper.postEnergyTask("open_game_time") } else { mUsageStatus = false - mBinding.usageStatsItem.switchIv.isChecked = false - mBinding.usageStatsItem.switchLottie.setSwitchAnimation(mUsageStatus) } + mBinding.usageStatsItem.switchLottie.setSwitchAnimation(mUsageStatus) } private fun initView() { @@ -243,7 +241,7 @@ class SettingsFragment : ToolbarFragment() { } mBinding.usageStatsItem.run { titleTv.text = getString(R.string.setting_usage_stats) - switchIv.visibility = View.VISIBLE + switchLottie.visibility = View.VISIBLE if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { root.setOnClickListener { if (UsageStatsHelper.checkForPermission()) { @@ -286,7 +284,7 @@ class SettingsFragment : ToolbarFragment() { mBinding.notificationAuthorityItem.run { titleTv.text = getString(R.string.setting_notification_authority) tipsTv.text = getString(R.string.setting_notification_authority_hint) - switchIv.visibility = View.VISIBLE + switchLottie.visibility = View.VISIBLE tipsTv.visibility = View.VISIBLE root.setOnClickListener { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -483,10 +481,6 @@ class SettingsFragment : ToolbarFragment() { }.show() } - - private fun LottieAnimationView.setSwitchAnimation(turnOff: Boolean) = - setAnimation(if (turnOff) "lottie/switch_turnoff.json" else "lottie/switch_turnon.json") - // 清除缓存 private fun clearCache() { Observable.create { emitter: ObservableEmitter -> @@ -547,7 +541,7 @@ class SettingsFragment : ToolbarFragment() { super.onResume() setNavigationTitle(getString(R.string.title_settings)) - mBinding.notificationAuthorityItem.switchIv.isChecked = NotificationHelper.notificationIsEnable() + mBinding.notificationAuthorityItem.switchLottie.setSwitchAnimation(NotificationHelper.notificationIsEnable()) if (BrowserInstallHelper.isUseBrowserToInstallEnabled()) { if (BrowserInstallHelper.shouldUseBrowserToInstall()) { @@ -628,6 +622,9 @@ class SettingsFragment : ToolbarFragment() { override fun onNightModeChange() { super.onNightModeChange() mBinding.root.setBackgroundColor(R.color.background.toColor(requireContext())) + mBinding.personalRecommendItem.switchLottie.setSwitchAnimation(SPUtils.getBoolean(PERSONAL_RECOMMEND_SP_KEY, true)) + mBinding.usageStatsItem.switchLottie.setSwitchAnimation(mUsageStatus) + mBinding.notificationAuthorityItem.switchLottie.setSwitchAnimation(NotificationHelper.notificationIsEnable()) } companion object { diff --git a/app/src/main/java/com/halo/assistant/fragment/SwitchInstallMethodFragment.kt b/app/src/main/java/com/halo/assistant/fragment/SwitchInstallMethodFragment.kt index 738d0f96e0..e316a63da8 100644 --- a/app/src/main/java/com/halo/assistant/fragment/SwitchInstallMethodFragment.kt +++ b/app/src/main/java/com/halo/assistant/fragment/SwitchInstallMethodFragment.kt @@ -24,6 +24,7 @@ import java.util.* class SwitchInstallMethodFragment : ToolbarFragment() { private var mBinding: FragmentSwitchInstallMethodBinding? = null + private var mUseBrowserToInstall = false override fun getLayoutId() = 0 @@ -90,12 +91,13 @@ class SwitchInstallMethodFragment : ToolbarFragment() { } private fun changeSwitch(useBrowserToInstall: Boolean) { + mUseBrowserToInstall = useBrowserToInstall if (useBrowserToInstall) { - mBinding?.defaultInstallItem?.selectedIv?.setImageResource(R.drawable.ic_install_method_select) + mBinding?.defaultInstallItem?.selectedIv?.setImageResource(R.drawable.ic_selector_default) mBinding?.browserInstallItem?.selectedIv?.setImageResource(R.drawable.ic_video_setting_select) } else { mBinding?.defaultInstallItem?.selectedIv?.setImageResource(R.drawable.ic_video_setting_select) - mBinding?.browserInstallItem?.selectedIv?.setImageResource(R.drawable.ic_install_method_select) + mBinding?.browserInstallItem?.selectedIv?.setImageResource(R.drawable.ic_selector_default) } if (SPUtils.getBoolean(Constants.SP_USE_BROWSER_TO_INSTALL) != useBrowserToInstall) { @@ -126,5 +128,6 @@ class SwitchInstallMethodFragment : ToolbarFragment() { backBtn.setImageResource(R.drawable.ic_bar_back) normalTitle.setTextColor(R.color.text_title.toColor(requireContext())) } + changeSwitch(mUseBrowserToInstall) } } \ No newline at end of file diff --git a/app/src/main/java/com/halo/assistant/fragment/WebFragment.kt b/app/src/main/java/com/halo/assistant/fragment/WebFragment.kt index a0f70c7d38..97fe420d1b 100644 --- a/app/src/main/java/com/halo/assistant/fragment/WebFragment.kt +++ b/app/src/main/java/com/halo/assistant/fragment/WebFragment.kt @@ -37,6 +37,7 @@ import com.gh.gamecenter.MessageDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.eventbus.EBShare import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.EmptyCallback import com.gh.gamecenter.databinding.FragmentWebBinding @@ -101,6 +102,18 @@ class WebFragment : LazyFragment(), IScrollable { private var mShareEntity: WebShareEntity? = null private var mSelectPicCallback: ValueCallback? = null private var mSelectPicCallbackAboveL: ValueCallback>? = null + private val mShareResultCallback by lazy { + object : ShareUtils.ShareCallBack { + + override fun onSuccess(label: String) { + if ("短信" == label || "复制链接" == label) { + onShareSuccess() + } + } + + override fun onCancel() {} + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -256,7 +269,7 @@ class WebFragment : LazyFragment(), IScrollable { toast("分享实体为空") return } - curActivity.showShare( + curActivity.showShareWithCallback( if (TextUtils.isEmpty(mShareEntity?.url)) requireArguments().getString( EntranceConsts.KEY_URL ) else mShareEntity?.url, @@ -264,7 +277,8 @@ class WebFragment : LazyFragment(), IScrollable { mShareEntity?.title, mShareEntity?.description, if (mQaType >= 0) ShareUtils.ShareEntrance.qaDetail else ShareUtils.ShareEntrance.web, - "" + "", + mShareResultCallback ) if (mQaType == 0) { onEvent("QA", "QA内容详情", "点击分享+" + mShareEntity?.title) @@ -342,11 +356,8 @@ class WebFragment : LazyFragment(), IScrollable { newsWebview.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { return if (isAdded) { - // return DefaultWebViewUrlHandler.interceptUrl(requireContext(), url, mEntrance + "+(光环浏览器)"); - val b = interceptUrl( - requireContext(), url, - "$mEntrance+(光环浏览器)" - ) + val originalUrl = requireArguments().getString(EntranceConsts.KEY_URL, "") + val b = if (Uri.parse(originalUrl).path != Uri.parse(url).path) interceptUrl(requireContext(), url, "$mEntrance+(光环浏览器)") else false if (mIsOpenNativePage && !b) { startActivity( WebActivity.getIntent( @@ -749,6 +760,17 @@ class WebFragment : LazyFragment(), IScrollable { } } + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(share: EBShare?) { + postDelayedRunnable({ + tryCatchInRelease { + if (share != null && share.shareEntrance == ShareUtils.ShareEntrance.web && isSupportVisible) { + onShareSuccess() + } + } + }, 200) + } + override fun onNightModeChange() { super.onNightModeChange() mMenuShare?.setIcon(R.drawable.icon_share_black) @@ -756,6 +778,11 @@ class WebFragment : LazyFragment(), IScrollable { mBinding?.newsWebview?.enableForceDark(NightModeUtils.isNightMode(requireContext())) } + private fun onShareSuccess() { + if (mShareEntity == null) return + mBinding?.newsWebview?.callHandler("onShareSuccess", arrayOf(mShareEntity?.url)) { _: Any? -> } + } + companion object { const val KEY_ISTOOLS = "isTools" const val KEY_IS_BIND_WECHAT = "is_bind_wechat" diff --git a/app/src/main/java/com/halo/assistant/fragment/comment/CommentDetailFragment.java b/app/src/main/java/com/halo/assistant/fragment/comment/CommentDetailFragment.java index 58134c4189..e3d8917668 100644 --- a/app/src/main/java/com/halo/assistant/fragment/comment/CommentDetailFragment.java +++ b/app/src/main/java/com/halo/assistant/fragment/comment/CommentDetailFragment.java @@ -228,7 +228,7 @@ public class CommentDetailFragment extends ToolbarFragment implements OnCommentC e1.printStackTrace(); } } - ErrorHelper.handleError(requireContext(), errorString, false); + ErrorHelper.handleError(requireContext(), errorString, false, () -> mCommentSend.performClick()); } }); } diff --git a/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_large.webp b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_large.webp new file mode 100644 index 0000000000..9f0fe20aa9 Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_large.webp differ diff --git a/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_large_primary.webp b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_large_primary.webp new file mode 100644 index 0000000000..f36369f729 Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_large_primary.webp differ diff --git a/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_large_right.webp b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_large_right.webp new file mode 100644 index 0000000000..18c3726129 Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_large_right.webp differ diff --git a/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_small.webp b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_small.webp new file mode 100644 index 0000000000..6c0308686d Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_small.webp differ diff --git a/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_small_right.webp b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_small_right.webp new file mode 100644 index 0000000000..b287b70b8f Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/bg_content_card_small_right.webp differ diff --git a/app/src/main/res/drawable-night/download_dialog_installed_background.xml b/app/src/main/res/drawable-night/download_dialog_installed_background.xml new file mode 100644 index 0000000000..e92c6c72e3 --- /dev/null +++ b/app/src/main/res/drawable-night/download_dialog_installed_background.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/download_dialog_status_collection_gray.xml b/app/src/main/res/drawable-night/download_dialog_status_collection_gray.xml new file mode 100644 index 0000000000..db1863ed0a --- /dev/null +++ b/app/src/main/res/drawable-night/download_dialog_status_collection_gray.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable-night/download_dialog_status_download_gray.xml b/app/src/main/res/drawable-night/download_dialog_status_download_gray.xml new file mode 100644 index 0000000000..f0d8c91414 --- /dev/null +++ b/app/src/main/res/drawable-night/download_dialog_status_download_gray.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable-night/download_dialog_version_note.xml b/app/src/main/res/drawable-night/download_dialog_version_note.xml new file mode 100644 index 0000000000..07969eac72 --- /dev/null +++ b/app/src/main/res/drawable-night/download_dialog_version_note.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/download_recommend_des_bg.xml b/app/src/main/res/drawable-night/download_recommend_des_bg.xml new file mode 100644 index 0000000000..260a61226c --- /dev/null +++ b/app/src/main/res/drawable-night/download_recommend_des_bg.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/ic_selector_default.xml b/app/src/main/res/drawable-night/ic_selector_default.xml new file mode 100644 index 0000000000..8dc87fc414 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_selector_default.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-night/icon_more.xml b/app/src/main/res/drawable-night/icon_more.xml new file mode 100644 index 0000000000..e961c16f9d --- /dev/null +++ b/app/src/main/res/drawable-night/icon_more.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable-xxhdpi/bg_video_control.xml b/app/src/main/res/drawable-xxhdpi/bg_video_control.xml index 5e78703298..e23cb35da6 100644 --- a/app/src/main/res/drawable-xxhdpi/bg_video_control.xml +++ b/app/src/main/res/drawable-xxhdpi/bg_video_control.xml @@ -2,6 +2,6 @@ diff --git a/app/src/main/res/drawable-xxhdpi/download_dialog_status_collection.png b/app/src/main/res/drawable-xxhdpi/download_dialog_status_collection.png deleted file mode 100644 index 0efe5baa30..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/download_dialog_status_collection.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/download_dialog_status_collection_gray.png b/app/src/main/res/drawable-xxhdpi/download_dialog_status_collection_gray.png deleted file mode 100644 index 4290dd16e3..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/download_dialog_status_collection_gray.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/download_dialog_status_download.png b/app/src/main/res/drawable-xxhdpi/download_dialog_status_download.png deleted file mode 100644 index 6dbeb6bfc1..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/download_dialog_status_download.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/download_dialog_status_download_gray.png b/app/src/main/res/drawable-xxhdpi/download_dialog_status_download_gray.png deleted file mode 100644 index b70b834222..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/download_dialog_status_download_gray.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/download_dialog_status_pause.png b/app/src/main/res/drawable-xxhdpi/download_dialog_status_pause.png deleted file mode 100644 index 4ce8f90d94..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/download_dialog_status_pause.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/forum_more_guide.webp b/app/src/main/res/drawable-xxhdpi/forum_more_guide.webp deleted file mode 100644 index 4a99a299f0..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/forum_more_guide.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/forum_more_guide_rectangle.webp b/app/src/main/res/drawable-xxhdpi/forum_more_guide_rectangle.webp deleted file mode 100644 index adb49da000..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/forum_more_guide_rectangle.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_search_hot.png b/app/src/main/res/drawable-xxhdpi/ic_search_hot.png deleted file mode 100755 index 6b61320008..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_search_hot.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_search_new.png b/app/src/main/res/drawable-xxhdpi/ic_search_new.png deleted file mode 100755 index 668090a109..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_search_new.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_search_surge.png b/app/src/main/res/drawable-xxhdpi/ic_search_surge.png deleted file mode 100755 index 3bc9c82d80..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_search_surge.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_content_card_large.webp b/app/src/main/res/drawable-xxxhdpi/bg_content_card_large.webp new file mode 100644 index 0000000000..3df87fcc26 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_content_card_large.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_content_card_large_primary.webp b/app/src/main/res/drawable-xxxhdpi/bg_content_card_large_primary.webp new file mode 100644 index 0000000000..1495b00358 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_content_card_large_primary.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_content_card_large_right.webp b/app/src/main/res/drawable-xxxhdpi/bg_content_card_large_right.webp new file mode 100644 index 0000000000..00667c8ce2 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_content_card_large_right.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_content_card_small.webp b/app/src/main/res/drawable-xxxhdpi/bg_content_card_small.webp new file mode 100644 index 0000000000..c067ffd380 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_content_card_small.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_content_card_small_right.webp b/app/src/main/res/drawable-xxxhdpi/bg_content_card_small_right.webp new file mode 100644 index 0000000000..ae0417fe79 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_content_card_small_right.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_vspace_banner.png b/app/src/main/res/drawable-xxxhdpi/bg_vspace_banner.png new file mode 100644 index 0000000000..cef12f776a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_vspace_banner.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_float_load_complete.webp b/app/src/main/res/drawable-xxxhdpi/ic_float_load_complete.webp new file mode 100644 index 0000000000..cb6cc0891e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_float_load_complete.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_float_load_complete_close.webp b/app/src/main/res/drawable-xxxhdpi/ic_float_load_complete_close.webp new file mode 100644 index 0000000000..3ee15725c1 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_float_load_complete_close.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_gamedetail_menu_follow.webp b/app/src/main/res/drawable-xxxhdpi/ic_gamedetail_menu_follow.webp new file mode 100644 index 0000000000..6fbc4b8333 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_gamedetail_menu_follow.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_gamedetail_menu_followed.webp b/app/src/main/res/drawable-xxxhdpi/ic_gamedetail_menu_followed.webp new file mode 100644 index 0000000000..32ea16b9a4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_gamedetail_menu_followed.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_hot.png b/app/src/main/res/drawable-xxxhdpi/ic_search_hot.png deleted file mode 100755 index 18e9ebd074..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_search_hot.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_hot.webp b/app/src/main/res/drawable-xxxhdpi/ic_search_hot.webp new file mode 100755 index 0000000000..c7f93bdfd1 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_search_hot.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_new.png b/app/src/main/res/drawable-xxxhdpi/ic_search_new.png deleted file mode 100755 index 822a5c71ff..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_search_new.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_new.webp b/app/src/main/res/drawable-xxxhdpi/ic_search_new.webp new file mode 100755 index 0000000000..0fe3318cc5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_search_new.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_rise.webp b/app/src/main/res/drawable-xxxhdpi/ic_search_rise.webp new file mode 100755 index 0000000000..33b2f87e0d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_search_rise.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_surge.png b/app/src/main/res/drawable-xxxhdpi/ic_search_surge.png deleted file mode 100755 index 0a6aa6cd2f..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_search_surge.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_update.webp b/app/src/main/res/drawable-xxxhdpi/ic_search_update.webp new file mode 100755 index 0000000000..41cb71d47e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_search_update.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_smooth_feature_no_password.webp b/app/src/main/res/drawable-xxxhdpi/ic_smooth_feature_no_password.webp new file mode 100644 index 0000000000..f3234c90d1 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_smooth_feature_no_password.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_smooth_feature_snap.webp b/app/src/main/res/drawable-xxxhdpi/ic_smooth_feature_snap.webp new file mode 100644 index 0000000000..7e3ed5f5e3 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_smooth_feature_snap.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_smooth_feature_tool.webp b/app/src/main/res/drawable-xxxhdpi/ic_smooth_feature_tool.webp new file mode 100644 index 0000000000..d647c60ad6 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_smooth_feature_tool.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_smooth_game_button_hint.png b/app/src/main/res/drawable-xxxhdpi/ic_smooth_game_button_hint.png new file mode 100644 index 0000000000..1ebef1cc35 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_smooth_game_button_hint.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_smooth_load_in_background.png b/app/src/main/res/drawable-xxxhdpi/ic_smooth_load_in_background.png new file mode 100644 index 0000000000..245ee7f7b8 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_smooth_load_in_background.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_smooth_manager.webp b/app/src/main/res/drawable-xxxhdpi/ic_smooth_manager.webp new file mode 100644 index 0000000000..1b7da4827b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_smooth_manager.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_smooth_title.webp b/app/src/main/res/drawable-xxxhdpi/ic_smooth_title.webp new file mode 100644 index 0000000000..e56eb1a400 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_smooth_title.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_switch_game_download.webp b/app/src/main/res/drawable-xxxhdpi/ic_switch_game_download.webp new file mode 100644 index 0000000000..d264c2ca0c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_switch_game_download.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_switch_game_smooth.webp b/app/src/main/res/drawable-xxxhdpi/ic_switch_game_smooth.webp new file mode 100644 index 0000000000..5026c38c39 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_switch_game_smooth.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_vgame_dialog_close.png b/app/src/main/res/drawable-xxxhdpi/ic_vgame_dialog_close.png new file mode 100644 index 0000000000..8c49e487b1 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_vgame_dialog_close.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_vgame_recent_hint.png b/app/src/main/res/drawable-xxxhdpi/ic_vgame_recent_hint.png new file mode 100644 index 0000000000..d5142c99d3 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_vgame_recent_hint.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_vgame_update_hint.png b/app/src/main/res/drawable-xxxhdpi/ic_vgame_update_hint.png new file mode 100644 index 0000000000..48945a17a2 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_vgame_update_hint.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_vspace_logo.png b/app/src/main/res/drawable-xxxhdpi/ic_vspace_logo.png new file mode 100644 index 0000000000..6dc0af9248 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_vspace_logo.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/icon_more.png b/app/src/main/res/drawable-xxxhdpi/icon_more.png deleted file mode 100644 index f673f1502f..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/icon_more.png and /dev/null differ diff --git a/app/src/main/res/drawable/bg_download_dialog_item_recommend.xml b/app/src/main/res/drawable/bg_download_dialog_item_recommend.xml index c8ee1277e8..5ff072a208 100644 --- a/app/src/main/res/drawable/bg_download_dialog_item_recommend.xml +++ b/app/src/main/res/drawable/bg_download_dialog_item_recommend.xml @@ -4,9 +4,6 @@ - + android:endColor="#002496FF" + android:startColor="#1F2496FF" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_float_load_complete.xml b/app/src/main/res/drawable/bg_float_load_complete.xml new file mode 100644 index 0000000000..0420e7dd65 --- /dev/null +++ b/app/src/main/res/drawable/bg_float_load_complete.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_forum_game_zone.xml b/app/src/main/res/drawable/bg_forum_game_zone.xml index 3582e13a59..4bfa5a5ea3 100644 --- a/app/src/main/res/drawable/bg_forum_game_zone.xml +++ b/app/src/main/res/drawable/bg_forum_game_zone.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_forum_rule.xml b/app/src/main/res/drawable/bg_forum_rule.xml index a67f7c8bba..3c8cdeadd6 100644 --- a/app/src/main/res/drawable/bg_forum_rule.xml +++ b/app/src/main/res/drawable/bg_forum_rule.xml @@ -8,6 +8,6 @@ android:width="1dp" android:color="@color/white_alpha_20" /> - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_forum_welfare_item.xml b/app/src/main/res/drawable/bg_forum_welfare_item.xml index 16ecf7b9b6..f5a001ed3e 100644 --- a/app/src/main/res/drawable/bg_forum_welfare_item.xml +++ b/app/src/main/res/drawable/bg_forum_welfare_item.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_game_collection_sfv_indicator.xml b/app/src/main/res/drawable/bg_game_collection_sfv_indicator.xml new file mode 100644 index 0000000000..9ab1c3f4b7 --- /dev/null +++ b/app/src/main/res/drawable/bg_game_collection_sfv_indicator.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_game_collection_tag_select.xml b/app/src/main/res/drawable/bg_game_collection_tag_select.xml index 4e59394287..f1fb6e9697 100644 --- a/app/src/main/res/drawable/bg_game_collection_tag_select.xml +++ b/app/src/main/res/drawable/bg_game_collection_tag_select.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_game_collection_user_container.xml b/app/src/main/res/drawable/bg_game_collection_user_container.xml index fa06805b62..6256437e4a 100644 --- a/app/src/main/res/drawable/bg_game_collection_user_container.xml +++ b/app/src/main/res/drawable/bg_game_collection_user_container.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_game_collectionchange_poster.xml b/app/src/main/res/drawable/bg_game_collectionchange_poster.xml index fa06805b62..6256437e4a 100644 --- a/app/src/main/res/drawable/bg_game_collectionchange_poster.xml +++ b/app/src/main/res/drawable/bg_game_collectionchange_poster.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_hint_red_radius_999.xml b/app/src/main/res/drawable/bg_hint_red_radius_999.xml new file mode 100644 index 0000000000..f9c6f807c0 --- /dev/null +++ b/app/src/main/res/drawable/bg_hint_red_radius_999.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_home_recent_vgame.xml b/app/src/main/res/drawable/bg_home_recent_vgame.xml new file mode 100644 index 0000000000..e68bf5e2a7 --- /dev/null +++ b/app/src/main/res/drawable/bg_home_recent_vgame.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_home_vgame_progress_active.xml b/app/src/main/res/drawable/bg_home_vgame_progress_active.xml new file mode 100644 index 0000000000..41008d3c83 --- /dev/null +++ b/app/src/main/res/drawable/bg_home_vgame_progress_active.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_home_vgame_progress_inactive.xml b/app/src/main/res/drawable/bg_home_vgame_progress_inactive.xml new file mode 100644 index 0000000000..895d0ddab5 --- /dev/null +++ b/app/src/main/res/drawable/bg_home_vgame_progress_inactive.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_horizontal_progressbar.xml b/app/src/main/res/drawable/bg_horizontal_progressbar.xml new file mode 100644 index 0000000000..600ae42f41 --- /dev/null +++ b/app/src/main/res/drawable/bg_horizontal_progressbar.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_original_label.xml b/app/src/main/res/drawable/bg_original_label.xml index 42a21d8681..da49c23a8b 100644 --- a/app/src/main/res/drawable/bg_original_label.xml +++ b/app/src/main/res/drawable/bg_original_label.xml @@ -3,6 +3,6 @@ android:shape = "rectangle" > - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_pic_count_label.xml b/app/src/main/res/drawable/bg_pic_count_label.xml index e918ee8da8..04c5f13230 100644 --- a/app/src/main/res/drawable/bg_pic_count_label.xml +++ b/app/src/main/res/drawable/bg_pic_count_label.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_shape_black_alpha_40_radius_2.xml b/app/src/main/res/drawable/bg_shape_black_alpha_40_radius_2.xml index da35f81358..8c1adb483d 100644 --- a/app/src/main/res/drawable/bg_shape_black_alpha_40_radius_2.xml +++ b/app/src/main/res/drawable/bg_shape_black_alpha_40_radius_2.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_shape_black_alpha_40_radius_3.xml b/app/src/main/res/drawable/bg_shape_black_alpha_40_radius_3.xml new file mode 100644 index 0000000000..24fd3312ac --- /dev/null +++ b/app/src/main/res/drawable/bg_shape_black_alpha_40_radius_3.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_shape_f8_radius_6.xml b/app/src/main/res/drawable/bg_shape_f8_radius_6.xml index 4427662808..b7bd6cc341 100644 --- a/app/src/main/res/drawable/bg_shape_f8_radius_6.xml +++ b/app/src/main/res/drawable/bg_shape_f8_radius_6.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_vfeedback_label.xml b/app/src/main/res/drawable/bg_vfeedback_label.xml new file mode 100644 index 0000000000..2ae7cc065d --- /dev/null +++ b/app/src/main/res/drawable/bg_vfeedback_label.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_vfeedback_label_selected.xml b/app/src/main/res/drawable/bg_vfeedback_label_selected.xml new file mode 100644 index 0000000000..e0442f404b --- /dev/null +++ b/app/src/main/res/drawable/bg_vfeedback_label_selected.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_vgame_icon_mask.xml b/app/src/main/res/drawable/bg_vgame_icon_mask.xml new file mode 100644 index 0000000000..1a21fffb10 --- /dev/null +++ b/app/src/main/res/drawable/bg_vgame_icon_mask.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/border_10p_black_round_16.xml b/app/src/main/res/drawable/border_10p_black_round_16.xml new file mode 100644 index 0000000000..375f02f27f --- /dev/null +++ b/app/src/main/res/drawable/border_10p_black_round_16.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_2496ff_alpha_10.xml b/app/src/main/res/drawable/button_round_2496ff_alpha_10.xml index 59dd042258..eac647bcc8 100644 --- a/app/src/main/res/drawable/button_round_2496ff_alpha_10.xml +++ b/app/src/main/res/drawable/button_round_2496ff_alpha_10.xml @@ -4,6 +4,6 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_black_alpha_60.xml b/app/src/main/res/drawable/button_round_black_alpha_60.xml index 2131b51661..e3597ca745 100644 --- a/app/src/main/res/drawable/button_round_black_alpha_60.xml +++ b/app/src/main/res/drawable/button_round_black_alpha_60.xml @@ -4,6 +4,6 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/download_dialog_item_progress.xml b/app/src/main/res/drawable/download_dialog_item_progress.xml index 8b81d5f24f..3b67680ce7 100644 --- a/app/src/main/res/drawable/download_dialog_item_progress.xml +++ b/app/src/main/res/drawable/download_dialog_item_progress.xml @@ -4,14 +4,14 @@ + android:endColor="#142496FF" + android:startColor="#142496FF" /> - + + android:color="#142496FF" /> @@ -21,12 +21,12 @@ - + - + + android:color="#2970BBFF" /> diff --git a/app/src/main/res/drawable/download_dialog_status_collection.xml b/app/src/main/res/drawable/download_dialog_status_collection.xml new file mode 100644 index 0000000000..8c910d2b23 --- /dev/null +++ b/app/src/main/res/drawable/download_dialog_status_collection.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/download_dialog_status_collection_gray.xml b/app/src/main/res/drawable/download_dialog_status_collection_gray.xml new file mode 100644 index 0000000000..e75288d5a1 --- /dev/null +++ b/app/src/main/res/drawable/download_dialog_status_collection_gray.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/download_dialog_status_download.xml b/app/src/main/res/drawable/download_dialog_status_download.xml new file mode 100644 index 0000000000..32c3a4ec95 --- /dev/null +++ b/app/src/main/res/drawable/download_dialog_status_download.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/download_dialog_status_download_gray.xml b/app/src/main/res/drawable/download_dialog_status_download_gray.xml new file mode 100644 index 0000000000..2f1a29b6e1 --- /dev/null +++ b/app/src/main/res/drawable/download_dialog_status_download_gray.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/download_dialog_status_pause.xml b/app/src/main/res/drawable/download_dialog_status_pause.xml new file mode 100644 index 0000000000..d513061f6f --- /dev/null +++ b/app/src/main/res/drawable/download_dialog_status_pause.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/download_dialog_version_note.xml b/app/src/main/res/drawable/download_dialog_version_note.xml index c8a7eef482..855830a0c8 100644 --- a/app/src/main/res/drawable/download_dialog_version_note.xml +++ b/app/src/main/res/drawable/download_dialog_version_note.xml @@ -4,7 +4,7 @@ + android:color="@color/divider" /> diff --git a/app/src/main/res/drawable/game_collection_poster_mask.xml b/app/src/main/res/drawable/game_collection_poster_mask.xml index 7227ba3990..6f3766673f 100644 --- a/app/src/main/res/drawable/game_collection_poster_mask.xml +++ b/app/src/main/res/drawable/game_collection_poster_mask.xml @@ -5,6 +5,6 @@ \ No newline at end of file diff --git a/app/src/main/res/drawable/game_detail_video_bottom_bg.xml b/app/src/main/res/drawable/game_detail_video_bottom_bg.xml index 83ed383b91..569bad62d9 100644 --- a/app/src/main/res/drawable/game_detail_video_bottom_bg.xml +++ b/app/src/main/res/drawable/game_detail_video_bottom_bg.xml @@ -7,7 +7,7 @@ android:topRightRadius="0dp" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_gamedetail_smooth_download_hint.xml b/app/src/main/res/drawable/ic_gamedetail_smooth_download_hint.xml new file mode 100644 index 0000000000..438e44856a --- /dev/null +++ b/app/src/main/res/drawable/ic_gamedetail_smooth_download_hint.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_selector_default.xml b/app/src/main/res/drawable/ic_selector_default.xml new file mode 100644 index 0000000000..edc57afcbf --- /dev/null +++ b/app/src/main/res/drawable/ic_selector_default.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/icon_more.xml b/app/src/main/res/drawable/icon_more.xml new file mode 100644 index 0000000000..5efd89f935 --- /dev/null +++ b/app/src/main/res/drawable/icon_more.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/oval_hint_accent_stroke_bg.xml b/app/src/main/res/drawable/oval_hint_accent_stroke_bg.xml new file mode 100644 index 0000000000..941f8e3052 --- /dev/null +++ b/app/src/main/res/drawable/oval_hint_accent_stroke_bg.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/progressbar_game_collection_primary.xml b/app/src/main/res/drawable/progressbar_game_collection_primary.xml index 9ab1c3f4b7..7f44f970bd 100644 --- a/app/src/main/res/drawable/progressbar_game_collection_primary.xml +++ b/app/src/main/res/drawable/progressbar_game_collection_primary.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/visit_history_video_bottom_bg.xml b/app/src/main/res/drawable/visit_history_video_bottom_bg.xml index 1cc116bec2..8e20ea5d51 100644 --- a/app/src/main/res/drawable/visit_history_video_bottom_bg.xml +++ b/app/src/main/res/drawable/visit_history_video_bottom_bg.xml @@ -3,7 +3,7 @@ \ No newline at end of file diff --git a/app/src/main/res/drawable/vspace_dialog_top_background.xml b/app/src/main/res/drawable/vspace_dialog_top_background.xml new file mode 100644 index 0000000000..8f8ee605c3 --- /dev/null +++ b/app/src/main/res/drawable/vspace_dialog_top_background.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/vspace_loading_top_background.xml b/app/src/main/res/drawable/vspace_loading_top_background.xml new file mode 100644 index 0000000000..cafd5724c0 --- /dev/null +++ b/app/src/main/res/drawable/vspace_loading_top_background.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_background_clip.xml b/app/src/main/res/layout/activity_background_clip.xml index 49c8ca158b..9a80012f5e 100644 --- a/app/src/main/res/layout/activity_background_clip.xml +++ b/app/src/main/res/layout/activity_background_clip.xml @@ -23,7 +23,7 @@ diff --git a/app/src/main/res/layout/activity_comment.xml b/app/src/main/res/layout/activity_comment.xml index c2abaa65f0..46b2fb1679 100644 --- a/app/src/main/res/layout/activity_comment.xml +++ b/app/src/main/res/layout/activity_comment.xml @@ -8,7 +8,7 @@ android:id="@+id/maskView" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black_alpha_60"/> + android:background="@color/black_alpha_40"/> diff --git a/app/src/main/res/layout/activity_poster_edit.xml b/app/src/main/res/layout/activity_poster_edit.xml index 540417a7a9..61047d1d48 100644 --- a/app/src/main/res/layout/activity_poster_edit.xml +++ b/app/src/main/res/layout/activity_poster_edit.xml @@ -89,6 +89,7 @@ android:id="@+id/activity_tab_layout" android:layout_width="match_parent" android:layout_height="match_parent" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/activity_shell.xml b/app/src/main/res/layout/activity_shell.xml index a8aa0da184..198715a30b 100644 --- a/app/src/main/res/layout/activity_shell.xml +++ b/app/src/main/res/layout/activity_shell.xml @@ -1,7 +1,7 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_vdownload_manager.xml b/app/src/main/res/layout/activity_vdownload_manager.xml new file mode 100644 index 0000000000..1b659a5a1e --- /dev/null +++ b/app/src/main/res/layout/activity_vdownload_manager.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_video_game.xml b/app/src/main/res/layout/activity_video_game.xml index 13a8c7d922..fa7940f7a1 100644 --- a/app/src/main/res/layout/activity_video_game.xml +++ b/app/src/main/res/layout/activity_video_game.xml @@ -153,6 +153,7 @@ app:tabIndicatorHeight="0dp" app:tabMode="fixed" app:tabSelectedTextColor="@color/theme_font" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/big_image_recommend_item.xml b/app/src/main/res/layout/big_image_recommend_item.xml new file mode 100644 index 0000000000..b1694aa9bc --- /dev/null +++ b/app/src/main/res/layout/big_image_recommend_item.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/community_answer_item.xml b/app/src/main/res/layout/community_answer_item.xml index bbb7e5fea6..160d8879bc 100644 --- a/app/src/main/res/layout/community_answer_item.xml +++ b/app/src/main/res/layout/community_answer_item.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/background_white" android:orientation="horizontal"> @@ -136,18 +136,64 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/background_white" - android:paddingTop="10dp" android:paddingEnd="20dp" android:paddingBottom="10dp"> + + + + + + + + @@ -171,10 +217,11 @@ android:layout_width="24dp" android:layout_height="24dp" android:layout_marginStart="20dp" + android:layout_marginTop="10dp" android:background="@drawable/ic_gamedetail_reserve" android:visibility="gone" app:layout_constraintStart_toEndOf="@id/iv_concern" - app:layout_constraintTop_toTopOf="@+id/iv_concern" + app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> + + diff --git a/app/src/main/res/layout/dialog_choose_forum.xml b/app/src/main/res/layout/dialog_choose_forum.xml index a06364832f..7814be902f 100644 --- a/app/src/main/res/layout/dialog_choose_forum.xml +++ b/app/src/main/res/layout/dialog_choose_forum.xml @@ -11,7 +11,7 @@ android:id="@+id/maskView" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black_alpha_60" /> + android:background="@color/black_alpha_40" /> diff --git a/app/src/main/res/layout/dialog_game_detail_more.xml b/app/src/main/res/layout/dialog_game_detail_more.xml index 32e33d7bf7..8cd185c586 100644 --- a/app/src/main/res/layout/dialog_game_detail_more.xml +++ b/app/src/main/res/layout/dialog_game_detail_more.xml @@ -160,6 +160,17 @@ android:orientation="horizontal" android:paddingLeft="16dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_vspace.xml b/app/src/main/res/layout/dialog_vspace.xml new file mode 100644 index 0000000000..34b417a5f2 --- /dev/null +++ b/app/src/main/res/layout/dialog_vspace.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/download_dialog_item.xml b/app/src/main/res/layout/download_dialog_item.xml index 326f0ccb25..38bd201966 100644 --- a/app/src/main/res/layout/download_dialog_item.xml +++ b/app/src/main/res/layout/download_dialog_item.xml @@ -100,7 +100,7 @@ android:layout_width="8dp" android:layout_height="8dp" android:layout_marginRight="8dp" - android:src="@drawable/download_dialog_status_download_gray" + app:srcCompat="@drawable/download_dialog_status_download_gray" app:layout_constraintBottom_toBottomOf="@+id/icon" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="@+id/icon" /> @@ -158,7 +158,7 @@ + app:srcCompat="@drawable/ic_recommend_label_left" /> + app:srcCompat="@drawable/ic_recommend_label_right" /> diff --git a/app/src/main/res/layout/forum_my_follow.xml b/app/src/main/res/layout/forum_my_follow.xml index 1a8ff3f591..240d700816 100644 --- a/app/src/main/res/layout/forum_my_follow.xml +++ b/app/src/main/res/layout/forum_my_follow.xml @@ -2,6 +2,7 @@ @@ -43,7 +44,7 @@ android:id="@+id/moreIv" android:layout_width="12dp" android:layout_height="12dp" - android:src="@drawable/icon_more" /> + app:srcCompat="@drawable/icon_more" /> diff --git a/app/src/main/res/layout/fragment_ask_recommends_wrapper.xml b/app/src/main/res/layout/fragment_ask_recommends_wrapper.xml deleted file mode 100644 index 362f88c591..0000000000 --- a/app/src/main/res/layout/fragment_ask_recommends_wrapper.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_avatar_border.xml b/app/src/main/res/layout/fragment_avatar_border.xml index a7b2bf458e..9650609eb0 100644 --- a/app/src/main/res/layout/fragment_avatar_border.xml +++ b/app/src/main/res/layout/fragment_avatar_border.xml @@ -136,6 +136,7 @@ app:tabPaddingEnd="0dp" app:tabIndicatorHeight="0dp" app:tabMode="scrollable" + app:tabIndicator="@null" app:tabRippleColor="@color/transparent" /> diff --git a/app/src/main/res/layout/fragment_comment.xml b/app/src/main/res/layout/fragment_comment.xml index 129b039428..d88b4f056f 100644 --- a/app/src/main/res/layout/fragment_comment.xml +++ b/app/src/main/res/layout/fragment_comment.xml @@ -30,7 +30,7 @@ android:id="@+id/shadowView" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black_alpha_60" + android:background="@color/black_alpha_40" android:visibility="gone" /> diff --git a/app/src/main/res/layout/fragment_community.xml b/app/src/main/res/layout/fragment_community.xml index 025a625ef7..42db6872ed 100644 --- a/app/src/main/res/layout/fragment_community.xml +++ b/app/src/main/res/layout/fragment_community.xml @@ -108,6 +108,7 @@ android:id="@+id/fragment_tab_layout" android:layout_width="match_parent" android:layout_height="match_parent" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/fragment_community_home.xml b/app/src/main/res/layout/fragment_community_home.xml index 0819f27234..4f2d34ac78 100644 --- a/app/src/main/res/layout/fragment_community_home.xml +++ b/app/src/main/res/layout/fragment_community_home.xml @@ -57,6 +57,7 @@ app:tabMaxWidth="0dp" app:tabMinWidth="0dp" app:tabMode="scrollable" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/fragment_energy_center.xml b/app/src/main/res/layout/fragment_energy_center.xml index df89218e7f..2c70744875 100644 --- a/app/src/main/res/layout/fragment_energy_center.xml +++ b/app/src/main/res/layout/fragment_energy_center.xml @@ -118,6 +118,7 @@ android:layout_height="44dp" app:tabMaxWidth="0dp" app:tabMode="fixed" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/fragment_energy_house.xml b/app/src/main/res/layout/fragment_energy_house.xml index 89207c0c33..1386295fb5 100644 --- a/app/src/main/res/layout/fragment_energy_house.xml +++ b/app/src/main/res/layout/fragment_energy_house.xml @@ -216,6 +216,7 @@ app:tabMaxWidth="0dp" app:tabMinWidth="0dp" app:tabMode="scrollable" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/fragment_forum_detail.xml b/app/src/main/res/layout/fragment_forum_detail.xml index 0a20d8955a..f1315a3260 100644 --- a/app/src/main/res/layout/fragment_forum_detail.xml +++ b/app/src/main/res/layout/fragment_forum_detail.xml @@ -379,7 +379,7 @@ android:layout_marginRight="16dp" app:containerBackground="@drawable/button_round_f5f5f5" app:containerPadding="1dp" - app:indicatorBackground="@drawable/progressbar_game_collection_primary" + app:indicatorBackground="@drawable/bg_game_collection_sfv_indicator" app:sfv_itemHeight="24dp" app:sfv_itemWidth="36dp" app:sfv_textColor="@color/game_collection_rg_button_selector" @@ -395,7 +395,7 @@ android:layout_marginRight="16dp" app:containerBackground="@drawable/button_round_f5f5f5" app:containerPadding="1dp" - app:indicatorBackground="@drawable/progressbar_game_collection_primary" + app:indicatorBackground="@drawable/bg_game_collection_sfv_indicator" app:sfv_itemHeight="24dp" app:sfv_itemWidth="36dp" app:sfv_textColor="@color/game_collection_rg_button_selector" diff --git a/app/src/main/res/layout/fragment_forum_home.xml b/app/src/main/res/layout/fragment_forum_home.xml deleted file mode 100644 index 9c53e3889d..0000000000 --- a/app/src/main/res/layout/fragment_forum_home.xml +++ /dev/null @@ -1,252 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_forum_video_detail.xml b/app/src/main/res/layout/fragment_forum_video_detail.xml index d2220a6710..3e4548f505 100644 --- a/app/src/main/res/layout/fragment_forum_video_detail.xml +++ b/app/src/main/res/layout/fragment_forum_video_detail.xml @@ -161,6 +161,7 @@ android:id="@+id/fragment_tab_layout" android:layout_width="match_parent" android:layout_height="@dimen/tab_layout_height" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> + android:background="@color/black_alpha_60" /> diff --git a/app/src/main/res/layout/fragment_main_home_wrapper.xml b/app/src/main/res/layout/fragment_main_home_wrapper.xml index d26d8077a4..cde90ea6d4 100644 --- a/app/src/main/res/layout/fragment_main_home_wrapper.xml +++ b/app/src/main/res/layout/fragment_main_home_wrapper.xml @@ -68,6 +68,7 @@ android:layout_height="match_parent" app:tabMaxWidth="0dp" app:tabMode="scrollable" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/fragment_no_padding_tablayout_viewpager.xml b/app/src/main/res/layout/fragment_no_padding_tablayout_viewpager.xml index 2aaa165b03..1086fe34d1 100644 --- a/app/src/main/res/layout/fragment_no_padding_tablayout_viewpager.xml +++ b/app/src/main/res/layout/fragment_no_padding_tablayout_viewpager.xml @@ -23,6 +23,7 @@ android:layout_height="@dimen/tab_layout_height" app:tabPaddingEnd="0dp" app:tabPaddingStart="0dp" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/fragment_preview_video.xml b/app/src/main/res/layout/fragment_preview_video.xml index ebbe8fd3a7..cfe140b107 100644 --- a/app/src/main/res/layout/fragment_preview_video.xml +++ b/app/src/main/res/layout/fragment_preview_video.xml @@ -20,7 +20,7 @@ android:id="@+id/statusBar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/black_alpha_40" + android:background="@color/black_alpha_60" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -28,7 +28,7 @@ @@ -44,7 +44,7 @@ diff --git a/app/src/main/res/layout/fragment_simulator_game.xml b/app/src/main/res/layout/fragment_simulator_game.xml index ad72db0dc6..47075daf76 100644 --- a/app/src/main/res/layout/fragment_simulator_game.xml +++ b/app/src/main/res/layout/fragment_simulator_game.xml @@ -27,6 +27,7 @@ android:layout_width="match_parent" android:layout_height="@dimen/tab_layout_height" app:tabMode="scrollable" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/fragment_subject_tab.xml b/app/src/main/res/layout/fragment_subject_tab.xml index 87fb7d4f7a..097340550d 100644 --- a/app/src/main/res/layout/fragment_subject_tab.xml +++ b/app/src/main/res/layout/fragment_subject_tab.xml @@ -35,6 +35,7 @@ app:tabIndicatorHeight="0dp" app:tabRippleColor="@color/transparent" app:tabSelectedTextColor="@color/theme" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/app/src/main/res/layout/fragment_vdownload_manager.xml b/app/src/main/res/layout/fragment_vdownload_manager.xml new file mode 100644 index 0000000000..fadac3a3bb --- /dev/null +++ b/app/src/main/res/layout/fragment_vdownload_manager.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_vdownload_manager_wrapper.xml b/app/src/main/res/layout/fragment_vdownload_manager_wrapper.xml new file mode 100644 index 0000000000..2c7d2488de --- /dev/null +++ b/app/src/main/res/layout/fragment_vdownload_manager_wrapper.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_video_comment_list.xml b/app/src/main/res/layout/fragment_video_comment_list.xml index 1d816d8442..17e0db558c 100644 --- a/app/src/main/res/layout/fragment_video_comment_list.xml +++ b/app/src/main/res/layout/fragment_video_comment_list.xml @@ -51,7 +51,7 @@ android:layout_centerVertical="true" app:containerBackground="@drawable/button_round_f5f5f5" app:containerPadding="1dp" - app:indicatorBackground="@drawable/progressbar_game_collection_primary" + app:indicatorBackground="@drawable/bg_game_collection_sfv_indicator" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/fragment_vspace_loading.xml b/app/src/main/res/layout/fragment_vspace_loading.xml new file mode 100644 index 0000000000..c155933fbd --- /dev/null +++ b/app/src/main/res/layout/fragment_vspace_loading.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_double_card_item.xml b/app/src/main/res/layout/game_double_card_item.xml new file mode 100644 index 0000000000..9e3a174ed4 --- /dev/null +++ b/app/src/main/res/layout/game_double_card_item.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_double_card_list.xml b/app/src/main/res/layout/game_double_card_list.xml new file mode 100644 index 0000000000..2c9a62b04f --- /dev/null +++ b/app/src/main/res/layout/game_double_card_list.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_horizontal_list.xml b/app/src/main/res/layout/game_horizontal_list.xml index f9de76c4af..a2d69d7245 100644 --- a/app/src/main/res/layout/game_horizontal_list.xml +++ b/app/src/main/res/layout/game_horizontal_list.xml @@ -12,10 +12,10 @@ android:layout_marginTop="37dp" android:background="@color/btn_gray_light" android:visibility="gone" - app:layout_constraintTop_toTopOf="@id/horizontal_rv" /> + app:layout_constraintTop_toTopOf="@id/recycler_view" /> + + diff --git a/app/src/main/res/layout/gamedetail_body.xml b/app/src/main/res/layout/gamedetail_body.xml index d731de90e6..23d9e364fe 100644 --- a/app/src/main/res/layout/gamedetail_body.xml +++ b/app/src/main/res/layout/gamedetail_body.xml @@ -223,6 +223,16 @@ tools:text="游戏大事件游戏大事件" tools:visibility="visible" /> + + diff --git a/app/src/main/res/layout/home_game_item.xml b/app/src/main/res/layout/home_game_item.xml index 941666cac7..dc149733d6 100644 --- a/app/src/main/res/layout/home_game_item.xml +++ b/app/src/main/res/layout/home_game_item.xml @@ -60,6 +60,7 @@ android:layout_marginStart="4dp" android:layout_marginLeft="4dp" android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" android:background="@drawable/bg_advance_download_game_subtitle" android:paddingStart="2dp" android:paddingEnd="2dp" @@ -68,36 +69,74 @@ android:textSize="@dimen/tag_text_size" android:visibility="gone" app:layout_constraintBottom_toBottomOf="@+id/game_name" - app:layout_constraintEnd_toStartOf="@+id/game_rating" + app:layout_constraintEnd_toStartOf="@+id/download_btn" app:layout_constraintStart_toEndOf="@+id/game_name" app:layout_constraintTop_toTopOf="@+id/game_name" app:layout_goneMarginStart="0dp" tools:text="副标题" /> - + app:layout_constraintTop_toBottomOf="@+id/game_name"> + + + + + + + diff --git a/app/src/main/res/layout/item_article_detail_comment.xml b/app/src/main/res/layout/item_article_detail_comment.xml index dbacc8fe1c..5f85115823 100644 --- a/app/src/main/res/layout/item_article_detail_comment.xml +++ b/app/src/main/res/layout/item_article_detail_comment.xml @@ -350,7 +350,7 @@ android:id="@+id/divider" android:layout_width="match_parent" android:layout_height="8dp" - android:background="@color/text_F8F8F8" + android:background="@color/background" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/item_community_image.xml b/app/src/main/res/layout/item_community_image.xml index 790df8a964..22ebc7bc46 100644 --- a/app/src/main/res/layout/item_community_image.xml +++ b/app/src/main/res/layout/item_community_image.xml @@ -44,7 +44,7 @@ android:id="@+id/pendingView" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black_alpha_40" + android:background="@color/black_alpha_60" android:gravity="center" android:lineSpacingExtra="4dp" android:paddingLeft="16dp" diff --git a/app/src/main/res/layout/item_float_game_load_complete.xml b/app/src/main/res/layout/item_float_game_load_complete.xml new file mode 100644 index 0000000000..be9a28e5d9 --- /dev/null +++ b/app/src/main/res/layout/item_float_game_load_complete.xml @@ -0,0 +1,43 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_game_detail_content_card_content.xml b/app/src/main/res/layout/item_game_detail_content_card_content.xml new file mode 100644 index 0000000000..cc8049d872 --- /dev/null +++ b/app/src/main/res/layout/item_game_detail_content_card_content.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_recent_vgame.xml b/app/src/main/res/layout/item_home_recent_vgame.xml new file mode 100644 index 0000000000..4e3f6ca6d4 --- /dev/null +++ b/app/src/main/res/layout/item_home_recent_vgame.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_vgame.xml b/app/src/main/res/layout/item_home_vgame.xml new file mode 100644 index 0000000000..0881adf7d0 --- /dev/null +++ b/app/src/main/res/layout/item_home_vgame.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_vfeedback_option.xml b/app/src/main/res/layout/item_vfeedback_option.xml new file mode 100644 index 0000000000..7d8a7f3fc6 --- /dev/null +++ b/app/src/main/res/layout/item_vfeedback_option.xml @@ -0,0 +1,24 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_vgame_download_manager.xml b/app/src/main/res/layout/item_vgame_download_manager.xml new file mode 100644 index 0000000000..df89ce04bd --- /dev/null +++ b/app/src/main/res/layout/item_vgame_download_manager.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_article_item_video.xml b/app/src/main/res/layout/layout_article_item_video.xml index c9e6bbf129..37452bf0f1 100644 --- a/app/src/main/res/layout/layout_article_item_video.xml +++ b/app/src/main/res/layout/layout_article_item_video.xml @@ -59,7 +59,7 @@ android:id="@+id/pendingView" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black_alpha_40" + android:background="@color/black_alpha_60" android:gravity="center" android:textColor="@color/white_alpha_80" android:textSize="12sp" @@ -107,7 +107,7 @@ android:id="@+id/completeContainer" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black_alpha_40" + android:background="@color/black_alpha_60" android:gravity="center" android:orientation="horizontal" android:visibility="gone" diff --git a/app/src/main/res/layout/layout_float_load_complete.xml b/app/src/main/res/layout/layout_float_load_complete.xml new file mode 100644 index 0000000000..a492126422 --- /dev/null +++ b/app/src/main/res/layout/layout_float_load_complete.xml @@ -0,0 +1,45 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_forum_video_detail_videoview_portrait.xml b/app/src/main/res/layout/layout_forum_video_detail_videoview_portrait.xml index 78cd422789..4786795e6b 100644 --- a/app/src/main/res/layout/layout_forum_video_detail_videoview_portrait.xml +++ b/app/src/main/res/layout/layout_forum_video_detail_videoview_portrait.xml @@ -54,7 +54,7 @@ android:id="@+id/pendingView" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black_alpha_40" + android:background="@color/black_alpha_60" android:gravity="center" android:text="@string/pending_status" android:textColor="@color/white_alpha_80" diff --git a/app/src/main/res/layout/layout_game_detail_content_card_large.xml b/app/src/main/res/layout/layout_game_detail_content_card_large.xml new file mode 100644 index 0000000000..0a939628f9 --- /dev/null +++ b/app/src/main/res/layout/layout_game_detail_content_card_large.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_game_detail_content_card_small.xml b/app/src/main/res/layout/layout_game_detail_content_card_small.xml new file mode 100644 index 0000000000..89b6ea1752 --- /dev/null +++ b/app/src/main/res/layout/layout_game_detail_content_card_small.xml @@ -0,0 +1,50 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_menu_download_manager.xml b/app/src/main/res/layout/layout_menu_download_manager.xml new file mode 100644 index 0000000000..20a70e2b9f --- /dev/null +++ b/app/src/main/res/layout/layout_menu_download_manager.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_setting_item.xml b/app/src/main/res/layout/layout_setting_item.xml index 25c8c929b6..238985fe11 100644 --- a/app/src/main/res/layout/layout_setting_item.xml +++ b/app/src/main/res/layout/layout_setting_item.xml @@ -114,10 +114,10 @@ android:id="@+id/selectedIv" android:layout_width="16dp" android:layout_height="16dp" - android:src="@drawable/ic_video_setting_select" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_video_setting_select" /> \ No newline at end of file diff --git a/app/src/main/res/layout/piece_article_detail_comment_filter.xml b/app/src/main/res/layout/piece_article_detail_comment_filter.xml index 211ed47be8..f741b61a8f 100644 --- a/app/src/main/res/layout/piece_article_detail_comment_filter.xml +++ b/app/src/main/res/layout/piece_article_detail_comment_filter.xml @@ -79,7 +79,7 @@ android:layout_marginRight="16dp" app:containerBackground="@drawable/button_round_f5f5f5" app:containerPadding="1dp" - app:indicatorBackground="@drawable/progressbar_game_collection_primary" + app:indicatorBackground="@drawable/bg_game_collection_sfv_indicator" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/search_default_hot_item.xml b/app/src/main/res/layout/search_default_hot_item.xml index 702d6893e4..955cd63e9b 100644 --- a/app/src/main/res/layout/search_default_hot_item.xml +++ b/app/src/main/res/layout/search_default_hot_item.xml @@ -33,6 +33,7 @@ android:ellipsize="end" android:maxLines="1" android:textSize="12sp" + android:textColor="@color/text_title" app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="@+id/icon" app:layout_constraintEnd_toStartOf="@+id/labelIv" @@ -46,7 +47,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="4dp" - android:layout_marginRight="4dp" + android:layout_marginRight="12dp" app:layout_constraintBottom_toBottomOf="@+id/name" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/name" diff --git a/app/src/main/res/layout/item_usage_stats.xml b/app/src/main/res/layout/view_simple_toggle.xml similarity index 61% rename from app/src/main/res/layout/item_usage_stats.xml rename to app/src/main/res/layout/view_simple_toggle.xml index 4be08254e5..da1cffb28f 100644 --- a/app/src/main/res/layout/item_usage_stats.xml +++ b/app/src/main/res/layout/view_simple_toggle.xml @@ -3,11 +3,12 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="40dp" - android:paddingLeft="16dp" - android:background="@color/text_FAFAFA" - android:paddingRight="16dp"> + android:background="@color/bg_F8F8F8" + android:paddingStart="16dp" + android:paddingEnd="16dp"> + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_download_manager.xml b/app/src/main/res/menu/menu_download_manager.xml new file mode 100644 index 0000000000..4d88b08cfb --- /dev/null +++ b/app/src/main/res/menu/menu_download_manager.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2c42aa51a3..972723e9bf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -372,12 +372,16 @@ 下载中 插件化 安装 + 继续 + 暂停 启动 更新 更新中 等待中 打开 试玩 + 畅玩 + 启动(畅玩) 展开 > 解压中 查看 diff --git a/build.gradle b/build.gradle index 7800a60303..b826e6fa10 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. apply from: 'dependencies.gradle' +apply from: 'vspace-bridge/config.gradle' buildscript { ext.kotlin_version = '1.6.21' diff --git a/dependencies.gradle b/dependencies.gradle index 2f31b5b79f..f7f37b8290 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,14 +1,14 @@ ext { //Android buildToolsVersion = "30.0.2" - compileSdkVersion = 30 + compileSdkVersion = 31 minSdkVersion = 16 targetSdkVersion = 28 // application info (每个大版本之间的 versionCode 增加 20) - versionCode = 574 - versionName = "5.11.4" + versionCode = 593 + versionName = "5.12.3" applicationId = "com.gh.gamecenter" // AndroidX @@ -19,7 +19,7 @@ ext { fragment = "1.2.5" annotation = "1.1.0" percentLayout = "1.0.0" - constraintLayout = "2.0.4" + constraintLayout = "2.1.3" databinding = "3.4.1" recyclerView = "1.1.0" lifeCycle = "2.2.0" @@ -109,7 +109,7 @@ ext { whatTheStack = "0.1.0_rt" apkParser = "v2.6.10" nanohttpd = "2.3.1" - aliyunLog = "2.5.14" + aliyunLog = "2.6.4" easyFloat = "2.0.4" shapeOfView = "1.4.7" splitties = "3.0.0" diff --git a/gradle.properties b/gradle.properties index c1170209d7..3a7e9c4aaa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -55,6 +55,8 @@ DEV_API_HOST=https\://dev-and-api.ghzs.com/v5d5d0/ API_HOST=https\://and-api.ghzs.com/v5d5d0/ NEW_DEV_API_HOST=https\://dev-app-api.ghzs.com/ NEW_API_HOST=https\://app-api.ghzs.com/ +DEV_VAPI_HOST=https://dev-app-api.796697.com +VAPI_HOST=https://app-api.796697.com android.useAndroidX=true android.enableJetifier=true diff --git a/libraries/LGLibrary b/libraries/LGLibrary index 692d58bc9b..768dcec118 160000 --- a/libraries/LGLibrary +++ b/libraries/LGLibrary @@ -1 +1 @@ -Subproject commit 692d58bc9b494bf1d04db7809d43acbedaeb326c +Subproject commit 768dcec118e957f9ae3c64c88f2a3a282c0cc53f diff --git a/module_common/build.gradle b/module_common/build.gradle index b37fd4b0bd..a25e4f8581 100644 --- a/module_common/build.gradle +++ b/module_common/build.gradle @@ -31,6 +31,7 @@ android { */ buildConfigField "long", "BUILD_TIME", "0" buildConfigField "boolean", "IS_NIGHT_MODE_ON", "true" + buildConfigField "boolean", "IS_VGAME_ON", "true" } buildFeatures { diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java index 7b25f31dde..1b2667dd40 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java @@ -235,13 +235,25 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy String shareTitle, String shareSummary, ShareUtils.ShareEntrance shareEntrance, String id) { - ShareUtils.getInstance(this).showShareWindows(this, + showShareWithCallback(url, icon, shareTitle, shareSummary, shareEntrance, id, null); + } + + public void showShareWithCallback(String url, + String icon, + String shareTitle, + String shareSummary, + ShareUtils.ShareEntrance shareEntrance, + String id, + ShareUtils.ShareCallBack callBack) { + ShareUtils.getInstance(this).showShareWindowsCallback(this, getWindow().getDecorView(), url, icon, shareTitle, shareSummary, - shareEntrance, id); + shareEntrance, + id, + callBack); if (shareEntrance == ShareUtils.ShareEntrance.game || shareEntrance == ShareUtils.ShareEntrance.plugin) { MtaHelper.onEvent("内容分享", "内容分享", shareTitle + shareSummary); } else { diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/ToolBarActivity.java b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/ToolBarActivity.java index 13497caf18..9c07d24dd2 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/ToolBarActivity.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/ToolBarActivity.java @@ -350,4 +350,12 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon mTitleTv.setTextColor(ContextCompat.getColor(this, R.color.text_black)); } } + + /** + * 更新 targetFragment 引用 + */ + protected void updateTargetFragment(Fragment fragment) { + mTargetFragment = fragment; + } + } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/callback/ConfirmListener.kt b/module_common/src/main/java/com/gh/gamecenter/common/callback/ConfirmListener.kt index 61536998a9..0b3a9beed9 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/callback/ConfirmListener.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/callback/ConfirmListener.kt @@ -1,5 +1,5 @@ package com.gh.gamecenter.common.callback -interface ConfirmListener { +fun interface ConfirmListener { fun onConfirm() } \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java index 5ea8ded2dd..6e298fbf37 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java @@ -50,7 +50,7 @@ public class Constants { // 最近显示的弹窗信息 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 SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID = "last_accepted_privacy_dialog_id"; // 游戏图标和图标角标 @@ -59,6 +59,8 @@ public class Constants { public static final String IS_PLATFORM_RECOMMEND = "isPlatformRecommend"; + public static final String APK_MD5 = "apk_md5"; + // 下载 id,一般来说跟下载文件名一样 public static final String DOWNLOAD_ID = "download_id"; @@ -69,6 +71,7 @@ public class Constants { public static final String SIMULATOR_DOWNLOAD = "下载模拟器"; public static final String SIMULATOR_GAME = "simulator_game"; public static final String SIMULATOR = "simulator"; + public static final String SMOOTH_GAME = "smooth_game"; // 畅玩类型的游戏 public static final String GAME_NAME = "game_name"; 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"; @@ -204,6 +207,9 @@ public class Constants { // 补充配置项 public static final String SP_NEW_SETTINGS = "new_settings"; + // 畅玩组件的配置 + public static final String SP_V_SETTINGS = "v_settings"; + // 头像挂件ID public static final String SP_CHOOSE_AVATAR_ID = "choose_avatar_id"; @@ -251,6 +257,8 @@ public class Constants { //游戏库导航栏小红点提示 public static final String SP_GAME_NAVIGATION = "game_navigation"; + // V游戏空间是否被使用过 + public static final String SP_IS_VSPACE_USED = "is_vspace_used"; //手机号码匹配规则 public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$"; @@ -293,6 +301,9 @@ public class Constants { 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 SMOOTH_GAME_PRIVACY_POLICY_ADDRESS = "https://sdg-static.79887.com/misc/privacy_CW.html"; + // 注销页面 public static final String LOGOUT_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_help_dev/help.html?content=5f6b1f02786564003944a693&from=ghzs"; public static final String LOGOUT_ADDRESS = "https://static-web.ghzs.com/ghzs_help/help.html?content=5f534111b1f72909fc225672&from=ghzs"; @@ -415,6 +426,9 @@ public class Constants { // 工具箱历史记录(最多4个) public static final String TOOLBOX_HISTORY = "toolbox_history"; + // 首页畅玩广场最近在玩的区域是否显示的 SP_KEY + public static final String SP_HOME_VGAME_AREA_ENABLED = "home_vgame_area_enabled"; + // 浏览器安装说明url public static final String SP_BROWSER_HINT_URL = "browser_hint_url"; public static final String DEFAULT_OPPO_BROWSER_HINT_URL = "https://static-web.ghzs.com/ghzs_help/help.html?content=5fa90fe143d91a022e0d33ff"; diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java index c66deae246..bf5b7dc823 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java @@ -202,6 +202,7 @@ public class EntranceConsts { public static final String KEY_CONFLICT_PHONE = "conflictPhone"; public static final String KEY_CONFLICT_USER = "conflictUser"; public static final String KEY_EXPOSURE_SOURCE = "exposure_source"; + public static final String KEY_EXPOSURE_SOURCE_LIST = "exposure_source_list"; public static final String KEY_BBS_ID = "bbs_id"; public static final String KEY_DIAGNOSIS = "diagnosis"; public static final String KEY_SIMULATOR = "simulator"; @@ -248,7 +249,9 @@ public class EntranceConsts { public static final String KET_TYPE = "KET_TYPE"; public static final String KET_SUMMARY = "KET_SUMMARY"; public static final String KEY_ACTIVITY_ID = "activity_id"; + public static final String KEY_IS_FROM_HOME_RECENT = "is_from_home_recent"; public static final String KEY_FORMAT = "format"; public static final String KEY_VERSION_CODE = "version_code"; public static final String KEY_PLATFORM_REQUESTS_ID = "platform_requests_id"; + public static final String KEY_TOP_ID = "top_id"; } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/ItemViewType.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/ItemViewType.java index 0efe7c0e2a..791857d269 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/ItemViewType.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/ItemViewType.java @@ -38,6 +38,8 @@ public class ItemViewType { public static final int COMMON_LINK_COLLECTION = 30; // 通用链接合集 public static final int RANK_COLLECTION = 31; // 排行榜样式专题合集 public static final int GAME_COLLECTION_ITEM = 32; // 游戏单 + public static final int DOUBLE_CARD_COLUMN = 33; // 双列卡片专题 + public static final int BIG_IMAGE_RECOMMEND = 34; // 大图推荐专题 /** * 普通列表 diff --git a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubDatabase.kt b/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubDatabase.kt deleted file mode 100644 index 2d98c8d10b..0000000000 --- a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubDatabase.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.gh.gamecenter.common.loghub - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -@Database(entities = [LoghubEvent::class], version = 2, exportSchema = false) -abstract class LoghubDatabase : RoomDatabase() { - companion object { - private const val DATABASE = "gh_loghub_database" - - private val MIGRATION_1_2: Migration = object : Migration(1, 2) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("Alter TABLE loghubEvent add isFlat INTEGER NOT NULL DEFAULT 0") - } - } - - fun buildDatabase(context: Context): LoghubDatabase { - return Room.databaseBuilder(context, LoghubDatabase::class.java, DATABASE) - .addMigrations(MIGRATION_1_2) - .build() - } - } - - abstract fun logHubEventDao(): LoghubEventDao -} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubEvent.kt b/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubEvent.kt index 11d49f6164..c503c06ee8 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubEvent.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubEvent.kt @@ -9,7 +9,6 @@ import java.util.* @Keep @Parcelize -@Entity(tableName = "loghubEvent") data class LoghubEvent( @PrimaryKey val id: String = UUID.randomUUID().toString(), diff --git a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubEventDao.kt b/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubEventDao.kt deleted file mode 100644 index 07b0f2f73a..0000000000 --- a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubEventDao.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.gh.gamecenter.common.loghub - -import androidx.room.* - -@Dao -interface LoghubEventDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertMany(eventList: List) - - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insert(event: LoghubEvent) - - @Query("SELECT * FROM LoghubEvent") - fun getAll(): List - - @Delete - fun deleteMany(eventList: List) -} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubHelper.kt b/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubHelper.kt index 621a643502..4153bc2800 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubHelper.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubHelper.kt @@ -17,13 +17,17 @@ object LoghubHelper { private val mClientMaps by lazy { hashMapOf() } - fun uploadLog(log: Log, logStore: String) { - getClient(logStore)?.addLog(log) + /** + * 上传日志至阿里云 loghub + * @param uploadImmediately 马上上传日志 + */ + fun uploadLog(log: Log, logStore: String, uploadImmediately: Boolean) { + getClient(logStore)?.addLog(log, if (uploadImmediately) 1 else 0) } fun uploadLogs(logs: List, logStore: String) { logs.forEach { - uploadLog(it, logStore) + uploadLog(it, logStore, false) } } @@ -44,19 +48,21 @@ object LoghubHelper { ACCESS_KEY_ID, ACCESS_KEY_SECRET ).apply { - // 1 开启断点续传功能, 0 关闭 - // 每次发送前会把日志保存到本地的binlog文件,只有发送成功才会删除,保证日志上传At Least Once - setPersistent(1) - // 持久化的文件名,需要保证文件所在的文件夹已创建。配置多个客户端时,不应设置相同文件 - setPersistentFilePath(HaloApp.getInstance().filesDir.absolutePath + "/${logStore}.dat") - // 是否每次AddLog强制刷新,高可靠性场景建议打开 - setPersistentForceFlush(1) - // 持久化文件滚动个数,建议设置成10。 - setPersistentMaxFileCount(10) - // 每个持久化文件的大小,建议设置成1-10M - setPersistentMaxFileSize(1024 * 1024) - // 本地最多缓存的日志数,不建议超过1M,通常设置为65536即可 - setPersistentMaxLogCount(65536) +// // 设置主题 +// setTopic("topic") +// // 设置tag信息,此tag会附加在每条日志上 +// addTag("key", "value") + // 每个缓存的日志包的大小上限,取值为1~5242880,单位为字节。默认为1024 * 1024 + setPacketLogBytes(1024 * 1024) + // 每个缓存的日志包中包含日志数量的最大值,取值为1~4096,默认为1024 + setPacketLogCount(1024) + // 被缓存日志的发送超时时间,如果缓存超时,则会被立即发送,单位为毫秒,默认为3000 + setPacketTimeout(3000) + // 单个Producer Client实例可以使用的内存的上限,超出缓存时add_log接口会立即返回失败 + // 默认为64 * 1024 * 1024 + setMaxBufferLimit(64 * 1024 * 1024) + // 发送线程数,默认为1 + setSendThreadCount(1) //网络连接超时时间,整数,单位秒,默认为10 setConnectTimeoutSec(15) @@ -66,6 +72,11 @@ object LoghubHelper { setDestroyFlusherWaitSec(2) //sender线程池销毁最大等待时间,整数,单位秒,默认为1 setDestroySenderWaitSec(2) + //数据上传时的压缩类型,默认为LZ4压缩,0 不压缩,1 LZ4压缩,默认为1 + setCompressType(1) + //设备时间与标准时间之差,值为标准时间-设备时间,一般此种情况用于客户端设备时间不同步的场景 + //整数,单位秒,默认为0;比如当前设备时间为1607064208, 标准时间为1607064308,则值设置为 1607064308 - 1607064208 = 10 + setNtpTimeOffset(0) //日志时间与本机时间之差,超过该大小后会根据 `drop_delay_log` 选项进行处理。 //一般此种情况只会在设置persistent的情况下出现,即设备下线后,超过几天/数月启动,发送退出前未发出的日志 //整数,单位秒,默认为7*24*3600,即7天 @@ -76,8 +87,34 @@ object LoghubHelper { //是否丢弃鉴权失败的日志,0 不丢弃,1丢弃 //默认为 0,即不丢弃 setDropUnauthorizedLog(0) + // 是否使用主线程回调 + // false: 使用主线程回调。回调会在主线程上执行,且每个 client 都有自己单独的回调。 + // true: 使用 sender 线程回调。回调会在 sender 线程上执行,每次执行回调时都会 attach 一个新的 java 线程,所有 client 共用一个回调。 + // 注意:默认使用 sender 线程回调。 + setCallbackFromSenderThread(true) + + /** + * 以下为开启断点续传的配置, 按照如下配置开启断点续传功能后, 日志会先缓存到本地 + */ + // 1 开启断点续传功能, 0 关闭 + // 每次发送前会把日志保存到本地的binlog文件,只有发送成功才会删除,保证日志上传At Least Once + setPersistent(1) + // 持久化的文件名,需要保证文件所在的文件夹已创建。 + // !!!!!!!!!!!!!!!!!!!注意!!!!!!!!!!!!!!!!!!! + // 配置多个客户端时,不应设置相同文件 + setPersistentFilePath(HaloApp.getInstance().filesDir.absolutePath + "/${logStore}.dat") + // 是否每次AddLog强制刷新,高可靠性场景建议打开 + setPersistentForceFlush(1) + // 持久化文件滚动个数,建议设置成10。 + setPersistentMaxFileCount(10) + // 每个持久化文件的大小,建议设置成1-10M + setPersistentMaxFileSize(1024 * 1024) + // 本地最多缓存的日志数,不建议超过1M,通常设置为65536即可 + setPersistentMaxLogCount(65536) } + // 通过 LogProducerConfig 构造一个 LogProducerClient 实例 + // callback为可选配置, 如果不需要关注日志的发送成功或失败状态, 可以不注册 callback(正式环境不注册callback) return if (!PackageFlavorHelper.IS_TEST_FLAVOR) { LogProducerClient(config) } else { diff --git a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubUtils.kt b/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubUtils.kt index bba448ea39..8150b4e782 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubUtils.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/loghub/LoghubUtils.kt @@ -1,55 +1,32 @@ package com.gh.gamecenter.common.loghub -import android.app.Application import androidx.annotation.Keep import com.aliyun.sls.android.producer.Log import com.gh.gamecenter.common.entity.ExposureEntity import com.gh.gamecenter.common.exposure.meta.Meta -import com.gh.gamecenter.common.utils.tryWithDefaultCatch +import com.gh.gamecenter.core.runOnUiThread import org.json.JSONObject -import java.util.concurrent.ExecutorService object LoghubUtils { private const val STORE_SIZE = 100 - private lateinit var mApplication: Application private val loghubEventSet by lazy { hashSetOf() } - private var loghubEventExecutor: ExecutorService? = null - private val loghubEventDao by lazy { LoghubDatabase.buildDatabase(mApplication).logHubEventDao() } - - @JvmStatic - fun init(application: Application, executor: ExecutorService) { - mApplication = application - - loghubEventExecutor = executor - loghubEventExecutor?.execute { - tryWithDefaultCatch { - val eventList = loghubEventDao.getAll() - loghubEventSet.addAll(eventList) - } - } - } @JvmStatic @JvmOverloads fun log(logJson: JSONObject, logStore: String, forcedUpload: Boolean, isFlat: Boolean = false) { - loghubEventExecutor?.execute { - try { - val event = LoghubEvent( - time = (System.currentTimeMillis() / 1000L).toString(), - content = logJson.toString(), - logStore = logStore, - isFlat = isFlat - ) - loghubEventSet.add(event) - loghubEventDao.insert(event) - } catch (e: Exception) { - e.printStackTrace() - } + runOnUiThread { + val event = LoghubEvent( + time = (System.currentTimeMillis() / 1000L).toString(), + content = logJson.toString(), + logStore = logStore, + isFlat = isFlat + ) + loghubEventSet.add(event) if (forcedUpload || loghubEventSet.size >= STORE_SIZE) { - commitSavedLoghubEvents() + commitSavedLoghubEvents(forcedUpload) } } } @@ -57,44 +34,30 @@ object LoghubUtils { @JvmStatic @JvmOverloads fun log(jsonString: String, logStore: String, forcedUpload: Boolean, isFlat: Boolean = false) { - loghubEventExecutor?.execute { - try { - val event = LoghubEvent( - time = (System.currentTimeMillis() / 1000L).toString(), - content = jsonString, - logStore = logStore, - isFlat = isFlat - ) - loghubEventSet.add(event) - loghubEventDao.insert(event) - } catch (e: Exception) { - e.printStackTrace() - } + runOnUiThread { + val event = LoghubEvent( + time = (System.currentTimeMillis() / 1000L).toString(), + content = jsonString, + logStore = logStore, + isFlat = isFlat + ) + loghubEventSet.add(event) if (forcedUpload || loghubEventSet.size >= STORE_SIZE) { - commitSavedLoghubEvents() + commitSavedLoghubEvents(forcedUpload) } } } - fun commitSavedLoghubEvents() { - loghubEventExecutor?.execute { - // TODO 初始化 loghubHelper 去掉这个 tryCatch 块 - tryWithDefaultCatch { - if (loghubEventSet.isEmpty()) return@execute + fun commitSavedLoghubEvents(forcedUpload: Boolean) { + if (loghubEventSet.isEmpty()) return - val exposureList = loghubEventSet.toList() - - uploadEvents() - loghubEventSet.removeAll(exposureList) - loghubEventDao.deleteMany(exposureList) - } - } + uploadEvents(loghubEventSet.toList(), forcedUpload) + loghubEventSet.clear() } - private fun uploadEvents() { - for (event in loghubEventSet) { - + private fun uploadEvents(eventList: List, forcedUpload: Boolean) { + for (event in eventList) { val log = Log() // 特殊处理,以下logStore不需要用content包裹数据 if (event.logStore == "collection" || event.logStore == "common" || event.logStore == "halo-api-device-installed") { @@ -117,7 +80,7 @@ object LoghubUtils { } } - LoghubHelper.uploadLog(log, event.logStore) + LoghubHelper.uploadLog(log, event.logStore, forcedUpload) } } @@ -129,4 +92,5 @@ data class SimpleLogContainerEntity( var action: String? = null, var meta: Meta? = null, var payload: ExposureEntity? = null, - var timestamp: Long? = 0) \ No newline at end of file + var timestamp: Long? = 0 +) \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt index 0b84328c69..95e6a96f3b 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt @@ -2,6 +2,7 @@ package com.gh.gamecenter.common.utils import com.alibaba.android.arouter.launcher.ARouter import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.BuildConfig import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.core.provider.IBuildConfigProvider import com.gh.gamecenter.core.utils.SPUtils @@ -28,6 +29,20 @@ object EnvHelper { } } + @JvmStatic + fun getVHost(): String { + val buildConfig = ARouter.getInstance().build(RouteConsts.provider.buildConfig).navigation() as? IBuildConfigProvider + return if (!PackageFlavorHelper.IS_TEST_FLAVOR) { + buildConfig?.getVDevApiHost() ?: "" + } else { + if (isDevEnv) { + buildConfig?.getVDevApiHost() ?: "" + } else { + buildConfig?.getVApiHost() ?: "" + } + } + } + @JvmStatic fun getNewHost(): String { val buildConfig = ARouter.getInstance().build(RouteConsts.provider.buildConfig).navigation() as? IBuildConfigProvider diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt index c26d08c247..95f18c3c2c 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt @@ -12,6 +12,7 @@ import android.graphics.Rect import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.os.Build +import android.os.Looper import android.text.* import android.text.format.Formatter import android.text.style.ClickableSpan @@ -44,14 +45,16 @@ import com.alibaba.android.arouter.launcher.ARouter import com.facebook.drawee.view.SimpleDraweeView import com.gh.gamecenter.common.BuildConfig import com.gh.gamecenter.common.R -import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.callback.SimpleCallback import com.gh.gamecenter.common.constant.Config +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.view.CustomLinkMovementMethod import com.gh.gamecenter.common.view.ExpandTextView +import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.HaloApp import com.gh.gamecenter.core.provider.* +import com.gh.gamecenter.core.runOnUiThread import com.gh.gamecenter.core.utils.* import com.google.gson.reflect.TypeToken import com.lightgame.download.DownloadEntity @@ -973,6 +976,10 @@ fun DownloadEntity.isSimulatorGame(): Boolean { return getMetaExtra(Constants.SIMULATOR_GAME).isNotEmpty() } +fun DownloadEntity.isVGame(): Boolean { + return Constants.SMOOTH_GAME == getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) +} + /** * Process related */ @@ -1259,4 +1266,18 @@ fun Activity.updateStatusBarColor(@ColorRes nightColor: Int, @ColorRes dayColor: if (NightModeUtils.isNightMode(this)) nightColor else dayColor ) } +} + +/** + * 设置开关Lottie动画 + */ +fun LottieAnimationView.setSwitchAnimation(turnOff: Boolean) { + cancelAnimation() + when { + turnOff && NightModeUtils.isNightMode(context) -> setAnimation("lottie/switch_turnoff_dark.json") + !turnOff && NightModeUtils.isNightMode(context) -> setAnimation("lottie/switch_turnon_dark.json") + turnOff && !NightModeUtils.isNightMode(context) -> setAnimation("lottie/switch_turnoff_light.json") + !turnOff && !NightModeUtils.isNightMode(context) -> setAnimation("lottie/switch_turnon_light.json") + } + progress = 0F } \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/NewLogUtils.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/NewLogUtils.kt index 71d7d8c8a4..5f49fcef3f 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/NewLogUtils.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/NewLogUtils.kt @@ -8,13 +8,15 @@ import com.gh.gamecenter.common.tracker.Tracker import com.lightgame.utils.Utils import org.json.JSONObject +// 使用平铺内容的新埋点请添加至 NewFlatLogUtils + object NewLogUtils { private fun log(jsonObject: JSONObject, logStore: String, uploadImmediately: Boolean) { Utils.log("NewLogUtils", jsonObject.toString(4)) LoghubUtils.log(jsonObject, logStore, uploadImmediately) } - fun parseAndPutMeta(): JsonObjectBuilder.() -> Unit = { + private fun parseAndPutMeta(): JsonObjectBuilder.() -> Unit = { val meta = LogUtils.getMetaObject() val metaKeys = meta.keys() while (metaKeys.hasNext()) { diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/DumbRefreshLayout.java b/module_common/src/main/java/com/gh/gamecenter/common/view/DumbRefreshLayout.java deleted file mode 100644 index 00a5cca5b5..0000000000 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/DumbRefreshLayout.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gh.gamecenter.common.view; - -import android.content.Context; -import android.util.AttributeSet; - -import com.scwang.smartrefresh.layout.SmartRefreshLayout; -import com.scwang.smartrefresh.layout.constant.RefreshState; - -public class DumbRefreshLayout extends SmartRefreshLayout { - - public DumbRefreshLayout(Context context) { - super(context); - } - - public DumbRefreshLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public DumbRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public boolean isRefreshing() { - return mState != RefreshState.None; - } -} diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/EmptyDrawable.java b/module_common/src/main/java/com/gh/gamecenter/common/view/EmptyDrawable.java deleted file mode 100644 index f5d690acae..0000000000 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/EmptyDrawable.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.gh.gamecenter.common.view; - -import android.annotation.SuppressLint; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -/** - * 一个空的类 主要是用来回调 onLevelChange(加载进度)的内容 - */ - -public class EmptyDrawable extends Drawable { - private OnLoadingListener mLoadingListener; - - public EmptyDrawable(OnLoadingListener listener) { - this.mLoadingListener = listener; - } - - @Override - public void draw(@NonNull Canvas canvas) { - - } - - @Override - public void setAlpha(int alpha) { - - } - - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - - } - - @SuppressLint("WrongConstant") - @Override - public int getOpacity() { - return 0; - } - - //Default Max:10000 - @Override - protected boolean onLevelChange(int level) { - if (mLoadingListener != null) mLoadingListener.onProgress(level); - return false; - } - - public interface OnLoadingListener { - void onProgress(int progress); - } -} diff --git a/module_common/src/main/res/layout/activity_tablayout_no_title_viewpager.xml b/module_common/src/main/res/layout/activity_tablayout_no_title_viewpager.xml index 683509cfdd..b998419b5c 100644 --- a/module_common/src/main/res/layout/activity_tablayout_no_title_viewpager.xml +++ b/module_common/src/main/res/layout/activity_tablayout_no_title_viewpager.xml @@ -52,6 +52,7 @@ android:id="@+id/activity_tab_layout" android:layout_width="match_parent" android:layout_height="match_parent" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/module_common/src/main/res/layout/activity_tablayout_viewpager.xml b/module_common/src/main/res/layout/activity_tablayout_viewpager.xml index 90564023b4..adefd87de9 100644 --- a/module_common/src/main/res/layout/activity_tablayout_viewpager.xml +++ b/module_common/src/main/res/layout/activity_tablayout_viewpager.xml @@ -23,6 +23,7 @@ android:id="@+id/activity_tab_layout" android:layout_width="match_parent" android:layout_height="@dimen/tab_layout_height" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/module_common/src/main/res/layout/fragment_tablayout_viewpager.xml b/module_common/src/main/res/layout/fragment_tablayout_viewpager.xml index 3013f31f6c..52571a97de 100644 --- a/module_common/src/main/res/layout/fragment_tablayout_viewpager.xml +++ b/module_common/src/main/res/layout/fragment_tablayout_viewpager.xml @@ -21,6 +21,7 @@ android:id="@+id/fragment_tab_layout" android:layout_width="match_parent" android:layout_height="@dimen/tab_layout_height" + app:tabIndicator="@null" app:tabTextAppearance="@style/TabLayoutTextAppearance" /> diff --git a/module_common/src/main/res/values-night/colors.xml b/module_common/src/main/res/values-night/colors.xml index f259427c39..20f3897d06 100644 --- a/module_common/src/main/res/values-night/colors.xml +++ b/module_common/src/main/res/values-night/colors.xml @@ -159,9 +159,9 @@ #1A000000 #33000000 #4D000000 - #99000000 - #66000000 - #80000000 + #99000000 + #66000000 + #80000000 #1AFFFFFF #33FFFFFF diff --git a/module_common/src/main/res/values/colors.xml b/module_common/src/main/res/values/colors.xml index 7568981473..b72811d038 100644 --- a/module_common/src/main/res/values/colors.xml +++ b/module_common/src/main/res/values/colors.xml @@ -4,6 +4,7 @@ #2496FF #332496FF #CC2496FF + #1A2496FF #FFA142 @@ -53,6 +54,13 @@ #33000000 + + #1A2496FF + #0D000000 + @color/theme + @color/text_4BC7FF + #0D000000 + @color/white @@ -159,9 +167,31 @@ #1A000000 #33000000 #4D000000 - #99000000 - #66000000 - #80000000 + #66000000 + #80000000 + #99000000 + + #CC000000 #1AFFFFFF #33FFFFFF @@ -176,6 +206,8 @@ #b3b3b3 + #8CA0B8 + #aaaaaa #AAAAAA @@ -202,6 +234,7 @@ #F7F7F7 #FFF6E6 #EBF5FF + #F5F8FA #3797FF #EFF7FF #806F9CEF diff --git a/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt b/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt index 6a4c7519af..7cbec3dc4e 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt @@ -6,7 +6,6 @@ import com.gh.gamecenter.core.AppExecutor.heavyWeightIoExecutor import com.gh.gamecenter.core.AppExecutor.ioExecutor import com.gh.gamecenter.core.AppExecutor.lightWeightIoExecutor import com.gh.gamecenter.core.AppExecutor.uiExecutor - import io.reactivex.schedulers.Schedulers import java.util.concurrent.* @@ -16,34 +15,34 @@ import java.util.concurrent.* * [ioExecutor] 是一个最大线程数固定的线程池,较为繁重的 IO 任务可以交给它 * [uiExecutor] 是主线程的包裹,需要切换至主线程执行可以用它 * [lightWeightIoExecutor] 是一个单线程的线程池,轻量级且需要保证同一线程的 IO 任务可以交给它 - * [heavyWeightIoExecutor] 重量级的线程池,一些高频调用但不用保证结果的任务可以交给它 - * [logExecutor] 只为上传 log 而使用的线程池 + * [heavyWeightIoExecutor] 是一个重量级任务的线程池,一些高频调用但不用保证结果的任务可以交给它 */ object AppExecutor { - private val mCoreSize = Runtime.getRuntime().availableProcessors() - private val mMinimumPoolSize = 6.coerceAtLeast(mCoreSize) - private val mMaximumPoolSize = 24.coerceAtLeast(mCoreSize * 3) + private const val CORE_POOL_SIZE = 3 + private const val MAX_POOL_SIZE = 24 @JvmStatic val uiExecutor by lazy { MainThreadExecutor() } @JvmStatic - val lightWeightIoExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_LIGHT_WEIGHT_IO_THREAD")) } + val lightWeightIoExecutor: ExecutorService by lazy { + Executors.newSingleThreadExecutor( + GHThreadFactory("GH_LIGHT_WEIGHT_IO_THREAD") + ) + } @JvmStatic - val heavyWeightIoExecutor: ExecutorService by lazy { Executors.newFixedThreadPool(2, GHThreadFactory("GH_HEAVY_WEIGHT_IO_THREAD")) } - - @JvmStatic - val logExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_LOG_THREAD")) } + val heavyWeightIoExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_HEAVY_WEIGHT_IO_THREAD")) } @JvmStatic val ioExecutor = ThreadPoolExecutor( - mMinimumPoolSize, - mMaximumPoolSize, - 20L, TimeUnit.SECONDS, - LinkedBlockingQueue(256), - GHThreadFactory("GH_IO_THREAD")) + CORE_POOL_SIZE, + MAX_POOL_SIZE, + 20L, TimeUnit.SECONDS, + LinkedBlockingQueue(256), + GHThreadFactory("GH_IO_THREAD") + ) val cachedScheduler by lazy { Schedulers.from(ioExecutor) } @@ -60,7 +59,11 @@ object AppExecutor { } } -fun runOnIoThread(isLightWeightTask: Boolean = false, isHeavyWightTask: Boolean = false, f: () -> Unit) { +fun runOnIoThread( + isLightWeightTask: Boolean = false, + isHeavyWightTask: Boolean = false, + f: () -> Unit +) { when { isLightWeightTask -> lightWeightIoExecutor.execute(f) isHeavyWightTask -> heavyWeightIoExecutor.execute(f) @@ -69,5 +72,9 @@ fun runOnIoThread(isLightWeightTask: Boolean = false, isHeavyWightTask: Boolean } fun runOnUiThread(f: () -> Unit) { - uiExecutor.execute(f) + if (Thread.currentThread() == Looper.getMainLooper().thread) { + f.invoke() + } else { + uiExecutor.execute(f) + } } \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IBuildConfigProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IBuildConfigProvider.kt index f073666036..03a7aeab64 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IBuildConfigProvider.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IBuildConfigProvider.kt @@ -18,4 +18,8 @@ interface IBuildConfigProvider : IProvider { fun getNewApiHost(): String fun getNewDevApiHost(): String + + fun getVApiHost(): String + + fun getVDevApiHost(): String } \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/utils/TimeUtils.kt b/module_core/src/main/java/com/gh/gamecenter/core/utils/TimeUtils.kt index ebe195fc93..ee11a81c11 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/utils/TimeUtils.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/utils/TimeUtils.kt @@ -66,6 +66,18 @@ object TimeUtils { return false } + //判断是不是明天 + fun isTomorrow(timestamp: Long): Boolean { + val format = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA) + format.timeZone = TimeZone.getTimeZone("Asia/Shanghai") + val today = format.parse(format.format(Date())).time + val day = timestamp * 1000 + if (day >= today + 86400 * 1000 && day < today + 86400 * 1000 * 2) { + return true + } + return false + } + //判断是不是本周 fun isThisWeek(timestamp: Long): Boolean { val calendar = Calendar.getInstance() diff --git a/module_core/src/main/java/com/gh/gamecenter/core/utils/ToastUtils.kt b/module_core/src/main/java/com/gh/gamecenter/core/utils/ToastUtils.kt index 950a243502..d7d2da3cd3 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/utils/ToastUtils.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/utils/ToastUtils.kt @@ -1,6 +1,7 @@ package com.gh.gamecenter.core.utils import com.gh.gamecenter.core.HaloApp +import com.gh.gamecenter.core.runOnUiThread import com.lightgame.utils.toast.ToastHelper object ToastUtils { @@ -12,12 +13,16 @@ object ToastUtils { @JvmStatic fun showToast(message: String) { - ToastHelper.showToast(HaloApp.getInstance(), message) + runOnUiThread { + ToastHelper.showToast(HaloApp.getInstance(), message) + } } @JvmStatic fun showToast(message: String, gravity: Int = -1, yOffset: Int = 0) { - ToastHelper.showToast(HaloApp.getInstance(), message, gravity, yOffset) + runOnUiThread { + ToastHelper.showToast(HaloApp.getInstance(), message, gravity, yOffset) + } } } diff --git a/scripts/build_with_simple_backup.sh b/scripts/build_with_simple_backup.sh index b8c3a4c1be..6b72e33d26 100755 --- a/scripts/build_with_simple_backup.sh +++ b/scripts/build_with_simple_backup.sh @@ -31,6 +31,7 @@ rm -r module_common/src/main/res/drawable-night-xxxhdpi rm -r module_common/src/main/res/drawable-night-nodpi rm -r module_common/src/main/res/drawable-night sed -i 's/buildConfigField "boolean", "IS_NIGHT_MODE_ON", "true"/buildConfigField "boolean", "IS_NIGHT_MODE_ON", "false"/g' module_common/build.gradle +sed -i 's/buildConfigField "boolean", "IS_VGAME_ON", "true"/buildConfigField "boolean", "IS_VGAME_ON", "true"/g' module_common/build.gradle ./gradlew --stop ./gradlew clean diff --git a/settings.gradle b/settings.gradle index e0cf03553b..e8f501e9cb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,12 +1,8 @@ include ':app' -//include ':libraries:im' include ':libraries:LGLibrary' -//include ':libraries:MTA' -include ':libraries:QQShare' -//include ':libraries:TalkingData' -//include ':libraries:UmengPush' -//include ':libraries:WechatShare' +include ':libraries:QQShare' include ':libraries:Matisse' +include ':vspace-bridge:vspace' //setBinding(new Binding([gradle: this])) //evaluate(new File(settingsDir, "assistant_flutter/.android/include_flutter.groovy")) include ':module_core' diff --git a/vspace-bridge b/vspace-bridge new file mode 160000 index 0000000000..4c1f17d7b8 --- /dev/null +++ b/vspace-bridge @@ -0,0 +1 @@ +Subproject commit 4c1f17d7b8554ef42489e6cb928b2564b966b09d