Compare commits
1 commit
dev
...
revert-544
Author | SHA1 | Date | |
---|---|---|---|
|
c6078c96aa |
358
.editorconfig
|
@ -1,358 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
# charset = utf-8
|
|
||||||
# end_of_line = lf
|
|
||||||
# indent_size = 4
|
|
||||||
# indent_style = space
|
|
||||||
# insert_final_newline = false
|
|
||||||
# max_line_length = 100
|
|
||||||
# tab_width = 4
|
|
||||||
# ij_continuation_indent_size = 8
|
|
||||||
# ij_formatter_off_tag = @formatter:off
|
|
||||||
# ij_formatter_on_tag = @formatter:on
|
|
||||||
# ij_formatter_tags_enabled = false
|
|
||||||
# ij_smart_tabs = false
|
|
||||||
# ij_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_resources = true
|
|
||||||
# ij_java_align_multiline_ternary_operation = false
|
|
||||||
# ij_java_align_multiline_text_blocks = false
|
|
||||||
# ij_java_align_multiline_throws_list = false
|
|
||||||
# ij_java_align_subsequent_simple_methods = false
|
|
||||||
# ij_java_align_throws_keyword = false
|
|
||||||
# ij_java_annotation_parameter_wrap = off
|
|
||||||
# ij_java_array_initializer_new_line_after_left_brace = false
|
|
||||||
# ij_java_array_initializer_right_brace_on_new_line = false
|
|
||||||
# ij_java_array_initializer_wrap = off
|
|
||||||
# ij_java_assert_statement_colon_on_next_line = false
|
|
||||||
# ij_java_assert_statement_wrap = off
|
|
||||||
# ij_java_assignment_wrap = off
|
|
||||||
# ij_java_binary_operation_sign_on_next_line = false
|
|
||||||
# ij_java_binary_operation_wrap = off
|
|
||||||
# ij_java_blank_lines_after_anonymous_class_header = 0
|
|
||||||
# ij_java_blank_lines_after_class_header = 0
|
|
||||||
# ij_java_blank_lines_after_imports = 1
|
|
||||||
# ij_java_blank_lines_after_package = 1
|
|
||||||
# ij_java_blank_lines_around_class = 1
|
|
||||||
# ij_java_blank_lines_around_field = 0
|
|
||||||
# ij_java_blank_lines_around_field_in_interface = 0
|
|
||||||
# ij_java_blank_lines_around_initializer = 1
|
|
||||||
# ij_java_blank_lines_around_method = 1
|
|
||||||
# ij_java_blank_lines_around_method_in_interface = 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_at_first_column = true
|
|
||||||
# ij_java_call_parameters_new_line_after_left_paren = false
|
|
||||||
# ij_java_call_parameters_right_paren_on_new_line = false
|
|
||||||
# ij_java_call_parameters_wrap = off
|
|
||||||
# ij_java_case_statement_on_separate_line = true
|
|
||||||
# ij_java_catch_on_new_line = false
|
|
||||||
# ij_java_class_annotation_wrap = split_into_lines
|
|
||||||
# ij_java_class_brace_style = end_of_line
|
|
||||||
# ij_java_class_count_to_use_import_on_demand = 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_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.**,|,*,|,$*,|
|
|
||||||
# ij_java_indent_case_from_switch = true
|
|
||||||
# ij_java_insert_inner_class_imports = false
|
|
||||||
# ij_java_insert_override_annotation = true
|
|
||||||
# ij_java_keep_blank_lines_before_right_brace = 2
|
|
||||||
# ij_java_keep_blank_lines_between_package_declaration_and_header = 2
|
|
||||||
# ij_java_keep_blank_lines_in_code = 2
|
|
||||||
# ij_java_keep_blank_lines_in_declarations = 2
|
|
||||||
# ij_java_keep_control_statement_in_one_line = true
|
|
||||||
# ij_java_keep_first_column_comment = true
|
|
||||||
# ij_java_keep_indents_on_empty_lines = false
|
|
||||||
# ij_java_keep_line_breaks = true
|
|
||||||
# ij_java_keep_multiple_expressions_in_one_line = false
|
|
||||||
# ij_java_keep_simple_blocks_in_one_line = false
|
|
||||||
# ij_java_keep_simple_classes_in_one_line = false
|
|
||||||
# ij_java_keep_simple_lambdas_in_one_line = 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_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_names_count_to_use_import_on_demand = 99
|
|
||||||
# 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_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_space_after_closing_angle_bracket_in_type_argument = false
|
|
||||||
# ij_java_space_after_colon = true
|
|
||||||
# ij_java_space_after_comma = true
|
|
||||||
# ij_java_space_after_comma_in_type_arguments = true
|
|
||||||
# ij_java_space_after_for_semicolon = true
|
|
||||||
# ij_java_space_after_quest = true
|
|
||||||
# ij_java_space_after_type_cast = true
|
|
||||||
# ij_java_space_before_annotation_array_initializer_left_brace = false
|
|
||||||
# ij_java_space_before_annotation_parameter_list = false
|
|
||||||
# ij_java_space_before_array_initializer_left_brace = false
|
|
||||||
# ij_java_space_before_catch_keyword = true
|
|
||||||
# ij_java_space_before_catch_left_brace = true
|
|
||||||
# ij_java_space_before_catch_parentheses = true
|
|
||||||
# ij_java_space_before_class_left_brace = true
|
|
||||||
# ij_java_space_before_colon = true
|
|
||||||
# ij_java_space_before_colon_in_foreach = true
|
|
||||||
# ij_java_space_before_comma = false
|
|
||||||
# ij_java_space_before_do_left_brace = true
|
|
||||||
# ij_java_space_before_else_keyword = true
|
|
||||||
# ij_java_space_before_else_left_brace = true
|
|
||||||
# ij_java_space_before_finally_keyword = true
|
|
||||||
# ij_java_space_before_finally_left_brace = true
|
|
||||||
# ij_java_space_before_for_left_brace = true
|
|
||||||
# ij_java_space_before_for_parentheses = true
|
|
||||||
# ij_java_space_before_for_semicolon = false
|
|
||||||
# ij_java_space_before_if_left_brace = true
|
|
||||||
# ij_java_space_before_if_parentheses = true
|
|
||||||
# ij_java_space_before_method_call_parentheses = false
|
|
||||||
# ij_java_space_before_method_left_brace = true
|
|
||||||
# ij_java_space_before_method_parentheses = false
|
|
||||||
# ij_java_space_before_opening_angle_bracket_in_type_parameter = false
|
|
||||||
# ij_java_space_before_quest = true
|
|
||||||
# ij_java_space_before_switch_left_brace = true
|
|
||||||
# ij_java_space_before_switch_parentheses = true
|
|
||||||
# ij_java_space_before_synchronized_left_brace = true
|
|
||||||
# ij_java_space_before_synchronized_parentheses = true
|
|
||||||
# ij_java_space_before_try_left_brace = true
|
|
||||||
# ij_java_space_before_try_parentheses = true
|
|
||||||
# ij_java_space_before_type_parameter_list = false
|
|
||||||
# ij_java_space_before_while_keyword = true
|
|
||||||
# ij_java_space_before_while_left_brace = true
|
|
||||||
# ij_java_space_before_while_parentheses = true
|
|
||||||
# ij_java_space_inside_one_line_enum_braces = false
|
|
||||||
# ij_java_space_within_empty_array_initializer_braces = false
|
|
||||||
# ij_java_space_within_empty_method_call_parentheses = false
|
|
||||||
# ij_java_space_within_empty_method_parentheses = false
|
|
||||||
# ij_java_spaces_around_additive_operators = true
|
|
||||||
# ij_java_spaces_around_assignment_operators = true
|
|
||||||
# ij_java_spaces_around_bitwise_operators = true
|
|
||||||
# ij_java_spaces_around_equality_operators = true
|
|
||||||
# ij_java_spaces_around_lambda_arrow = true
|
|
||||||
# ij_java_spaces_around_logical_operators = true
|
|
||||||
# ij_java_spaces_around_method_ref_dbl_colon = false
|
|
||||||
# ij_java_spaces_around_multiplicative_operators = true
|
|
||||||
# ij_java_spaces_around_relational_operators = true
|
|
||||||
# ij_java_spaces_around_shift_operators = true
|
|
||||||
# ij_java_spaces_around_type_bounds_in_type_parameters = true
|
|
||||||
# ij_java_spaces_around_unary_operator = false
|
|
||||||
# ij_java_spaces_within_angle_brackets = false
|
|
||||||
# ij_java_spaces_within_annotation_parentheses = false
|
|
||||||
# ij_java_spaces_within_array_initializer_braces = false
|
|
||||||
# ij_java_spaces_within_braces = false
|
|
||||||
# ij_java_spaces_within_brackets = false
|
|
||||||
# ij_java_spaces_within_cast_parentheses = false
|
|
||||||
# ij_java_spaces_within_catch_parentheses = false
|
|
||||||
# ij_java_spaces_within_for_parentheses = false
|
|
||||||
# ij_java_spaces_within_if_parentheses = false
|
|
||||||
# ij_java_spaces_within_method_call_parentheses = false
|
|
||||||
# ij_java_spaces_within_method_parentheses = false
|
|
||||||
# ij_java_spaces_within_parentheses = false
|
|
||||||
# ij_java_spaces_within_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
|
|
||||||
|
|
||||||
[{*.htm,*.shtm,*.sht,*.shtml,*.html}]
|
|
||||||
# ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
|
||||||
# ij_html_align_attributes = true
|
|
||||||
# ij_html_align_text = false
|
|
||||||
# ij_html_attribute_wrap = normal
|
|
||||||
# ij_html_block_comment_at_first_column = true
|
|
||||||
# ij_html_do_not_align_children_of_min_lines = 0
|
|
||||||
# ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
|
|
||||||
# ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
|
|
||||||
# ij_html_enforce_quotes = false
|
|
||||||
# ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
|
|
||||||
# ij_html_keep_blank_lines = 2
|
|
||||||
# ij_html_keep_indents_on_empty_lines = false
|
|
||||||
# ij_html_keep_line_breaks = true
|
|
||||||
# ij_html_keep_line_breaks_in_text = true
|
|
||||||
# ij_html_keep_whitespaces = false
|
|
||||||
# ij_html_keep_whitespaces_inside = span,pre,textarea
|
|
||||||
# ij_html_line_comment_at_first_column = true
|
|
||||||
# ij_html_new_line_after_last_attribute = never
|
|
||||||
# ij_html_new_line_before_first_attribute = never
|
|
||||||
# ij_html_quote_style = double
|
|
||||||
# ij_html_remove_new_line_before_tags = br
|
|
||||||
# ij_html_space_after_tag_name = false
|
|
||||||
# ij_html_space_around_equality_in_attribute = false
|
|
||||||
# ij_html_space_inside_empty_tag = false
|
|
||||||
# ij_html_text_wrap = normal
|
|
||||||
|
|
||||||
[{*.jhm,*.xslt,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.wsdl,*.jrxml,*.xml,*.jnlp,*.xul}]
|
|
||||||
# ij_continuation_indent_size = 4
|
|
||||||
# ij_xml_block_comment_at_first_column = true
|
|
||||||
# ij_xml_keep_indents_on_empty_lines = false
|
|
||||||
# ij_xml_line_comment_at_first_column = true
|
|
||||||
# ij_xml_use_custom_settings = true
|
|
||||||
# ij_continuation_indent_size = 4
|
|
||||||
# ij_xml_block_comment_at_first_column = true
|
|
||||||
# ij_xml_keep_indents_on_empty_lines = false
|
|
||||||
# ij_xml_line_comment_at_first_column = true
|
|
||||||
# ij_xml_use_custom_settings = true
|
|
||||||
|
|
||||||
[{*.kt,*.kts}]
|
|
||||||
# ij_kotlin_align_in_columns_case_branch = false
|
|
||||||
# ij_kotlin_align_multiline_binary_operation = false
|
|
||||||
# ij_kotlin_align_multiline_extends_list = false
|
|
||||||
# ij_kotlin_align_multiline_method_parentheses = false
|
|
||||||
# ij_kotlin_align_multiline_parameters = true
|
|
||||||
# ij_kotlin_align_multiline_parameters_in_calls = false
|
|
||||||
# ij_kotlin_assignment_wrap = normal
|
|
||||||
# ij_kotlin_blank_lines_after_class_header = 0
|
|
||||||
# ij_kotlin_blank_lines_around_block_when_branches = 0
|
|
||||||
# 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_insert_whitespaces_in_simple_one_line_method = true
|
|
||||||
# ij_kotlin_keep_blank_lines_before_right_brace = 2
|
|
||||||
# ij_kotlin_keep_blank_lines_in_code = 2
|
|
||||||
# ij_kotlin_keep_blank_lines_in_declarations = 2
|
|
||||||
# ij_kotlin_keep_first_column_comment = true
|
|
||||||
# ij_kotlin_keep_indents_on_empty_lines = false
|
|
||||||
# ij_kotlin_keep_line_breaks = true
|
|
||||||
# ij_kotlin_lbrace_on_next_line = false
|
|
||||||
# ij_kotlin_line_comment_add_space = false
|
|
||||||
# ij_kotlin_line_comment_at_first_column = true
|
|
||||||
# ij_kotlin_method_annotation_wrap = 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_parameter_annotation_wrap = off
|
|
||||||
# ij_kotlin_space_after_comma = true
|
|
||||||
# ij_kotlin_space_after_extend_colon = true
|
|
||||||
# ij_kotlin_space_after_type_colon = true
|
|
||||||
# ij_kotlin_space_before_catch_parentheses = true
|
|
||||||
# ij_kotlin_space_before_comma = false
|
|
||||||
# ij_kotlin_space_before_extend_colon = true
|
|
||||||
# ij_kotlin_space_before_for_parentheses = true
|
|
||||||
# ij_kotlin_space_before_if_parentheses = true
|
|
||||||
# ij_kotlin_space_before_lambda_arrow = true
|
|
||||||
# ij_kotlin_space_before_type_colon = false
|
|
||||||
# ij_kotlin_space_before_when_parentheses = true
|
|
||||||
# ij_kotlin_space_before_while_parentheses = true
|
|
||||||
# ij_kotlin_spaces_around_additive_operators = true
|
|
||||||
# ij_kotlin_spaces_around_assignment_operators = true
|
|
||||||
# ij_kotlin_spaces_around_equality_operators = true
|
|
||||||
# ij_kotlin_spaces_around_function_type_arrow = true
|
|
||||||
# ij_kotlin_spaces_around_logical_operators = true
|
|
||||||
# ij_kotlin_spaces_around_multiplicative_operators = true
|
|
||||||
# ij_kotlin_spaces_around_range = false
|
|
||||||
# ij_kotlin_spaces_around_relational_operators = true
|
|
||||||
# ij_kotlin_spaces_around_unary_operator = false
|
|
||||||
# ij_kotlin_spaces_around_when_arrow = true
|
|
||||||
# ij_kotlin_variable_annotation_wrap = off
|
|
||||||
# ij_kotlin_while_on_new_line = false
|
|
||||||
# ij_kotlin_wrap_elvis_expressions = 1
|
|
||||||
# ij_kotlin_wrap_expression_body_functions = 1
|
|
||||||
# ij_kotlin_wrap_first_method_in_call_chain = false
|
|
4
.gitattributes
vendored
|
@ -1,4 +0,0 @@
|
||||||
* text=auto eol=lf
|
|
||||||
|
|
||||||
*.bat text eol=crlf
|
|
||||||
*.jar binary
|
|
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,51 +0,0 @@
|
||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve. Please write in English only.
|
|
||||||
title: ''
|
|
||||||
labels: bug
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Don't report bugs that are already in the issue list of Retro Music: https://github.com/h4h13/RetroMusicPlayer/issues
|
|
||||||
|
|
||||||
[ ] Yes
|
|
||||||
-->
|
|
||||||
|
|
||||||
**Have you read the [FAQ](https://github.com/RetroMusicPlayer/RetroMusicPlayer/blob/master/FAQ.md)?**
|
|
||||||
[Yes/No]
|
|
||||||
|
|
||||||
**Has the bug already been reported? Please search in GitHub issue tab before creating an issue.**
|
|
||||||
[Yes/No]
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**How To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
4.
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Crash log**
|
|
||||||
If the app is crashing, add a crash log
|
|
||||||
<details>
|
|
||||||
<summary>Click to view logs</summary>
|
|
||||||
PASTE YOUR LOGS HERE.
|
|
||||||
</details>
|
|
||||||
|
|
||||||
**Device info:**
|
|
||||||
- Device: [e.g. OnePlus 7]
|
|
||||||
- Android version: [e.g. Android 9]
|
|
||||||
- App version [e.g. 3.5.300_0517]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
26
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,26 +0,0 @@
|
||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project. Please write in English only.
|
|
||||||
title: ''
|
|
||||||
labels: enhancement
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Don't report bugs that are already in the issue list of Retro Music: https://github.com/h4h13/RetroMusicPlayer/issues
|
|
||||||
|
|
||||||
[ ] Yes
|
|
||||||
-->
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
61
.github/stale.yml
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# Configuration for probot-stale - https://github.com/probot/stale
|
||||||
|
|
||||||
|
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||||
|
daysUntilStale: 60
|
||||||
|
|
||||||
|
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||||
|
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||||
|
daysUntilClose: 7
|
||||||
|
|
||||||
|
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||||
|
onlyLabels: []
|
||||||
|
|
||||||
|
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||||
|
exemptLabels:
|
||||||
|
- pinned
|
||||||
|
- security
|
||||||
|
- "[Status] Maybe Later"
|
||||||
|
|
||||||
|
# Set to true to ignore issues in a project (defaults to false)
|
||||||
|
exemptProjects: false
|
||||||
|
|
||||||
|
# Set to true to ignore issues in a milestone (defaults to false)
|
||||||
|
exemptMilestones: false
|
||||||
|
|
||||||
|
# Set to true to ignore issues with an assignee (defaults to false)
|
||||||
|
exemptAssignees: false
|
||||||
|
|
||||||
|
# Label to use when marking as stale
|
||||||
|
staleLabel: stale
|
||||||
|
|
||||||
|
# Comment to post when marking as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
|
||||||
|
# Comment to post when removing the stale label.
|
||||||
|
# unmarkComment: >
|
||||||
|
# Your comment here.
|
||||||
|
|
||||||
|
# Comment to post when closing a stale Issue or Pull Request.
|
||||||
|
# closeComment: >
|
||||||
|
# Your comment here.
|
||||||
|
|
||||||
|
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||||
|
limitPerRun: 30
|
||||||
|
|
||||||
|
# Limit to only `issues` or `pulls`
|
||||||
|
# only: issues
|
||||||
|
|
||||||
|
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||||
|
# pulls:
|
||||||
|
# daysUntilStale: 30
|
||||||
|
# markComment: >
|
||||||
|
# This pull request has been automatically marked as stale because it has not had
|
||||||
|
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
# for your contributions.
|
||||||
|
|
||||||
|
# issues:
|
||||||
|
# exemptLabels:
|
||||||
|
# - confirmed
|
34
.github/workflows/android.yml
vendored
|
@ -1,34 +0,0 @@
|
||||||
name: Android CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ dev ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ dev ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 10
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
|
||||||
- uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
distribution: 'zulu'
|
|
||||||
java-version: 17
|
|
||||||
- uses: gradle/gradle-build-action@v2
|
|
||||||
- name: Lint Android
|
|
||||||
run: ./gradlew lint
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
java-version: '17'
|
|
||||||
distribution: 'zulu'
|
|
||||||
- uses: gradle/gradle-build-action@v2
|
|
||||||
- name: Build
|
|
||||||
run: ./gradlew app:assemble
|
|
5
.gitignore
vendored
|
@ -38,4 +38,7 @@ obj/
|
||||||
captures
|
captures
|
||||||
app/normal/release/
|
app/normal/release/
|
||||||
/models/
|
/models/
|
||||||
/app/release/
|
|
||||||
|
app/font/
|
||||||
|
app/src/debug/
|
||||||
|
app/src/font/
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
# Contributing
|
|
||||||
|
|
||||||
## Using the issue tracker
|
|
||||||
The [issue tracker](https://github.com/RetroMusicPlayer/RetroMusicPlayer/issues) is the preferred channel for bug reports, feature requests and submitting pull requests, but please follow these rules:
|
|
||||||
|
|
||||||
* Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others.
|
|
||||||
|
|
||||||
* Please **do not** post comments consisting solely of "+1" or "👍". Use [GitHub's "reactions" feature](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) instead.
|
|
||||||
|
|
||||||
* Please **do not** write [enhancement]/[bug]/[feature request] or similar stuff in the title of issues, as there are labels for that purpose that will be added by devs or collaborators.
|
|
||||||
|
|
||||||
## Bug reports
|
|
||||||
A bug is a _demonstrable problem_. Good bug reports are extremely helpful, so thanks!
|
|
||||||
|
|
||||||
Guidelines for bug reports:
|
|
||||||
|
|
||||||
* Use the GitHub issue search, check if the issue has already been reported both in the open issues and in the closed ones, if there are some missing details, add them in issue comments, without creating a new one
|
|
||||||
* Check if the issue has been fixed — try to reproduce it using the latest available build
|
|
||||||
* Isolate the problem — ideally create a reproducible scenario and a live example
|
|
||||||
* Do not report multiple bugs in a single ticket, otherwise it will be confusing.
|
|
||||||
|
|
||||||
A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report.
|
|
||||||
|
|
||||||
|
|
||||||
## Feature requests
|
|
||||||
Feature requests are welcome, please make sure to be as detailed as possible and use screenshots, videos, GIFs, to demonstrate it better, if possible.
|
|
||||||
|
|
||||||
|
|
||||||
## Pull requests
|
|
||||||
**Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code), otherwise you risk spending a lot of time working on something that developers might not want to merge into the project. To avoid that, you can join the official [Telegram group](https://t.me/retromusicapp) or open an issue.
|
|
||||||
|
|
||||||
## License
|
|
||||||
By contributing your code, you agree to license your contribution under the [GNU General Public License](https://github.com/RetroMusicPlayer/RetroMusicPlayer/blob/master/LICENSE.md).
|
|
||||||
|
|
||||||
Please note we have a code of conduct, please follow it in all your interactions with the project.
|
|
155
FAQ.md
|
@ -1,157 +1,38 @@
|
||||||
## **Q: How do I get the beta version of Retro Music?**
|
#### Q: How do I get the beta version of Retro Music?
|
||||||
You can opt-in for the beta build by clicking on this link: https://play.google.com/apps/testing/code.name.monkey.retromusic
|
You can opt-in for the beta build by clicking on this link: https://play.google.com/apps/testing/code.name.monkey.retromusic
|
||||||
|
|
||||||
___
|
#### Q: How do I use offline synced lyrics?
|
||||||
|
- STEP 1: Look for a song that doesn't have lyrics.
|
||||||
|
- STEP 2: Search the web for the lyrics of your desired song (you either have to find a ".lrc" file or lyrics with timestamps for example "[00:04:02] Some lyrics text"). If you find a lyrics file that's great or find lyrics with timestamps you have to create a ".lrc" file.
|
||||||
|
- STEP 3: Now you have to rename the file you created in this way: <song_name> - <artist_name>.lrc or for better matching copy the <song_name> and the <artist_name> from the tag editor and then rename the file.
|
||||||
|
- STEP 4: You have to copy or move the file into a location on the SD Card: whatever_sdcard/RetroMusic/lyrics/ and paste inside your ".lrc" file in there.
|
||||||
|
|
||||||
## **Q: How to restore my purchases?**
|
#### Q: Why isn't the artist's image downloading?
|
||||||
Make sure to switch and use your account in the Play Store app through which you purchased before installing Retro Music. The Google account used to install the app is also used to purchase/restore the pro license.
|
Last.fm has disabled the download of artist's images for the time being, whether functionality for this will be restored in future is uncertain.
|
||||||
|
|
||||||
If you've already installed the app, remove all other accounts except the one from which you purchased premium, and then restore the purchase.
|
#### Q: How do I change the theme?
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
## Q: **How do I use offline synced lyrics?**
|
|
||||||
There are three methods for adding offline synced lyrics in Retro Music.
|
|
||||||
|
|
||||||
### ***Method 1:-***
|
|
||||||
#### STEP 1:
|
|
||||||
Find the time-stamped lyrics for your songs that don't have lyrics already. A time-stamped lyric looks like this, "[00:04:02] Some lyrics text" for example.
|
|
||||||
#### STEP 2:
|
|
||||||
Copy these time-stamped lyrics.
|
|
||||||
#### STEP 3:
|
|
||||||
Open retro music and head to the song synced lyrics editor.
|
|
||||||
#### STEP 4:
|
|
||||||
Paste the lyrics there normally and exit the editor
|
|
||||||
#### STEP 5:
|
|
||||||
Open lyrics and you should see your time-stamped lyrics there.
|
|
||||||
|
|
||||||
### ***Method 2:-***
|
|
||||||
#### STEP 1:
|
|
||||||
Download the time-stamped lyrics for your songs that don't have lyrics already. You can find ".lrc" files for popular songs at either of the websites given below. A time-stamped lyric looks like this, "[00:04:02] Some lyrics text" for example.
|
|
||||||
#### STEP 2:
|
|
||||||
A ".lrc" is like a text file that contains the time-stamped lyrics for example, "[00:04:02] Some lyrics text". Save your time-stamped lyrics are ".lrc" file.
|
|
||||||
#### STEP 3:
|
|
||||||
Now you have to rename the file you created in this way: <song_name> - <artist_name>.lrc or for better matching copy the <song_name> and the <artist_name> from the tag editor and then rename the file.
|
|
||||||
### STEP 4:
|
|
||||||
Now paste the LRC files to the following path: /sdcard/Retromusic/lyrics/
|
|
||||||
Here sdcard is your internal storage.
|
|
||||||
|
|
||||||
### ***Method 3:- (Requires third-party app)***
|
|
||||||
#### STEP 1:
|
|
||||||
Download automatag or autotagger from Play Store.
|
|
||||||
#### STEP 2:
|
|
||||||
Find the time-stamped lyrics for your songs that don't have lyrics already. You can find ".lrc" files for popular songs at either of the websites given below. A time-stamped lyric looks like this, "[00:04:02] Some lyrics text" for example.
|
|
||||||
#### STEP 3:
|
|
||||||
Find your song to edit and paste the synced lyrics.
|
|
||||||
> These apps add those synced lyrics in the music file itself instead of creating a ".lrc file for it."
|
|
||||||
|
|
||||||
**Lyrics Website Links:**
|
|
||||||
- https://www.lyricsify.com/
|
|
||||||
- https://www.syair.info
|
|
||||||
|
|
||||||
**Some Important Notes:**
|
|
||||||
- If you want to skip to a particular timestamp, simply scroll to the time stamp from where you want to start and a 'Play' icon will appear left to the particular stamp. Tap on the play button to play from there.
|
|
||||||
- When you save lyrics by pasting lyrics in lyrics editor, close the lyrics and open again for lyrics to show.
|
|
||||||
- For those who are having difficulty in making the synced lyrics work, we have a short tutorial video on it. Hope this helps you. {[Link in the note or here](https://youtu.be/1oIOTGWhNMY)}
|
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: How do I change the theme?**
|
|
||||||
Settings -> Look and feel -> Select your theme.
|
Settings -> Look and feel -> Select your theme.
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: Equalizer is very laggy and unstable or I am getting a "No equalizer found" error. Why?**
|
#### Q: Why aren't last added songs showing?
|
||||||
- The Retro music in-built equalizer was removed updates ago so the only equalizer you will have by your OEM or Android native equalizer which isn't made by us and have no control over them. So you can report those issues to your OEM so that they can provide a fix in the next updates.
|
|
||||||
|
|
||||||
- If you are seeing "No Equalizer Found" in your device, this means your device doesn't have a stock equalizer "MusicFx" Equalizer. You can try using this one. It's made by AEX ROM developers.
|
|
||||||
|
|
||||||
https://drive.google.com/file/d/1_1bpsn6roeEyElGKikbU39lVKUH8O3xp/view?**usp=drivesdk
|
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: Why aren't last added songs showing?**
|
|
||||||
Settings -> Other -> Last added playlist interval -> Select an option from the list.
|
Settings -> Other -> Last added playlist interval -> Select an option from the list.
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: How do I enable fullscreen lock screen controls?**
|
#### Q: How do I enable fullscreen lockscreen controls?
|
||||||
Settings -> Personalize -> Fullscreen controls -> Enable (this will only be visible when songs are playing from Retro Music).
|
Settings -> Personalize -> Fullscreen controls -> Enable (this will only be visible when songs are playing from Retro Music).
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: Why are my gallery or random pictures showing up as album art?**
|
#### Q: Why are gallery or random pictures showing up as album art?
|
||||||
Settings -> Images -> Ignore media store covers -> Enable
|
Settings -> Images -> Ignore media store covers -> Enable
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: Which file types are supported?**
|
#### Q: Which file types are supported?
|
||||||
Retro Music uses the native media player that comes with your Android phone, so as long as a file type is supported by your phone, it's supported by Retro Music.
|
Retro Music uses the native media player that comes with your Android phone, so as long as a file type is supported by your phone, it's supported by Retro Music.
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: Why is my device slowing down when I'm using the app?**
|
#### Q: Why is my device slowing down when I'm using the app?
|
||||||
Retro Music is image intensive, it keeps images in the cache for quick loading.
|
Retro Music is image intensive, it keeps images in the cache for quick loading.
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: The title "Retro Music" is showing on the top of the app, how can I fix this?**
|
#### Q: The title "Retro Music" is showing on the top of the app, how can i fix this?
|
||||||
Clear the app's cache and data.
|
Clear the app's cache and data.
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: My app is crashing, how do I fix this?** (Sorry, settings have changed internally)
|
##### Q: My app is crashing, how do i fix this? (Sorry, settings have changed internally)
|
||||||
Please try to clear the data of the app. If it doesn't work, reinstalling fresh from the play store should help.
|
Reinstalling the app should fix this issue.
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: Why has all the text gone white/disappeared?**
|
##### Q: Why has all the text gone white/dissapeared?
|
||||||
Change the theme to Black or Dark and change it back to what you had before.
|
Change the theme to Black or Dark and change it back to what you had before.
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: Why some of my songs are not showing in my library?**
|
|
||||||
- Try checking up if those songs are not less than 30 seconds, if so head to settings -> other -> filter song duration. Put this to zero and see the songs that should start appearing in the library.
|
|
||||||
|
|
||||||
- If this doesn't work out for you, re-scanning the media folder should help and subsequently rebooting the device to refresh the media store.
|
|
||||||
|
|
||||||
- At last, resort, If nothing worked and your audio files are stored in SD card. Try moving them to internal memory then back to SD card.
|
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: Why does my library shows song files twice or no song at all?**
|
|
||||||
If you are seeing duplication of songs in the library or no songs at all, then it's because of the Media Store issue which got affected by some other app.
|
|
||||||
|
|
||||||
***To fix this:***
|
|
||||||
1. Head to your device settings
|
|
||||||
|
|
||||||
1. Open up "Apps & notifications" (This name depends from ROM to ROM)
|
|
||||||
|
|
||||||
1. Find the 'Media storage' app and clear storage (both data and cache) of it.
|
|
||||||
|
|
||||||
1. Then open the Retro Music app and manually scan your music from your storage.
|
|
||||||
|
|
||||||
1. Reboot the device to refresh the media store (Not sure if this is necessary)
|
|
||||||
|
|
||||||
**NOTE:** Don't panic when you will open Retro Music and see "Zero" songs there in the library. It's because you cleared Media Store which is responsible for recognising files on your device.
|
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: I can't find the folder menu anymore after the latest update?**
|
|
||||||
Head to settings -> personalise. And select folders from "library categories". If there is no option of folders, tap on reset and select folders.
|
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: After updating the app to the latest version, the font got removed. Why?**
|
|
||||||
- Retro Music's font has now been replaced with system font, which means the default font your system uses will be used by Retro Music too. It fixes all font-related issues you used to face/are facing in the app.
|
|
||||||
|
|
||||||
- With the recent Retro Music v5 release, we have a built-in optional font "Manrope font" which you can toggle from Settings > Look & Feel > Toggle "Use manrope font".
|
|
||||||
|
|
||||||
- If you think the font looks ugly, then you just need to change the default font from your Android settings (or use any Magisk module). If you can't, there's nothing we can do about it.
|
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: How to export playlist?**
|
|
||||||
- ***From Retro Music:***
|
|
||||||
|
|
||||||
Head to the playlists tab > tap on the three-dot menu on the playlist you want to export > save as a file.
|
|
||||||
|
|
||||||
- ***From Other Music Players:***
|
|
||||||
|
|
||||||
In your built-in music player, there should be an option to save that playlist as a file. Save them and import them from the file manager by opening it into retro music.
|
|
||||||
|
|
||||||
> Note that such playlist must be of your offline music only since retro music is an offline music player, not an online music player. So if your playlist is of online music, it can't be opened on other offline players nor can be exported
|
|
||||||
___
|
|
||||||
|
|
||||||
## **Q: How to restore/import playlist?**
|
|
||||||
Retro Music will automatically detect any playlist file when that playlist file is stored in internal storage/Playlist. However, if it doesn't, just open any "File manager" and open that playlist file with Retro Music.
|
|
||||||
|
|
||||||
For restoring playlists successfully, the location of songs must be the same in both the "Playlist" file and in your storage. For example, If your music is in "Internal storage/Music" and the playlist file has songs location "Internal storage/Songs". Then it will not be going to work since both these locations are different.
|
|
111
README.md
|
@ -1,97 +1,36 @@
|
||||||
# Metro
|
# Retro Music Player
|
||||||
|
|
||||||
Material Design music player for Android music lovers
|
[](https://github.com/h4h13/RetroMusicPlayer/blob/master/LICENSE.txt)
|
||||||
|
|
||||||
## Downloads
|
**Android Material Design music player**
|
||||||
|
|
||||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
Retro Music is an open source, local music player for Android.
|
||||||
alt="Get it on F-Droid"
|
|
||||||
height="80">](https://f-droid.org/en/packages/io.github.muntashirakon.Music/)
|
|
||||||
|
|
||||||
## Differences between Metro and [RetroMusicPlayer](https://github.com/h4h13/RetroMusicPlayer)
|
#### Features
|
||||||
- Google Play libraries removed (fully libre)
|
|
||||||
- Pro features available for free
|
|
||||||
- Fully offline (INTERNET permission removed)
|
|
||||||
- Bug fixes
|
|
||||||
- Minor differences in UI
|
|
||||||
|
|
||||||
## 📱 Screenshots
|
- Local playback only (based on the MediaStore)
|
||||||
### App Themes
|
|
||||||
| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg" width="200"/> | <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg" width="200"/> | <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/4.jpg" width="200"/> |
|
|
||||||
|:---:|:---:|:---:|
|
|
||||||
|Clearly white| Kinda dark | Just black|
|
|
||||||
|
|
||||||
### Player screen
|
|
||||||
| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg" width="200"/>| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/5.jpg" width="200"/>| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/6.jpg" width="200"/>| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/7.jpg" width="200"/>| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/8.jpg" width="200"/>|
|
|
||||||
|:---:|:---:|:---:|:---:|:---:|
|
|
||||||
| Home | Songs | Albums | Artists | Settings |
|
|
||||||
|
|
||||||
### Synced lyrics screen (Over Cover)
|
|
||||||
| <img src="screenshots/synced_over_light.jpg" width="200"/>| <img src="screenshots/synced_over_dark.jpg" width="200"/>| <img src="screenshots/synced_over_black.jpg" width="200"/>|
|
|
||||||
|:---:|:---:|:---:|
|
|
||||||
| Synced Over Cover light | Synced Over Cover dark | Synced Over Cover black |
|
|
||||||
|
|
||||||
### Synced lyrics screen (Replace Cover)
|
|
||||||
| <img src="screenshots/synced_replace_light.jpg" width="200"/>| <img src="screenshots/synced_replace_dark.jpg" width="200"/>| <img src="screenshots/synced_replace_black.jpg" width="200"/>|
|
|
||||||
|:---:|:---:|:---:|
|
|
||||||
| Synced Replace Cover light | Synced Replace Cover dark | Synced Replace Cover black |
|
|
||||||
|
|
||||||
### 10+ Now playing themes
|
|
||||||
| <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg" width="200"/> |<img src="screenshots/fit.jpg" width="200"/>| <img src="screenshots/flat.jpg" width="200"/> | <img src="screenshots/color.jpg" width="200"/> | <img src="screenshots/material.jpg" width="200"/> |
|
|
||||||
|:-----: |:-----: |:-----: |:-----: |:-----: |
|
|
||||||
| Normal | Fit | Flat | Color | Material |
|
|
||||||
|
|
||||||
| <img src="screenshots/classic.jpg" width="200"/> |<img src="screenshots/adaptive.jpg" width="200"/>| <img src="screenshots/blur.jpg" width="200"/> | <img src="screenshots/tiny.jpg" width="200"/> | <img src="screenshots/peek.jpg" width="200"/> |
|
|
||||||
|:-----: |:-----: |:-----: |:-----: |:-----: |
|
|
||||||
| Classic | Adaptive | Blur | Tiny | Peek |
|
|
||||||
|
|
||||||
## 🧭 Navigation never made easier
|
|
||||||
Self-explanatory interface without overloaded menus.
|
|
||||||
|
|
||||||
## 🎨 Colorful
|
|
||||||
You can choose between three different main themes: Clearly White, Kinda
|
|
||||||
Dark and Just Black for AMOLED displays. Select your favorite accent
|
|
||||||
color from a color palette.
|
|
||||||
|
|
||||||
## 🏠 Home
|
|
||||||
Where you can view your recently/top played artists, albums and
|
|
||||||
favorite songs. No other music player has this feature.
|
|
||||||
|
|
||||||
## 📦 Included Features
|
|
||||||
- Base 3 themes (Clearly White, Kinda Dark and Just Black)
|
|
||||||
- Choose from 10+ now playing themes
|
|
||||||
- Driving Mode
|
|
||||||
- Headset/Bluetooth support
|
|
||||||
- Music duration filter
|
|
||||||
- Android auto support
|
|
||||||
- Wallpaper accent picker on Android 8.1+
|
|
||||||
- Material You support on Android 12+
|
|
||||||
- Monet themed icon support on Android 13+
|
|
||||||
- Folder support - Play songs by folder
|
|
||||||
- Gapless playback
|
|
||||||
- Volume controls
|
|
||||||
- Carousel effect for album covers
|
|
||||||
- Home screen widgets
|
|
||||||
- Lock screen playback controls
|
|
||||||
- Lyrics screen (download and sync with music)
|
|
||||||
- Sleep timer
|
- Sleep timer
|
||||||
- Easy drag to sort playlist & play queue
|
- Folder browser
|
||||||
- Tag editor
|
- Home screen widgets
|
||||||
- Create, edit and import playlists
|
- Themes (Just Black, Kinda Dark, Clearly White)
|
||||||
- Playing queue with reorder
|
- Now playing themes
|
||||||
- User profile
|
- Volume controls
|
||||||
- 30+ languages support
|
- Blacklist
|
||||||
- Browse and play your music by songs, albums, artists, playlists and
|
- Album artist support
|
||||||
genre
|
|
||||||
- Smart Auto Playlists - Recently played, most played and history
|
|
||||||
- Build your playlist on the go
|
|
||||||
|
|
||||||
We are trying our best to bring you the best user experience. The app is regularly being updated for bug fixes and new features.
|
#### Download
|
||||||
|
|
||||||
## 🗂️ License
|
<a href='https://play.google.com/store/apps/details?id=code.name.monkey.retromusic&hl=en&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img width="250" alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png'/></a>
|
||||||
|
|
||||||
Metro is released under the GNU General Public License v3.0
|
#### Translations
|
||||||
(GPLv3), which can be found [here](LICENSE.md)
|
Help us translate the app to your language [here](http://monkeycodeapp.oneskyapp.com/collaboration/project?id=238534).
|
||||||
|
|
||||||
|
|
||||||
> Please note: Metro is an offline music player app. It doesn't support music downloading or online music streaming.
|
#### License
|
||||||
|
|
||||||
|
Retro Music Player is released under the GNU General Public License v3.0 (GPLv3), which can be found here: [License](LICENSE.md)
|
||||||
|
|
||||||
|
#### FAQ [Here](FAQ.md)
|
||||||
|
|
||||||
|
#### Change-Log and Release [Here](https://github.com/h4h13/RetroMusicPlayer/releases)
|
||||||
|
|
||||||
|
|
214
app/build.gradle
|
@ -1,30 +1,40 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: "androidx.navigation.safeargs.kotlin"
|
apply plugin: 'kotlin-android-extensions'
|
||||||
apply plugin: 'kotlin-parcelize'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: 'com.google.devtools.ksp'
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk 33
|
compileSdkVersion 28
|
||||||
namespace "code.name.monkey.retromusic"
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk 21
|
minSdkVersion 21
|
||||||
targetSdk 33
|
targetSdkVersion 28
|
||||||
|
|
||||||
|
renderscriptTargetApi 28 //must match target sdk and build tools
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
applicationId 'io.github.muntashirakon.Music'
|
applicationId "code.name.monkey.retromusic"
|
||||||
versionCode 10603
|
versionCode 390
|
||||||
versionName '6.1.0'
|
versionName '3.4.600'
|
||||||
|
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
||||||
|
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
||||||
|
}
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
Properties properties = getProperties('/Users/hemanths/Desktop/KeepSafe/retro.properties')
|
||||||
|
storeFile file(getProperty(properties, 'storeFile'))
|
||||||
|
keyAlias getProperty(properties, 'keyAlias')
|
||||||
|
storePassword getProperty(properties, 'storePassword')
|
||||||
|
keyPassword getProperty(properties, 'keyPassword')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
shrinkResources true
|
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
shrinkResources true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
applicationIdSuffix '.debug'
|
applicationIdSuffix '.debug'
|
||||||
|
@ -32,101 +42,129 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
viewBinding true
|
flavorDimensions "default"
|
||||||
|
|
||||||
|
productFlavors {
|
||||||
|
normal {
|
||||||
|
versionCode defaultConfig.versionCode + 10000
|
||||||
|
versionName defaultConfig.versionName + "_" + getDate()
|
||||||
|
dimension "default"
|
||||||
}
|
}
|
||||||
|
font {
|
||||||
|
versionCode defaultConfig.versionCode + 10000
|
||||||
|
versionName defaultConfig.versionName + "_" + getDate()
|
||||||
|
dimension "default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
resources {
|
exclude 'META-INF/LICENSE'
|
||||||
excludes += ['META-INF/LICENSE', 'META-INF/NOTICE', 'META-INF/java.properties']
|
exclude 'META-INF/NOTICE'
|
||||||
|
exclude 'META-INF/rxjava.properties'
|
||||||
}
|
}
|
||||||
}
|
lintOptions {
|
||||||
lint {
|
disable 'MissingTranslation'
|
||||||
abortOnError true
|
disable 'InvalidPackage'
|
||||||
warning 'ImpliedQuantity', 'Instantiatable', 'MissingQuantity', 'MissingTranslation'
|
abortOnError false
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility '1.8'
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility '1.8'
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
buildToolsVersion = '29.0.2'
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
configurations.all {
|
||||||
dependenciesInfo {
|
|
||||||
includeInApk = false
|
|
||||||
includeInBundle = false
|
|
||||||
}
|
|
||||||
configurations.configureEach {
|
|
||||||
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
|
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
|
||||||
}
|
}
|
||||||
|
androidExtensions {
|
||||||
|
experimental = true
|
||||||
|
}
|
||||||
|
kapt {
|
||||||
|
generateStubs = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def getProperties(String fileName) {
|
||||||
|
final Properties properties = new Properties()
|
||||||
|
def file = file(fileName)
|
||||||
|
if (file.exists()) {
|
||||||
|
file.withInputStream { stream -> properties.load(stream) }
|
||||||
|
}
|
||||||
|
return properties
|
||||||
|
}
|
||||||
|
|
||||||
|
static def getProperty(Properties properties, String name) {
|
||||||
|
return properties.getProperty(name) ?: "$name missing"
|
||||||
|
}
|
||||||
|
|
||||||
|
static def getDate() {
|
||||||
|
new Date().format('MMdd')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
implementation project(':appthemehelper')
|
implementation project(':appthemehelper')
|
||||||
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
|
implementation 'androidx.fragment:fragment:1.2.0-rc01'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01'
|
||||||
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
||||||
|
implementation "androidx.cardview:cardview:1.0.0"
|
||||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
implementation "androidx.palette:palette:1.0.0"
|
||||||
implementation 'androidx.annotation:annotation:1.6.0'
|
implementation 'androidx.annotation:annotation:1.1.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
|
||||||
implementation "androidx.preference:preference-ktx:$preference_version"
|
|
||||||
implementation "androidx.core:core-ktx:$core_version"
|
|
||||||
implementation 'androidx.palette:palette-ktx:1.0.0'
|
implementation 'androidx.palette:palette-ktx:1.0.0'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
|
||||||
implementation 'androidx.mediarouter:mediarouter:1.3.1'
|
implementation 'com.google.android.material:material:1.2.0-alpha01'
|
||||||
|
implementation 'com.google.android.play:core:1.6.3'
|
||||||
|
|
||||||
implementation "androidx.navigation:navigation-runtime-ktx:$navigation_version"
|
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
|
||||||
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
|
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
|
||||||
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
|
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.2'
|
||||||
|
|
||||||
def room_version = '2.5.1'
|
implementation 'com.afollestad.material-dialogs:core:3.1.1'
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation 'com.afollestad.material-dialogs:input:3.1.1'
|
||||||
implementation "androidx.room:room-ktx:$room_version"
|
implementation 'com.afollestad.material-dialogs:color:3.1.1'
|
||||||
ksp "androidx.room:room-compiler:$room_version"
|
implementation 'com.afollestad.material-dialogs:bottomsheets:3.1.1'
|
||||||
|
implementation 'com.afollestad:material-cab:0.1.12'
|
||||||
|
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
|
||||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
|
||||||
|
|
||||||
implementation "androidx.core:core-splashscreen:1.0.0"
|
implementation 'com.github.bumptech.glide:glide:3.8.0'
|
||||||
|
implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0'
|
||||||
|
|
||||||
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||||
|
implementation 'io.reactivex.rxjava2:rxjava:2.2.9'
|
||||||
|
|
||||||
|
implementation('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.11.0@aar') {
|
||||||
|
transitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*UI Library*/
|
||||||
|
implementation 'me.zhanghai.android.materialprogressbar:library:1.6.1'
|
||||||
|
|
||||||
|
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"
|
||||||
|
|
||||||
|
|
||||||
|
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:3.4.0.201406110918-r'
|
||||||
|
|
||||||
|
implementation 'com.github.ksoichiro:android-observablescrollview:1.6.0'
|
||||||
|
implementation 'com.github.kabouzeid:recyclerview-fastscroll:1.9-kmod'
|
||||||
|
|
||||||
|
implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3'
|
||||||
|
|
||||||
|
implementation 'com.anjlab.android.iab.v3:library:1.1.0'
|
||||||
|
implementation 'com.r0adkll:slidableactivity:2.1.0'
|
||||||
|
implementation 'com.heinrichreimersoftware:material-intro:1.6'
|
||||||
|
|
||||||
|
implementation 'com.google.dagger:dagger:2.23.1'
|
||||||
|
kapt 'com.google.dagger:dagger-compiler:2.23.1'
|
||||||
|
|
||||||
implementation "com.google.android.material:material:$mdc_version"
|
|
||||||
|
|
||||||
def retrofit_version = '2.9.0'
|
|
||||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
|
||||||
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
|
|
||||||
|
|
||||||
def material_dialog_version = "3.3.0"
|
|
||||||
implementation "com.afollestad.material-dialogs:core:$material_dialog_version"
|
|
||||||
implementation "com.afollestad.material-dialogs:input:$material_dialog_version"
|
|
||||||
implementation "com.afollestad.material-dialogs:color:$material_dialog_version"
|
|
||||||
|
|
||||||
implementation 'com.afollestad:material-cab:2.0.1'
|
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
|
|
||||||
|
|
||||||
def koin_version = '3.4.0'
|
|
||||||
implementation "io.insert-koin:koin-core:$koin_version"
|
|
||||||
implementation "io.insert-koin:koin-android:$koin_version"
|
|
||||||
|
|
||||||
def glide_version = '4.15.1'
|
|
||||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
|
||||||
ksp "com.github.bumptech.glide:ksp:$glide_version"
|
|
||||||
|
|
||||||
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
|
|
||||||
|
|
||||||
implementation 'com.github.bosphere.android-fadingedgelayout:fadingedgelayout:1.0.0'
|
|
||||||
|
|
||||||
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC3'
|
|
||||||
implementation 'com.github.jetradarmobile:android-snowfall:1.2.1'
|
|
||||||
|
|
||||||
implementation "dev.chrisbanes.insetter:insetter:0.6.1"
|
|
||||||
|
|
||||||
implementation 'com.github.Adonai:jaudiotagger:2.3.15'
|
|
||||||
implementation 'com.r0adkll:slidableactivity:2.1.0'
|
|
||||||
implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
|
|
||||||
implementation 'com.github.dhaval2404:imagepicker:2.1'
|
|
||||||
implementation 'me.zhanghai.android.fastscroll:library:1.2.0'
|
|
||||||
implementation 'cat.ereza:customactivityoncrash:2.4.0'
|
|
||||||
implementation 'me.tankery.lib:circularSeekBar:1.4.2'
|
|
||||||
}
|
}
|
BIN
app/libs/juniversalchardet-1.0.3.jar
Normal file
61
app/proguard-rules.pro
vendored
|
@ -16,9 +16,9 @@
|
||||||
# public *;
|
# public *;
|
||||||
#}
|
#}
|
||||||
|
|
||||||
# Preserve the line number information for
|
# Uncomment this to preserve the line number information for
|
||||||
# debugging stack traces.
|
# debugging stack traces.
|
||||||
-keepattributes SourceFile,LineNumberTable
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
|
@ -26,46 +26,45 @@
|
||||||
|
|
||||||
-dontwarn java.lang.invoke.*
|
-dontwarn java.lang.invoke.*
|
||||||
-dontwarn **$$Lambda$*
|
-dontwarn **$$Lambda$*
|
||||||
-dontwarn javax.annotation.**
|
|
||||||
|
|
||||||
# RetroFit
|
# RetroFit
|
||||||
-dontwarn retrofit.**
|
-dontwarn retrofit.**
|
||||||
-keep class retrofit.** { *; }
|
-keep class retrofit.** { *; }
|
||||||
|
-keepattributes Signature
|
||||||
|
-keepattributes Exceptions
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
# Glide
|
# Glide
|
||||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||||
-keep class * extends com.bumptech.glide.module.AppGlideModule {
|
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
||||||
<init>(...);
|
|
||||||
}
|
|
||||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
|
||||||
**[] $VALUES;
|
**[] $VALUES;
|
||||||
public *;
|
public *;
|
||||||
}
|
}
|
||||||
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
|
|
||||||
*** rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
# OkHttp
|
-keep class !android.support.v7.internal.view.menu.**,** {*;}
|
||||||
-keepattributes Signature
|
|
||||||
-keepattributes *Annotation*
|
|
||||||
-keep interface com.squareup.okhttp3.** { *; }
|
|
||||||
-dontwarn com.squareup.okhttp3.**
|
|
||||||
|
|
||||||
#-dontwarn
|
-dontwarn
|
||||||
#-ignorewarnings
|
-ignorewarnings
|
||||||
|
|
||||||
#Jaudiotagger
|
# ------- FastScrollRecycleView START -------
|
||||||
-dontwarn org.jaudiotagger.**
|
-keep class com.simplecityapps.recyclerview_fastscroll.views.FastScrollPopup { *; }
|
||||||
-dontwarn org.jcodec.**
|
# ------- FastScrollRecycleView END -------
|
||||||
-keep class org.jaudiotagger.** { *; }
|
|
||||||
-keep class org.jcodec.** { *; }
|
|
||||||
|
|
||||||
-keepclassmembers enum * { *; }
|
-keep public class android.support.design.widget.BottomNavigationView { *; }
|
||||||
-keepattributes *Annotation*, Signature, Exception
|
-keep public class android.support.design.internal.BottomNavigationMenuView { *; }
|
||||||
-keepnames class androidx.navigation.fragment.NavHostFragment
|
-keep public class android.support.design.internal.BottomNavigationPresenter { *; }
|
||||||
-keep class * extends androidx.fragment.app.Fragment{}
|
-keep public class android.support.design.internal.BottomNavigationItemView { *; }
|
||||||
-keepnames class * extends android.os.Parcelable
|
|
||||||
-keepnames class * extends java.io.Serializable
|
#-dontwarn android.support.v8.renderscript.*
|
||||||
-keep class code.name.monkey.retromusic.network.model.** { *; }
|
#-keepclassmembers class android.support.v8.renderscript.RenderScript {
|
||||||
-keep class code.name.monkey.retromusic.model.** { *; }
|
# native *** rsn*(...);
|
||||||
-keep class com.google.android.material.bottomsheet.** { *; }
|
# native *** n*(...);
|
||||||
|
#}
|
||||||
|
|
||||||
|
#-keep class org.jaudiotagger.** { *; }
|
||||||
|
|
||||||
|
#For cast
|
||||||
|
-keep class code.name.monkey.retromusic.cast.CastOptionsProvider { *; }
|
||||||
|
-keep class android.support.** { *; }
|
||||||
|
-keep class com.google.** { *; }
|
||||||
|
-keep class java.nio.file.** { *; }
|
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<font
|
|
||||||
android:font="@font/google_sans_regular"
|
|
||||||
android:fontWeight="400" />
|
|
||||||
<font
|
|
||||||
android:font="@font/google_sans_medium"
|
|
||||||
android:fontWeight="600" />
|
|
||||||
<font
|
|
||||||
android:font="@font/google_sans_bold"
|
|
||||||
android:fontWeight="700" />
|
|
||||||
</font-family>
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<bool name="md3_available">true</bool>
|
|
||||||
<bool name="allowBackup">false</bool>
|
|
||||||
</resources>
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="app_name" translatable="false">Metro Debug</string>
|
|
||||||
</resources>
|
|
|
@ -1,105 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<style name="TextViewNormal" parent="">
|
|
||||||
<item name="android:textSize">14sp</item>
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewNormalCompress" parent="TextAppearance.MaterialComponents.Caption">
|
|
||||||
<item name="android:textSize">14sp</item>
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewHeadline4" parent="TextAppearance.MaterialComponents.Headline4">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewHeadline4.Compress" parent="TextAppearance.MaterialComponents.Headline4">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
<item name="android:textSize">32sp</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewHeadline5" parent="TextAppearance.MaterialComponents.Headline5">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewCaption" parent="TextAppearance.MaterialComponents.Caption">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewHeadline6" parent="TextAppearance.MaterialComponents.Headline6">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewHeadline3" parent="TextAppearance.MaterialComponents.Headline3">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewHeadline2" parent="TextAppearance.MaterialComponents.Headline2">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewSubtitle1" parent="TextAppearance.MaterialComponents.Subtitle1">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewSubtitle2" parent="TextAppearance.MaterialComponents.Subtitle2">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewBody1" parent="TextAppearance.MaterialComponents.Body1">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewButton" parent="TextAppearance.MaterialComponents.Button">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewBody2" parent="TextAppearance.MaterialComponents.Body2">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextViewOverline" parent="TextAppearance.MaterialComponents.Overline">
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="AppTextAppearance.MaterialAlertDialog.Button" parent="Widget.MaterialComponents.Button.TextButton">
|
|
||||||
<item name="android:textAppearance">@style/TextViewButton</item>
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
<item name="android:textStyle">bold</item>
|
|
||||||
<item name="android:padding">0dp</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="AppTextAppearance.MaterialAlertDialog.Body" parent="MaterialAlertDialog.MaterialComponents.Body.Text">
|
|
||||||
<item name="android:textAppearance">@style/TextViewBody1</item>
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
<item name="android:paddingTop">16dp</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="AppTextAppearance.MaterialAlertDialog.Title" parent="MaterialAlertDialog.MaterialComponents.Title.Text">
|
|
||||||
<item name="android:textAppearance">@style/TextViewHeadline6</item>
|
|
||||||
<item name="android:textStyle">bold</item>
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
<item name="android:padding">16dp</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="ToolbarTextAppearanceNormal">
|
|
||||||
<item name="android:textStyle">bold</item>
|
|
||||||
<item name="android:textAllCaps">false</item>
|
|
||||||
<item name="android:textAppearance">@style/TextViewHeadline6</item>
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
<item name="android:textSize">20sp</item>
|
|
||||||
<item name="android:letterSpacing">0.0125</item>
|
|
||||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="circleImageView" parent="ShapeAppearance.MaterialComponents">
|
|
||||||
<item name="cornerSize">40dp</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="BottomSheetItemTextAppearance" parent="Widget.MaterialComponents.BottomNavigationView.Colored">
|
|
||||||
<item name="android:textSize">13sp</item>
|
|
||||||
<item name="fontFamily">@font/sans</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
|
@ -1,57 +1,33 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="code.name.monkey.retromusic"
|
package="code.name.monkey.retromusic">
|
||||||
android:installLocation="auto">
|
|
||||||
|
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.BLUETOOTH"
|
|
||||||
android:maxSdkVersion="30" />
|
|
||||||
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
|
||||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
android:maxSdkVersion="32" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
|
||||||
android:maxSdkVersion="29" />
|
|
||||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.WRITE_SETTINGS"
|
|
||||||
tools:ignore="ProtectedPermissions" />
|
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
<uses-permission android:name="com.android.vending.BILLING" />
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.BLUETOOTH_CONNECT"
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
android:usesPermissionFlags="neverForLocation"
|
|
||||||
tools:targetApi="s" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:allowBackup="@bool/allowBackup"
|
android:allowBackup="true"
|
||||||
android:appCategory="audio"
|
|
||||||
android:configChanges="locale|layoutDirection"
|
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:localeConfig="@xml/locales_config"
|
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:restoreAnyVersion="true"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.RetroMusic.FollowSystem"
|
android:theme="@style/Theme.RetroMusic.Light"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="false"
|
||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.MainActivity"
|
android:name=".activities.MainActivity"
|
||||||
android:exported="true"
|
android:label="@string/app_name">
|
||||||
android:launchMode="singleTop"
|
|
||||||
android:theme="@style/Theme.RetroMusic.SplashScreen">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<action android:name="android.intent.action.MUSIC_PLAYER" />
|
<action android:name="android.intent.action.MUSIC_PLAYER" />
|
||||||
|
@ -62,6 +38,7 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
|
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -98,7 +75,7 @@
|
||||||
<data android:mimeType="application/x-ogg" />
|
<data android:mimeType="application/x-ogg" />
|
||||||
<data android:mimeType="application/itunes" />
|
<data android:mimeType="application/itunes" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter tools:ignore="AppLinkUrlError">
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
@ -121,54 +98,38 @@
|
||||||
<data android:mimeType="vnd.android.cursor.dir/audio" />
|
<data android:mimeType="vnd.android.cursor.dir/audio" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity android:name=".activities.AlbumDetailsActivity" />
|
||||||
|
<activity android:name=".activities.ArtistDetailActivity" />
|
||||||
|
<activity android:name=".activities.PlaylistDetailActivity" />
|
||||||
|
<activity android:name=".activities.PlayingQueueActivity" />
|
||||||
|
<activity android:name=".activities.AboutActivity" />
|
||||||
<activity android:name=".activities.tageditor.AlbumTagEditorActivity" />
|
<activity android:name=".activities.tageditor.AlbumTagEditorActivity" />
|
||||||
<activity android:name=".activities.tageditor.SongTagEditorActivity" />
|
<activity android:name=".activities.tageditor.SongTagEditorActivity" />
|
||||||
|
<activity android:name=".activities.SettingsActivity" />
|
||||||
|
<activity android:name=".activities.LyricsActivity" />
|
||||||
|
<activity android:name=".activities.UserInfoActivity" />
|
||||||
|
<activity android:name=".activities.SupportDevelopmentActivity" />
|
||||||
|
<activity android:name=".activities.GenreDetailsActivity" />
|
||||||
<activity android:name=".activities.LicenseActivity" />
|
<activity android:name=".activities.LicenseActivity" />
|
||||||
|
<activity android:name=".activities.PurchaseActivity" />
|
||||||
|
<activity android:name=".activities.WhatsNewActivity" />
|
||||||
<activity android:name=".activities.bugreport.BugReportActivity" />
|
<activity android:name=".activities.bugreport.BugReportActivity" />
|
||||||
<activity android:name=".activities.ShareInstagramStory" />
|
|
||||||
<activity android:name=".activities.DriveModeActivity" />
|
<activity
|
||||||
<activity android:name=".activities.PermissionActivity" />
|
android:name=".activities.SearchActivity"
|
||||||
|
android:windowSoftInputMode="stateVisible" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.bugreport.ErrorHandlerActivity"
|
||||||
|
android:immersive="true"
|
||||||
|
android:label="@string/error"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:theme="@style/ErrorHandlingTheme" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.LockScreenActivity"
|
android:name=".activities.LockScreenActivity"
|
||||||
android:excludeFromRecents="true"
|
android:noHistory="true"
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:showOnLockScreen="true" />
|
android:showOnLockScreen="true" />
|
||||||
<activity
|
|
||||||
android:name=".fragments.backup.RestoreActivity"
|
|
||||||
android:excludeFromRecents="false"
|
|
||||||
android:exported="true"
|
|
||||||
android:label="@string/restore"
|
|
||||||
android:theme="@style/Theme.RetroMusic.Dialog">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
|
|
||||||
<data android:mimeType="application/octet-stream" />
|
|
||||||
<data android:mimeType="application/x-zip-compressed" />
|
|
||||||
<data android:mimeType="application/zip" />
|
|
||||||
|
|
||||||
<data android:scheme="file" />
|
|
||||||
<data android:scheme="content" />
|
|
||||||
<data android:mimeType="*/*" />
|
|
||||||
<!--
|
|
||||||
Work around Android's ugly primitive PatternMatcher
|
|
||||||
implementation that can't cope with finding a . early in
|
|
||||||
the path unless it's explicitly matched.
|
|
||||||
-->
|
|
||||||
<data android:host="*" />
|
|
||||||
<data android:pathPattern=".*\\.rmbak" />
|
|
||||||
<data android:pathPattern=".*\\..*\\.rmbak" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\.rmbak" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.rmbak" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rmbak" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".appshortcuts.AppShortcutLauncherActivity"
|
android:name=".appshortcuts.AppShortcutLauncherActivity"
|
||||||
|
@ -179,13 +140,16 @@
|
||||||
android:name=".activities.saf.SAFGuideActivity"
|
android:name=".activities.saf.SAFGuideActivity"
|
||||||
android:theme="@style/Theme.Intro" />
|
android:theme="@style/Theme.Intro" />
|
||||||
|
|
||||||
<activity
|
<provider
|
||||||
android:name=".activities.ErrorActivity"
|
android:name=".misc.GenericFileProvider"
|
||||||
android:exported="true">
|
android:authorities="${applicationId}.provider"
|
||||||
<intent-filter>
|
android:exported="false"
|
||||||
<action android:name="cat.ereza.customactivityoncrash.RESTART" />
|
android:grantUriPermissions="true">
|
||||||
</intent-filter>
|
<meta-data
|
||||||
</activity>
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
@ -197,17 +161,13 @@
|
||||||
android:resource="@xml/provider_paths" />
|
android:resource="@xml/provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<receiver
|
<receiver android:name=".service.MediaButtonIntentReceiver">
|
||||||
android:name=".service.MediaButtonIntentReceiver"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver android:name=".appwidgets.BootReceiver">
|
||||||
android:name=".appwidgets.BootReceiver"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||||
|
@ -216,7 +176,7 @@
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".appwidgets.AppWidgetBig"
|
android:name=".appwidgets.AppWidgetBig"
|
||||||
android:exported="true"
|
android:exported="false"
|
||||||
android:label="@string/app_widget_big_name">
|
android:label="@string/app_widget_big_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
@ -229,7 +189,7 @@
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".appwidgets.AppWidgetClassic"
|
android:name=".appwidgets.AppWidgetClassic"
|
||||||
android:exported="true"
|
android:exported="false"
|
||||||
android:label="@string/app_widget_classic_name">
|
android:label="@string/app_widget_classic_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
@ -241,7 +201,7 @@
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".appwidgets.AppWidgetSmall"
|
android:name=".appwidgets.AppWidgetSmall"
|
||||||
android:exported="true"
|
android:exported="false"
|
||||||
android:label="@string/app_widget_small_name">
|
android:label="@string/app_widget_small_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
@ -253,7 +213,7 @@
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".appwidgets.AppWidgetText"
|
android:name=".appwidgets.AppWidgetText"
|
||||||
android:exported="true"
|
android:exported="false"
|
||||||
android:label="@string/app_widget_text_name">
|
android:label="@string/app_widget_text_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
@ -265,7 +225,7 @@
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".appwidgets.AppWidgetCard"
|
android:name=".appwidgets.AppWidgetCard"
|
||||||
android:exported="true"
|
android:exported="false"
|
||||||
android:label="@string/app_widget_card_name">
|
android:label="@string/app_widget_card_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
@ -275,35 +235,11 @@
|
||||||
android:name="android.appwidget.provider"
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/app_widget_card_info" />
|
android:resource="@xml/app_widget_card_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver
|
|
||||||
android:name=".appwidgets.AppWidgetMD3"
|
|
||||||
android:exported="true"
|
|
||||||
android:label="@string/app_widget_md3_name">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="android.appwidget.provider"
|
|
||||||
android:resource="@xml/app_widget_md3_info" />
|
|
||||||
</receiver>
|
|
||||||
<receiver
|
|
||||||
android:name=".appwidgets.AppWidgetCircle"
|
|
||||||
android:exported="true"
|
|
||||||
android:label="@string/app_widget_circle_name">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="android.appwidget.provider"
|
|
||||||
android:resource="@xml/app_widget_circle_info" />
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.MusicService"
|
android:name=".service.MusicService"
|
||||||
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:foregroundServiceType="mediaPlayback"
|
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.media.browse.MediaBrowserService" />
|
<action android:name="android.media.browse.MediaBrowserService" />
|
||||||
|
@ -318,33 +254,15 @@
|
||||||
android:name="com.lge.support.SPLIT_WINDOW"
|
android:name="com.lge.support.SPLIT_WINDOW"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
|
|
||||||
<!-- Android Auto -->
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.car.application"
|
android:name="code.name.monkey.retromusic.glide.RetroMusicGlideModule"
|
||||||
android:resource="@xml/automotive_app_desc" />
|
android:value="GlideModule" />
|
||||||
<meta-data
|
|
||||||
android:name="com.google.android.gms.car.application.theme"
|
|
||||||
android:resource="@style/CarTheme" />
|
|
||||||
<meta-data
|
|
||||||
android:name="com.google.android.gms.car.notification.SmallIcon"
|
|
||||||
android:resource="@drawable/ic_notification" />
|
|
||||||
|
|
||||||
<!-- For auto-storage of locale on Android 12 and lower -->
|
|
||||||
<service
|
|
||||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
|
||||||
android:enabled="false"
|
|
||||||
android:exported="false">
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="autoStoreLocales"
|
android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"
|
||||||
|
android:value="GlideModule" />
|
||||||
|
<meta-data
|
||||||
|
android:name="com.android.vending.splits.required"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
</service>
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<!--
|
|
||||||
This is not that important, it's just here so that we can query equalizer package
|
|
||||||
and check if it's present on A11+ because of Package visibility restrictions.
|
|
||||||
-->
|
|
||||||
<queries>
|
|
||||||
<package android:name="com.android.musicfx" />
|
|
||||||
</queries>
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
32
app/src/main/assets/contributors.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Hemanth Savarala",
|
||||||
|
"summary": "Lead developer",
|
||||||
|
"link": "https://github.com/h4h13",
|
||||||
|
"profile_image": "https://i.imgur.com/AoVs9oj.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Lennart Glamann",
|
||||||
|
"summary": "Designer",
|
||||||
|
"link": "https://t.me/FlixbusLennart",
|
||||||
|
"profile_image": "https://i.imgur.com/Q5Nsx1R.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Gaming Inc.",
|
||||||
|
"summary": "Telegram group maintainer",
|
||||||
|
"link": "https://www.gaminginc.tk/",
|
||||||
|
"profile_image": "https://i.imgur.com/pfvN7d9.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Milind Goel",
|
||||||
|
"summary": "Github & Telegram maintainer",
|
||||||
|
"link": "https://t.me/MilindGoel15",
|
||||||
|
"profile_image": "https://i.imgur.com/Bz4De21_d.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Abilas Sathiya",
|
||||||
|
"summary": "Design & Suggestions",
|
||||||
|
"link": "https://t.me/@abs2606",
|
||||||
|
"profile_image": "https://i.imgur.com/MUyEWlx.jpg"
|
||||||
|
}
|
||||||
|
]
|
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 3.1 KiB |
68
app/src/main/assets/index.html
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
|
||||||
|
* {
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
{style-placeholder}
|
||||||
|
a {
|
||||||
|
color: {link-color};
|
||||||
|
}
|
||||||
|
a:active {
|
||||||
|
color: {link-color-active};
|
||||||
|
}
|
||||||
|
ol {
|
||||||
|
list-style-position: inside;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<p><b><a href="https://github.com/kabouzeid/Phonograph" title="Phonograph"> Phonograph</a></b> by
|
||||||
|
Karim Abou Zeid</p>
|
||||||
|
<p><b><a href="https://github.com/AdrienPoupa/VinylMusicPlayer" title="VinylMusicPlayer">
|
||||||
|
VinylMusicPlayer</a></b> by Adrien Poupa</p>
|
||||||
|
<p><b><a href="https://github.com/ReactiveX/RxAndroid" title="RxJava"> RxJava</a></b> by RxJava
|
||||||
|
authors</p>
|
||||||
|
<p><b><a href="https://github.com/afollestad" title="Material Dialogs"> Material Dialogs and Cab</a></b>
|
||||||
|
by Aidan Michael Follestad</p>
|
||||||
|
<p><b><a href="https://github.com/umano/AndroidSlidingUpPanel" title="Android Sliding Up Panel">
|
||||||
|
Android Sliding Up Panel</a></b>by The Umano Team</p>
|
||||||
|
<p><b><a href="http://developer.android.com/tools/support-library/index.html"
|
||||||
|
title="AOSP Support Libraries"> AOSP Support Libraries</a></b>by AOSP contributors</p>
|
||||||
|
<p><b><a href="https://github.com/bumptech/glide" title="Glide"> Glide</a></b> by Sam Judd</p>
|
||||||
|
<p><b><a href="https://github.com/square/retrofit" title="Retrofit"> Retrofit</a></b> by Square team
|
||||||
|
</p>
|
||||||
|
<p><b><a href="https://github.com/afollestad/material-cab" title="Material Contextual Action Bar">
|
||||||
|
Material Contextual Action Bar</a></b> by Aidan Michael Follestad</p>
|
||||||
|
<p><b><a href="http://square.github.io/okhttp/" title="OkHttp"> OkHttp</a></b> by Square team</p>
|
||||||
|
<p><b><a href="https://github.com/hdodenhof/CircleImageView" title="CircleImageView">
|
||||||
|
CircleImageView</a></b> by Henning Dodenhof</p>
|
||||||
|
<p><b><a href="https://github.com/DreaminginCodeZH/MaterialProgressBar" title="MaterialProgressBar">
|
||||||
|
MaterialProgressBar</a></b> by Zhang Hai</p>
|
||||||
|
<p><b><a href="https://github.com/anjlab/android-inapp-billing-v3"
|
||||||
|
title="Android In-App Billing v3 Library"> Android In-App Billing v3 Library</a></b> by
|
||||||
|
Henning Dodenhof</p>
|
||||||
|
<p><b><a href="https://github.com/h6ah4i/android-advancedrecyclerview"
|
||||||
|
title="Advanced RecyclerView"> Advanced RecyclerView</a></b> by Haruki Hasegawa</p>
|
||||||
|
<p><b><a href="https://github.com/ksoichiro/Android-ObservableScrollView"
|
||||||
|
title="Android-ObservableScrollView"> Android-ObservableScrollView</a></b> by Soichiro
|
||||||
|
Kashima</p>
|
||||||
|
<p><b><a href="http://www.cufonfonts.com/es/font/14289/circular-std-book" title="Font used"> Font
|
||||||
|
used(CIRCULAR STD BOOK FONT)</a></b> by NIELSON CAETANO</p>
|
||||||
|
<p><b><a href="https://materialdesignicons.com" title="Icons"> Icons</a></b> by Austin Andrews</p>
|
||||||
|
<p><b><a href="https://www.techjuice.pk" title="City wallpaper"> Material Design City Wallpaper</a></b>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -1,76 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta content="IE=edge" http-equiv="X-UA-Compatible">
|
|
||||||
<style media="screen" type="text/css">
|
|
||||||
|
|
||||||
* {
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
{style-placeholder}
|
|
||||||
a {
|
|
||||||
color: {link-color};
|
|
||||||
}
|
|
||||||
a:active {
|
|
||||||
color: {link-color-active};
|
|
||||||
}
|
|
||||||
ol {
|
|
||||||
list-style-position: inside;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
padding-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<p><b><a href="https://github.com/kabouzeid/Phonograph" title="Phonograph"> Phonograph</a></b> by
|
|
||||||
Karim Abou Zeid</p>
|
|
||||||
<p><b><a href="http://developer.android.com/tools/support-library/index.html"
|
|
||||||
title="AOSP Support Libraries">AOSP Support Libraries</a></b> by AOSP contributors</p>
|
|
||||||
<p><b><a href="https://github.com/bumptech/glide" title="Glide"> Glide</a></b> by Sam Judd</p>
|
|
||||||
<p><b><a href="https://github.com/square/retrofit" title="Retrofit"> Retrofit</a></b> by Square team
|
|
||||||
</p>
|
|
||||||
<p><b><a href="http://square.github.io/okhttp/" title="OkHttp"> OkHttp</a></b> by Square team</p>
|
|
||||||
<p><b><a href="https://github.com/InsertKoinIO/koin"
|
|
||||||
title="Koin">Koin</a></b> by Arnaud Giuliani</p>
|
|
||||||
<p><b><a href="https://github.com/afollestad" title="Material Dialogs"> Material Dialogs and Cab</a></b>
|
|
||||||
by Aidan Michael Follestad</p>
|
|
||||||
<p><b><a href="https://github.com/afollestad/material-cab" title="Material Contextual Action Bar">
|
|
||||||
Material Contextual Action Bar</a></b> by Aidan Michael Follestad</p>
|
|
||||||
<p><b><a href="https://github.com/h6ah4i/android-advancedrecyclerview"
|
|
||||||
title="Advanced RecyclerView"> Advanced RecyclerView</a></b> by Haruki Hasegawa</p>
|
|
||||||
<p><b><a href="https://github.com/Ereza/CustomActivityOnCrash"
|
|
||||||
title="Custom Activity on Crash">Custom Activity on Crash</a></b> by Eduard Ereza Martínez
|
|
||||||
</p>
|
|
||||||
<p><b><a href="https://github.com/NanoHttpd/nanohttpd"
|
|
||||||
title="NanoHttpd">NanoHttpd</a></b> by NanoHttpd Team</p>
|
|
||||||
<p><b><a href="https://github.com/tankery/CircularSeekBar"
|
|
||||||
title="Circular Seekbar">Circular Seekbar</a></b> by Tankery</p>
|
|
||||||
<p><b><a href="https://github.com/Kaned1as/jaudiotagger"
|
|
||||||
title="jAudioTagger">jAudioTagger</a></b> by Kanedias</p>
|
|
||||||
<p><b><a href="https://github.com/zhanghai/AndroidFastScroll"
|
|
||||||
title="Android Fast Scroll">Android Fast Scroll</a></b> by Zhang Hai</p>
|
|
||||||
<p><b><a href="https://github.com/Dhaval2404/ImagePicker"
|
|
||||||
title="Image Picker">Image Picker</a></b> by Dhaval Patel</p>
|
|
||||||
<p><b><a href="https://github.com/heinrichreimer/material-intro"
|
|
||||||
title="Material Intro">Material Intro</a></b> by Jan Heinrich Reimer</p>
|
|
||||||
<p><b><a href="https://github.com/r0adkll/Slidr"
|
|
||||||
title="Slidr">Slidr</a></b> by Drew Heavner</p>
|
|
||||||
<p><b><a href="https://github.com/bosphere/Android-FadingEdgeLayout"
|
|
||||||
title="FadingEdgeLayout">FadingEdgeLayout</a></b> by bosphere</p>
|
|
||||||
<p><b><a href="https://github.com/yshrsmz/KeyboardVisibilityEvent"
|
|
||||||
title="KeyboardVisibilityEvent">KeyboardVisibilityEvent</a></b> by Yasuhiro SHIMIZU</p>
|
|
||||||
<p><b><a href="https://github.com/JetradarMobile/android-snowfall"
|
|
||||||
title="android-snowfall">android-snowfall</a></b> by Jetradar Mobile</p>
|
|
||||||
<p><b><a href="https://github.com/chrisbanes/insetter"
|
|
||||||
title="Insetter">Insetter</a></b> by Chris Banes</p>
|
|
||||||
<p><b><a href="https://materialdesignicons.com" title="Icons"> Icons</a></b> by Austin Andrews</p>
|
|
||||||
<p><b><a href="https://www.techjuice.pk" title="City wallpaper"> Material Design City Wallpaper</a></b>
|
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 37 KiB |
|
@ -1,67 +1,74 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
* Copyright (c) 2019 Hemanth Savarala.
|
||||||
*
|
*
|
||||||
* Licensed under the GNU General Public License v3
|
* Licensed under the GNU General Public License v3
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify it
|
* This is free software: you can redistribute it and/or modify it under
|
||||||
* under the terms of the GNU General Public License as published by
|
* the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* See the GNU General Public License for more details.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package code.name.monkey.retromusic
|
package code.name.monkey.retromusic
|
||||||
|
|
||||||
import android.app.Application
|
import android.widget.Toast
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.multidex.MultiDexApplication
|
||||||
import cat.ereza.customactivityoncrash.config.CaocConfig
|
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||||
import code.name.monkey.retromusic.activities.ErrorActivity
|
|
||||||
import code.name.monkey.retromusic.activities.MainActivity
|
|
||||||
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
|
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
|
||||||
import code.name.monkey.retromusic.helper.WallpaperAccentManager
|
import code.name.monkey.retromusic.dagger.DaggerMusicComponent
|
||||||
import org.koin.android.ext.koin.androidContext
|
import code.name.monkey.retromusic.dagger.MusicComponent
|
||||||
import org.koin.core.context.startKoin
|
import code.name.monkey.retromusic.dagger.module.AppModule
|
||||||
|
import com.anjlab.android.iab.v3.BillingProcessor
|
||||||
|
import com.anjlab.android.iab.v3.TransactionDetails
|
||||||
|
|
||||||
class App : Application() {
|
|
||||||
|
|
||||||
private val wallpaperAccentManager = WallpaperAccentManager(this)
|
class App : MultiDexApplication() {
|
||||||
|
|
||||||
|
lateinit var billingProcessor: BillingProcessor
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
/* if (MissingSplitsManagerFactory.create(this).disableAppIfMissingRequiredSplits()) {
|
||||||
|
return
|
||||||
|
}*/
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
instance = this
|
instance = this
|
||||||
|
musicComponent = DaggerMusicComponent.builder()
|
||||||
|
.appModule(AppModule(this))
|
||||||
|
.build()
|
||||||
|
|
||||||
startKoin {
|
|
||||||
androidContext(this@App)
|
|
||||||
modules(appModules)
|
|
||||||
}
|
|
||||||
// default theme
|
// default theme
|
||||||
if (!ThemeStore.isConfigured(this, 3)) {
|
if (!ThemeStore.isConfigured(this, 3)) {
|
||||||
ThemeStore.editTheme(this)
|
ThemeStore.editTheme(this)
|
||||||
.accentColorRes(code.name.monkey.appthemehelper.R.color.md_deep_purple_A200)
|
.accentColorRes(R.color.md_green_A200)
|
||||||
.coloredNavigationBar(true)
|
.coloredNavigationBar(true)
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
wallpaperAccentManager.init()
|
|
||||||
|
|
||||||
if (VersionUtils.hasNougatMR())
|
if (VersionUtils.hasNougatMR())
|
||||||
DynamicShortcutManager(this).initDynamicShortcuts()
|
DynamicShortcutManager(this).initDynamicShortcuts()
|
||||||
|
|
||||||
// setting Error activity
|
// automatically restores purchases
|
||||||
CaocConfig.Builder.create().errorActivity(ErrorActivity::class.java)
|
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY,
|
||||||
.restartActivity(MainActivity::class.java).apply()
|
object : BillingProcessor.IBillingHandler {
|
||||||
|
override fun onProductPurchased(productId: String, details: TransactionDetails?) {}
|
||||||
|
|
||||||
// Set Default values for now playing preferences
|
override fun onPurchaseHistoryRestored() {
|
||||||
// This will reduce startup time for now playing settings fragment as Preference listener of AbsSlidingMusicPanelActivity won't be called
|
Toast.makeText(this@App, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG).show();
|
||||||
PreferenceManager.setDefaultValues(this, R.xml.pref_now_playing_screen, false)
|
}
|
||||||
|
|
||||||
|
override fun onBillingError(errorCode: Int, error: Throwable?) {}
|
||||||
|
|
||||||
|
override fun onBillingInitialized() {}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTerminate() {
|
override fun onTerminate() {
|
||||||
super.onTerminate()
|
super.onTerminate()
|
||||||
wallpaperAccentManager.release()
|
billingProcessor.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -70,5 +77,14 @@ class App : Application() {
|
||||||
fun getContext(): App {
|
fun getContext(): App {
|
||||||
return instance!!
|
return instance!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isProVersion(): Boolean {
|
||||||
|
return BuildConfig.DEBUG || instance?.billingProcessor!!.isPurchased(PRO_VERSION_PRODUCT_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
lateinit var musicComponent: MusicComponent
|
||||||
|
|
||||||
|
const val PRO_VERSION_PRODUCT_ID = "pro_version"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,154 +1,51 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
* Copyright (c) 2019 Hemanth Savarala.
|
||||||
*
|
*
|
||||||
* Licensed under the GNU General Public License v3
|
* Licensed under the GNU General Public License v3
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify it
|
* This is free software: you can redistribute it and/or modify it under
|
||||||
* under the terms of the GNU General Public License as published by
|
* the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* See the GNU General Public License for more details.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package code.name.monkey.retromusic
|
package code.name.monkey.retromusic
|
||||||
|
|
||||||
import android.provider.BaseColumns
|
import android.provider.BaseColumns
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
const val TRANSLATE = "https://crowdin.com/project/retromusicplayer"
|
|
||||||
const val GITHUB_PROJECT = "https://github.com/MuntashirAkon/Metro"
|
const val RATE_ON_GOOGLE_PLAY = "https://play.google.com/store/apps/details?id=code.name.monkey.retromusic"
|
||||||
const val TELEGRAM_CHANGE_LOG = "https://t.me/AppManagerChannel"
|
const val TRANSLATE = "http://monkeycodeapp.oneskyapp.com/collaboration/project?id=238534"
|
||||||
|
const val GITHUB_PROJECT = "https://github.com/h4h13/RetroMusicPlayer"
|
||||||
|
const val TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog"
|
||||||
const val USER_PROFILE = "profile.jpg"
|
const val USER_PROFILE = "profile.jpg"
|
||||||
const val USER_BANNER = "banner.jpg"
|
const val USER_BANNER = "banner.jpg"
|
||||||
const val FAQ_LINK = "https://github.com/MuntashirAkon/Metro/blob/master/FAQ.md"
|
const val APP_INSTAGRAM_LINK = "https://www.instagram.com/retromusicapp/"
|
||||||
|
const val APP_TELEGRAM_LINK = "https://t.me/retromusicapp/"
|
||||||
|
const val APP_TWITTER_LINK = "https://twitter.com/retromusicapp"
|
||||||
|
const val FAQ_LINK = "https://github.com/h4h13/RetroMusicPlayer/blob/master/FAQ.md"
|
||||||
|
const val PINTEREST = "https://in.pinterest.com/retromusicapp/"
|
||||||
|
|
||||||
const val IS_MUSIC =
|
const val BASE_SELECTION = MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"
|
||||||
MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"
|
|
||||||
|
|
||||||
const val DATA = "_data"
|
val baseProjection = arrayOf(BaseColumns._ID, // 0
|
||||||
|
|
||||||
@Suppress("Deprecation")
|
|
||||||
val baseProjection = arrayOf(
|
|
||||||
BaseColumns._ID, // 0
|
|
||||||
MediaStore.Audio.AudioColumns.TITLE, // 1
|
MediaStore.Audio.AudioColumns.TITLE, // 1
|
||||||
MediaStore.Audio.AudioColumns.TRACK, // 2
|
MediaStore.Audio.AudioColumns.TRACK, // 2
|
||||||
MediaStore.Audio.AudioColumns.YEAR, // 3
|
MediaStore.Audio.AudioColumns.YEAR, // 3
|
||||||
MediaStore.Audio.AudioColumns.DURATION, // 4
|
MediaStore.Audio.AudioColumns.DURATION, // 4
|
||||||
DATA, // 5
|
MediaStore.Audio.AudioColumns.DATA, // 5
|
||||||
MediaStore.Audio.AudioColumns.DATE_MODIFIED, // 6
|
MediaStore.Audio.AudioColumns.DATE_MODIFIED, // 6
|
||||||
MediaStore.Audio.AudioColumns.ALBUM_ID, // 7
|
MediaStore.Audio.AudioColumns.ALBUM_ID, // 7
|
||||||
MediaStore.Audio.AudioColumns.ALBUM, // 8
|
MediaStore.Audio.AudioColumns.ALBUM, // 8
|
||||||
MediaStore.Audio.AudioColumns.ARTIST_ID, // 9
|
MediaStore.Audio.AudioColumns.ARTIST_ID, // 9
|
||||||
MediaStore.Audio.AudioColumns.ARTIST,// 10
|
MediaStore.Audio.AudioColumns.ARTIST,// 10
|
||||||
MediaStore.Audio.AudioColumns.COMPOSER, // 11
|
MediaStore.Audio.AudioColumns.COMPOSER)// 11
|
||||||
ALBUM_ARTIST // 12
|
|
||||||
)
|
|
||||||
const val NUMBER_OF_TOP_TRACKS = 99
|
const val NUMBER_OF_TOP_TRACKS = 99
|
||||||
}
|
|
||||||
|
|
||||||
const val EXTRA_PLAYLIST_TYPE = "type"
|
|
||||||
const val EXTRA_GENRE = "extra_genre"
|
}
|
||||||
const val EXTRA_PLAYLIST = "extra_playlist"
|
|
||||||
const val EXTRA_PLAYLIST_ID = "extra_playlist_id"
|
|
||||||
const val EXTRA_ALBUM_ID = "extra_album_id"
|
|
||||||
const val EXTRA_ARTIST_ID = "extra_artist_id"
|
|
||||||
const val EXTRA_SONG = "extra_songs"
|
|
||||||
const val EXTRA_PLAYLISTS = "extra_playlists"
|
|
||||||
const val LIBRARY_CATEGORIES = "library_categories"
|
|
||||||
const val EXTRA_SONG_INFO = "extra_song_info"
|
|
||||||
const val DESATURATED_COLOR = "desaturated_color"
|
|
||||||
const val BLACK_THEME = "black_theme"
|
|
||||||
const val KEEP_SCREEN_ON = "keep_screen_on"
|
|
||||||
const val TOGGLE_HOME_BANNER = "toggle_home_banner"
|
|
||||||
const val NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"
|
|
||||||
const val CAROUSEL_EFFECT = "carousel_effect"
|
|
||||||
const val COLORED_NOTIFICATION = "colored_notification"
|
|
||||||
const val CLASSIC_NOTIFICATION = "classic_notification"
|
|
||||||
const val GAP_LESS_PLAYBACK = "gapless_playback"
|
|
||||||
const val ALBUM_ART_ON_LOCK_SCREEN = "album_art_on_lock_screen"
|
|
||||||
const val BLURRED_ALBUM_ART = "blurred_album_art"
|
|
||||||
const val NEW_BLUR_AMOUNT = "new_blur_amount"
|
|
||||||
const val TOGGLE_HEADSET = "toggle_headset"
|
|
||||||
const val GENERAL_THEME = "general_theme"
|
|
||||||
const val ACCENT_COLOR = "accent_color"
|
|
||||||
const val SHOULD_COLOR_APP_SHORTCUTS = "should_color_app_shortcuts"
|
|
||||||
const val CIRCULAR_ALBUM_ART = "circular_album_art"
|
|
||||||
const val USER_NAME = "user_name"
|
|
||||||
const val TOGGLE_FULL_SCREEN = "toggle_full_screen"
|
|
||||||
const val TOGGLE_VOLUME = "toggle_volume"
|
|
||||||
const val ADAPTIVE_COLOR_APP = "adaptive_color_app"
|
|
||||||
const val HOME_ARTIST_GRID_STYLE = "home_artist_grid_style"
|
|
||||||
const val HOME_ALBUM_GRID_STYLE = "home_album_grid_style"
|
|
||||||
const val TOGGLE_ADD_CONTROLS = "toggle_add_controls"
|
|
||||||
const val ALBUM_COVER_STYLE = "album_cover_style_id"
|
|
||||||
const val ALBUM_COVER_TRANSFORM = "album_cover_transform"
|
|
||||||
const val TAB_TEXT_MODE = "tab_text_mode"
|
|
||||||
const val LANGUAGE_NAME = "language_name"
|
|
||||||
const val LOCALE_AUTO_STORE_ENABLED = "locale_auto_store_enabled"
|
|
||||||
const val SLEEP_TIMER_FINISH_SONG = "sleep_timer_finish_song"
|
|
||||||
const val ALBUM_GRID_STYLE = "album_grid_style_home"
|
|
||||||
const val ARTIST_GRID_STYLE = "artist_grid_style_home"
|
|
||||||
const val SAF_SDCARD_URI = "saf_sdcard_uri"
|
|
||||||
const val SONG_SORT_ORDER = "song_sort_order"
|
|
||||||
const val SONG_GRID_SIZE = "song_grid_size"
|
|
||||||
const val GENRE_SORT_ORDER = "genre_sort_order"
|
|
||||||
const val BLUETOOTH_PLAYBACK = "bluetooth_playback"
|
|
||||||
const val INITIALIZED_BLACKLIST = "initialized_blacklist"
|
|
||||||
const val ARTIST_SORT_ORDER = "artist_sort_order"
|
|
||||||
const val ARTIST_ALBUM_SORT_ORDER = "artist_album_sort_order"
|
|
||||||
const val ALBUM_SORT_ORDER = "album_sort_order"
|
|
||||||
const val PLAYLIST_SORT_ORDER = "playlist_sort_order"
|
|
||||||
const val ALBUM_SONG_SORT_ORDER = "album_song_sort_order"
|
|
||||||
const val ARTIST_SONG_SORT_ORDER = "artist_song_sort_order"
|
|
||||||
const val ALBUM_GRID_SIZE = "album_grid_size"
|
|
||||||
const val ALBUM_GRID_SIZE_LAND = "album_grid_size_land"
|
|
||||||
const val SONG_GRID_SIZE_LAND = "song_grid_size_land"
|
|
||||||
const val ARTIST_GRID_SIZE = "artist_grid_size"
|
|
||||||
const val ARTIST_GRID_SIZE_LAND = "artist_grid_size_land"
|
|
||||||
const val PLAYLIST_GRID_SIZE = "playlist_grid_size"
|
|
||||||
const val PLAYLIST_GRID_SIZE_LAND = "playlist_grid_size_land"
|
|
||||||
const val COLORED_APP_SHORTCUTS = "colored_app_shortcuts"
|
|
||||||
const val LAST_ADDED_CUTOFF = "last_added_interval"
|
|
||||||
const val LAST_SLEEP_TIMER_VALUE = "last_sleep_timer_value"
|
|
||||||
const val NEXT_SLEEP_TIMER_ELAPSED_REALTIME = "next_sleep_timer_elapsed_real_time"
|
|
||||||
const val IGNORE_MEDIA_STORE_ARTWORK = "ignore_media_store_artwork"
|
|
||||||
const val LAST_CHANGELOG_VERSION = "last_changelog_version"
|
|
||||||
const val START_DIRECTORY = "start_directory"
|
|
||||||
const val RECENTLY_PLAYED_CUTOFF = "recently_played_interval"
|
|
||||||
const val LOCK_SCREEN = "lock_screen"
|
|
||||||
const val ALBUM_ARTISTS_ONLY = "album_artists_only"
|
|
||||||
const val ALBUM_ARTIST = "album_artist"
|
|
||||||
const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"
|
|
||||||
const val ARTIST_DETAIL_SONG_SORT_ORDER = "artist_detail_song_sort_order"
|
|
||||||
const val LYRICS_OPTIONS = "lyrics_tab_position"
|
|
||||||
const val CHOOSE_EQUALIZER = "choose_equalizer"
|
|
||||||
const val EQUALIZER = "equalizer"
|
|
||||||
const val SONG_GRID_STYLE = "song_grid_style"
|
|
||||||
const val PAUSE_ON_ZERO_VOLUME = "pause_on_zero_volume"
|
|
||||||
const val FILTER_SONG = "filter_song"
|
|
||||||
const val EXPAND_NOW_PLAYING_PANEL = "expand_now_playing_panel"
|
|
||||||
const val EXTRA_ARTIST_NAME = "extra_artist_name"
|
|
||||||
const val TOGGLE_SUGGESTIONS = "toggle_suggestions"
|
|
||||||
const val AUDIO_FADE_DURATION = "audio_fade_duration"
|
|
||||||
const val CROSS_FADE_DURATION = "cross_fade_duration"
|
|
||||||
const val SHOW_LYRICS = "show_lyrics"
|
|
||||||
const val REMEMBER_LAST_TAB = "remember_last_tab"
|
|
||||||
const val LAST_USED_TAB = "last_used_tab"
|
|
||||||
const val WHITELIST_MUSIC = "whitelist_music"
|
|
||||||
const val MATERIAL_YOU = "material_you"
|
|
||||||
const val SNOWFALL = "snowfall"
|
|
||||||
const val LYRICS_TYPE = "lyrics_type"
|
|
||||||
const val PLAYBACK_SPEED = "playback_speed"
|
|
||||||
const val PLAYBACK_PITCH = "playback_pitch"
|
|
||||||
const val CUSTOM_FONT = "custom_font"
|
|
||||||
const val APPBAR_MODE = "appbar_mode"
|
|
||||||
const val WALLPAPER_ACCENT = "wallpaper_accent"
|
|
||||||
const val SCREEN_ON_LYRICS = "screen_on_lyrics"
|
|
||||||
const val CIRCLE_PLAY_BUTTON = "circle_play_button"
|
|
||||||
const val SWIPE_ANYWHERE_NOW_PLAYING = "swipe_anywhere_now_playing"
|
|
||||||
const val PAUSE_HISTORY = "pause_history"
|
|
||||||
const val MANAGE_AUDIO_FOCUS = "manage_audio_focus"
|
|
||||||
const val SWIPE_DOWN_DISMISS = "swipe_to_dismiss"
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic
|
|
||||||
|
|
||||||
import androidx.annotation.IntDef
|
|
||||||
|
|
||||||
@IntDef(
|
|
||||||
RECENT_ALBUMS,
|
|
||||||
TOP_ALBUMS,
|
|
||||||
RECENT_ARTISTS,
|
|
||||||
TOP_ARTISTS,
|
|
||||||
SUGGESTIONS,
|
|
||||||
FAVOURITES,
|
|
||||||
GENRES,
|
|
||||||
PLAYLISTS
|
|
||||||
)
|
|
||||||
@Retention(AnnotationRetention.SOURCE)
|
|
||||||
annotation class HomeSection
|
|
||||||
|
|
||||||
const val RECENT_ALBUMS = 3
|
|
||||||
const val TOP_ALBUMS = 1
|
|
||||||
const val RECENT_ARTISTS = 2
|
|
||||||
const val TOP_ARTISTS = 0
|
|
||||||
const val SUGGESTIONS = 5
|
|
||||||
const val FAVOURITES = 4
|
|
||||||
const val GENRES = 6
|
|
||||||
const val PLAYLISTS = 7
|
|
||||||
const val HISTORY_PLAYLIST = 8
|
|
||||||
const val LAST_ADDED_PLAYLIST = 9
|
|
||||||
const val TOP_PLAYED_PLAYLIST = 10
|
|
|
@ -1,156 +0,0 @@
|
||||||
package code.name.monkey.retromusic
|
|
||||||
|
|
||||||
import androidx.room.Room
|
|
||||||
import code.name.monkey.retromusic.auto.AutoMusicProvider
|
|
||||||
import code.name.monkey.retromusic.db.MIGRATION_23_24
|
|
||||||
import code.name.monkey.retromusic.db.RetroDatabase
|
|
||||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
|
||||||
import code.name.monkey.retromusic.fragments.albums.AlbumDetailsViewModel
|
|
||||||
import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel
|
|
||||||
import code.name.monkey.retromusic.fragments.genres.GenreDetailsViewModel
|
|
||||||
import code.name.monkey.retromusic.fragments.playlists.PlaylistDetailsViewModel
|
|
||||||
import code.name.monkey.retromusic.model.Genre
|
|
||||||
import code.name.monkey.retromusic.repository.*
|
|
||||||
import org.koin.android.ext.koin.androidContext
|
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
|
||||||
import org.koin.dsl.bind
|
|
||||||
import org.koin.dsl.module
|
|
||||||
|
|
||||||
private val roomModule = module {
|
|
||||||
|
|
||||||
single {
|
|
||||||
Room.databaseBuilder(androidContext(), RetroDatabase::class.java, "playlist.db")
|
|
||||||
.addMigrations(MIGRATION_23_24)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
factory {
|
|
||||||
get<RetroDatabase>().playlistDao()
|
|
||||||
}
|
|
||||||
|
|
||||||
factory {
|
|
||||||
get<RetroDatabase>().playCountDao()
|
|
||||||
}
|
|
||||||
|
|
||||||
factory {
|
|
||||||
get<RetroDatabase>().historyDao()
|
|
||||||
}
|
|
||||||
|
|
||||||
single {
|
|
||||||
RealRoomRepository(get(), get(), get())
|
|
||||||
} bind RoomRepository::class
|
|
||||||
}
|
|
||||||
private val autoModule = module {
|
|
||||||
single {
|
|
||||||
AutoMusicProvider(
|
|
||||||
androidContext(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private val mainModule = module {
|
|
||||||
single {
|
|
||||||
androidContext().contentResolver
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private val dataModule = module {
|
|
||||||
single {
|
|
||||||
RealRepository(
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
)
|
|
||||||
} bind Repository::class
|
|
||||||
|
|
||||||
single {
|
|
||||||
RealSongRepository(get())
|
|
||||||
} bind SongRepository::class
|
|
||||||
|
|
||||||
single {
|
|
||||||
RealGenreRepository(get(), get())
|
|
||||||
} bind GenreRepository::class
|
|
||||||
|
|
||||||
single {
|
|
||||||
RealAlbumRepository(get())
|
|
||||||
} bind AlbumRepository::class
|
|
||||||
|
|
||||||
single {
|
|
||||||
RealArtistRepository(get(), get())
|
|
||||||
} bind ArtistRepository::class
|
|
||||||
|
|
||||||
single {
|
|
||||||
RealPlaylistRepository(get())
|
|
||||||
} bind PlaylistRepository::class
|
|
||||||
|
|
||||||
single {
|
|
||||||
RealTopPlayedRepository(get(), get(), get(), get())
|
|
||||||
} bind TopPlayedRepository::class
|
|
||||||
|
|
||||||
single {
|
|
||||||
RealLastAddedRepository(
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get()
|
|
||||||
)
|
|
||||||
} bind LastAddedRepository::class
|
|
||||||
|
|
||||||
single {
|
|
||||||
RealSearchRepository(
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get(),
|
|
||||||
get()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val viewModules = module {
|
|
||||||
|
|
||||||
viewModel {
|
|
||||||
LibraryViewModel(get())
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel { (albumId: Long) ->
|
|
||||||
AlbumDetailsViewModel(
|
|
||||||
get(),
|
|
||||||
albumId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel { (artistId: Long?, artistName: String?) ->
|
|
||||||
ArtistDetailsViewModel(
|
|
||||||
get(),
|
|
||||||
artistId,
|
|
||||||
artistName
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel { (playlistId: Long) ->
|
|
||||||
PlaylistDetailsViewModel(
|
|
||||||
get(),
|
|
||||||
playlistId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel { (genre: Genre) ->
|
|
||||||
GenreDetailsViewModel(
|
|
||||||
get(),
|
|
||||||
genre
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val appModules = listOf(mainModule, dataModule, autoModule, viewModules, roomModule)
|
|
24
app/src/main/java/code/name/monkey/retromusic/Result.kt
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Hemanth Savarala.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU General Public License v3
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package code.name.monkey.retromusic
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by hemanths on 2019-10-23.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sealed class Result<out T : Any> {
|
||||||
|
class Success<out T : Any>(val data: T) : Result<T>()
|
||||||
|
class Error(val exception: Throwable) : Result<Nothing>()
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.app.ShareCompat
|
||||||
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||||
|
import code.name.monkey.retromusic.Constants.APP_INSTAGRAM_LINK
|
||||||
|
import code.name.monkey.retromusic.Constants.APP_TELEGRAM_LINK
|
||||||
|
import code.name.monkey.retromusic.Constants.APP_TWITTER_LINK
|
||||||
|
import code.name.monkey.retromusic.Constants.FAQ_LINK
|
||||||
|
import code.name.monkey.retromusic.Constants.GITHUB_PROJECT
|
||||||
|
import code.name.monkey.retromusic.Constants.PINTEREST
|
||||||
|
import code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY
|
||||||
|
import code.name.monkey.retromusic.Constants.TELEGRAM_CHANGE_LOG
|
||||||
|
import code.name.monkey.retromusic.Constants.TRANSLATE
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
|
import code.name.monkey.retromusic.adapter.ContributorAdapter
|
||||||
|
import code.name.monkey.retromusic.model.Contributor
|
||||||
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import com.afollestad.materialdialogs.LayoutMode
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
|
||||||
|
import com.afollestad.materialdialogs.list.listItems
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import kotlinx.android.synthetic.main.activity_about.*
|
||||||
|
import kotlinx.android.synthetic.main.card_credit.*
|
||||||
|
import kotlinx.android.synthetic.main.card_other.*
|
||||||
|
import kotlinx.android.synthetic.main.card_retro_info.*
|
||||||
|
import kotlinx.android.synthetic.main.card_social.*
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
class AboutActivity : AbsBaseActivity(), View.OnClickListener {
|
||||||
|
|
||||||
|
private val assetJsonData: String?
|
||||||
|
get() {
|
||||||
|
val json: String
|
||||||
|
try {
|
||||||
|
val inputStream = assets.open("contributors.json")
|
||||||
|
val size = inputStream.available()
|
||||||
|
val buffer = ByteArray(size)
|
||||||
|
inputStream.read(buffer)
|
||||||
|
inputStream.close()
|
||||||
|
json = String(buffer, StandardCharsets.UTF_8)
|
||||||
|
} catch (ex: IOException) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_about)
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationBarColorPrimary()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
|
||||||
|
loadContributors()
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
ToolbarContentTintHelper.colorBackButton(toolbar )
|
||||||
|
version.setSummary ( getAppVersion())
|
||||||
|
setUpView()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == android.R.id.home) {
|
||||||
|
onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openUrl(url: String) {
|
||||||
|
val i = Intent(Intent.ACTION_VIEW)
|
||||||
|
i.data = Uri.parse(url)
|
||||||
|
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
startActivity(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpView() {
|
||||||
|
appGithub.setOnClickListener(this)
|
||||||
|
faqLink.setOnClickListener(this)
|
||||||
|
telegramLink.setOnClickListener(this)
|
||||||
|
appRate.setOnClickListener(this)
|
||||||
|
appTranslation.setOnClickListener(this)
|
||||||
|
appShare.setOnClickListener(this)
|
||||||
|
donateLink.setOnClickListener(this)
|
||||||
|
instagramLink.setOnClickListener(this)
|
||||||
|
twitterLink.setOnClickListener(this)
|
||||||
|
changelog.setOnClickListener(this)
|
||||||
|
openSource.setOnClickListener(this)
|
||||||
|
pinterestLink.setOnClickListener(this)
|
||||||
|
bugReportLink.setOnClickListener(this)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(view: View) {
|
||||||
|
when (view.id) {
|
||||||
|
R.id.pinterestLink -> openUrl(PINTEREST)
|
||||||
|
R.id.faqLink -> openUrl(FAQ_LINK)
|
||||||
|
R.id.telegramLink -> openUrl(APP_TELEGRAM_LINK)
|
||||||
|
R.id.appGithub -> openUrl(GITHUB_PROJECT)
|
||||||
|
R.id.appTranslation -> openUrl(TRANSLATE)
|
||||||
|
R.id.appRate -> openUrl(RATE_ON_GOOGLE_PLAY)
|
||||||
|
R.id.appShare -> shareApp()
|
||||||
|
R.id.donateLink -> NavigationUtil.goToSupportDevelopment(this)
|
||||||
|
R.id.instagramLink -> openUrl(APP_INSTAGRAM_LINK)
|
||||||
|
R.id.twitterLink -> openUrl(APP_TWITTER_LINK)
|
||||||
|
R.id.changelog -> showChangeLogOptions()
|
||||||
|
R.id.openSource -> NavigationUtil.goToOpenSource(this)
|
||||||
|
R.id.bugReportLink -> NavigationUtil.bugReport(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showChangeLogOptions() {
|
||||||
|
MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT))
|
||||||
|
.show {
|
||||||
|
cornerRadius(PreferenceUtil.getInstance(this@AboutActivity).dialogCorner)
|
||||||
|
listItems(items = listOf("Telegram Channel", "App")) { _, position, _ ->
|
||||||
|
if (position == 0) {
|
||||||
|
openUrl(TELEGRAM_CHANGE_LOG)
|
||||||
|
} else {
|
||||||
|
NavigationUtil.gotoWhatNews(this@AboutActivity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAppVersion(): String {
|
||||||
|
return try {
|
||||||
|
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||||||
|
packageInfo.versionName
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
"0.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shareApp() {
|
||||||
|
ShareCompat.IntentBuilder.from(this)
|
||||||
|
.setType("text/plain")
|
||||||
|
.setChooserTitle(R.string.share_app)
|
||||||
|
.setText(String.format(getString(R.string.app_share), packageName))
|
||||||
|
.startChooser()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadContributors() {
|
||||||
|
val data = assetJsonData
|
||||||
|
val type = object : TypeToken<List<Contributor>>() {
|
||||||
|
|
||||||
|
}.type
|
||||||
|
val contributors = Gson().fromJson<List<Contributor>>(data, type)
|
||||||
|
|
||||||
|
val contributorAdapter = ContributorAdapter(contributors)
|
||||||
|
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
|
recyclerView.itemAnimator = DefaultItemAnimator()
|
||||||
|
recyclerView.adapter = contributorAdapter
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,305 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.app.ActivityOptions
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.transition.Slide
|
||||||
|
import android.view.*
|
||||||
|
import android.view.animation.AnimationUtils
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
|
import code.name.monkey.retromusic.App
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||||
|
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
||||||
|
import code.name.monkey.retromusic.activities.tageditor.AlbumTagEditorActivity
|
||||||
|
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
||||||
|
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
||||||
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
||||||
|
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
|
||||||
|
import code.name.monkey.retromusic.extensions.show
|
||||||
|
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||||
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder
|
||||||
|
import code.name.monkey.retromusic.model.Album
|
||||||
|
import code.name.monkey.retromusic.model.Artist
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsView
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import kotlinx.android.synthetic.main.activity_album.*
|
||||||
|
import kotlinx.android.synthetic.main.activity_album_content.*
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import android.util.Pair as UtilPair
|
||||||
|
|
||||||
|
class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView {
|
||||||
|
|
||||||
|
private lateinit var simpleSongAdapter: SimpleSongAdapter
|
||||||
|
private var disposable = CompositeDisposable()
|
||||||
|
|
||||||
|
private lateinit var album: Album
|
||||||
|
|
||||||
|
private val savedSortOrder: String
|
||||||
|
get() = PreferenceUtil.getInstance(this).albumDetailSongSortOrder
|
||||||
|
|
||||||
|
override fun createContentView(): View {
|
||||||
|
return wrapSlidingMusicPanel(R.layout.activity_album)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupWindowTransition() {
|
||||||
|
val slide = Slide(Gravity.BOTTOM)
|
||||||
|
slide.excludeTarget(android.R.id.statusBarBackground, true)
|
||||||
|
slide.excludeTarget(android.R.id.navigationBarBackground, true)
|
||||||
|
slide.excludeTarget(toolbar, true)
|
||||||
|
slide.interpolator = AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in)
|
||||||
|
window.enterTransition = slide
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var albumDetailsPresenter: AlbumDetailsPresenter
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setDrawUnderStatusBar()
|
||||||
|
//setupWindowTransition()
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
toggleBottomNavigationView(true)
|
||||||
|
setStatusbarColor(Color.TRANSPARENT)
|
||||||
|
setNavigationbarColorAuto()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
|
||||||
|
|
||||||
|
App.musicComponent.inject(this)
|
||||||
|
postponeEnterTransition()
|
||||||
|
|
||||||
|
artistImage = findViewById(R.id.artistImage)
|
||||||
|
|
||||||
|
setupRecyclerView()
|
||||||
|
|
||||||
|
artistImage.setOnClickListener {
|
||||||
|
val artistPairs = ActivityOptions.makeSceneTransitionAnimation(this, UtilPair.create(artistImage, getString(R.string.transition_artist_image)))
|
||||||
|
NavigationUtil.goToArtistOptions(this, album.artistId, artistPairs)
|
||||||
|
}
|
||||||
|
playAction.apply {
|
||||||
|
setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) }
|
||||||
|
}
|
||||||
|
shuffleAction.apply {
|
||||||
|
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(album.songs!!, true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
albumDetailsPresenter.attachView(this)
|
||||||
|
|
||||||
|
if (intent.extras!!.containsKey(EXTRA_ALBUM_ID)) {
|
||||||
|
intent.extras?.getInt(EXTRA_ALBUM_ID)?.let { albumDetailsPresenter.loadAlbum(it) }
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecyclerView() {
|
||||||
|
simpleSongAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song)
|
||||||
|
recyclerView.apply {
|
||||||
|
layoutManager = LinearLayoutManager(this@AlbumDetailsActivity)
|
||||||
|
itemAnimator = DefaultItemAnimator()
|
||||||
|
isNestedScrollingEnabled = false
|
||||||
|
adapter = simpleSongAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
disposable.dispose()
|
||||||
|
albumDetailsPresenter.detachView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun complete() {
|
||||||
|
scheduleStartPostponedTransition(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun album(album: Album) {
|
||||||
|
|
||||||
|
if (album.songs!!.isEmpty()) {
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.album = album
|
||||||
|
|
||||||
|
albumTitle.text = album.title
|
||||||
|
if (MusicUtil.getYearString(album.year) == "-") {
|
||||||
|
albumText.text = String.format("%s • %s", album.artistName, MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs)))
|
||||||
|
} else {
|
||||||
|
albumText.text = String.format("%s • %s • %s", album.artistName, MusicUtil.getYearString(album.year), MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs)))
|
||||||
|
}
|
||||||
|
loadAlbumCover()
|
||||||
|
simpleSongAdapter.swapDataSet(album.songs)
|
||||||
|
albumDetailsPresenter.loadMore(album.artistId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var artistImage: ImageView
|
||||||
|
|
||||||
|
override fun moreAlbums(albums: ArrayList<Album>) {
|
||||||
|
moreTitle.show()
|
||||||
|
moreRecyclerView.show()
|
||||||
|
moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName)
|
||||||
|
|
||||||
|
val albumAdapter = HorizontalAlbumAdapter(this, albums, false, null)
|
||||||
|
moreRecyclerView.layoutManager = GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false)
|
||||||
|
moreRecyclerView.adapter = albumAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadArtistImage(artist: Artist) {
|
||||||
|
ArtistGlideRequest.Builder.from(Glide.with(this), artist)
|
||||||
|
.generatePalette(this).build()
|
||||||
|
.dontAnimate()
|
||||||
|
.dontTransform()
|
||||||
|
.into(object : RetroMusicColoredTarget(artistImage) {
|
||||||
|
override fun onColorReady(color: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadAlbumCover() {
|
||||||
|
SongGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong())
|
||||||
|
.checkIgnoreMediaStore(this)
|
||||||
|
.generatePalette(this).build()
|
||||||
|
.dontAnimate().dontTransform()
|
||||||
|
.into(object : RetroMusicColoredTarget(image) {
|
||||||
|
override fun onColorReady(color: Int) {
|
||||||
|
setColors(color)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scheduleStartPostponedTransition(image: ImageView) {
|
||||||
|
image.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
|
||||||
|
override fun onPreDraw(): Boolean {
|
||||||
|
image.viewTreeObserver.removeOnPreDrawListener(this)
|
||||||
|
startPostponedEnterTransition();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setColors(color: Int) {
|
||||||
|
val themeColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color
|
||||||
|
else ThemeStore.accentColor(this)
|
||||||
|
|
||||||
|
songTitle.setTextColor(themeColor)
|
||||||
|
moreTitle.setTextColor(themeColor)
|
||||||
|
|
||||||
|
val buttonColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color
|
||||||
|
else ATHUtil.resolveColor(this, R.attr.cardBackgroundColor)
|
||||||
|
|
||||||
|
MaterialUtil.setTint(button = shuffleAction, color = buttonColor)
|
||||||
|
MaterialUtil.setTint(button = playAction, color = buttonColor)
|
||||||
|
|
||||||
|
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
supportActionBar?.title = null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.menu_album_detail, menu)
|
||||||
|
val sortOrder = menu.findItem(R.id.action_sort_order)
|
||||||
|
setUpSortOrderMenu(sortOrder.subMenu)
|
||||||
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return handleSortOrderMenuItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
|
||||||
|
var sortOrder: String? = null
|
||||||
|
val songs = simpleSongAdapter.dataSet
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.action_play_next -> {
|
||||||
|
MusicPlayerRemote.playNext(songs)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_add_to_current_playing -> {
|
||||||
|
MusicPlayerRemote.enqueue(songs)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_add_to_playlist -> {
|
||||||
|
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_delete_from_device -> {
|
||||||
|
DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
android.R.id.home -> {
|
||||||
|
super.onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_tag_editor -> {
|
||||||
|
val intent = Intent(this, AlbumTagEditorActivity::class.java)
|
||||||
|
intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id)
|
||||||
|
startActivityForResult(intent, TAG_EDITOR_REQUEST)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/*Sort*/
|
||||||
|
R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z
|
||||||
|
R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A
|
||||||
|
R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST
|
||||||
|
R.id.action_sort_order_artist_song_duration -> sortOrder = AlbumSongSortOrder.SONG_DURATION
|
||||||
|
}
|
||||||
|
if (sortOrder != null) {
|
||||||
|
item.isChecked = true
|
||||||
|
setSaveSortOrder(sortOrder)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpSortOrderMenu(sortOrder: SubMenu) {
|
||||||
|
when (savedSortOrder) {
|
||||||
|
AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title).isChecked = true
|
||||||
|
AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc).isChecked = true
|
||||||
|
AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list).isChecked = true
|
||||||
|
AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration).isChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setSaveSortOrder(sortOrder: String?) {
|
||||||
|
PreferenceUtil.getInstance(this).albumDetailSongSortOrder = sortOrder
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMediaStoreChanged() {
|
||||||
|
super.onMediaStoreChanged()
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reload() {
|
||||||
|
if (intent.extras!!.containsKey(EXTRA_ALBUM_ID)) {
|
||||||
|
intent.extras?.getInt(EXTRA_ALBUM_ID)?.let { albumDetailsPresenter.loadAlbum(it) }
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val EXTRA_ALBUM_ID = "extra_album_id"
|
||||||
|
private const val TAG_EDITOR_REQUEST = 2001
|
||||||
|
}
|
||||||
|
}
|
311
app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt
Executable file
|
@ -0,0 +1,311 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Html
|
||||||
|
import android.text.Spanned
|
||||||
|
import android.transition.Slide
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.animation.AnimationUtils
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
|
import code.name.monkey.retromusic.App
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||||
|
import code.name.monkey.retromusic.adapter.album.AlbumAdapter
|
||||||
|
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
||||||
|
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
||||||
|
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
||||||
|
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||||
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
import code.name.monkey.retromusic.model.Artist
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsView
|
||||||
|
import code.name.monkey.retromusic.rest.LastFMRestClient
|
||||||
|
import code.name.monkey.retromusic.rest.model.LastFmArtist
|
||||||
|
import code.name.monkey.retromusic.util.*
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import kotlinx.android.synthetic.main.activity_artist_content.*
|
||||||
|
import kotlinx.android.synthetic.main.activity_artist_details.*
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView {
|
||||||
|
|
||||||
|
private var biography: Spanned? = null
|
||||||
|
private lateinit var artist: Artist
|
||||||
|
private var lastFMRestClient: LastFMRestClient? = null
|
||||||
|
private lateinit var songAdapter: SimpleSongAdapter
|
||||||
|
private lateinit var albumAdapter: AlbumAdapter
|
||||||
|
private var forceDownload: Boolean = false
|
||||||
|
|
||||||
|
private fun setupWindowTransitions() {
|
||||||
|
val slide = Slide(Gravity.BOTTOM)
|
||||||
|
slide.interpolator = AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in)
|
||||||
|
window.enterTransition = slide
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createContentView(): View {
|
||||||
|
return wrapSlidingMusicPanel(R.layout.activity_artist_details)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var artistDetailsPresenter: ArtistDetailsPresenter
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setDrawUnderStatusBar()
|
||||||
|
// setupWindowTransitions()
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
toggleBottomNavigationView(true)
|
||||||
|
setStatusbarColor(Color.TRANSPARENT)
|
||||||
|
setNavigationbarColorAuto()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
|
||||||
|
|
||||||
|
ActivityCompat.postponeEnterTransition(this)
|
||||||
|
|
||||||
|
lastFMRestClient = LastFMRestClient(this)
|
||||||
|
|
||||||
|
setUpViews()
|
||||||
|
|
||||||
|
playAction.apply {
|
||||||
|
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) }
|
||||||
|
}
|
||||||
|
shuffleAction.apply {
|
||||||
|
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
biographyText.setOnClickListener {
|
||||||
|
if (biographyText.maxLines == 4) {
|
||||||
|
biographyText.maxLines = Integer.MAX_VALUE
|
||||||
|
} else {
|
||||||
|
biographyText.maxLines = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
App.musicComponent.inject(this)
|
||||||
|
artistDetailsPresenter.attachView(this)
|
||||||
|
|
||||||
|
if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
|
||||||
|
intent.extras?.getInt(EXTRA_ARTIST_ID)?.let { artistDetailsPresenter.loadArtist(it) }
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
artistDetailsPresenter.detachView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpViews() {
|
||||||
|
setupRecyclerView()
|
||||||
|
setupContainerHeight()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupContainerHeight() {
|
||||||
|
imageContainer?.let {
|
||||||
|
val params = it.layoutParams
|
||||||
|
params.width = DensityUtil.getScreenHeight(this) / 2
|
||||||
|
it.layoutParams = params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun setupRecyclerView() {
|
||||||
|
albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), false, null)
|
||||||
|
albumRecyclerView.apply {
|
||||||
|
itemAnimator = DefaultItemAnimator()
|
||||||
|
layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false)
|
||||||
|
adapter = albumAdapter
|
||||||
|
}
|
||||||
|
songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song)
|
||||||
|
recyclerView.apply {
|
||||||
|
itemAnimator = DefaultItemAnimator()
|
||||||
|
layoutManager = LinearLayoutManager(this.context)
|
||||||
|
adapter = songAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
when (requestCode) {
|
||||||
|
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
|
||||||
|
data?.data?.let { CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, it) }
|
||||||
|
}
|
||||||
|
else -> if (resultCode == Activity.RESULT_OK) {
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showEmptyView() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun complete() {
|
||||||
|
ActivityCompat.startPostponedEnterTransition(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun artist(artist: Artist) {
|
||||||
|
if (artist.songCount <= 0) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
this.artist = artist
|
||||||
|
loadArtistImage()
|
||||||
|
|
||||||
|
if (RetroUtil.isAllowedToDownloadMetadata(this)) {
|
||||||
|
loadBiography(artist.name)
|
||||||
|
}
|
||||||
|
artistTitle.text = artist.name
|
||||||
|
text.text = String.format("%s • %s", MusicUtil.getArtistInfoString(this, artist), MusicUtil
|
||||||
|
.getReadableDurationString(MusicUtil.getTotalDuration(this, artist.songs)))
|
||||||
|
|
||||||
|
songAdapter.swapDataSet(artist.songs)
|
||||||
|
albumAdapter.swapDataSet(artist.albums!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadBiography(name: String,
|
||||||
|
lang: String? = Locale.getDefault().language) {
|
||||||
|
biography = null
|
||||||
|
this.lang = lang
|
||||||
|
artistDetailsPresenter.loadBiography(name, lang, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun artistInfo(lastFmArtist: LastFmArtist?) {
|
||||||
|
if (lastFmArtist != null && lastFmArtist.artist != null) {
|
||||||
|
val bioContent = lastFmArtist.artist.bio.content
|
||||||
|
if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) {
|
||||||
|
biographyText.visibility = View.VISIBLE
|
||||||
|
biographyTitle.visibility = View.VISIBLE
|
||||||
|
biography = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
Html.fromHtml(bioContent, Html.FROM_HTML_MODE_LEGACY)
|
||||||
|
} else {
|
||||||
|
Html.fromHtml(bioContent)
|
||||||
|
}
|
||||||
|
biographyText.text = biography
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the "lang" parameter is set and no biography is given, retry with default language
|
||||||
|
if (biography == null && lang != null) {
|
||||||
|
loadBiography(artist.name, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var lang: String? = null
|
||||||
|
|
||||||
|
private fun loadArtistImage() {
|
||||||
|
ArtistGlideRequest.Builder.from(Glide.with(this), artist)
|
||||||
|
.generatePalette(this).build()
|
||||||
|
.dontAnimate()
|
||||||
|
.into(object : RetroMusicColoredTarget(artistImage) {
|
||||||
|
override fun onColorReady(color: Int) {
|
||||||
|
setColors(color)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setColors(color: Int) {
|
||||||
|
|
||||||
|
val textColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color
|
||||||
|
else ThemeStore.accentColor(this)
|
||||||
|
|
||||||
|
albumTitle.setTextColor(textColor)
|
||||||
|
songTitle.setTextColor(textColor)
|
||||||
|
biographyTitle.setTextColor(textColor)
|
||||||
|
|
||||||
|
val buttonColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color
|
||||||
|
else ATHUtil.resolveColor(this, R.attr.cardBackgroundColor)
|
||||||
|
|
||||||
|
MaterialUtil.setTint(button = shuffleAction, color = buttonColor)
|
||||||
|
MaterialUtil.setTint(button = playAction, color = buttonColor)
|
||||||
|
|
||||||
|
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
supportActionBar?.title = null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return handleSortOrderMenuItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
|
||||||
|
val songs = artist.songs
|
||||||
|
when (item.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
super.onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_play_next -> {
|
||||||
|
MusicPlayerRemote.playNext(songs)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_add_to_current_playing -> {
|
||||||
|
MusicPlayerRemote.enqueue(songs)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_add_to_playlist -> {
|
||||||
|
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_set_artist_image -> {
|
||||||
|
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||||
|
intent.type = "image/*"
|
||||||
|
startActivityForResult(Intent.createChooser(intent, getString(R.string.pick_from_local_storage)), REQUEST_CODE_SELECT_IMAGE)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
R.id.action_reset_artist_image -> {
|
||||||
|
Toast.makeText(this@ArtistDetailActivity, resources.getString(R.string.updating),
|
||||||
|
Toast.LENGTH_SHORT).show()
|
||||||
|
CustomArtistImageUtil.getInstance(this@ArtistDetailActivity).resetCustomArtistImage(artist)
|
||||||
|
forceDownload = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.menu_artist_detail, menu)
|
||||||
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMediaStoreChanged() {
|
||||||
|
super.onMediaStoreChanged()
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reload() {
|
||||||
|
if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
|
||||||
|
intent.extras?.getInt(EXTRA_ARTIST_ID)?.let { artistDetailsPresenter.loadArtist(it) }
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val EXTRA_ARTIST_ID = "extra_artist_id"
|
||||||
|
const val REQUEST_CODE_SELECT_IMAGE = 9003
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,258 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.PorterDuff
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
|
||||||
import code.name.monkey.retromusic.databinding.ActivityDriveModeBinding
|
|
||||||
import code.name.monkey.retromusic.db.toSongEntity
|
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
|
||||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
|
||||||
import code.name.monkey.retromusic.glide.BlurTransformation
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
|
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
|
||||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
|
||||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
|
|
||||||
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
|
|
||||||
import code.name.monkey.retromusic.model.Song
|
|
||||||
import code.name.monkey.retromusic.repository.RealRepository
|
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.google.android.material.slider.Slider
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.koin.android.ext.android.inject
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by hemanths on 2020-02-02.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityDriveModeBinding
|
|
||||||
private var lastPlaybackControlsColor: Int = Color.GRAY
|
|
||||||
private var lastDisabledPlaybackControlsColor: Int = Color.GRAY
|
|
||||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
|
||||||
private val repository: RealRepository by inject()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
binding = ActivityDriveModeBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
setUpMusicControllers()
|
|
||||||
|
|
||||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
|
||||||
lastPlaybackControlsColor = accentColor()
|
|
||||||
binding.close.setOnClickListener {
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
|
||||||
}
|
|
||||||
binding.repeatButton.drawAboveSystemBars()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpMusicControllers() {
|
|
||||||
setUpPlayPauseFab()
|
|
||||||
setUpPrevNext()
|
|
||||||
setUpRepeatButton()
|
|
||||||
setUpShuffleButton()
|
|
||||||
setUpProgressSlider()
|
|
||||||
setupFavouriteToggle()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupFavouriteToggle() {
|
|
||||||
binding.songFavourite.setOnClickListener {
|
|
||||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun toggleFavorite(song: Song) {
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
val playlist = repository.favoritePlaylist()
|
|
||||||
val songEntity = song.toSongEntity(playlist.playListId)
|
|
||||||
val isFavorite = repository.isSongFavorite(song.id)
|
|
||||||
if (isFavorite) {
|
|
||||||
repository.removeSongFromPlaylist(songEntity)
|
|
||||||
} else {
|
|
||||||
repository.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
|
|
||||||
}
|
|
||||||
sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateFavorite() {
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
val isFavorite: Boolean =
|
|
||||||
repository.isSongFavorite(MusicPlayerRemote.currentSong.id)
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
binding.songFavourite.setImageResource(if (isFavorite) R.drawable.ic_favorite else R.drawable.ic_favorite_border)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpProgressSlider() {
|
|
||||||
binding.progressSlider.addOnChangeListener { _: Slider, progress: Float, fromUser: Boolean ->
|
|
||||||
if (fromUser) {
|
|
||||||
MusicPlayerRemote.seekTo(progress.toInt())
|
|
||||||
onUpdateProgressViews(
|
|
||||||
MusicPlayerRemote.songProgressMillis,
|
|
||||||
MusicPlayerRemote.songDurationMillis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
progressViewUpdateHelper.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
progressViewUpdateHelper.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpPrevNext() {
|
|
||||||
binding.nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
|
||||||
binding.previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpShuffleButton() {
|
|
||||||
binding.shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpRepeatButton() {
|
|
||||||
binding.repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpPlayPauseFab() {
|
|
||||||
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRepeatModeChanged() {
|
|
||||||
super.onRepeatModeChanged()
|
|
||||||
updateRepeatState()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onShuffleModeChanged() {
|
|
||||||
super.onShuffleModeChanged()
|
|
||||||
updateShuffleState()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlayStateChanged() {
|
|
||||||
super.onPlayStateChanged()
|
|
||||||
updatePlayPauseDrawableState()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServiceConnected() {
|
|
||||||
super.onServiceConnected()
|
|
||||||
updatePlayPauseDrawableState()
|
|
||||||
updateSong()
|
|
||||||
updateRepeatState()
|
|
||||||
updateShuffleState()
|
|
||||||
updateFavorite()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updatePlayPauseDrawableState() {
|
|
||||||
if (MusicPlayerRemote.isPlaying) {
|
|
||||||
binding.playPauseButton.setImageResource(R.drawable.ic_pause)
|
|
||||||
} else {
|
|
||||||
binding.playPauseButton.setImageResource(R.drawable.ic_play_arrow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateShuffleState() {
|
|
||||||
when (MusicPlayerRemote.shuffleMode) {
|
|
||||||
MusicService.SHUFFLE_MODE_SHUFFLE -> binding.shuffleButton.setColorFilter(
|
|
||||||
lastPlaybackControlsColor,
|
|
||||||
PorterDuff.Mode.SRC_IN
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> binding.shuffleButton.setColorFilter(
|
|
||||||
lastDisabledPlaybackControlsColor,
|
|
||||||
PorterDuff.Mode.SRC_IN
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateRepeatState() {
|
|
||||||
when (MusicPlayerRemote.repeatMode) {
|
|
||||||
MusicService.REPEAT_MODE_NONE -> {
|
|
||||||
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
|
|
||||||
binding.repeatButton.setColorFilter(
|
|
||||||
lastDisabledPlaybackControlsColor,
|
|
||||||
PorterDuff.Mode.SRC_IN
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
MusicService.REPEAT_MODE_ALL -> {
|
|
||||||
binding.repeatButton.setImageResource(R.drawable.ic_repeat)
|
|
||||||
binding.repeatButton.setColorFilter(
|
|
||||||
lastPlaybackControlsColor,
|
|
||||||
PorterDuff.Mode.SRC_IN
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
MusicService.REPEAT_MODE_THIS -> {
|
|
||||||
binding.repeatButton.setImageResource(R.drawable.ic_repeat_one)
|
|
||||||
binding.repeatButton.setColorFilter(
|
|
||||||
lastPlaybackControlsColor,
|
|
||||||
PorterDuff.Mode.SRC_IN
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlayingMetaChanged() {
|
|
||||||
super.onPlayingMetaChanged()
|
|
||||||
updateSong()
|
|
||||||
updateFavorite()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFavoriteStateChanged() {
|
|
||||||
super.onFavoriteStateChanged()
|
|
||||||
updateFavorite()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateSong() {
|
|
||||||
val song = MusicPlayerRemote.currentSong
|
|
||||||
|
|
||||||
binding.songTitle.text = song.title
|
|
||||||
binding.songText.text = song.artistName
|
|
||||||
|
|
||||||
Glide.with(this)
|
|
||||||
.load(RetroGlideExtension.getSongModel(song))
|
|
||||||
.songCoverOptions(song)
|
|
||||||
.transform(BlurTransformation.Builder(this).build())
|
|
||||||
.into(binding.image)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
|
||||||
binding.progressSlider.run {
|
|
||||||
valueTo = total.toFloat()
|
|
||||||
value = progress.toFloat().coerceIn(valueFrom, valueTo)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
|
||||||
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
package code.name.monkey.retromusic.activities
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.ImageView
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import cat.ereza.customactivityoncrash.CustomActivityOnCrash
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.util.FileUtils.createFile
|
|
||||||
import code.name.monkey.retromusic.util.Share.shareFile
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import java.text.DateFormat
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class ErrorActivity : AppCompatActivity() {
|
|
||||||
private val dayFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
|
||||||
private val reportPrefix = "bug_report-"
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setContentView(cat.ereza.customactivityoncrash.R.layout.customactivityoncrash_default_error_activity)
|
|
||||||
|
|
||||||
val restartButton =
|
|
||||||
findViewById<Button>(cat.ereza.customactivityoncrash.R.id.customactivityoncrash_error_activity_restart_button)
|
|
||||||
|
|
||||||
val config = CustomActivityOnCrash.getConfigFromIntent(intent)
|
|
||||||
if (config == null) {
|
|
||||||
finish()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
restartButton.setText(cat.ereza.customactivityoncrash.R.string.customactivityoncrash_error_activity_restart_app)
|
|
||||||
restartButton.setOnClickListener {
|
|
||||||
CustomActivityOnCrash.restartApplication(
|
|
||||||
this@ErrorActivity,
|
|
||||||
config
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val moreInfoButton =
|
|
||||||
findViewById<Button>(cat.ereza.customactivityoncrash.R.id.customactivityoncrash_error_activity_more_info_button)
|
|
||||||
|
|
||||||
moreInfoButton.setOnClickListener { //We retrieve all the error data and show it
|
|
||||||
MaterialAlertDialogBuilder(this@ErrorActivity)
|
|
||||||
.setTitle(cat.ereza.customactivityoncrash.R.string.customactivityoncrash_error_activity_error_details_title)
|
|
||||||
.setMessage(
|
|
||||||
CustomActivityOnCrash.getAllErrorDetailsFromIntent(
|
|
||||||
this@ErrorActivity,
|
|
||||||
intent
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.setPositiveButton(
|
|
||||||
cat.ereza.customactivityoncrash.R.string.customactivityoncrash_error_activity_error_details_close,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
.setNeutralButton(
|
|
||||||
R.string.customactivityoncrash_error_activity_error_details_share
|
|
||||||
) { _, _ ->
|
|
||||||
|
|
||||||
val bugReport = createFile(
|
|
||||||
context = this,
|
|
||||||
"Bug Report",
|
|
||||||
"$reportPrefix${dayFormat.format(Date())}",
|
|
||||||
CustomActivityOnCrash.getAllErrorDetailsFromIntent(
|
|
||||||
this@ErrorActivity,
|
|
||||||
intent
|
|
||||||
), ".txt"
|
|
||||||
)
|
|
||||||
shareFile(this, bugReport, "text/*")
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
val errorActivityDrawableId = config.errorDrawable
|
|
||||||
val errorImageView =
|
|
||||||
findViewById<ImageView>(cat.ereza.customactivityoncrash.R.id.customactivityoncrash_error_activity_image)
|
|
||||||
if (errorActivityDrawableId != null) {
|
|
||||||
errorImageView.setImageResource(
|
|
||||||
errorActivityDrawableId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.retromusic.App
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||||
|
import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter
|
||||||
|
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||||
|
import code.name.monkey.retromusic.helper.menu.GenreMenuHelper
|
||||||
|
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||||
|
import code.name.monkey.retromusic.model.Genre
|
||||||
|
import code.name.monkey.retromusic.model.Song
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsView
|
||||||
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
|
import code.name.monkey.retromusic.util.ViewUtil
|
||||||
|
import com.afollestad.materialcab.MaterialCab
|
||||||
|
import kotlinx.android.synthetic.main.activity_playlist_detail.*
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Hemanth S (h4h13).
|
||||||
|
*/
|
||||||
|
|
||||||
|
class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder, GenreDetailsView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var genreDetailsPresenter: GenreDetailsPresenter
|
||||||
|
|
||||||
|
private lateinit var genre: Genre
|
||||||
|
private lateinit var songAdapter: ShuffleButtonSongAdapter
|
||||||
|
private var cab: MaterialCab? = null
|
||||||
|
|
||||||
|
private fun checkIsEmpty() {
|
||||||
|
empty?.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setDrawUnderStatusBar()
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
setStatusbarColor(Color.TRANSPARENT)
|
||||||
|
setNavigationbarColorAuto()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
|
||||||
|
toggleBottomNavigationView(true)
|
||||||
|
|
||||||
|
if (intent.extras != null) {
|
||||||
|
genre = intent?.extras?.getParcelable(EXTRA_GENRE_ID)!!
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpToolBar()
|
||||||
|
setupRecyclerView()
|
||||||
|
|
||||||
|
App.musicComponent.inject(this)
|
||||||
|
genreDetailsPresenter.attachView(this)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpToolBar() {
|
||||||
|
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorPrimary)
|
||||||
|
appBarLayout.setBackgroundColor(primaryColor)
|
||||||
|
applyToolbar(toolbar)
|
||||||
|
title = genre.name
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
genreDetailsPresenter.loadGenreSongs(genre.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
genreDetailsPresenter.detachView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createContentView(): View {
|
||||||
|
return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showEmptyView() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.menu_genre_detail, menu)
|
||||||
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == android.R.id.home) {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
return GenreMenuHelper.handleMenuClick(this, genre, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecyclerView() {
|
||||||
|
ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView, ThemeStore.accentColor(this))
|
||||||
|
songAdapter = ShuffleButtonSongAdapter(this, ArrayList(), R.layout.item_list, false, this)
|
||||||
|
recyclerView.apply {
|
||||||
|
itemAnimator = DefaultItemAnimator()
|
||||||
|
layoutManager = LinearLayoutManager(this@GenreDetailsActivity)
|
||||||
|
adapter = songAdapter
|
||||||
|
}
|
||||||
|
songAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
|
override fun onChanged() {
|
||||||
|
super.onChanged()
|
||||||
|
checkIsEmpty()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun songs(songs: ArrayList<Song>) {
|
||||||
|
songAdapter.swapDataSet(songs)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||||
|
if (cab != null && cab!!.isActive) cab!!.finish()
|
||||||
|
cab = MaterialCab(this, R.id.cab_stub)
|
||||||
|
.setMenu(menuRes)
|
||||||
|
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||||
|
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
|
||||||
|
.start(callback)
|
||||||
|
return cab!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (cab != null && cab!!.isActive)
|
||||||
|
cab!!.finish()
|
||||||
|
else {
|
||||||
|
recyclerView!!.stopScroll()
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMediaStoreChanged() {
|
||||||
|
super.onMediaStoreChanged()
|
||||||
|
genreDetailsPresenter.loadGenreSongs(genre.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXTRA_GENRE_ID = "extra_genre_id"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Hemanth Savarala.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU General Public License v3
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package code.name.monkey.retromusic.activities;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil;
|
||||||
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
||||||
|
|
||||||
|
import static code.name.monkey.appthemehelper.util.ATHUtil.INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by hemanths on 2019-09-27.
|
||||||
|
*/
|
||||||
|
public class LicenseActivity extends AbsBaseActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
onBackPressed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String colorToCSS(int color) {
|
||||||
|
return String.format("rgb(%d, %d, %d)", Color.red(color), Color.green(color), Color.blue(color)); // on API 29, WebView doesn't load with hex colors
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_license);
|
||||||
|
setStatusbarColorAuto();
|
||||||
|
setNavigationBarColorPrimary();
|
||||||
|
setTaskDescriptionColorAuto();
|
||||||
|
setLightNavigationBar(true);
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
ToolbarContentTintHelper.colorBackButton(toolbar );
|
||||||
|
WebView webView = findViewById(R.id.license);
|
||||||
|
try {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
InputStream json = getAssets().open("index.html");
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8));
|
||||||
|
String str;
|
||||||
|
while ((str = in.readLine()) != null)
|
||||||
|
buf.append(str);
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
// Inject color values for WebView body background and links
|
||||||
|
final boolean isDark = INSTANCE.isWindowBackgroundDark(this);
|
||||||
|
final String backgroundColor = colorToCSS(INSTANCE.resolveColor(this, R.attr.colorPrimary, Color.parseColor(isDark ? "#424242" : "#ffffff")));
|
||||||
|
final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000"));
|
||||||
|
final String changeLog = buf.toString()
|
||||||
|
.replace("{style-placeholder}",
|
||||||
|
String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor))
|
||||||
|
.replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this)))
|
||||||
|
.replace("{link-color-active}", colorToCSS(ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))));
|
||||||
|
|
||||||
|
webView.loadData(changeLog, "text/html", "UTF-8");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
webView.loadData("<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,94 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 Hemanth Savarala.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it under
|
|
||||||
* the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities
|
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.MenuItem
|
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark
|
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil.lightenColor
|
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
|
||||||
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
|
||||||
import code.name.monkey.retromusic.databinding.ActivityLicenseBinding
|
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
|
||||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
|
||||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
|
||||||
import java.io.BufferedReader
|
|
||||||
import java.io.InputStreamReader
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
/** Created by hemanths on 2019-09-27. */
|
|
||||||
class LicenseActivity : AbsThemeActivity() {
|
|
||||||
private lateinit var binding: ActivityLicenseBinding
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
binding = ActivityLicenseBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
setSupportActionBar(binding.toolbar)
|
|
||||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
|
||||||
try {
|
|
||||||
val buf = StringBuilder()
|
|
||||||
val json = assets.open("license.html")
|
|
||||||
BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)).use { br ->
|
|
||||||
var str: String?
|
|
||||||
while (br.readLine().also { str = it } != null) {
|
|
||||||
buf.append(str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject color values for WebView body background and links
|
|
||||||
val isDark = isWindowBackgroundDark(this)
|
|
||||||
val backgroundColor = colorToCSS(
|
|
||||||
surfaceColor(Color.parseColor(if (isDark) "#424242" else "#ffffff"))
|
|
||||||
)
|
|
||||||
val contentColor = colorToCSS(Color.parseColor(if (isDark) "#ffffff" else "#000000"))
|
|
||||||
val changeLog = buf.toString()
|
|
||||||
.replace(
|
|
||||||
"{style-placeholder}", String.format(
|
|
||||||
"body { background-color: %s; color: %s; }", backgroundColor, contentColor
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.replace("{link-color}", colorToCSS(accentColor()))
|
|
||||||
.replace(
|
|
||||||
"{link-color-active}",
|
|
||||||
colorToCSS(
|
|
||||||
lightenColor(accentColor())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
binding.license.loadData(changeLog, "text/html", "UTF-8")
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
binding.license.loadData(
|
|
||||||
"<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "text/html", "UTF-8"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
binding.license.drawAboveSystemBars()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
if (item.itemId == android.R.id.home) {
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun colorToCSS(color: Int): String {
|
|
||||||
return String.format(
|
|
||||||
"rgb(%d, %d, %d)",
|
|
||||||
Color.red(color),
|
|
||||||
Color.green(color),
|
|
||||||
Color.blue(color)
|
|
||||||
) // on API 29, WebView doesn't load with hex colors
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +1,89 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
import android.app.KeyguardManager
|
import android.app.KeyguardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.view.ViewCompat
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
import code.name.monkey.retromusic.databinding.ActivityLockScreenBinding
|
import code.name.monkey.retromusic.fragments.player.lockscreen.LockScreenPlayerControlsFragment
|
||||||
import code.name.monkey.retromusic.extensions.hideStatusBar
|
|
||||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
|
||||||
import code.name.monkey.retromusic.extensions.whichFragment
|
|
||||||
import code.name.monkey.retromusic.fragments.player.lockscreen.LockScreenControlsFragment
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
|
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.r0adkll.slidr.Slidr
|
import com.r0adkll.slidr.Slidr
|
||||||
import com.r0adkll.slidr.model.SlidrConfig
|
import com.r0adkll.slidr.model.SlidrConfig
|
||||||
import com.r0adkll.slidr.model.SlidrListener
|
import com.r0adkll.slidr.model.SlidrListener
|
||||||
import com.r0adkll.slidr.model.SlidrPosition
|
import com.r0adkll.slidr.model.SlidrPosition
|
||||||
|
import kotlinx.android.synthetic.main.activity_album.*
|
||||||
|
|
||||||
class LockScreenActivity : AbsMusicServiceActivity() {
|
class LockScreenActivity : AbsMusicServiceActivity() {
|
||||||
private lateinit var binding: ActivityLockScreenBinding
|
private var fragment: LockScreenPlayerControlsFragment? = null
|
||||||
private var fragment: LockScreenControlsFragment? = null
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
lockScreenInit()
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||||
binding = ActivityLockScreenBinding.inflate(layoutInflater)
|
setShowWhenLocked(true)
|
||||||
setContentView(binding.root)
|
setTurnScreenOn(true)
|
||||||
hideStatusBar()
|
} else {
|
||||||
setTaskDescriptionColorAuto()
|
this.window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
|
||||||
|
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
|
||||||
|
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
|
||||||
|
}
|
||||||
|
setDrawUnderStatusBar()
|
||||||
|
setContentView(R.layout.activity_lock_screen)
|
||||||
|
|
||||||
val config = SlidrConfig.Builder().listener(object : SlidrListener {
|
hideStatusBar()
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationBarColorPrimary()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
|
||||||
|
val config = SlidrConfig.Builder()
|
||||||
|
.listener(object : SlidrListener {
|
||||||
override fun onSlideStateChanged(state: Int) {
|
override fun onSlideStateChanged(state: Int) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSlideChange(percent: Float) {
|
override fun onSlideChange(percent: Float) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSlideOpened() {
|
override fun onSlideOpened() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSlideClosed(): Boolean {
|
override fun onSlideClosed(): Boolean {
|
||||||
if (VersionUtils.hasOreo()) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val keyguardManager =
|
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||||
getSystemService<KeyguardManager>()
|
keyguardManager.requestDismissKeyguard(this@LockScreenActivity, null)
|
||||||
keyguardManager?.requestDismissKeyguard(this@LockScreenActivity, null)
|
|
||||||
}
|
}
|
||||||
finish()
|
finish()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}).position(SlidrPosition.BOTTOM).build()
|
})
|
||||||
|
.position(SlidrPosition.BOTTOM)
|
||||||
|
.build()
|
||||||
|
|
||||||
Slidr.attach(this, config)
|
Slidr.attach(this, config)
|
||||||
|
|
||||||
fragment = whichFragment<LockScreenControlsFragment>(R.id.playback_controls_fragment)
|
fragment = supportFragmentManager.findFragmentById(R.id.playback_controls_fragment) as LockScreenPlayerControlsFragment?
|
||||||
|
|
||||||
binding.slide.apply {
|
findViewById<View>(R.id.slide).apply {
|
||||||
translationY = 100f
|
translationY = 100f
|
||||||
alpha = 0f
|
alpha = 0f
|
||||||
animate().translationY(0f).alpha(1f).setDuration(1500).start()
|
ViewCompat.animate(this)
|
||||||
}
|
.translationY(0f)
|
||||||
|
.alpha(1f)
|
||||||
|
.setDuration(1500)
|
||||||
|
.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("Deprecation")
|
findViewById<View>(R.id.root_layout).setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
private fun lockScreenInit() {
|
|
||||||
if (VersionUtils.hasOreoMR1()) {
|
|
||||||
setShowWhenLocked(true)
|
|
||||||
//setTurnScreenOn(true)
|
|
||||||
} else {
|
|
||||||
window.addFlags(
|
|
||||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
|
||||||
// or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayingMetaChanged() {
|
override fun onPlayingMetaChanged() {
|
||||||
|
@ -107,14 +98,13 @@ class LockScreenActivity : AbsMusicServiceActivity() {
|
||||||
|
|
||||||
private fun updateSongs() {
|
private fun updateSongs() {
|
||||||
val song = MusicPlayerRemote.currentSong
|
val song = MusicPlayerRemote.currentSong
|
||||||
Glide.with(this)
|
SongGlideRequest.Builder.from(Glide.with(this), song)
|
||||||
.asBitmapPalette()
|
.checkIgnoreMediaStore(this)
|
||||||
.songCoverOptions(song)
|
.generatePalette(this).build()
|
||||||
.load(RetroGlideExtension.getSongModel(song))
|
|
||||||
.dontAnimate()
|
.dontAnimate()
|
||||||
.into(object : RetroMusicColoredTarget(binding.image) {
|
.into(object : RetroMusicColoredTarget(image) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(color: Int) {
|
||||||
fragment?.setColor(colors)
|
fragment?.setDark(color)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,378 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.InputType
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.*
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.*
|
||||||
|
import code.name.monkey.retromusic.App
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
|
import code.name.monkey.retromusic.activities.tageditor.WriteTagsAsyncTask
|
||||||
|
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||||
|
import code.name.monkey.retromusic.lyrics.LrcHelper
|
||||||
|
import code.name.monkey.retromusic.lyrics.LrcView
|
||||||
|
import code.name.monkey.retromusic.model.Song
|
||||||
|
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||||
|
import code.name.monkey.retromusic.util.LyricUtil
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
|
import com.afollestad.materialdialogs.LayoutMode
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
|
||||||
|
import com.afollestad.materialdialogs.input.getInputLayout
|
||||||
|
import com.afollestad.materialdialogs.input.input
|
||||||
|
import kotlinx.android.synthetic.main.activity_lyrics.*
|
||||||
|
import kotlinx.android.synthetic.main.fragment_lyrics.*
|
||||||
|
import kotlinx.android.synthetic.main.fragment_synced.*
|
||||||
|
import org.jaudiotagger.tag.FieldKey
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPager.OnPageChangeListener {
|
||||||
|
override fun onPageScrollStateChanged(state: Int) {
|
||||||
|
when (state) {
|
||||||
|
ViewPager.SCROLL_STATE_IDLE ->
|
||||||
|
fab.show()
|
||||||
|
ViewPager.SCROLL_STATE_DRAGGING,
|
||||||
|
ViewPager.SCROLL_STATE_SETTLING ->
|
||||||
|
fab.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPageSelected(position: Int) {
|
||||||
|
PreferenceUtil.getInstance(this).lyricsOptions = position
|
||||||
|
if (position == 0) fab.text = getString(R.string.synced_lyrics)
|
||||||
|
else if (position == 1) fab.text = getString(R.string.lyrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
when (viewPager.currentItem) {
|
||||||
|
0 -> showSyncedLyrics()
|
||||||
|
1 -> showLyricsSaveDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var song: Song
|
||||||
|
private var lyricsString: String? = null
|
||||||
|
|
||||||
|
private val googleSearchLrcUrl: String
|
||||||
|
get() {
|
||||||
|
var baseUrl = "http://www.google.com/search?"
|
||||||
|
var query = song.title + "+" + song.artistName
|
||||||
|
query = "q=" + query.replace(" ", "+") + " .lrc"
|
||||||
|
baseUrl += query
|
||||||
|
return baseUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_lyrics)
|
||||||
|
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setNavigationBarColorPrimary()
|
||||||
|
|
||||||
|
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorPrimary)
|
||||||
|
appBarLayout.setBackgroundColor(primaryColor)
|
||||||
|
toolbar.apply {
|
||||||
|
setBackgroundColor(primaryColor)
|
||||||
|
navigationIcon = TintHelper.createTintedDrawable(ContextCompat.getDrawable(this@LyricsActivity, R.drawable.ic_keyboard_backspace_black_24dp), ThemeStore.textColorSecondary(this@LyricsActivity))
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||||
|
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||||
|
fab.setTextColor(this)
|
||||||
|
fab.iconTint = this
|
||||||
|
}
|
||||||
|
setupWakelock()
|
||||||
|
|
||||||
|
viewPager.apply {
|
||||||
|
adapter = PagerAdapter(supportFragmentManager)
|
||||||
|
currentItem = PreferenceUtil.getInstance(this@LyricsActivity).lyricsOptions
|
||||||
|
addOnPageChangeListener(this@LyricsActivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
tabs.apply {
|
||||||
|
setupWithViewPager(viewPager)
|
||||||
|
setSelectedTabIndicator(TintHelper.createTintedDrawable(ContextCompat.getDrawable(this@LyricsActivity, R.drawable.tab_indicator), ThemeStore.accentColor(this@LyricsActivity)))
|
||||||
|
setTabTextColors(ThemeStore.textColorSecondary(this@LyricsActivity), ThemeStore.accentColor(this@LyricsActivity))
|
||||||
|
setSelectedTabIndicatorColor(ThemeStore.accentColor(context))
|
||||||
|
}
|
||||||
|
fab.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayingMetaChanged() {
|
||||||
|
super.onPlayingMetaChanged()
|
||||||
|
song = MusicPlayerRemote.currentSong
|
||||||
|
toolbar.title = song.title
|
||||||
|
toolbar.subtitle = song.artistName
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceConnected() {
|
||||||
|
super.onServiceConnected()
|
||||||
|
song = MusicPlayerRemote.currentSong
|
||||||
|
toolbar.title = song.title
|
||||||
|
toolbar.subtitle = song.artistName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupWakelock() {
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == android.R.id.home) {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSyncedLyrics() {
|
||||||
|
var content = ""
|
||||||
|
try {
|
||||||
|
content = LyricUtil.getStringFromFile(song.title, song.artistName)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
val materialDialog = MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||||
|
title(R.string.add_time_framed_lryics)
|
||||||
|
negativeButton(R.string.action_search) { RetroUtil.openUrl(this@LyricsActivity, googleSearchLrcUrl) }
|
||||||
|
input(hint = getString(R.string.paste_lyrics_here),
|
||||||
|
prefill = content,
|
||||||
|
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE) { _, input ->
|
||||||
|
LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString())
|
||||||
|
}
|
||||||
|
positiveButton(android.R.string.ok) {
|
||||||
|
updateSong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MaterialUtil.setTint(materialDialog.getInputLayout(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateSong() {
|
||||||
|
val page = supportFragmentManager.findFragmentByTag("android:switcher:" + R.id.viewPager + ":" + viewPager.currentItem)
|
||||||
|
if (viewPager.currentItem == 0 && page != null) {
|
||||||
|
(page as BaseLyricsFragment).upDateSong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showLyricsSaveDialog() {
|
||||||
|
val content: String = if (lyricsString == null) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
lyricsString!!
|
||||||
|
}
|
||||||
|
|
||||||
|
val materialDialog = MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||||
|
title(R.string.add_lyrics)
|
||||||
|
negativeButton(R.string.action_search) { RetroUtil.openUrl(this@LyricsActivity, getGoogleSearchUrl()) }
|
||||||
|
input(hint = getString(R.string.paste_lyrics_here),
|
||||||
|
prefill = content,
|
||||||
|
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE) { _, input ->
|
||||||
|
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||||
|
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
|
||||||
|
WriteTagsAsyncTask(this@LyricsActivity).execute(WriteTagsAsyncTask.LoadingInfo(getSongPaths(song), fieldKeyValueMap, null))
|
||||||
|
}
|
||||||
|
positiveButton(android.R.string.ok) {
|
||||||
|
updateSong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MaterialUtil.setTint(materialDialog.getInputLayout(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSongPaths(song: Song): ArrayList<String> {
|
||||||
|
val paths = ArrayList<String>(1)
|
||||||
|
paths.add(song.data)
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getGoogleSearchUrl(): String {
|
||||||
|
var baseUrl = "http://www.google.com/search?"
|
||||||
|
var query = song.title + "+" + song.artistName
|
||||||
|
query = "q=" + query.replace(" ", "+") + " lyrics"
|
||||||
|
baseUrl += query
|
||||||
|
return baseUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
|
||||||
|
class Tabs(@StringRes val title: Int,
|
||||||
|
val fragment: Fragment)
|
||||||
|
|
||||||
|
private var tabs = ArrayList<Tabs>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||||
|
tabs.add(Tabs(R.string.synced_lyrics, SyncedLyricsFragment()))
|
||||||
|
}
|
||||||
|
tabs.add(Tabs(R.string.normal_lyrics, OfflineLyricsFragment()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(position: Int): Fragment {
|
||||||
|
return tabs[position].fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPageTitle(position: Int): CharSequence? {
|
||||||
|
return App.getContext().getString(tabs[position].title)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCount(): Int {
|
||||||
|
return tabs.size
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BaseLyricsFragment : AbsMusicServiceFragment() {
|
||||||
|
abstract fun upDateSong()
|
||||||
|
|
||||||
|
override fun onPlayingMetaChanged() {
|
||||||
|
super.onPlayingMetaChanged()
|
||||||
|
upDateSong()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceConnected() {
|
||||||
|
super.onServiceConnected()
|
||||||
|
upDateSong()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class OfflineLyricsFragment : BaseLyricsFragment() {
|
||||||
|
override fun upDateSong() {
|
||||||
|
loadSongLyrics()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null
|
||||||
|
private var lyrics: Lyrics? = null
|
||||||
|
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
private fun loadSongLyrics() {
|
||||||
|
if (updateLyricsAsyncTask != null) {
|
||||||
|
updateLyricsAsyncTask!!.cancel(false)
|
||||||
|
}
|
||||||
|
val song = MusicPlayerRemote.currentSong
|
||||||
|
updateLyricsAsyncTask = object : AsyncTask<Void?, Void?, Lyrics?>() {
|
||||||
|
override fun doInBackground(vararg params: Void?): Lyrics? {
|
||||||
|
val data = MusicUtil.getLyrics(song)
|
||||||
|
return if (TextUtils.isEmpty(data)) {
|
||||||
|
null
|
||||||
|
} else Lyrics.parse(song, data!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPreExecute() {
|
||||||
|
super.onPreExecute()
|
||||||
|
lyrics = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostExecute(l: Lyrics?) {
|
||||||
|
lyrics = l
|
||||||
|
offlineLyrics?.visibility = View.VISIBLE
|
||||||
|
if (l == null) {
|
||||||
|
offlineLyrics?.setText(R.string.no_lyrics_found)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
(activity as LyricsActivity).lyricsString = l.text
|
||||||
|
offlineLyrics?.text = l.text
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancelled(s: Lyrics?) {
|
||||||
|
onPostExecute(null)
|
||||||
|
}
|
||||||
|
}.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask!!.isCancelled) {
|
||||||
|
updateLyricsAsyncTask!!.cancel(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_lyrics, container, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SyncedLyricsFragment : BaseLyricsFragment(), MusicProgressViewUpdateHelper.Callback {
|
||||||
|
override fun upDateSong() {
|
||||||
|
loadLRCLyrics()
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var updateHelper: MusicProgressViewUpdateHelper
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_synced, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setupLyricsView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupLyricsView() {
|
||||||
|
lyricsView.apply {
|
||||||
|
val context = activity!!
|
||||||
|
setCurrentPlayLineColor(ThemeStore.accentColor(context))
|
||||||
|
setIndicatorTextColor(ThemeStore.accentColor(context))
|
||||||
|
setCurrentIndicateLineTextColor(ThemeStore.textColorPrimary(context))
|
||||||
|
setNoLrcTextColor(ThemeStore.textColorPrimary(context))
|
||||||
|
setOnPlayIndicatorLineListener(object : LrcView.OnPlayIndicatorLineListener {
|
||||||
|
override fun onPlay(time: Long, content: String) {
|
||||||
|
MusicPlayerRemote.seekTo(time.toInt())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
updateHelper.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
updateHelper.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||||
|
lyricsView.updateTime(progress.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadLRCLyrics() {
|
||||||
|
val song = MusicPlayerRemote.currentSong
|
||||||
|
if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||||
|
showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showLyricsLocal(file: File?) {
|
||||||
|
if (file != null) {
|
||||||
|
lyricsView.setLrcData(LrcHelper.parseLrcFromFile(file))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,149 +1,136 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.*
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import androidx.lifecycle.lifecycleScope
|
import android.util.Log
|
||||||
import androidx.navigation.contains
|
import android.view.View
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||||
import code.name.monkey.retromusic.extensions.*
|
import code.name.monkey.retromusic.fragments.mainactivity.LibraryFragment
|
||||||
|
import code.name.monkey.retromusic.fragments.mainactivity.folders.FoldersFragment
|
||||||
|
import code.name.monkey.retromusic.fragments.mainactivity.home.BannerHomeFragment
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs
|
import code.name.monkey.retromusic.helper.SearchQueryHelper
|
||||||
import code.name.monkey.retromusic.interfaces.IScrollHelper
|
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
|
||||||
import code.name.monkey.retromusic.model.CategoryInfo
|
import code.name.monkey.retromusic.loaders.AlbumLoader
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.loaders.ArtistLoader
|
||||||
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
|
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
|
||||||
import code.name.monkey.retromusic.service.MusicService
|
import code.name.monkey.retromusic.service.MusicService
|
||||||
|
import code.name.monkey.retromusic.util.AppRater
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.logE
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import java.util.*
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.android.ext.android.get
|
|
||||||
|
|
||||||
class MainActivity : AbsSlidingMusicPanelActivity() {
|
|
||||||
companion object {
|
|
||||||
const val TAG = "MainActivity"
|
|
||||||
const val EXPAND_PANEL = "expand_panel"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setTaskDescriptionColorAuto()
|
|
||||||
hideStatusBar()
|
|
||||||
updateTabs()
|
|
||||||
|
|
||||||
setupNavigationController()
|
private lateinit var currentFragment: MainActivityFragmentCallbacks
|
||||||
|
|
||||||
WhatsNewFragment.showChangeLog(this)
|
private var blockRequestPermissions: Boolean = false
|
||||||
|
private val disposable = CompositeDisposable()
|
||||||
|
private val broadcastReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val action = intent.action
|
||||||
|
if (action != null && action == Intent.ACTION_SCREEN_OFF) {
|
||||||
|
if (PreferenceUtil.getInstance(this@MainActivity).lockScreen && MusicPlayerRemote.isPlaying) {
|
||||||
|
val activity = Intent(context, LockScreenActivity::class.java)
|
||||||
|
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
|
||||||
|
ActivityCompat.startActivity(context, activity, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupNavigationController() {
|
|
||||||
val navController = findNavController(R.id.fragment_container)
|
|
||||||
val navInflater = navController.navInflater
|
|
||||||
val navGraph = navInflater.inflate(R.navigation.main_graph)
|
|
||||||
|
|
||||||
val categoryInfo: CategoryInfo = PreferenceUtil.libraryCategory.first { it.visible }
|
|
||||||
if (categoryInfo.visible) {
|
|
||||||
if (!navGraph.contains(PreferenceUtil.lastTab)) PreferenceUtil.lastTab =
|
|
||||||
categoryInfo.category.id
|
|
||||||
navGraph.setStartDestination(
|
|
||||||
if (PreferenceUtil.rememberLastTab) {
|
|
||||||
PreferenceUtil.lastTab.let {
|
|
||||||
if (it == 0) {
|
|
||||||
categoryInfo.category.id
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else categoryInfo.category.id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
navController.graph = navGraph
|
|
||||||
navigationView.setupWithNavController(navController)
|
|
||||||
// Scroll Fragment to top
|
|
||||||
navigationView.setOnItemReselectedListener {
|
|
||||||
currentFragment(R.id.fragment_container).apply {
|
|
||||||
if (this is IScrollHelper) {
|
|
||||||
scrollToTop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
|
||||||
if (destination.id == navGraph.startDestinationId) {
|
|
||||||
currentFragment(R.id.fragment_container)?.enterTransition = null
|
|
||||||
}
|
|
||||||
when (destination.id) {
|
|
||||||
R.id.action_home, R.id.action_song, R.id.action_album, R.id.action_artist, R.id.action_folder, R.id.action_playlist, R.id.action_genre, R.id.action_search -> {
|
|
||||||
// Save the last tab
|
|
||||||
if (PreferenceUtil.rememberLastTab) {
|
|
||||||
saveTab(destination.id)
|
|
||||||
}
|
|
||||||
// Show Bottom Navigation Bar
|
|
||||||
setBottomNavVisibility(visible = true, animate = true)
|
|
||||||
}
|
|
||||||
R.id.playing_queue_fragment -> {
|
|
||||||
setBottomNavVisibility(visible = false, hideBottomSheet = true)
|
|
||||||
}
|
|
||||||
else -> setBottomNavVisibility(
|
|
||||||
visible = false,
|
|
||||||
animate = true
|
|
||||||
) // Hide Bottom Navigation Bar
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveTab(id: Int) {
|
override fun createContentView(): View {
|
||||||
if (PreferenceUtil.libraryCategory.firstOrNull { it.category.id == id }?.visible == true) {
|
return wrapSlidingMusicPanel(R.layout.activity_main_content)
|
||||||
PreferenceUtil.lastTab = id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean =
|
override fun onCreate(
|
||||||
findNavController(R.id.fragment_container).navigateUp()
|
savedInstanceState: Bundle?
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
|
||||||
super.onNewIntent(intent)
|
|
||||||
val expand = intent?.extra<Boolean>(EXPAND_PANEL)?.value ?: false
|
|
||||||
if (expand && PreferenceUtil.isExpandPanel) {
|
|
||||||
fromNotification = true
|
|
||||||
slidingPanel.bringToFront()
|
|
||||||
expandPanel()
|
|
||||||
intent?.removeExtra(EXPAND_PANEL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServiceConnected() {
|
|
||||||
super.onServiceConnected()
|
|
||||||
intent ?: return
|
|
||||||
handlePlaybackIntent(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handlePlaybackIntent(intent: Intent) {
|
|
||||||
lifecycleScope.launch(IO) {
|
|
||||||
val uri: Uri? = intent.data
|
|
||||||
val mimeType: String? = intent.type
|
|
||||||
var handled = false
|
|
||||||
if (intent.action != null &&
|
|
||||||
intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
|
|
||||||
) {
|
) {
|
||||||
val songs: List<Song> = getSongs(intent.extras!!)
|
setDrawUnderStatusBar()
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
getBottomNavigationView().selectedItemId = PreferenceUtil.getInstance(this).lastPage
|
||||||
|
|
||||||
|
getBottomNavigationView().setOnNavigationItemSelectedListener {
|
||||||
|
PreferenceUtil.getInstance(this).lastPage = it.itemId
|
||||||
|
selectedFragment(it.itemId)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
setMusicChooser(PreferenceUtil.getInstance(this).lastMusicChooser)
|
||||||
|
} else {
|
||||||
|
restoreCurrentFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
checkShowChangelog()
|
||||||
|
AppRater.appLaunched(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkShowChangelog() {
|
||||||
|
try {
|
||||||
|
val pInfo = packageManager.getPackageInfo(packageName, 0)
|
||||||
|
val currentVersion = pInfo.versionCode
|
||||||
|
if (currentVersion != PreferenceUtil.getInstance(this).lastChangelogVersion) {
|
||||||
|
startActivityForResult(Intent(this, WhatsNewActivity::class.java), APP_INTRO_REQUEST)
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
val screenOnOff = IntentFilter()
|
||||||
|
screenOnOff.addAction(Intent.ACTION_SCREEN_OFF)
|
||||||
|
registerReceiver(broadcastReceiver, screenOnOff)
|
||||||
|
|
||||||
|
PreferenceUtil.getInstance(this).registerOnSharedPreferenceChangedListener(this)
|
||||||
|
|
||||||
|
if (intent.hasExtra("expand")) {
|
||||||
|
if (intent.getBooleanExtra("expand", false)) {
|
||||||
|
expandPanel()
|
||||||
|
intent.putExtra("expand", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
disposable.clear()
|
||||||
|
unregisterReceiver(broadcastReceiver)
|
||||||
|
PreferenceUtil.getInstance(this).unregisterOnSharedPreferenceChangedListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setCurrentFragment(fragment: Fragment, tag: String) {
|
||||||
|
println("setCurrentFragment -> $tag -> ${supportFragmentManager.findFragmentById(R.id.fragment_container)?.tag}")
|
||||||
|
if (tag != supportFragmentManager.findFragmentById(R.id.fragment_container)?.tag) {
|
||||||
|
supportFragmentManager.beginTransaction().replace(R.id.fragment_container, fragment, tag).commit()
|
||||||
|
currentFragment = fragment as MainActivityFragmentCallbacks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restoreCurrentFragment() {
|
||||||
|
currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as MainActivityFragmentCallbacks
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePlaybackIntent(intent: Intent?) {
|
||||||
|
if (intent == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val uri = intent.data
|
||||||
|
val mimeType = intent.type
|
||||||
|
var handled = false
|
||||||
|
if (intent.action != null && intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH) {
|
||||||
|
val songs = SearchQueryHelper.getSongs(this, intent.extras!!)
|
||||||
if (MusicPlayerRemote.shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) {
|
if (MusicPlayerRemote.shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) {
|
||||||
MusicPlayerRemote.openAndShuffleQueue(songs, true)
|
MusicPlayerRemote.openAndShuffleQueue(songs, true)
|
||||||
} else {
|
} else {
|
||||||
|
@ -151,39 +138,30 @@ class MainActivity : AbsSlidingMusicPanelActivity() {
|
||||||
}
|
}
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uri != null && uri.toString().isNotEmpty()) {
|
if (uri != null && uri.toString().isNotEmpty()) {
|
||||||
MusicPlayerRemote.playFromUri(this@MainActivity, uri)
|
MusicPlayerRemote.playFromUri(uri)
|
||||||
handled = true
|
handled = true
|
||||||
} else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
|
} else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
|
||||||
val id = parseLongFromIntent(intent, "playlistId", "playlist")
|
val id = parseIdFromIntent(intent, "playlistId", "playlist").toInt()
|
||||||
if (id >= 0L) {
|
if (id >= 0) {
|
||||||
val position: Int = intent.getIntExtra("position", 0)
|
val position = intent.getIntExtra("position", 0)
|
||||||
val songs: List<Song> = PlaylistSongsLoader.getPlaylistSongList(get(), id)
|
val songs = ArrayList(PlaylistSongsLoader.getPlaylistSongList(this, id))
|
||||||
MusicPlayerRemote.openQueue(songs, position, true)
|
MusicPlayerRemote.openQueue(songs, position, true)
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
|
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
|
||||||
val id = parseLongFromIntent(intent, "albumId", "album")
|
val id = parseIdFromIntent(intent, "albumId", "album").toInt()
|
||||||
if (id >= 0L) {
|
if (id >= 0) {
|
||||||
val position: Int = intent.getIntExtra("position", 0)
|
val position = intent.getIntExtra("position", 0)
|
||||||
val songs = libraryViewModel.albumById(id).songs
|
MusicPlayerRemote.openQueue(AlbumLoader.getAlbum(this, id).songs!!, position, true)
|
||||||
MusicPlayerRemote.openQueue(
|
|
||||||
songs,
|
|
||||||
position,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
|
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
|
||||||
val id = parseLongFromIntent(intent, "artistId", "artist")
|
val id = parseIdFromIntent(intent, "artistId", "artist").toInt()
|
||||||
if (id >= 0L) {
|
if (id >= 0) {
|
||||||
val position: Int = intent.getIntExtra("position", 0)
|
val position = intent.getIntExtra("position", 0)
|
||||||
val songs: List<Song> = libraryViewModel.artistById(id).songs
|
MusicPlayerRemote.openQueue(ArtistLoader.getArtist(this, id).songs, position, true)
|
||||||
MusicPlayerRemote.openQueue(
|
|
||||||
songs,
|
|
||||||
position,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,24 +169,131 @@ class MainActivity : AbsSlidingMusicPanelActivity() {
|
||||||
setIntent(Intent())
|
setIntent(Intent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseLongFromIntent(
|
private fun parseIdFromIntent(intent: Intent, longKey: String, stringKey: String): Long {
|
||||||
intent: Intent,
|
|
||||||
longKey: String,
|
|
||||||
stringKey: String,
|
|
||||||
): Long {
|
|
||||||
var id = intent.getLongExtra(longKey, -1)
|
var id = intent.getLongExtra(longKey, -1)
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
val idString = intent.getStringExtra(stringKey)
|
val idString = intent.getStringExtra(stringKey)
|
||||||
if (idString != null) {
|
if (idString != null) {
|
||||||
try {
|
try {
|
||||||
id = idString.toLong()
|
id = java.lang.Long.parseLong(idString)
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
logE(e)
|
Log.e(TAG, e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
when (requestCode) {
|
||||||
|
APP_INTRO_REQUEST -> {
|
||||||
|
blockRequestPermissions = false
|
||||||
|
if (!hasPermissions()) {
|
||||||
|
requestPermissions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
REQUEST_CODE_THEME, APP_USER_INFO_REQUEST -> postRecreate()
|
||||||
|
PURCHASE_REQUEST -> {
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
//checkSetUpPro();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleBackPress(): Boolean {
|
||||||
|
return super.handleBackPress() || currentFragment.handleBackPress()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceConnected() {
|
||||||
|
super.onServiceConnected()
|
||||||
|
handlePlaybackIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun requestPermissions() {
|
||||||
|
if (!blockRequestPermissions) {
|
||||||
|
super.requestPermissions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||||
|
if (key == PreferenceUtil.GENERAL_THEME ||
|
||||||
|
key == PreferenceUtil.BLACK_THEME ||
|
||||||
|
key == PreferenceUtil.ADAPTIVE_COLOR_APP ||
|
||||||
|
key == PreferenceUtil.DOMINANT_COLOR ||
|
||||||
|
key == PreferenceUtil.USER_NAME ||
|
||||||
|
key == PreferenceUtil.TOGGLE_FULL_SCREEN ||
|
||||||
|
key == PreferenceUtil.TOGGLE_VOLUME ||
|
||||||
|
key == PreferenceUtil.ROUND_CORNERS ||
|
||||||
|
key == PreferenceUtil.CAROUSEL_EFFECT ||
|
||||||
|
key == PreferenceUtil.NOW_PLAYING_SCREEN_ID ||
|
||||||
|
key == PreferenceUtil.TOGGLE_GENRE ||
|
||||||
|
key == PreferenceUtil.BANNER_IMAGE_PATH ||
|
||||||
|
key == PreferenceUtil.PROFILE_IMAGE_PATH ||
|
||||||
|
key == PreferenceUtil.CIRCULAR_ALBUM_ART ||
|
||||||
|
key == PreferenceUtil.KEEP_SCREEN_ON ||
|
||||||
|
key == PreferenceUtil.TOGGLE_SEPARATE_LINE ||
|
||||||
|
key == PreferenceUtil.ALBUM_GRID_STYLE ||
|
||||||
|
key == PreferenceUtil.ARTIST_GRID_STYLE ||
|
||||||
|
key == PreferenceUtil.TOGGLE_HOME_BANNER ||
|
||||||
|
key == PreferenceUtil.TOGGLE_ADD_CONTROLS ||
|
||||||
|
key == PreferenceUtil.ALBUM_COVER_STYLE ||
|
||||||
|
key == PreferenceUtil.HOME_ARTIST_GRID_STYLE ||
|
||||||
|
key == PreferenceUtil.ALBUM_COVER_TRANSFORM ||
|
||||||
|
key == PreferenceUtil.DESATURATED_COLOR ||
|
||||||
|
key == PreferenceUtil.TAB_TEXT_MODE ||
|
||||||
|
key == PreferenceUtil.LIBRARY_CATEGORIES)
|
||||||
|
postRecreate()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showPromotionalOffer() {
|
||||||
|
/*MaterialDialog(this).show {
|
||||||
|
positiveButton(text = "Buy") { startActivity(Intent(this@MainActivity, PurchaseActivity::class.java)) }
|
||||||
|
negativeButton(android.R.string.cancel)
|
||||||
|
customView(R.layout.dialog_promotional_offer)
|
||||||
|
onDismiss {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this@MainActivity)
|
||||||
|
.edit()
|
||||||
|
.putBoolean("shown", true)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectedFragment(itemId: Int) {
|
||||||
|
when (itemId) {
|
||||||
|
R.id.action_album,
|
||||||
|
R.id.action_artist,
|
||||||
|
R.id.action_playlist,
|
||||||
|
R.id.action_genre,
|
||||||
|
R.id.action_song -> setCurrentFragment(LibraryFragment.newInstance(itemId), itemId.toString())
|
||||||
|
R.id.action_home -> setCurrentFragment(BannerHomeFragment.newInstance(), BannerHomeFragment.TAG)
|
||||||
|
else -> {
|
||||||
|
setCurrentFragment(BannerHomeFragment.newInstance(), BannerHomeFragment.TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMusicChooser(key: Int) {
|
||||||
|
PreferenceUtil.getInstance(this).lastMusicChooser = key
|
||||||
|
when (key) {
|
||||||
|
FOLDER -> setCurrentFragment(FoldersFragment.newInstance(this), FoldersFragment.TAG)
|
||||||
|
else -> selectedFragment(PreferenceUtil.getInstance(this).lastPage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val APP_INTRO_REQUEST = 2323
|
||||||
|
const val HOME = 0
|
||||||
|
const val FOLDER = 1
|
||||||
|
const val LIBRARY = 2
|
||||||
|
private const val TAG = "MainActivity"
|
||||||
|
private const val APP_USER_INFO_REQUEST = 9003
|
||||||
|
private const val REQUEST_CODE_THEME = 9002
|
||||||
|
private const val PURCHASE_REQUEST = 101
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities
|
|
||||||
|
|
||||||
import android.Manifest.permission.BLUETOOTH_CONNECT
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.provider.Settings
|
|
||||||
import androidx.activity.OnBackPressedCallback
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.core.text.parseAsHtml
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
|
||||||
import code.name.monkey.retromusic.databinding.ActivityPermissionBinding
|
|
||||||
import code.name.monkey.retromusic.extensions.*
|
|
||||||
|
|
||||||
class PermissionActivity : AbsMusicServiceActivity() {
|
|
||||||
private lateinit var binding: ActivityPermissionBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
binding = ActivityPermissionBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
setStatusBarColorAuto()
|
|
||||||
setTaskDescriptionColorAuto()
|
|
||||||
setupTitle()
|
|
||||||
|
|
||||||
binding.storagePermission.setButtonClick {
|
|
||||||
requestPermissions()
|
|
||||||
}
|
|
||||||
if (VersionUtils.hasMarshmallow()) {
|
|
||||||
binding.audioPermission.show()
|
|
||||||
binding.audioPermission.setButtonClick {
|
|
||||||
if (!hasAudioPermission()) {
|
|
||||||
val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
|
|
||||||
intent.data = ("package:" + applicationContext.packageName).toUri()
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VersionUtils.hasS()) {
|
|
||||||
binding.bluetoothPermission.show()
|
|
||||||
binding.bluetoothPermission.setButtonClick {
|
|
||||||
ActivityCompat.requestPermissions(
|
|
||||||
this,
|
|
||||||
arrayOf(BLUETOOTH_CONNECT),
|
|
||||||
BLUETOOTH_PERMISSION_REQUEST
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
binding.audioPermission.setNumber("2")
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.finish.accentBackgroundColor()
|
|
||||||
binding.finish.setOnClickListener {
|
|
||||||
if (hasPermissions()) {
|
|
||||||
startActivity(
|
|
||||||
Intent(this, MainActivity::class.java).addFlags(
|
|
||||||
Intent.FLAG_ACTIVITY_NEW_TASK or
|
|
||||||
Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
||||||
)
|
|
||||||
)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
|
|
||||||
override fun handleOnBackPressed() {
|
|
||||||
finishAffinity()
|
|
||||||
remove()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupTitle() {
|
|
||||||
val appName =
|
|
||||||
getString(
|
|
||||||
R.string.message_welcome,
|
|
||||||
"<b>Metro</b>"
|
|
||||||
)
|
|
||||||
.parseAsHtml()
|
|
||||||
binding.appNameText.text = appName
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
binding.finish.isEnabled = hasStoragePermission()
|
|
||||||
if (hasStoragePermission()) {
|
|
||||||
binding.storagePermission.checkImage.isVisible = true
|
|
||||||
binding.storagePermission.checkImage.imageTintList =
|
|
||||||
ColorStateList.valueOf(accentColor())
|
|
||||||
}
|
|
||||||
if (VersionUtils.hasMarshmallow()) {
|
|
||||||
if (hasAudioPermission()) {
|
|
||||||
binding.audioPermission.checkImage.isVisible = true
|
|
||||||
binding.audioPermission.checkImage.imageTintList =
|
|
||||||
ColorStateList.valueOf(accentColor())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (VersionUtils.hasS()) {
|
|
||||||
if (hasBluetoothPermission()) {
|
|
||||||
binding.bluetoothPermission.checkImage.isVisible = true
|
|
||||||
binding.bluetoothPermission.checkImage.imageTintList =
|
|
||||||
ColorStateList.valueOf(accentColor())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hasStoragePermission(): Boolean {
|
|
||||||
return hasPermissions()
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.S)
|
|
||||||
private fun hasBluetoothPermission(): Boolean {
|
|
||||||
return ActivityCompat.checkSelfPermission(
|
|
||||||
this,
|
|
||||||
BLUETOOTH_CONNECT
|
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.M)
|
|
||||||
private fun hasAudioPermission(): Boolean {
|
|
||||||
return Settings.System.canWrite(this)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
|
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
|
||||||
|
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
import code.name.monkey.retromusic.util.DensityUtil
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator
|
||||||
|
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||||
|
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||||
|
import kotlinx.android.synthetic.main.activity_playing_queue.*
|
||||||
|
|
||||||
|
|
||||||
|
open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||||
|
|
||||||
|
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||||
|
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||||
|
private var playingQueueAdapter: PlayingQueueAdapter? = null
|
||||||
|
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||||
|
|
||||||
|
|
||||||
|
private fun getUpNextAndQueueTime(): String {
|
||||||
|
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
|
||||||
|
|
||||||
|
return MusicUtil.buildInfoString(
|
||||||
|
resources.getString(R.string.up_next),
|
||||||
|
MusicUtil.getReadableDurationString(duration)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_playing_queue)
|
||||||
|
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationBarColorPrimary()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
|
||||||
|
setupToolbar()
|
||||||
|
setUpRecyclerView()
|
||||||
|
|
||||||
|
clearQueue.setOnClickListener {
|
||||||
|
MusicPlayerRemote.clearQueue()
|
||||||
|
}
|
||||||
|
checkForPadding()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return when (item.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
onBackPressed()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpRecyclerView() {
|
||||||
|
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||||
|
val animator = RefactoredDefaultItemAnimator()
|
||||||
|
|
||||||
|
playingQueueAdapter = PlayingQueueAdapter(
|
||||||
|
this,
|
||||||
|
MusicPlayerRemote.playingQueue,
|
||||||
|
MusicPlayerRemote.position,
|
||||||
|
R.layout.item_queue)
|
||||||
|
wrappedAdapter = recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!)
|
||||||
|
|
||||||
|
linearLayoutManager = LinearLayoutManager(this)
|
||||||
|
|
||||||
|
recyclerView.apply {
|
||||||
|
layoutManager = linearLayoutManager
|
||||||
|
adapter = wrappedAdapter
|
||||||
|
itemAnimator = animator
|
||||||
|
recyclerViewDragDropManager?.attachRecyclerView(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||||
|
|
||||||
|
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
if (dy > 0) {
|
||||||
|
clearQueue.shrink()
|
||||||
|
} else if (dy < 0) {
|
||||||
|
clearQueue.extend()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkForPadding() {
|
||||||
|
val height = DensityUtil.dip2px(this, 102f)
|
||||||
|
recyclerView.setPadding(0, 0, 0, (height))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueueChanged() {
|
||||||
|
if (MusicPlayerRemote.playingQueue.isEmpty()) {
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
checkForPadding()
|
||||||
|
updateQueue()
|
||||||
|
updateCurrentSong()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMediaStoreChanged() {
|
||||||
|
updateQueue()
|
||||||
|
updateCurrentSong()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCurrentSong() {
|
||||||
|
playerQueueSubHeader.text = getUpNextAndQueueTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlayingMetaChanged() {
|
||||||
|
updateQueuePosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateQueuePosition() {
|
||||||
|
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
|
||||||
|
resetToCurrentPosition()
|
||||||
|
playerQueueSubHeader.text = getUpNextAndQueueTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateQueue() {
|
||||||
|
playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
|
||||||
|
resetToCurrentPosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetToCurrentPosition() {
|
||||||
|
recyclerView.stopScroll()
|
||||||
|
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
if (recyclerViewDragDropManager != null) {
|
||||||
|
recyclerViewDragDropManager!!.cancelDrag()
|
||||||
|
}
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
if (recyclerViewDragDropManager != null) {
|
||||||
|
recyclerViewDragDropManager!!.release()
|
||||||
|
recyclerViewDragDropManager = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrappedAdapter != null) {
|
||||||
|
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||||
|
wrappedAdapter = null
|
||||||
|
}
|
||||||
|
playingQueueAdapter = null
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupToolbar() {
|
||||||
|
playerQueueSubHeader.text = getUpNextAndQueueTime()
|
||||||
|
playerQueueSubHeader.setTextColor(ThemeStore.accentColor(this))
|
||||||
|
|
||||||
|
applyToolbar(toolbar)
|
||||||
|
appBarLayout.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
|
|
||||||
|
clearQueue.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||||
|
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||||
|
clearQueue.setTextColor(this)
|
||||||
|
clearQueue.iconTint = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.retromusic.App
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||||
|
import code.name.monkey.retromusic.adapter.song.OrderablePlaylistSongAdapter
|
||||||
|
import code.name.monkey.retromusic.adapter.song.PlaylistSongAdapter
|
||||||
|
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||||
|
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||||
|
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
|
||||||
|
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||||
|
import code.name.monkey.retromusic.loaders.PlaylistLoader
|
||||||
|
import code.name.monkey.retromusic.model.AbsCustomPlaylist
|
||||||
|
import code.name.monkey.retromusic.model.Playlist
|
||||||
|
import code.name.monkey.retromusic.model.Song
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsView
|
||||||
|
import code.name.monkey.retromusic.util.DensityUtil
|
||||||
|
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||||
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
|
import code.name.monkey.retromusic.util.ViewUtil
|
||||||
|
import com.afollestad.materialcab.MaterialCab
|
||||||
|
import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator
|
||||||
|
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||||
|
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||||
|
import kotlinx.android.synthetic.main.activity_playlist_detail.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, PlaylistSongsView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var playlistSongsPresenter: PlaylistSongsPresenter
|
||||||
|
|
||||||
|
private lateinit var playlist: Playlist
|
||||||
|
private var cab: MaterialCab? = null
|
||||||
|
private lateinit var adapter: SongAdapter
|
||||||
|
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||||
|
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setDrawUnderStatusBar()
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
App.musicComponent.inject(this)
|
||||||
|
|
||||||
|
playlistSongsPresenter.attachView(this)
|
||||||
|
|
||||||
|
setStatusbarColor(Color.TRANSPARENT)
|
||||||
|
setNavigationbarColorAuto()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
|
||||||
|
|
||||||
|
toggleBottomNavigationView(true)
|
||||||
|
|
||||||
|
if (intent.extras != null) {
|
||||||
|
playlist = intent.extras!!.getParcelable(EXTRA_PLAYLIST)!!
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpToolBar()
|
||||||
|
setUpRecyclerView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createContentView(): View {
|
||||||
|
return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpRecyclerView() {
|
||||||
|
ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView, ThemeStore.accentColor(this))
|
||||||
|
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
|
if (playlist is AbsCustomPlaylist) {
|
||||||
|
adapter = PlaylistSongAdapter(this, ArrayList(), R.layout.item_list, false, this)
|
||||||
|
recyclerView.adapter = adapter
|
||||||
|
} else {
|
||||||
|
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||||
|
val animator = RefactoredDefaultItemAnimator()
|
||||||
|
adapter = OrderablePlaylistSongAdapter(this, ArrayList(), R.layout.item_list, false, this,
|
||||||
|
object : OrderablePlaylistSongAdapter.OnMoveItemListener {
|
||||||
|
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
||||||
|
if (PlaylistsUtil.moveItem(this@PlaylistDetailActivity, playlist.id, fromPosition, toPosition)) {
|
||||||
|
val song = adapter.dataSet.removeAt(fromPosition)
|
||||||
|
adapter.dataSet.add(toPosition, song)
|
||||||
|
adapter.notifyItemMoved(fromPosition, toPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(adapter)
|
||||||
|
|
||||||
|
recyclerView.adapter = wrappedAdapter
|
||||||
|
recyclerView.itemAnimator = animator
|
||||||
|
|
||||||
|
recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
|
||||||
|
}
|
||||||
|
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
|
override fun onChanged() {
|
||||||
|
super.onChanged()
|
||||||
|
checkIsEmpty()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
playlistSongsPresenter.loadPlaylistSongs(playlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpToolBar() {
|
||||||
|
applyToolbar(toolbar)
|
||||||
|
title = playlist.name
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
menuInflater.inflate(if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail else R.menu.menu_playlist_detail, menu)
|
||||||
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PlaylistMenuHelper.handleMenuClick(this, playlist, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||||
|
if (cab != null && cab!!.isActive) {
|
||||||
|
cab!!.finish()
|
||||||
|
}
|
||||||
|
cab = MaterialCab(this, R.id.cab_stub)
|
||||||
|
.setMenu(menuRes)
|
||||||
|
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||||
|
.setBackgroundColor(
|
||||||
|
RetroColorUtil.shiftBackgroundColorForLightText(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
|
||||||
|
.start(callback)
|
||||||
|
return cab!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (cab != null && cab!!.isActive) {
|
||||||
|
cab!!.finish()
|
||||||
|
} else {
|
||||||
|
recyclerView!!.stopScroll()
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMediaStoreChanged() {
|
||||||
|
super.onMediaStoreChanged()
|
||||||
|
if (playlist !is AbsCustomPlaylist) {
|
||||||
|
// Playlist deleted
|
||||||
|
if (!PlaylistsUtil.doesPlaylistExist(this, playlist.id)) {
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Playlist renamed
|
||||||
|
val playlistName = PlaylistsUtil.getNameForPlaylist(this, playlist.id.toLong())
|
||||||
|
if (playlistName != playlist.name) {
|
||||||
|
playlist = PlaylistLoader.getPlaylist(this, playlist.id)
|
||||||
|
setToolbarTitle(playlist.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playlistSongsPresenter.loadPlaylistSongs(playlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setToolbarTitle(title: String) {
|
||||||
|
supportActionBar!!.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkForPadding() {
|
||||||
|
val height = DensityUtil.dip2px(this, 52f)
|
||||||
|
recyclerView.setPadding(0, 0, 0, (height ))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkIsEmpty() {
|
||||||
|
checkForPadding()
|
||||||
|
empty.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||||
|
emptyText.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onPause() {
|
||||||
|
if (recyclerViewDragDropManager != null) {
|
||||||
|
recyclerViewDragDropManager!!.cancelDrag()
|
||||||
|
}
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
if (recyclerViewDragDropManager != null) {
|
||||||
|
recyclerViewDragDropManager!!.release()
|
||||||
|
recyclerViewDragDropManager = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recyclerView != null) {
|
||||||
|
recyclerView!!.itemAnimator = null
|
||||||
|
recyclerView!!.adapter = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrappedAdapter != null) {
|
||||||
|
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||||
|
wrappedAdapter = null
|
||||||
|
}
|
||||||
|
super.onDestroy()
|
||||||
|
playlistSongsPresenter.detachView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showEmptyView() {
|
||||||
|
empty.visibility = View.VISIBLE
|
||||||
|
emptyText.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun songs(songs: ArrayList<Song>) {
|
||||||
|
adapter.swapDataSet(songs)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var EXTRA_PLAYLIST = "extra_playlist"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.widget.Toast
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
|
import code.name.monkey.retromusic.App
|
||||||
|
import code.name.monkey.retromusic.BuildConfig
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
|
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||||
|
import com.anjlab.android.iab.v3.BillingProcessor
|
||||||
|
import com.anjlab.android.iab.v3.TransactionDetails
|
||||||
|
import kotlinx.android.synthetic.main.activity_pro_version.*
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||||
|
|
||||||
|
|
||||||
|
private lateinit var billingProcessor: BillingProcessor
|
||||||
|
private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_pro_version)
|
||||||
|
setDrawUnderStatusBar()
|
||||||
|
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationBarColorPrimary()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
|
||||||
|
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorPrimary)
|
||||||
|
toolbar.setBackgroundColor(primaryColor)
|
||||||
|
appBarLayout.setBackgroundColor(primaryColor)
|
||||||
|
|
||||||
|
applyToolbar(toolbar)
|
||||||
|
|
||||||
|
restoreButton.isEnabled = false
|
||||||
|
purchaseButton.isEnabled = false
|
||||||
|
|
||||||
|
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
||||||
|
|
||||||
|
MaterialUtil.setTint(restoreButton, false)
|
||||||
|
MaterialUtil.setTint(purchaseButton, true)
|
||||||
|
|
||||||
|
restoreButton.setOnClickListener {
|
||||||
|
if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) {
|
||||||
|
restorePurchase()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
purchaseButton.setOnClickListener {
|
||||||
|
billingProcessor.purchase(this@PurchaseActivity, App.PRO_VERSION_PRODUCT_ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restorePurchase() {
|
||||||
|
if (restorePurchaseAsyncTask != null) {
|
||||||
|
restorePurchaseAsyncTask!!.cancel(false)
|
||||||
|
}
|
||||||
|
restorePurchaseAsyncTask = RestorePurchaseAsyncTask(this).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onProductPurchased(productId: String, details: TransactionDetails?) {
|
||||||
|
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
|
||||||
|
setResult(RESULT_OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPurchaseHistoryRestored() {
|
||||||
|
if (App.isProVersion()) {
|
||||||
|
Toast.makeText(this, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG).show()
|
||||||
|
setResult(RESULT_OK)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, R.string.no_purchase_found, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBillingError(errorCode: Int, error: Throwable?) {
|
||||||
|
Log.e(TAG, "Billing error: code = $errorCode", error)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onBillingInitialized() {
|
||||||
|
restoreButton.isEnabled = true
|
||||||
|
purchaseButton.isEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
if (!billingProcessor.handleActivityResult(requestCode, resultCode, data)) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
android.R.id.home -> finish()
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
billingProcessor.release()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RestorePurchaseAsyncTask internal constructor(purchaseActivity: PurchaseActivity) : AsyncTask<Void, Void, Boolean>() {
|
||||||
|
|
||||||
|
private val buyActivityWeakReference: WeakReference<PurchaseActivity> = WeakReference(purchaseActivity)
|
||||||
|
|
||||||
|
override fun onPreExecute() {
|
||||||
|
super.onPreExecute()
|
||||||
|
val purchaseActivity = buyActivityWeakReference.get()
|
||||||
|
if (purchaseActivity != null) {
|
||||||
|
Toast.makeText(purchaseActivity, R.string.restoring_purchase, Toast.LENGTH_SHORT).show()
|
||||||
|
} else {
|
||||||
|
cancel(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doInBackground(vararg params: Void): Boolean? {
|
||||||
|
val purchaseActivity = buyActivityWeakReference.get()
|
||||||
|
if (purchaseActivity != null) {
|
||||||
|
return purchaseActivity.billingProcessor.loadOwnedPurchasesFromGoogle()
|
||||||
|
}
|
||||||
|
cancel(false)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostExecute(b: Boolean?) {
|
||||||
|
super.onPostExecute(b)
|
||||||
|
val purchaseActivity = buyActivityWeakReference.get()
|
||||||
|
if (purchaseActivity == null || b == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b) {
|
||||||
|
purchaseActivity.onPurchaseHistoryRestored()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(purchaseActivity, R.string.could_not_restore_purchase, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG: String = "PurchaseActivity"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.speech.RecognizerIntent
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.TextView.BufferType
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
|
import code.name.monkey.retromusic.App
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||||
|
import code.name.monkey.retromusic.adapter.SearchAdapter
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.SearchPresenter
|
||||||
|
import code.name.monkey.retromusic.mvp.presenter.SearchView
|
||||||
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
|
import kotlinx.android.synthetic.main.activity_search.*
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatcher, SearchView {
|
||||||
|
@Inject
|
||||||
|
lateinit var searchPresenter: SearchPresenter
|
||||||
|
|
||||||
|
private var searchAdapter: SearchAdapter? = null
|
||||||
|
private var query: String? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setDrawUnderStatusBar()
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_search)
|
||||||
|
App.musicComponent.inject(this)
|
||||||
|
searchPresenter.attachView(this)
|
||||||
|
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationBarColorPrimary()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
|
||||||
|
setupRecyclerView()
|
||||||
|
setUpToolBar()
|
||||||
|
setupSearchView()
|
||||||
|
|
||||||
|
if (intent.getBooleanExtra(EXTRA_SHOW_MIC, false)) {
|
||||||
|
startMicSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
back.setOnClickListener { onBackPressed() }
|
||||||
|
voiceSearch.setOnClickListener { startMicSearch() }
|
||||||
|
|
||||||
|
searchContainer.setCardBackgroundColor(RetroColorUtil.toolbarColor(this))
|
||||||
|
|
||||||
|
keyboardPopup.setOnClickListener {
|
||||||
|
val inputManager = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
inputManager.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboardPopup.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||||
|
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||||
|
keyboardPopup.setTextColor(this)
|
||||||
|
keyboardPopup.iconTint = this
|
||||||
|
}
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
query = savedInstanceState.getString(QUERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecyclerView() {
|
||||||
|
searchAdapter = SearchAdapter(this, emptyList())
|
||||||
|
searchAdapter!!.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
|
override fun onChanged() {
|
||||||
|
super.onChanged()
|
||||||
|
empty.visibility = if (searchAdapter!!.itemCount < 1) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
recyclerView.apply {
|
||||||
|
layoutManager = LinearLayoutManager(this@SearchActivity)
|
||||||
|
adapter = searchAdapter
|
||||||
|
}
|
||||||
|
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
if (dy > 0) {
|
||||||
|
keyboardPopup.shrink()
|
||||||
|
} else if (dy < 0) {
|
||||||
|
keyboardPopup.extend()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupSearchView() {
|
||||||
|
searchView.addTextChangedListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
searchPresenter.detachView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putString(QUERY, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpToolBar() {
|
||||||
|
title = null
|
||||||
|
appBarLayout.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun search(query: String) {
|
||||||
|
this.query = query
|
||||||
|
voiceSearch.visibility = if (query.isNotEmpty()) View.GONE else View.VISIBLE
|
||||||
|
searchPresenter.search(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMediaStoreChanged() {
|
||||||
|
super.onMediaStoreChanged()
|
||||||
|
query?.let { search(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextSubmit(query: String): Boolean {
|
||||||
|
hideSoftKeyboard()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String): Boolean {
|
||||||
|
search(newText)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideSoftKeyboard() {
|
||||||
|
RetroUtil.hideSoftKeyboard(this@SearchActivity)
|
||||||
|
if (searchView != null) {
|
||||||
|
searchView.clearFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showEmptyView() {
|
||||||
|
searchAdapter?.swapDataSet(ArrayList())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showData(data: MutableList<Any>) {
|
||||||
|
searchAdapter?.swapDataSet(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
when (requestCode) {
|
||||||
|
REQ_CODE_SPEECH_INPUT -> {
|
||||||
|
if (resultCode == Activity.RESULT_OK && null != data) {
|
||||||
|
val result: ArrayList<String>? = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
|
||||||
|
query = result?.get(0)
|
||||||
|
searchView.setText(query, BufferType.EDITABLE)
|
||||||
|
searchPresenter.search(query!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startMicSearch() {
|
||||||
|
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
|
||||||
|
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
|
||||||
|
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
|
||||||
|
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.speech_prompt))
|
||||||
|
try {
|
||||||
|
startActivityForResult(intent, REQ_CODE_SPEECH_INPUT)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
Toast.makeText(this, getString(R.string.speech_not_supported), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTextChanged(newText: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
|
search(newText.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val TAG: String = SearchActivity::class.java.simpleName
|
||||||
|
|
||||||
|
const val EXTRA_SHOW_MIC = "extra_show_mic"
|
||||||
|
const val QUERY: String = "query"
|
||||||
|
|
||||||
|
private const val REQ_CODE_SPEECH_INPUT = 9002
|
||||||
|
}
|
||||||
|
}
|
86
app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt
Executable file
|
@ -0,0 +1,86 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.transition.TransitionManager
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
|
import code.name.monkey.retromusic.fragments.settings.MainSettingsFragment
|
||||||
|
import kotlinx.android.synthetic.main.activity_settings.*
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
|
private val fragmentManager = supportFragmentManager
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_settings)
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationBarColorPrimary()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
|
||||||
|
setupToolbar()
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
fragmentManager.beginTransaction().replace(R.id.contentFrame, MainSettingsFragment())
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupToolbar() {
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
setTitle(R.string.action_settings)
|
||||||
|
toolbar.apply {
|
||||||
|
setTitleTextColor(ATHUtil.resolveColor(this@SettingsActivity, R.attr.colorOnPrimary))
|
||||||
|
setBackgroundColor(ATHUtil.resolveColor(this@SettingsActivity, R.attr.colorPrimary))
|
||||||
|
setNavigationOnClickListener { onBackPressed() }
|
||||||
|
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||||
|
}
|
||||||
|
appBarLayout.setBackgroundColor(ATHUtil.resolveColor(this@SettingsActivity, R.attr.colorPrimary))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setupFragment(fragment: Fragment, @StringRes titleName: Int) {
|
||||||
|
val fragmentTransaction = fragmentManager
|
||||||
|
.beginTransaction()
|
||||||
|
.setCustomAnimations(R.anim.sliding_in_left, R.anim.sliding_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right)
|
||||||
|
|
||||||
|
if (detailContentFrame == null) {
|
||||||
|
fragmentTransaction.replace(R.id.contentFrame, fragment, fragment.tag)
|
||||||
|
fragmentTransaction.addToBackStack(null)
|
||||||
|
fragmentTransaction.commit()
|
||||||
|
} else {
|
||||||
|
fragmentTransaction.replace(R.id.detailContentFrame, fragment, fragment.tag)
|
||||||
|
fragmentTransaction.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
TransitionManager.beginDelayedTransition(appBarLayout)
|
||||||
|
setTitle(titleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (fragmentManager.backStackEntryCount == 0) {
|
||||||
|
super.onBackPressed()
|
||||||
|
} else {
|
||||||
|
setTitle(R.string.action_settings)
|
||||||
|
fragmentManager.popBackStack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == android.R.id.home) {
|
||||||
|
onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG: String = "SettingsActivity"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities
|
|
||||||
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.GradientDrawable
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.provider.MediaStore.Images.Media
|
|
||||||
import android.view.MenuItem
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.core.os.BundleCompat
|
|
||||||
import androidx.core.view.drawToBitmap
|
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
|
||||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
|
||||||
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
|
||||||
import code.name.monkey.retromusic.databinding.ActivityShareInstagramBinding
|
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
|
||||||
import code.name.monkey.retromusic.extensions.setStatusBarColor
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
|
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
|
||||||
import code.name.monkey.retromusic.model.Song
|
|
||||||
import code.name.monkey.retromusic.util.Share
|
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by hemanths on 2020-02-02.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class ShareInstagramStory : AbsThemeActivity() {
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityShareInstagramBinding
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val EXTRA_SONG = "extra_song"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
if (item.itemId == android.R.id.home) {
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
binding = ActivityShareInstagramBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
setStatusBarColor(Color.TRANSPARENT)
|
|
||||||
|
|
||||||
binding.toolbar.setBackgroundColor(Color.TRANSPARENT)
|
|
||||||
setSupportActionBar(binding.toolbar)
|
|
||||||
|
|
||||||
val song = intent.extras?.let { BundleCompat.getParcelable(it, EXTRA_SONG, Song::class.java) }
|
|
||||||
song?.let { songFinal ->
|
|
||||||
Glide.with(this)
|
|
||||||
.asBitmapPalette()
|
|
||||||
.songCoverOptions(songFinal)
|
|
||||||
.load(RetroGlideExtension.getSongModel(songFinal))
|
|
||||||
.into(object : RetroMusicColoredTarget(binding.image) {
|
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
|
||||||
setColors(colors.backgroundColor)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
binding.shareTitle.text = songFinal.title
|
|
||||||
binding.shareText.text = songFinal.artistName
|
|
||||||
binding.shareButton.setOnClickListener {
|
|
||||||
val path: String = Media.insertImage(
|
|
||||||
contentResolver,
|
|
||||||
binding.mainContent.drawToBitmap(Bitmap.Config.ARGB_8888),
|
|
||||||
"Design", null
|
|
||||||
)
|
|
||||||
Share.shareStoryToSocial(
|
|
||||||
this@ShareInstagramStory,
|
|
||||||
path.toUri()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.shareButton.setTextColor(
|
|
||||||
MaterialValueHelper.getPrimaryTextColor(
|
|
||||||
this,
|
|
||||||
ColorUtil.isColorLight(accentColor())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
binding.shareButton.backgroundTintList =
|
|
||||||
ColorStateList.valueOf(accentColor())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setColors(color: Int) {
|
|
||||||
binding.mainContent.background =
|
|
||||||
GradientDrawable(
|
|
||||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
|
||||||
intArrayOf(color, Color.BLACK)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.TintHelper
|
||||||
|
import code.name.monkey.retromusic.BuildConfig
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
|
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||||
|
import com.anjlab.android.iab.v3.BillingProcessor
|
||||||
|
import com.anjlab.android.iab.v3.SkuDetails
|
||||||
|
import com.anjlab.android.iab.v3.TransactionDetails
|
||||||
|
import kotlinx.android.synthetic.main.activity_donation.*
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val TAG: String = SupportDevelopmentActivity::class.java.simpleName
|
||||||
|
const val DONATION_PRODUCT_IDS = R.array.donation_ids
|
||||||
|
private const val TEZ_REQUEST_CODE = 123
|
||||||
|
}
|
||||||
|
|
||||||
|
var billingProcessor: BillingProcessor? = null
|
||||||
|
private var skuDetailsLoadAsyncTask: AsyncTask<*, *, *>? = null
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == android.R.id.home) {
|
||||||
|
onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun donate(i: Int) {
|
||||||
|
val ids = resources.getStringArray(DONATION_PRODUCT_IDS)
|
||||||
|
billingProcessor!!.purchase(this, ids[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_donation)
|
||||||
|
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationBarColorPrimary()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
|
||||||
|
setupToolbar()
|
||||||
|
|
||||||
|
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
||||||
|
TintHelper.setTint(progress, ThemeStore.accentColor(this))
|
||||||
|
donation.setTextColor(ThemeStore.accentColor(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupToolbar() {
|
||||||
|
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorPrimary)
|
||||||
|
appBarLayout.setBackgroundColor(primaryColor)
|
||||||
|
applyToolbar(toolbar)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBillingInitialized() {
|
||||||
|
loadSkuDetails()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadSkuDetails() {
|
||||||
|
if (skuDetailsLoadAsyncTask != null) {
|
||||||
|
skuDetailsLoadAsyncTask!!.cancel(false)
|
||||||
|
}
|
||||||
|
skuDetailsLoadAsyncTask = SkuDetailsLoadAsyncTask(this).execute()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onProductPurchased(productId: String, details: TransactionDetails?) {
|
||||||
|
//loadSkuDetails();
|
||||||
|
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBillingError(errorCode: Int, error: Throwable?) {
|
||||||
|
Log.e(TAG, "Billing error: code = $errorCode", error)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPurchaseHistoryRestored() {
|
||||||
|
//loadSkuDetails();
|
||||||
|
Toast.makeText(this, R.string.restored_previous_purchases, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
if (!billingProcessor!!.handleActivityResult(requestCode, resultCode, data)) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
}
|
||||||
|
if (requestCode == TEZ_REQUEST_CODE) {
|
||||||
|
// Process based on the data in response.
|
||||||
|
Log.d("result", data!!.getStringExtra("Status"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
if (billingProcessor != null) {
|
||||||
|
billingProcessor!!.release()
|
||||||
|
}
|
||||||
|
if (skuDetailsLoadAsyncTask != null) {
|
||||||
|
skuDetailsLoadAsyncTask!!.cancel(true)
|
||||||
|
}
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SkuDetailsLoadAsyncTask internal constructor(supportDevelopmentActivity: SupportDevelopmentActivity) : AsyncTask<Void, Void, List<SkuDetails>>() {
|
||||||
|
private val weakReference: WeakReference<SupportDevelopmentActivity> = WeakReference(supportDevelopmentActivity)
|
||||||
|
|
||||||
|
override fun onPreExecute() {
|
||||||
|
super.onPreExecute()
|
||||||
|
val supportDevelopmentActivity = weakReference.get() ?: return
|
||||||
|
|
||||||
|
supportDevelopmentActivity.progressContainer.visibility = View.VISIBLE
|
||||||
|
supportDevelopmentActivity.recyclerView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doInBackground(vararg params: Void): List<SkuDetails>? {
|
||||||
|
val dialog = weakReference.get()
|
||||||
|
if (dialog != null) {
|
||||||
|
val ids = dialog.resources.getStringArray(SupportDevelopmentActivity.DONATION_PRODUCT_IDS)
|
||||||
|
return dialog.billingProcessor!!.getPurchaseListingDetails(ArrayList(Arrays.asList(*ids)))
|
||||||
|
}
|
||||||
|
cancel(false)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostExecute(skuDetails: List<SkuDetails>?) {
|
||||||
|
super.onPostExecute(skuDetails)
|
||||||
|
val dialog = weakReference.get() ?: return
|
||||||
|
|
||||||
|
if (skuDetails == null || skuDetails.isEmpty()) {
|
||||||
|
dialog.progressContainer.visibility = View.GONE
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.progressContainer.visibility = View.GONE
|
||||||
|
dialog.recyclerView.itemAnimator = DefaultItemAnimator()
|
||||||
|
dialog.recyclerView.layoutManager = GridLayoutManager(dialog, 2)
|
||||||
|
dialog.recyclerView.adapter = SkuDetailsAdapter(dialog, skuDetails)
|
||||||
|
dialog.recyclerView.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SkuDetailsAdapter(
|
||||||
|
private var donationsDialog: SupportDevelopmentActivity,
|
||||||
|
objects: List<SkuDetails>
|
||||||
|
) : RecyclerView.Adapter<SkuDetailsAdapter.ViewHolder>() {
|
||||||
|
private var skuDetailsList: List<SkuDetails> = ArrayList()
|
||||||
|
|
||||||
|
init {
|
||||||
|
skuDetailsList = objects
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getIcon(position: Int): Int {
|
||||||
|
return when (position) {
|
||||||
|
0 -> R.drawable.ic_cookie_white_24dp
|
||||||
|
1 -> R.drawable.ic_take_away_white_24dp
|
||||||
|
2 -> R.drawable.ic_take_away_coffe_white_24dp
|
||||||
|
3 -> R.drawable.ic_beer_white_24dp
|
||||||
|
4 -> R.drawable.ic_fast_food_meal_white_24dp
|
||||||
|
5 -> R.drawable.ic_popcorn_white_24dp
|
||||||
|
6 -> R.drawable.ic_card_giftcard_white_24dp
|
||||||
|
else -> R.drawable.ic_card_giftcard_white_24dp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
|
||||||
|
return ViewHolder(LayoutInflater.from(donationsDialog).inflate(LAYOUT_RES_ID, viewGroup, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
|
||||||
|
val skuDetails = skuDetailsList[i]
|
||||||
|
viewHolder.title.text = skuDetails.title.replace("(Retro Music Player)", "").trim { it <= ' ' }
|
||||||
|
viewHolder.text.text = skuDetails.description
|
||||||
|
viewHolder.text.visibility = View.GONE
|
||||||
|
viewHolder.price.text = skuDetails.priceText
|
||||||
|
viewHolder.image.setImageResource(getIcon(i))
|
||||||
|
|
||||||
|
val purchased = donationsDialog.billingProcessor!!.isPurchased(skuDetails.productId)
|
||||||
|
val titleTextColor = if (purchased) ATHUtil.resolveColor(donationsDialog, android.R.attr.textColorHint) else ThemeStore.textColorPrimary(donationsDialog)
|
||||||
|
val contentTextColor = if (purchased) titleTextColor else ThemeStore.textColorSecondary(donationsDialog)
|
||||||
|
|
||||||
|
|
||||||
|
viewHolder.title.setTextColor(titleTextColor)
|
||||||
|
viewHolder.text.setTextColor(contentTextColor)
|
||||||
|
viewHolder.price.setTextColor(titleTextColor)
|
||||||
|
|
||||||
|
strikeThrough(viewHolder.title, purchased)
|
||||||
|
strikeThrough(viewHolder.text, purchased)
|
||||||
|
strikeThrough(viewHolder.price, purchased)
|
||||||
|
|
||||||
|
viewHolder.itemView.setOnTouchListener { _, _ -> purchased }
|
||||||
|
viewHolder.itemView.setOnClickListener { donationsDialog.donate(i) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return skuDetailsList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
var title: TextView = view.findViewById(R.id.itemTitle)
|
||||||
|
var text: TextView = view.findViewById(R.id.itemText)
|
||||||
|
var price: TextView = view.findViewById(R.id.itemPrice)
|
||||||
|
var image: AppCompatImageView = view.findViewById(R.id.itemImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@LayoutRes
|
||||||
|
private val LAYOUT_RES_ID = R.layout.item_donation_option
|
||||||
|
|
||||||
|
private fun strikeThrough(textView: TextView, strikeThrough: Boolean) {
|
||||||
|
textView.paintFlags = if (strikeThrough)
|
||||||
|
textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||||
|
else
|
||||||
|
textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
package code.name.monkey.retromusic.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ContentUris
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import android.provider.MediaStore.Images.Media
|
||||||
|
import android.provider.MediaStore.Images.Media.getBitmap
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.widget.Toast
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
|
import code.name.monkey.retromusic.App
|
||||||
|
import code.name.monkey.retromusic.Constants.USER_BANNER
|
||||||
|
import code.name.monkey.retromusic.Constants.USER_PROFILE
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
|
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||||
|
import code.name.monkey.retromusic.util.Compressor
|
||||||
|
import code.name.monkey.retromusic.util.ImageUtil.getResizedBitmap
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import com.afollestad.materialdialogs.LayoutMode
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
|
||||||
|
import com.afollestad.materialdialogs.list.listItems
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import kotlinx.android.synthetic.main.activity_user_info.*
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class UserInfoActivity : AbsBaseActivity() {
|
||||||
|
|
||||||
|
private var disposable = CompositeDisposable()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_user_info)
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationBarColorPrimary()
|
||||||
|
setTaskDescriptionColorAuto()
|
||||||
|
setLightNavigationBar(true)
|
||||||
|
|
||||||
|
setupToolbar()
|
||||||
|
|
||||||
|
MaterialUtil.setTint(nameContainer, false)
|
||||||
|
name.setText(PreferenceUtil.getInstance(this).userName)
|
||||||
|
|
||||||
|
if (PreferenceUtil.getInstance(this).profileImage.isNotEmpty()) {
|
||||||
|
loadImageFromStorage(PreferenceUtil.getInstance(this).profileImage)
|
||||||
|
}
|
||||||
|
if (PreferenceUtil.getInstance(this).bannerImage.isNotEmpty()) {
|
||||||
|
loadBannerFromStorage(PreferenceUtil.getInstance(this).bannerImage)
|
||||||
|
}
|
||||||
|
userImage.setOnClickListener {
|
||||||
|
MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||||
|
title(text = getString(R.string.set_photo))
|
||||||
|
listItems(items = listOf(getString(R.string.new_profile_photo), getString(R.string.remove_profile_photo))) { _, position, _ ->
|
||||||
|
when (position) {
|
||||||
|
0 -> pickNewPhoto()
|
||||||
|
1 -> PreferenceUtil.getInstance(this@UserInfoActivity).saveProfileImage("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bannerSelect.setOnClickListener {
|
||||||
|
showBannerOptions()
|
||||||
|
}
|
||||||
|
next.setOnClickListener {
|
||||||
|
val nameString = name.text.toString().trim { it <= ' ' }
|
||||||
|
if (TextUtils.isEmpty(nameString)) {
|
||||||
|
Toast.makeText(this, "Umm name is empty", Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
/*val bioString = bio.text.toString().trim() { it <= ' ' }
|
||||||
|
if (TextUtils.isEmpty(bioString)) {
|
||||||
|
Toast.makeText(this, "Umm bio is empty", Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
|
||||||
|
}*/
|
||||||
|
PreferenceUtil.getInstance(this).userName = nameString
|
||||||
|
//PreferenceUtil.getInstance().userBio = bioString
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
next.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||||
|
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||||
|
next.setTextColor(this)
|
||||||
|
next.iconTint = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == android.R.id.home) {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupToolbar() {
|
||||||
|
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorPrimary)
|
||||||
|
applyToolbar(toolbar)
|
||||||
|
appBarLayout.setBackgroundColor(primaryColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showBannerOptions() {
|
||||||
|
MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||||
|
title(R.string.select_banner_photo)
|
||||||
|
listItems(items = listOf(getString(R.string.new_banner_photo), getString(R.string.remove_banner_photo)))
|
||||||
|
{ _, position, _ ->
|
||||||
|
when (position) {
|
||||||
|
0 -> selectBannerImage()
|
||||||
|
1 -> PreferenceUtil.getInstance(this@UserInfoActivity).setBannerImagePath("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectBannerImage() {
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(PreferenceUtil.getInstance(this).bannerImage)) {
|
||||||
|
val pickImageIntent = Intent(Intent.ACTION_PICK, Media.EXTERNAL_CONTENT_URI)
|
||||||
|
pickImageIntent.type = "image/*"
|
||||||
|
//pickImageIntent.putExtra("crop", "true")
|
||||||
|
pickImageIntent.putExtra("outputX", 1290)
|
||||||
|
pickImageIntent.putExtra("outputY", 720)
|
||||||
|
pickImageIntent.putExtra("aspectX", 16)
|
||||||
|
pickImageIntent.putExtra("aspectY", 9)
|
||||||
|
pickImageIntent.putExtra("scale", true)
|
||||||
|
//intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||||
|
startActivityForResult(Intent.createChooser(pickImageIntent, "Select Picture"), PICK_BANNER_REQUEST)
|
||||||
|
} else {
|
||||||
|
PreferenceUtil.getInstance(this).setBannerImagePath("")
|
||||||
|
bannerImage.setImageResource(android.R.color.transparent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun pickNewPhoto() {
|
||||||
|
val pickImageIntent = Intent(Intent.ACTION_PICK, Media.EXTERNAL_CONTENT_URI)
|
||||||
|
pickImageIntent.type = "image/*"
|
||||||
|
pickImageIntent.putExtra("crop", "true")
|
||||||
|
pickImageIntent.putExtra("outputX", 512)
|
||||||
|
pickImageIntent.putExtra("outputY", 512)
|
||||||
|
pickImageIntent.putExtra("aspectX", 1)
|
||||||
|
pickImageIntent.putExtra("aspectY", 1)
|
||||||
|
pickImageIntent.putExtra("scale", true)
|
||||||
|
startActivityForResult(Intent.createChooser(pickImageIntent, "Select Picture"), PICK_IMAGE_REQUEST)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||||
|
when (requestCode) {
|
||||||
|
PICK_IMAGE_REQUEST -> {
|
||||||
|
try {
|
||||||
|
data.data?.let {
|
||||||
|
val bitmap = getResizedBitmap(getBitmap(contentResolver, it), PROFILE_ICON_SIZE)
|
||||||
|
val profileImagePath = saveToInternalStorage(bitmap, USER_PROFILE)
|
||||||
|
PreferenceUtil.getInstance(this).saveProfileImage(profileImagePath)
|
||||||
|
loadImageFromStorage(profileImagePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PICK_BANNER_REQUEST -> {
|
||||||
|
try {
|
||||||
|
data.data?.let {
|
||||||
|
val bitmap = getBitmap(contentResolver, it)
|
||||||
|
val profileImagePath = saveToInternalStorage(bitmap, USER_BANNER)
|
||||||
|
PreferenceUtil.getInstance(this).setBannerImagePath(profileImagePath)
|
||||||
|
loadBannerFromStorage(profileImagePath)
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getImagePathFromUri(aUri: Uri?): String? {
|
||||||
|
var imagePath: String? = null
|
||||||
|
if (aUri == null) {
|
||||||
|
return imagePath
|
||||||
|
}
|
||||||
|
if (DocumentsContract.isDocumentUri(App.getContext(), aUri)) {
|
||||||
|
val documentId = DocumentsContract.getDocumentId(aUri)
|
||||||
|
if ("com.android.providers.media.documents" == aUri.authority) {
|
||||||
|
val id = documentId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1]
|
||||||
|
val selection = Media._ID + "=" + id
|
||||||
|
imagePath = getImagePath(Media.EXTERNAL_CONTENT_URI, selection)
|
||||||
|
} else if ("com.android.providers.downloads.documents" == aUri.authority) {
|
||||||
|
val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),
|
||||||
|
java.lang.Long.valueOf(documentId))
|
||||||
|
imagePath = getImagePath(contentUri, null)
|
||||||
|
}
|
||||||
|
} else if ("content".equals(aUri.scheme!!, ignoreCase = true)) {
|
||||||
|
imagePath = getImagePath(aUri, null)
|
||||||
|
} else if ("file".equals(aUri.scheme!!, ignoreCase = true)) {
|
||||||
|
imagePath = aUri.path
|
||||||
|
}
|
||||||
|
return imagePath
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getImagePath(aUri: Uri, aSelection: String?): String? {
|
||||||
|
var path: String? = null
|
||||||
|
val cursor = App.getContext().contentResolver.query(aUri, null, aSelection, null, null)
|
||||||
|
if (cursor != null) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
path = cursor.getString(cursor.getColumnIndex(Media.DATA))
|
||||||
|
}
|
||||||
|
cursor.close()
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadBannerFromStorage(profileImagePath: String) {
|
||||||
|
disposable.add(Compressor(this)
|
||||||
|
.setQuality(100)
|
||||||
|
.setCompressFormat(Bitmap.CompressFormat.WEBP)
|
||||||
|
.compressToBitmapAsFlowable(File(profileImagePath, USER_BANNER))
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({ bitmap -> bannerImage.setImageBitmap(bitmap) }, { t -> println() }))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadImageFromStorage(path: String) {
|
||||||
|
disposable.add(Compressor(this)
|
||||||
|
.setMaxHeight(300)
|
||||||
|
.setMaxWidth(300)
|
||||||
|
.setQuality(75)
|
||||||
|
.setCompressFormat(Bitmap.CompressFormat.WEBP)
|
||||||
|
.compressToBitmapAsFlowable(File(path, USER_PROFILE))
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({ bitmap -> userImage!!.setImageBitmap(bitmap) }, { t -> println() }))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveToInternalStorage(bitmapImage: Bitmap, userBanner: String): String {
|
||||||
|
val cw = ContextWrapper(this)
|
||||||
|
val directory = cw.getDir("imageDir", Context.MODE_PRIVATE)
|
||||||
|
val myPath = File(directory, userBanner)
|
||||||
|
var fos: FileOutputStream? = null
|
||||||
|
try {
|
||||||
|
fos = FileOutputStream(myPath)
|
||||||
|
// Use the compress method on the BitMap object to write image to the OutputStream
|
||||||
|
bitmapImage.compress(Bitmap.CompressFormat.WEBP, 100, fos)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
fos?.close()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return directory.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val PICK_IMAGE_REQUEST = 9002
|
||||||
|
private const val PICK_BANNER_REQUEST = 9004
|
||||||
|
private const val PROFILE_ICON_SIZE = 400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Activity.pickImage(requestCode: Int) {
|
||||||
|
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
type = "image/*"
|
||||||
|
startActivityForResult(this, requestCode)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package code.name.monkey.retromusic.activities;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil;
|
||||||
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
|
||||||
|
import static code.name.monkey.appthemehelper.util.ATHUtil.INSTANCE;
|
||||||
|
|
||||||
|
public class WhatsNewActivity extends AbsBaseActivity {
|
||||||
|
WebView webView;
|
||||||
|
Toolbar toolbar;
|
||||||
|
AppBarLayout appBarLayout;
|
||||||
|
|
||||||
|
|
||||||
|
private static void setChangelogRead(@NonNull Context context) {
|
||||||
|
try {
|
||||||
|
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||||
|
int currentVersion = pInfo.versionCode;
|
||||||
|
PreferenceUtil.getInstance(context).setLastChangeLogVersion(currentVersion);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String colorToCSS(int color) {
|
||||||
|
return String.format("rgb(%d, %d, %d)", Color.red(color), Color.green(color), Color.blue(color)); // on API 29, WebView doesn't load with hex colors
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_whats_new);
|
||||||
|
|
||||||
|
setStatusbarColorAuto();
|
||||||
|
setNavigationBarColorPrimary();
|
||||||
|
setTaskDescriptionColorAuto();
|
||||||
|
|
||||||
|
webView = findViewById(R.id.webView);
|
||||||
|
toolbar = findViewById(R.id.toolbar);
|
||||||
|
appBarLayout = findViewById(R.id.appBarLayout);
|
||||||
|
|
||||||
|
int primaryColor = INSTANCE.resolveColor(this, R.attr.colorPrimary);
|
||||||
|
toolbar.setBackgroundColor(primaryColor);
|
||||||
|
appBarLayout.setBackgroundColor(primaryColor);
|
||||||
|
//setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||||
|
ToolbarContentTintHelper.colorBackButton(toolbar);
|
||||||
|
|
||||||
|
try {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
InputStream json = getAssets().open("retro-changelog.html");
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8));
|
||||||
|
String str;
|
||||||
|
while ((str = in.readLine()) != null)
|
||||||
|
buf.append(str);
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
// Inject color values for WebView body background and links
|
||||||
|
final boolean isDark = INSTANCE.isWindowBackgroundDark(this);
|
||||||
|
final String backgroundColor = colorToCSS(INSTANCE.resolveColor(this, R.attr.colorPrimary, Color.parseColor(isDark ? "#424242" : "#ffffff")));
|
||||||
|
final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000"));
|
||||||
|
final String changeLog = buf.toString()
|
||||||
|
.replace("{style-placeholder}",
|
||||||
|
String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor))
|
||||||
|
.replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this)))
|
||||||
|
.replace("{link-color-active}", colorToCSS(ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))));
|
||||||
|
|
||||||
|
webView.loadData(changeLog, "text/html", "UTF-8");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
webView.loadData("<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
|
||||||
|
}
|
||||||
|
setChangelogRead(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,153 +0,0 @@
|
||||||
package code.name.monkey.retromusic.activities
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.webkit.WebResourceRequest
|
|
||||||
import android.webkit.WebView
|
|
||||||
import android.webkit.WebViewClient
|
|
||||||
import androidx.core.content.pm.PackageInfoCompat
|
|
||||||
import androidx.core.widget.NestedScrollView
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark
|
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil.isColorLight
|
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil.lightenColor
|
|
||||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper.getPrimaryTextColor
|
|
||||||
import code.name.monkey.retromusic.BuildConfig
|
|
||||||
import code.name.monkey.retromusic.Constants
|
|
||||||
import code.name.monkey.retromusic.databinding.FragmentWhatsNewBinding
|
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
|
||||||
import code.name.monkey.retromusic.extensions.openUrl
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil.lastVersion
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class WhatsNewFragment : BottomSheetDialogFragment() {
|
|
||||||
private var _binding: FragmentWhatsNewBinding? = null
|
|
||||||
val binding get() = _binding!!
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
_binding = FragmentWhatsNewBinding.inflate(inflater, container, false)
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
try {
|
|
||||||
val buf = StringBuilder()
|
|
||||||
val stream = requireContext().assets.open("retro-changelog.html")
|
|
||||||
stream.reader(StandardCharsets.UTF_8).buffered().use { br ->
|
|
||||||
var str: String?
|
|
||||||
while (br.readLine().also { str = it } != null) {
|
|
||||||
buf.append(str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject color values for WebView body background and links
|
|
||||||
val isDark = isWindowBackgroundDark(requireContext())
|
|
||||||
val accentColor = accentColor()
|
|
||||||
binding.webView.setBackgroundColor(0)
|
|
||||||
val contentColor = colorToCSS(Color.parseColor(if (isDark) "#ffffff" else "#000000"))
|
|
||||||
val textColor = colorToCSS(Color.parseColor(if (isDark) "#60FFFFFF" else "#80000000"))
|
|
||||||
val accentColorString = colorToCSS(accentColor())
|
|
||||||
val cardBackgroundColor =
|
|
||||||
colorToCSS(Color.parseColor(if (isDark) "#353535" else "#ffffff"))
|
|
||||||
val accentTextColor = colorToCSS(
|
|
||||||
getPrimaryTextColor(
|
|
||||||
requireContext(), isColorLight(accentColor)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val changeLog = buf.toString()
|
|
||||||
.replace(
|
|
||||||
"{style-placeholder}",
|
|
||||||
"body { color: $contentColor; } li {color: $textColor;} h3 {color: $accentColorString;} .tag {background-color: $accentColorString; color: $accentTextColor; } div{background-color: $cardBackgroundColor;}"
|
|
||||||
)
|
|
||||||
.replace("{link-color}", colorToCSS(accentColor()))
|
|
||||||
.replace(
|
|
||||||
"{link-color-active}",
|
|
||||||
colorToCSS(
|
|
||||||
lightenColor(accentColor())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
binding.webView.loadData(changeLog, "text/html", "UTF-8")
|
|
||||||
binding.webView.webViewClient = object : WebViewClient() {
|
|
||||||
override fun shouldOverrideUrlLoading(
|
|
||||||
view: WebView?,
|
|
||||||
request: WebResourceRequest?
|
|
||||||
): Boolean {
|
|
||||||
val url = request?.url ?: return false
|
|
||||||
//you can do checks here e.g. url.host equals to target one
|
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, url))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
binding.webView.loadData(
|
|
||||||
"<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "text/html", "UTF-8"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
setChangelogRead(requireContext())
|
|
||||||
binding.tgFab.setOnClickListener {
|
|
||||||
openUrl(Constants.TELEGRAM_CHANGE_LOG)
|
|
||||||
}
|
|
||||||
binding.tgFab.accentColor()
|
|
||||||
binding.tgFab.shrink()
|
|
||||||
binding.container.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int ->
|
|
||||||
val dy = scrollY - oldScrollY
|
|
||||||
if (dy > 0) {
|
|
||||||
binding.tgFab.shrink()
|
|
||||||
} else if (dy < 0) {
|
|
||||||
binding.tgFab.extend()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
const val TAG = "WhatsNewFragment"
|
|
||||||
private fun colorToCSS(color: Int): String {
|
|
||||||
return String.format(
|
|
||||||
Locale.getDefault(),
|
|
||||||
"rgba(%d, %d, %d, %d)",
|
|
||||||
Color.red(color),
|
|
||||||
Color.green(color),
|
|
||||||
Color.blue(color),
|
|
||||||
Color.alpha(color)
|
|
||||||
) // on API 29, WebView doesn't load with hex colors
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setChangelogRead(context: Context) {
|
|
||||||
try {
|
|
||||||
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
|
||||||
val currentVersion = PackageInfoCompat.getLongVersionCode(pInfo)
|
|
||||||
lastVersion = currentVersion
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showChangeLog(activity: FragmentActivity) {
|
|
||||||
val pInfo = activity.packageManager.getPackageInfo(activity.packageName, 0)
|
|
||||||
val currentVersion = PackageInfoCompat.getLongVersionCode(pInfo)
|
|
||||||
if (currentVersion > lastVersion && !BuildConfig.DEBUG) {
|
|
||||||
val changelogBottomSheet = WhatsNewFragment()
|
|
||||||
changelogBottomSheet.show(activity.supportFragmentManager, TAG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +1,26 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities.base
|
package code.name.monkey.retromusic.activities.base
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Rect
|
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.InputMethodManager
|
|
||||||
import android.widget.EditText
|
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.getSystemService
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
|
||||||
import code.name.monkey.retromusic.extensions.rootView
|
|
||||||
import code.name.monkey.retromusic.util.logD
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
|
||||||
|
|
||||||
abstract class AbsBaseActivity : AbsThemeActivity() {
|
abstract class AbsBaseActivity : AbsThemeActivity() {
|
||||||
private var hadPermissions: Boolean = false
|
private var hadPermissions: Boolean = false
|
||||||
private lateinit var permissions: Array<String>
|
private lateinit var permissions: Array<String>
|
||||||
private var permissionDeniedMessage: String? = null
|
private var permissionDeniedMessage: String? = null
|
||||||
|
|
||||||
|
|
||||||
open fun getPermissionsToRequest(): Array<String> {
|
open fun getPermissionsToRequest(): Array<String> {
|
||||||
return arrayOf()
|
return arrayOf()
|
||||||
}
|
}
|
||||||
|
@ -50,11 +30,13 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPermissionDeniedMessage(): String {
|
fun getPermissionDeniedMessage(): String {
|
||||||
return if (permissionDeniedMessage == null) getString(R.string.permissions_denied) else permissionDeniedMessage!!
|
return if (permissionDeniedMessage == null) getString(code.name.monkey.retromusic.R.string.permissions_denied) else permissionDeniedMessage!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val snackBarContainer: View
|
private val snackBarContainer: View
|
||||||
get() = rootView
|
get() = window.decorView
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -64,12 +46,19 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
|
||||||
permissionDeniedMessage = null
|
permissionDeniedMessage = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onPostCreate(savedInstanceState)
|
||||||
|
if (!hasPermissions()) {
|
||||||
|
requestPermissions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
val hasPermissions = hasPermissions()
|
val hasPermissions = hasPermissions()
|
||||||
if (hasPermissions != hadPermissions) {
|
if (hasPermissions != hadPermissions) {
|
||||||
hadPermissions = hasPermissions
|
hadPermissions = hasPermissions
|
||||||
if (VersionUtils.hasMarshmallow()) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
onHasPermissionsChanged(hasPermissions)
|
onHasPermissionsChanged(hasPermissions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +66,6 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
|
||||||
|
|
||||||
protected open fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
protected open fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
||||||
// implemented by sub classes
|
// implemented by sub classes
|
||||||
logD(hasPermissions)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
|
@ -88,117 +76,63 @@ abstract class AbsBaseActivity : AbsThemeActivity() {
|
||||||
return super.dispatchKeyEvent(event)
|
return super.dispatchKeyEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showOverflowMenu() {
|
protected fun showOverflowMenu() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun requestPermissions() {
|
protected open fun requestPermissions() {
|
||||||
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
requestPermissions(permissions, PERMISSION_REQUEST)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun hasPermissions(): Boolean {
|
protected fun hasPermissions(): Boolean {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
for (permission in permissions) {
|
for (permission in permissions) {
|
||||||
if (ActivityCompat.checkSelfPermission(this,
|
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
|
||||||
permission) != PackageManager.PERMISSION_GRANTED
|
|
||||||
) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
requestCode: Int,
|
|
||||||
permissions: Array<String>,
|
|
||||||
grantResults: IntArray,
|
|
||||||
) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
if (requestCode == PERMISSION_REQUEST) {
|
if (requestCode == PERMISSION_REQUEST) {
|
||||||
for (grantResult in grantResults) {
|
for (grantResult in grantResults) {
|
||||||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||||
if (ActivityCompat.shouldShowRequestPermissionRationale(
|
if (ActivityCompat.shouldShowRequestPermissionRationale(this@AbsBaseActivity,
|
||||||
this@AbsBaseActivity, Manifest.permission.READ_EXTERNAL_STORAGE,
|
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
) || ActivityCompat.shouldShowRequestPermissionRationale(
|
|
||||||
this@AbsBaseActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
//User has deny from permission dialog
|
//User has deny from permission dialog
|
||||||
Snackbar.make(
|
Snackbar.make(snackBarContainer, permissionDeniedMessage!!,
|
||||||
snackBarContainer,
|
Snackbar.LENGTH_INDEFINITE)
|
||||||
permissionDeniedMessage!!,
|
.setAction(code.name.monkey.retromusic.R.string.action_grant) { requestPermissions() }
|
||||||
Snackbar.LENGTH_SHORT
|
.setActionTextColor(ThemeStore.accentColor(this))
|
||||||
)
|
.show()
|
||||||
.setAction(R.string.action_grant) { requestPermissions() }
|
|
||||||
.setActionTextColor(accentColor()).show()
|
|
||||||
} else {
|
} else {
|
||||||
// User has deny permission and checked never show permission dialog so you can redirect to Application settings page
|
// User has deny permission and checked never show permission dialog so you can redirect to Application settings page
|
||||||
Snackbar.make(
|
Snackbar.make(snackBarContainer, permissionDeniedMessage!!,
|
||||||
snackBarContainer,
|
Snackbar.LENGTH_INDEFINITE)
|
||||||
permissionDeniedMessage!!,
|
.setAction(code.name.monkey.retromusic.R.string.action_settings) {
|
||||||
Snackbar.LENGTH_INDEFINITE
|
|
||||||
)
|
|
||||||
.setAction(R.string.action_settings) {
|
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||||
val uri = Uri.fromParts(
|
val uri = Uri.fromParts("package", this@AbsBaseActivity.packageName, null)
|
||||||
"package",
|
|
||||||
this@AbsBaseActivity.packageName,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
intent.data = uri
|
intent.data = uri
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}.setActionTextColor(accentColor()).show()
|
}
|
||||||
|
.setActionTextColor(ThemeStore.accentColor(this))
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hadPermissions = true
|
hadPermissions = true
|
||||||
onHasPermissionsChanged(true)
|
onHasPermissionsChanged(true)
|
||||||
} else if (requestCode == BLUETOOTH_PERMISSION_REQUEST) {
|
|
||||||
for (grantResult in grantResults) {
|
|
||||||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
if (ActivityCompat.shouldShowRequestPermissionRationale(
|
|
||||||
this@AbsBaseActivity, Manifest.permission.BLUETOOTH_CONNECT
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// User has deny from permission dialog
|
|
||||||
Snackbar.make(
|
|
||||||
snackBarContainer,
|
|
||||||
R.string.permission_bluetooth_denied,
|
|
||||||
Snackbar.LENGTH_SHORT
|
|
||||||
)
|
|
||||||
.setAction(R.string.action_grant) {
|
|
||||||
ActivityCompat.requestPermissions(this,
|
|
||||||
arrayOf(Manifest.permission.BLUETOOTH_CONNECT),
|
|
||||||
BLUETOOTH_PERMISSION_REQUEST)
|
|
||||||
}
|
|
||||||
.setActionTextColor(accentColor()).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PERMISSION_REQUEST = 100
|
const val PERMISSION_REQUEST = 100
|
||||||
const val BLUETOOTH_PERMISSION_REQUEST = 101
|
|
||||||
}
|
|
||||||
|
|
||||||
// this lets keyboard close when clicked in background
|
|
||||||
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
|
||||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
|
||||||
val v = currentFocus
|
|
||||||
if (v is EditText) {
|
|
||||||
val outRect = Rect()
|
|
||||||
v.getGlobalVisibleRect(outRect)
|
|
||||||
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
|
|
||||||
v.clearFocus()
|
|
||||||
getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(
|
|
||||||
v.windowToken,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.dispatchTouchEvent(event)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package code.name.monkey.retromusic.activities.base
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import code.name.monkey.appthemehelper.ATHActivity
|
||||||
|
import code.name.monkey.retromusic.helper.TopExceptionHandler
|
||||||
|
|
||||||
|
abstract class AbsCrashCollector : ATHActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(TopExceptionHandler())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,49 +1,21 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities.base
|
package code.name.monkey.retromusic.activities.base
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.*
|
import android.content.*
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.db.toPlayCount
|
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.repository.RealRepository
|
import code.name.monkey.retromusic.service.MusicService.*
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.FAVORITE_STATE_CHANGED
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.MEDIA_STORE_CHANGED
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.META_CHANGED
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.PLAY_STATE_CHANGED
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.QUEUE_CHANGED
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.REPEAT_MODE_CHANGED
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.SHUFFLE_MODE_CHANGED
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
|
||||||
import code.name.monkey.retromusic.util.logD
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.android.ext.android.inject
|
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventListener {
|
|
||||||
|
|
||||||
private val mMusicServiceEventListeners = ArrayList<IMusicServiceEventListener>()
|
abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventListener {
|
||||||
private val repository: RealRepository by inject()
|
|
||||||
|
private val mMusicServiceEventListeners = ArrayList<MusicServiceEventListener>()
|
||||||
|
|
||||||
private var serviceToken: MusicPlayerRemote.ServiceToken? = null
|
private var serviceToken: MusicPlayerRemote.ServiceToken? = null
|
||||||
private var musicStateReceiver: MusicStateReceiver? = null
|
private var musicStateReceiver: MusicStateReceiver? = null
|
||||||
private var receiverRegistered: Boolean = false
|
private var receiverRegistered: Boolean = false
|
||||||
|
@ -60,7 +32,7 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setPermissionDeniedMessage(getString(R.string.permission_external_storage_denied))
|
setPermissionDeniedMessage(getString(R.string.permission_external_storage_denied));
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
@ -72,15 +44,15 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addMusicServiceEventListener(listenerI: IMusicServiceEventListener?) {
|
fun addMusicServiceEventListener(listener: MusicServiceEventListener?) {
|
||||||
if (listenerI != null) {
|
if (listener != null) {
|
||||||
mMusicServiceEventListeners.add(listenerI)
|
mMusicServiceEventListeners.add(listener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeMusicServiceEventListener(listenerI: IMusicServiceEventListener?) {
|
fun removeMusicServiceEventListener(listener: MusicServiceEventListener?) {
|
||||||
if (listenerI != null) {
|
if (listener != null) {
|
||||||
mMusicServiceEventListeners.remove(listenerI)
|
mMusicServiceEventListeners.remove(listener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +69,8 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
||||||
filter.addAction(MEDIA_STORE_CHANGED)
|
filter.addAction(MEDIA_STORE_CHANGED)
|
||||||
filter.addAction(FAVORITE_STATE_CHANGED)
|
filter.addAction(FAVORITE_STATE_CHANGED)
|
||||||
|
|
||||||
ContextCompat.registerReceiver(this, musicStateReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
registerReceiver(musicStateReceiver, filter)
|
||||||
|
|
||||||
receiverRegistered = true
|
receiverRegistered = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,16 +94,6 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
||||||
for (listener in mMusicServiceEventListeners) {
|
for (listener in mMusicServiceEventListeners) {
|
||||||
listener.onPlayingMetaChanged()
|
listener.onPlayingMetaChanged()
|
||||||
}
|
}
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
if (!PreferenceUtil.pauseHistory) {
|
|
||||||
repository.upsertSongInHistory(MusicPlayerRemote.currentSong)
|
|
||||||
}
|
|
||||||
val song = repository.findSongExistInPlayCount(MusicPlayerRemote.currentSong.id)
|
|
||||||
?.apply { playCount += 1 }
|
|
||||||
?: MusicPlayerRemote.currentSong.toPlayCount()
|
|
||||||
|
|
||||||
repository.upsertSongInPlayCount(song)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onQueueChanged() {
|
override fun onQueueChanged() {
|
||||||
|
@ -163,35 +126,16 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFavoriteStateChanged() {
|
|
||||||
for (listener in mMusicServiceEventListeners) {
|
|
||||||
listener.onFavoriteStateChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
override fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
||||||
super.onHasPermissionsChanged(hasPermissions)
|
super.onHasPermissionsChanged(hasPermissions)
|
||||||
val intent = Intent(MEDIA_STORE_CHANGED)
|
val intent = Intent(MEDIA_STORE_CHANGED)
|
||||||
intent.putExtra(
|
intent.putExtra("from_permissions_changed", true) // just in case we need to know this at some point
|
||||||
"from_permissions_changed",
|
|
||||||
true
|
|
||||||
) // just in case we need to know this at some point
|
|
||||||
sendBroadcast(intent)
|
sendBroadcast(intent)
|
||||||
logD("sendBroadcast $hasPermissions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun getPermissionsToRequest(): Array<String> {
|
override fun getPermissionsToRequest(): Array<String> {
|
||||||
return mutableListOf<String>().apply {
|
return arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
if (VersionUtils.hasT()) {
|
|
||||||
add(Manifest.permission.READ_MEDIA_AUDIO)
|
|
||||||
add(Manifest.permission.POST_NOTIFICATIONS)
|
|
||||||
} else {
|
|
||||||
add(Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
||||||
}
|
|
||||||
if (!VersionUtils.hasR()) {
|
|
||||||
add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
}
|
|
||||||
}.toTypedArray()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MusicStateReceiver(activity: AbsMusicServiceActivity) : BroadcastReceiver() {
|
private class MusicStateReceiver(activity: AbsMusicServiceActivity) : BroadcastReceiver() {
|
||||||
|
@ -203,7 +147,7 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventLi
|
||||||
val activity = reference.get()
|
val activity = reference.get()
|
||||||
if (activity != null && action != null) {
|
if (activity != null && action != null) {
|
||||||
when (action) {
|
when (action) {
|
||||||
FAVORITE_STATE_CHANGED -> activity.onFavoriteStateChanged()
|
FAVORITE_STATE_CHANGED,
|
||||||
META_CHANGED -> activity.onPlayingMetaChanged()
|
META_CHANGED -> activity.onPlayingMetaChanged()
|
||||||
QUEUE_CHANGED -> activity.onQueueChanged()
|
QUEUE_CHANGED -> activity.onQueueChanged()
|
||||||
PLAY_STATE_CHANGED -> activity.onPlayStateChanged()
|
PLAY_STATE_CHANGED -> activity.onPlayStateChanged()
|
||||||
|
|
|
@ -1,226 +1,114 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities.base
|
package code.name.monkey.retromusic.activities.base
|
||||||
|
|
||||||
import android.animation.ArgbEvaluator
|
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.content.Intent
|
import android.annotation.SuppressLint
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.graphics.Rect
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import android.view.animation.PathInterpolator
|
import androidx.annotation.LayoutRes
|
||||||
import android.widget.FrameLayout
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.activity.OnBackPressedCallback
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import androidx.core.animation.doOnEnd
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
import androidx.core.view.*
|
|
||||||
import androidx.fragment.app.commit
|
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
|
||||||
import code.name.monkey.retromusic.ADAPTIVE_COLOR_APP
|
|
||||||
import code.name.monkey.retromusic.ALBUM_COVER_STYLE
|
|
||||||
import code.name.monkey.retromusic.ALBUM_COVER_TRANSFORM
|
|
||||||
import code.name.monkey.retromusic.CAROUSEL_EFFECT
|
|
||||||
import code.name.monkey.retromusic.CIRCLE_PLAY_BUTTON
|
|
||||||
import code.name.monkey.retromusic.EXTRA_SONG_INFO
|
|
||||||
import code.name.monkey.retromusic.KEEP_SCREEN_ON
|
|
||||||
import code.name.monkey.retromusic.LIBRARY_CATEGORIES
|
|
||||||
import code.name.monkey.retromusic.NOW_PLAYING_SCREEN_ID
|
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.SCREEN_ON_LYRICS
|
import code.name.monkey.retromusic.extensions.hide
|
||||||
import code.name.monkey.retromusic.SWIPE_ANYWHERE_NOW_PLAYING
|
import code.name.monkey.retromusic.extensions.show
|
||||||
import code.name.monkey.retromusic.SWIPE_DOWN_DISMISS
|
import code.name.monkey.retromusic.fragments.MiniPlayerFragment
|
||||||
import code.name.monkey.retromusic.TAB_TEXT_MODE
|
|
||||||
import code.name.monkey.retromusic.TOGGLE_ADD_CONTROLS
|
|
||||||
import code.name.monkey.retromusic.TOGGLE_FULL_SCREEN
|
|
||||||
import code.name.monkey.retromusic.TOGGLE_VOLUME
|
|
||||||
import code.name.monkey.retromusic.activities.PermissionActivity
|
|
||||||
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
|
|
||||||
import code.name.monkey.retromusic.extensions.*
|
|
||||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
|
||||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen
|
import code.name.monkey.retromusic.fragments.NowPlayingScreen
|
||||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.other.MiniPlayerFragment
|
|
||||||
import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment
|
import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment
|
import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.card.CardFragment
|
import code.name.monkey.retromusic.fragments.player.card.CardFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.cardblur.CardBlurFragment
|
import code.name.monkey.retromusic.fragments.player.cardblur.CardBlurFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.circle.CirclePlayerFragment
|
|
||||||
import code.name.monkey.retromusic.fragments.player.classic.ClassicPlayerFragment
|
|
||||||
import code.name.monkey.retromusic.fragments.player.color.ColorFragment
|
import code.name.monkey.retromusic.fragments.player.color.ColorFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.fit.FitFragment
|
import code.name.monkey.retromusic.fragments.player.fit.FitFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
|
import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.full.FullPlayerFragment
|
import code.name.monkey.retromusic.fragments.player.full.FullPlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.gradient.GradientPlayerFragment
|
|
||||||
import code.name.monkey.retromusic.fragments.player.material.MaterialFragment
|
import code.name.monkey.retromusic.fragments.player.material.MaterialFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.md3.MD3PlayerFragment
|
|
||||||
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
|
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.peek.PeekPlayerFragment
|
import code.name.monkey.retromusic.fragments.player.peak.PeakPlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment
|
import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment
|
import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment
|
import code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment
|
||||||
import code.name.monkey.retromusic.fragments.queue.PlayingQueueFragment
|
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.model.CategoryInfo
|
import code.name.monkey.retromusic.util.DensityUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.ViewUtil
|
import code.name.monkey.retromusic.views.BottomNavigationBarTinted
|
||||||
import code.name.monkey.retromusic.util.logD
|
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
|
import kotlinx.android.synthetic.main.sliding_music_panel_layout.*
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_DRAGGING
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_SETTLING
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.from
|
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
|
||||||
|
|
||||||
|
|
||||||
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
|
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlayerFragment.Callbacks {
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
|
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
|
||||||
}
|
}
|
||||||
|
|
||||||
var fromNotification = false
|
private lateinit var bottomSheetBehavior: BottomSheetBehavior<MaterialCardView>
|
||||||
private var windowInsets: WindowInsetsCompat? = null
|
|
||||||
protected val libraryViewModel by viewModel<LibraryViewModel>()
|
|
||||||
private lateinit var bottomSheetBehavior: BottomSheetBehavior<FrameLayout>
|
|
||||||
private lateinit var playerFragment: AbsPlayerFragment
|
|
||||||
private var miniPlayerFragment: MiniPlayerFragment? = null
|
private var miniPlayerFragment: MiniPlayerFragment? = null
|
||||||
private var nowPlayingScreen: NowPlayingScreen? = null
|
private var playerFragment: AbsPlayerFragment? = null
|
||||||
|
private var currentNowPlayingScreen: NowPlayingScreen? = null
|
||||||
|
private var navigationBarColor: Int = 0
|
||||||
private var taskColor: Int = 0
|
private var taskColor: Int = 0
|
||||||
private var paletteColor: Int = Color.WHITE
|
private var lightStatusBar: Boolean = false
|
||||||
private var navigationBarColor = 0
|
private var lightNavigationBar: Boolean = false
|
||||||
|
private var navigationBarColorAnimator: ValueAnimator? = null
|
||||||
|
protected abstract fun createContentView(): View
|
||||||
private val panelState: Int
|
private val panelState: Int
|
||||||
get() = bottomSheetBehavior.state
|
get() = bottomSheetBehavior.state
|
||||||
private lateinit var binding: SlidingMusicPanelLayoutBinding
|
|
||||||
private var isInOneTabMode = false
|
|
||||||
|
|
||||||
private var navigationBarColorAnimator: ValueAnimator? = null
|
private val bottomSheetCallbackList = object : BottomSheetBehavior.BottomSheetCallback() {
|
||||||
private val argbEvaluator: ArgbEvaluator = ArgbEvaluator()
|
|
||||||
|
|
||||||
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
|
|
||||||
override fun handleOnBackPressed() {
|
|
||||||
println("Handle back press ${bottomSheetBehavior.state}")
|
|
||||||
if (!handleBackPress()) {
|
|
||||||
remove()
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val bottomSheetCallbackList by lazy {
|
|
||||||
object : BottomSheetCallback() {
|
|
||||||
|
|
||||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||||
setMiniPlayerAlphaProgress(slideOffset)
|
setMiniPlayerAlphaProgress(slideOffset)
|
||||||
navigationBarColorAnimator?.cancel()
|
dimBackground.show()
|
||||||
setNavigationBarColorPreOreo(
|
dimBackground.alpha = slideOffset
|
||||||
argbEvaluator.evaluate(
|
|
||||||
slideOffset,
|
|
||||||
surfaceColor(),
|
|
||||||
navigationBarColor
|
|
||||||
) as Int
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||||
onBackPressedCallback.isEnabled = newState == STATE_EXPANDED
|
|
||||||
when (newState) {
|
when (newState) {
|
||||||
STATE_EXPANDED -> {
|
BottomSheetBehavior.STATE_EXPANDED -> {
|
||||||
onPanelExpanded()
|
onPanelExpanded()
|
||||||
if (PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) {
|
|
||||||
keepScreenOn(true)
|
|
||||||
}
|
}
|
||||||
}
|
BottomSheetBehavior.STATE_COLLAPSED -> {
|
||||||
|
|
||||||
STATE_COLLAPSED -> {
|
|
||||||
onPanelCollapsed()
|
onPanelCollapsed()
|
||||||
if ((PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics) || !PreferenceUtil.isScreenOnEnabled) {
|
dimBackground.hide()
|
||||||
keepScreenOn(false)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
STATE_SETTLING, STATE_DRAGGING -> {
|
|
||||||
if (fromNotification) {
|
|
||||||
binding.navigationView.bringToFront()
|
|
||||||
fromNotification = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
STATE_HIDDEN -> {
|
|
||||||
MusicPlayerRemote.clearQueue()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
logD("Do a flip")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBottomSheetBehavior() = bottomSheetBehavior
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
if (!hasPermissions()) {
|
setContentView(createContentView())
|
||||||
startActivity(Intent(this, PermissionActivity::class.java))
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
binding = SlidingMusicPanelLayoutBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
binding.root.setOnApplyWindowInsetsListener { _, insets ->
|
|
||||||
windowInsets = WindowInsetsCompat.toWindowInsetsCompat(insets)
|
|
||||||
insets
|
|
||||||
}
|
|
||||||
chooseFragmentForTheme()
|
chooseFragmentForTheme()
|
||||||
setupSlidingUpPanel()
|
setupSlidingUpPanel()
|
||||||
setupBottomSheet()
|
|
||||||
updateColor()
|
|
||||||
if (!PreferenceUtil.materialYou) {
|
|
||||||
binding.slidingPanel.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
|
|
||||||
navigationView.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
|
|
||||||
}
|
|
||||||
|
|
||||||
navigationBarColor = surfaceColor()
|
updateTabs()
|
||||||
|
|
||||||
onBackPressedDispatcher.addCallback(onBackPressedCallback)
|
bottomSheetBehavior = BottomSheetBehavior.from(slidingPanel)
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupBottomSheet() {
|
val themeColor = ATHUtil.resolveColor(this, R.attr.colorPrimary, Color.GRAY)
|
||||||
bottomSheetBehavior = from(binding.slidingPanel)
|
dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f))
|
||||||
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
|
|
||||||
bottomSheetBehavior.isHideable = PreferenceUtil.swipeDownToDismiss
|
|
||||||
bottomSheetBehavior.significantVelocityThreshold = 300
|
|
||||||
setMiniPlayerAlphaProgress(0F)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
if (currentNowPlayingScreen != PreferenceUtil.getInstance(this).nowPlayingScreen) {
|
||||||
if (nowPlayingScreen != PreferenceUtil.nowPlayingScreen) {
|
|
||||||
postRecreate()
|
postRecreate()
|
||||||
}
|
}
|
||||||
if (bottomSheetBehavior.state == STATE_EXPANDED) {
|
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
|
||||||
|
|
||||||
|
if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
setMiniPlayerAlphaProgress(1f)
|
setMiniPlayerAlphaProgress(1f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,354 +116,252 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallbackList)
|
bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallbackList)
|
||||||
PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
|
if (navigationBarColorAnimator != null) navigationBarColorAnimator!!.cancel() // just in case
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
|
||||||
when (key) {
|
protected fun wrapSlidingMusicPanel(@LayoutRes resId: Int): View {
|
||||||
SWIPE_DOWN_DISMISS -> {
|
@SuppressLint("InflateParams")
|
||||||
bottomSheetBehavior.isHideable = PreferenceUtil.swipeDownToDismiss
|
val slidingMusicPanelLayout = layoutInflater.inflate(R.layout.sliding_music_panel_layout, null)
|
||||||
|
val contentContainer = slidingMusicPanelLayout.findViewById<ViewGroup>(R.id.mainContentFrame)
|
||||||
|
layoutInflater.inflate(resId, contentContainer)
|
||||||
|
return slidingMusicPanelLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
TOGGLE_ADD_CONTROLS -> {
|
private fun collapsePanel() {
|
||||||
miniPlayerFragment?.setUpButtons()
|
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
}
|
|
||||||
|
|
||||||
NOW_PLAYING_SCREEN_ID -> {
|
|
||||||
chooseFragmentForTheme()
|
|
||||||
binding.slidingPanel.updateLayoutParams<ViewGroup.LayoutParams> {
|
|
||||||
height = if (nowPlayingScreen != Peek) {
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT
|
|
||||||
} else {
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
||||||
}
|
|
||||||
onServiceConnected()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ALBUM_COVER_TRANSFORM, CAROUSEL_EFFECT,
|
|
||||||
ALBUM_COVER_STYLE, TOGGLE_VOLUME, EXTRA_SONG_INFO, CIRCLE_PLAY_BUTTON,
|
|
||||||
-> {
|
|
||||||
chooseFragmentForTheme()
|
|
||||||
onServiceConnected()
|
|
||||||
}
|
|
||||||
|
|
||||||
SWIPE_ANYWHERE_NOW_PLAYING -> {
|
|
||||||
playerFragment.addSwipeDetector()
|
|
||||||
}
|
|
||||||
|
|
||||||
ADAPTIVE_COLOR_APP -> {
|
|
||||||
if (PreferenceUtil.nowPlayingScreen in listOf(Normal, Material, Flat)) {
|
|
||||||
chooseFragmentForTheme()
|
|
||||||
onServiceConnected()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LIBRARY_CATEGORIES -> {
|
|
||||||
updateTabs()
|
|
||||||
}
|
|
||||||
|
|
||||||
TAB_TEXT_MODE -> {
|
|
||||||
navigationView.labelVisibilityMode = PreferenceUtil.tabTitleMode
|
|
||||||
}
|
|
||||||
|
|
||||||
TOGGLE_FULL_SCREEN -> {
|
|
||||||
recreate()
|
|
||||||
}
|
|
||||||
|
|
||||||
SCREEN_ON_LYRICS -> {
|
|
||||||
keepScreenOn(bottomSheetBehavior.state == STATE_EXPANDED && PreferenceUtil.lyricsScreenOn && PreferenceUtil.showLyrics || PreferenceUtil.isScreenOnEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
KEEP_SCREEN_ON -> {
|
|
||||||
maybeSetScreenOn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun collapsePanel() {
|
|
||||||
bottomSheetBehavior.state = STATE_COLLAPSED
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun expandPanel() {
|
fun expandPanel() {
|
||||||
bottomSheetBehavior.state = STATE_EXPANDED
|
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
setMiniPlayerAlphaProgress(1f)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setMiniPlayerAlphaProgress(progress: Float) {
|
private fun setMiniPlayerAlphaProgress(progress: Float) {
|
||||||
if (progress < 0) return
|
if (miniPlayerFragment?.view == null) return
|
||||||
val alpha = 1 - progress
|
val alpha = 1 - progress
|
||||||
miniPlayerFragment?.view?.alpha = 1 - (progress / 0.2F)
|
miniPlayerFragment?.view?.alpha = alpha
|
||||||
miniPlayerFragment?.view?.isGone = alpha == 0f
|
// necessary to make the views below clickable
|
||||||
if (!isLandscape) {
|
miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
|
||||||
binding.navigationView.translationY = progress * 500
|
|
||||||
binding.navigationView.alpha = alpha
|
|
||||||
}
|
|
||||||
binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun animateNavigationBarColor(color: Int) {
|
bottomNavigationView.translationY = progress * 500
|
||||||
if (VersionUtils.hasOreo()) return
|
bottomNavigationView.alpha = alpha
|
||||||
navigationBarColorAnimator?.cancel()
|
|
||||||
navigationBarColorAnimator = ValueAnimator
|
|
||||||
.ofArgb(window.navigationBarColor, color).apply {
|
|
||||||
duration = ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong()
|
|
||||||
interpolator = PathInterpolator(0.4f, 0f, 1f, 1f)
|
|
||||||
addUpdateListener { animation: ValueAnimator ->
|
|
||||||
setNavigationBarColorPreOreo(
|
|
||||||
animation.animatedValue as Int
|
|
||||||
)
|
|
||||||
}
|
|
||||||
start()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun onPanelCollapsed() {
|
open fun onPanelCollapsed() {
|
||||||
setMiniPlayerAlphaProgress(0F)
|
|
||||||
// restore values
|
// restore values
|
||||||
animateNavigationBarColor(surfaceColor())
|
super.setLightStatusbar(lightStatusBar)
|
||||||
setLightStatusBarAuto()
|
super.setTaskDescriptionColor(taskColor)
|
||||||
setLightNavigationBarAuto()
|
super.setNavigationbarColor(navigationBarColor)
|
||||||
setTaskDescriptionColor(taskColor)
|
super.setLightNavigationBar(lightNavigationBar)
|
||||||
//playerFragment?.onHide()
|
|
||||||
|
|
||||||
|
playerFragment?.setMenuVisibility(false)
|
||||||
|
playerFragment?.userVisibleHint = false
|
||||||
|
playerFragment?.onHide()
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun onPanelExpanded() {
|
open fun onPanelExpanded() {
|
||||||
setMiniPlayerAlphaProgress(1F)
|
val playerFragmentColor = playerFragment!!.paletteColor
|
||||||
|
super.setTaskDescriptionColor(playerFragmentColor)
|
||||||
|
|
||||||
|
playerFragment?.setMenuVisibility(true)
|
||||||
|
playerFragment?.userVisibleHint = true
|
||||||
|
playerFragment?.onShow()
|
||||||
onPaletteColorChanged()
|
onPaletteColorChanged()
|
||||||
//playerFragment?.onShow()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupSlidingUpPanel() {
|
private fun setupSlidingUpPanel() {
|
||||||
binding.slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
slidingPanel.viewTreeObserver
|
||||||
ViewTreeObserver.OnGlobalLayoutListener {
|
.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
override fun onGlobalLayout() {
|
override fun onGlobalLayout() {
|
||||||
binding.slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||||
if (nowPlayingScreen != Peek) {
|
if (currentNowPlayingScreen != PEAK) {
|
||||||
binding.slidingPanel.updateLayoutParams<ViewGroup.LayoutParams> {
|
val params = slidingPanel.layoutParams as ViewGroup.LayoutParams
|
||||||
height = ViewGroup.LayoutParams.MATCH_PARENT
|
params.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
}
|
slidingPanel.layoutParams = params
|
||||||
}
|
}
|
||||||
when (panelState) {
|
when (panelState) {
|
||||||
STATE_EXPANDED -> onPanelExpanded()
|
BottomSheetBehavior.STATE_EXPANDED -> {
|
||||||
STATE_COLLAPSED -> onPanelCollapsed()
|
onPanelExpanded()
|
||||||
else -> {
|
|
||||||
// playerFragment!!.onHide()
|
|
||||||
}
|
}
|
||||||
|
BottomSheetBehavior.STATE_COLLAPSED -> onPanelCollapsed()
|
||||||
|
else -> playerFragment!!.onHide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
val navigationView get() = binding.navigationView
|
fun toggleBottomNavigationView(toggle: Boolean) {
|
||||||
|
bottomNavigationView.visibility = if (toggle) View.GONE else View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
val slidingPanel get() = binding.slidingPanel
|
fun getBottomNavigationView(): BottomNavigationBarTinted {
|
||||||
|
return bottomNavigationView
|
||||||
|
}
|
||||||
|
|
||||||
val isBottomNavVisible get() = navigationView.isVisible && navigationView is BottomNavigationView
|
private fun hideBottomBar(hide: Boolean) {
|
||||||
|
val heightOfBar = resources.getDimensionPixelSize(R.dimen.mini_player_height)
|
||||||
|
val heightOfBarWithTabs = resources.getDimensionPixelSize(R.dimen.mini_player_height_expanded)
|
||||||
|
|
||||||
|
if (hide) {
|
||||||
|
bottomSheetBehavior.isHideable = true
|
||||||
|
bottomSheetBehavior.peekHeight = 0
|
||||||
|
collapsePanel()
|
||||||
|
bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat()
|
||||||
|
} else {
|
||||||
|
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||||
|
slidingPanel.cardElevation = DensityUtil.dip2px(this, 10f).toFloat()
|
||||||
|
bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat()
|
||||||
|
bottomSheetBehavior.isHideable = false
|
||||||
|
bottomSheetBehavior.peekHeight = if (bottomNavigationView.visibility == View.VISIBLE) heightOfBarWithTabs else heightOfBar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBottomBarVisibility(gone: Int) {
|
||||||
|
bottomNavigationView.visibility = gone
|
||||||
|
hideBottomBar(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun chooseFragmentForTheme() {
|
||||||
|
currentNowPlayingScreen = PreferenceUtil.getInstance(this).nowPlayingScreen
|
||||||
|
|
||||||
|
val fragment: Fragment = when (currentNowPlayingScreen) {
|
||||||
|
BLUR -> BlurPlayerFragment()
|
||||||
|
ADAPTIVE -> AdaptiveFragment()
|
||||||
|
NORMAL -> PlayerFragment()
|
||||||
|
CARD -> CardFragment()
|
||||||
|
BLUR_CARD -> CardBlurFragment()
|
||||||
|
FIT -> FitFragment()
|
||||||
|
FLAT -> FlatPlayerFragment()
|
||||||
|
FULL -> FullPlayerFragment()
|
||||||
|
PLAIN -> PlainPlayerFragment()
|
||||||
|
SIMPLE -> SimplePlayerFragment()
|
||||||
|
MATERIAL -> MaterialFragment()
|
||||||
|
COLOR -> ColorFragment()
|
||||||
|
TINY -> TinyPlayerFragment()
|
||||||
|
PEAK -> PeakPlayerFragment()
|
||||||
|
else -> PlayerFragment()
|
||||||
|
} // must implement AbsPlayerFragment
|
||||||
|
supportFragmentManager.beginTransaction().replace(R.id.playerFragmentContainer, fragment).commit()
|
||||||
|
supportFragmentManager.executePendingTransactions()
|
||||||
|
|
||||||
|
playerFragment = supportFragmentManager.findFragmentById(R.id.playerFragmentContainer) as AbsPlayerFragment
|
||||||
|
miniPlayerFragment = supportFragmentManager.findFragmentById(R.id.miniPlayerFragment) as MiniPlayerFragment
|
||||||
|
miniPlayerFragment!!.view!!.setOnClickListener { expandPanel() }
|
||||||
|
}
|
||||||
|
|
||||||
override fun onServiceConnected() {
|
override fun onServiceConnected() {
|
||||||
super.onServiceConnected()
|
super.onServiceConnected()
|
||||||
hideBottomSheet(false)
|
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||||
|
slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
|
override fun onGlobalLayout() {
|
||||||
|
slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||||
|
hideBottomBar(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onQueueChanged() {
|
override fun onQueueChanged() {
|
||||||
super.onQueueChanged()
|
super.onQueueChanged()
|
||||||
// Mini player should be hidden in Playing Queue
|
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||||
// it may pop up if hideBottomSheet is called
|
|
||||||
if (currentFragment(R.id.fragment_container) !is PlayingQueueFragment) {
|
|
||||||
hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleBackPress(): Boolean {
|
override fun onBackPressed() {
|
||||||
if (panelState == STATE_EXPANDED) {
|
if (!handleBackPress())
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun handleBackPress(): Boolean {
|
||||||
|
if (bottomSheetBehavior.peekHeight != 0 && playerFragment!!.onBackPressed())
|
||||||
|
return true
|
||||||
|
if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
collapsePanel()
|
collapsePanel()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onPaletteColorChanged() {
|
override fun onPaletteColorChanged() {
|
||||||
if (panelState == STATE_EXPANDED) {
|
if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
navigationBarColor = surfaceColor()
|
val paletteColor = playerFragment!!.paletteColor
|
||||||
setTaskDescColor(paletteColor)
|
super.setTaskDescriptionColor(paletteColor)
|
||||||
val isColorLight = paletteColor.isColorLight
|
|
||||||
if (PreferenceUtil.isAdaptiveColor && (nowPlayingScreen == Normal || nowPlayingScreen == Flat || nowPlayingScreen == Material)) {
|
val isColorLight = ColorUtil.isColorLight(paletteColor)
|
||||||
setLightNavigationBar(true)
|
|
||||||
setLightStatusBar(isColorLight)
|
if (PreferenceUtil.getInstance(this).adaptiveColor &&
|
||||||
} else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) {
|
(currentNowPlayingScreen == NORMAL || currentNowPlayingScreen == FLAT)) {
|
||||||
animateNavigationBarColor(Color.BLACK)
|
super.setLightNavigationBar(true)
|
||||||
navigationBarColor = Color.BLACK
|
super.setLightStatusbar(isColorLight)
|
||||||
setLightStatusBar(false)
|
} else if (currentNowPlayingScreen == FULL || currentNowPlayingScreen == CARD ||
|
||||||
setLightNavigationBar(true)
|
currentNowPlayingScreen == FIT || currentNowPlayingScreen == BLUR || currentNowPlayingScreen == BLUR_CARD) {
|
||||||
} else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) {
|
super.setLightStatusbar(false)
|
||||||
animateNavigationBarColor(paletteColor)
|
super.setLightNavigationBar(true)
|
||||||
navigationBarColor = paletteColor
|
} else if (currentNowPlayingScreen == COLOR || currentNowPlayingScreen == TINY) {
|
||||||
setLightNavigationBar(isColorLight)
|
super.setNavigationbarColor(paletteColor)
|
||||||
setLightStatusBar(isColorLight)
|
super.setLightNavigationBar(isColorLight)
|
||||||
} else if (nowPlayingScreen == Full) {
|
super.setLightStatusbar(isColorLight)
|
||||||
animateNavigationBarColor(paletteColor)
|
} else {
|
||||||
navigationBarColor = paletteColor
|
super.setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
|
||||||
setLightNavigationBar(isColorLight)
|
super.setLightNavigationBar(true)
|
||||||
setLightStatusBar(false)
|
|
||||||
} else if (nowPlayingScreen == Classic) {
|
|
||||||
setLightStatusBar(false)
|
|
||||||
} else if (nowPlayingScreen == Fit) {
|
|
||||||
setLightStatusBar(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setTaskDescColor(color: Int) {
|
override fun setLightStatusbar(enabled: Boolean) {
|
||||||
|
lightStatusBar = enabled
|
||||||
|
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||||
|
super.setLightStatusbar(enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setLightNavigationBar(enabled: Boolean) {
|
||||||
|
lightNavigationBar = enabled
|
||||||
|
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||||
|
super.setLightNavigationBar(enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setNavigationbarColor(color: Int) {
|
||||||
|
navigationBarColor = color
|
||||||
|
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||||
|
if (navigationBarColorAnimator != null) navigationBarColorAnimator!!.cancel()
|
||||||
|
super.setNavigationbarColor(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTaskDescriptionColor(color: Int) {
|
||||||
taskColor = color
|
taskColor = color
|
||||||
if (panelState == STATE_COLLAPSED) {
|
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||||
setTaskDescriptionColor(color)
|
super.setTaskDescriptionColor(color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateTabs() {
|
|
||||||
binding.navigationView.menu.clear()
|
private fun updateTabs() {
|
||||||
val currentTabs: List<CategoryInfo> = PreferenceUtil.libraryCategory
|
bottomNavigationView.menu.clear()
|
||||||
|
val currentTabs = PreferenceUtil.getInstance(this).libraryCategoryInfos
|
||||||
for (tab in currentTabs) {
|
for (tab in currentTabs) {
|
||||||
if (tab.visible) {
|
if (tab.visible) {
|
||||||
val menu = tab.category
|
val menu = tab.category
|
||||||
binding.navigationView.menu.add(0, menu.id, 0, menu.stringRes)
|
bottomNavigationView.menu.add(0, menu.id, 0, menu.stringRes)
|
||||||
.setIcon(menu.icon)
|
.setIcon(menu.icon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (binding.navigationView.menu.size() == 1) {
|
|
||||||
isInOneTabMode = true
|
|
||||||
binding.navigationView.isVisible = false
|
|
||||||
} else {
|
|
||||||
isInOneTabMode = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateColor() {
|
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
||||||
libraryViewModel.paletteColor.observe(this) { color ->
|
if (ev?.action == MotionEvent.ACTION_DOWN) {
|
||||||
this.paletteColor = color
|
if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
onPaletteColorChanged()
|
val outRect = Rect()
|
||||||
}
|
slidingPanel.getGlobalVisibleRect(outRect)
|
||||||
}
|
if (!outRect.contains(ev.rawX.toInt(), ev.rawY.toInt())) {
|
||||||
|
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
fun setBottomNavVisibility(
|
|
||||||
visible: Boolean,
|
|
||||||
animate: Boolean = false,
|
|
||||||
hideBottomSheet: Boolean = MusicPlayerRemote.playingQueue.isEmpty(),
|
|
||||||
) {
|
|
||||||
if (!ViewCompat.isLaidOut(navigationView)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (isInOneTabMode) {
|
|
||||||
hideBottomSheet(
|
|
||||||
hide = hideBottomSheet,
|
|
||||||
animate = animate,
|
|
||||||
isBottomNavVisible = false
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (visible xor navigationView.isVisible) {
|
|
||||||
val mAnimate = animate && bottomSheetBehavior.state == STATE_COLLAPSED
|
|
||||||
if (mAnimate) {
|
|
||||||
if (visible) {
|
|
||||||
binding.navigationView.bringToFront()
|
|
||||||
binding.navigationView.show()
|
|
||||||
} else {
|
|
||||||
binding.navigationView.hide()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
binding.navigationView.isVisible = visible
|
|
||||||
if (visible && bottomSheetBehavior.state != STATE_EXPANDED) {
|
|
||||||
binding.navigationView.bringToFront()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hideBottomSheet(
|
return super.dispatchTouchEvent(ev)
|
||||||
hide = hideBottomSheet,
|
|
||||||
animate = animate,
|
|
||||||
isBottomNavVisible = visible && navigationView is BottomNavigationView
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hideBottomSheet(
|
|
||||||
hide: Boolean,
|
|
||||||
animate: Boolean = false,
|
|
||||||
isBottomNavVisible: Boolean = navigationView.isVisible && navigationView is BottomNavigationView,
|
|
||||||
) {
|
|
||||||
val heightOfBar = windowInsets.getBottomInsets() + dip(R.dimen.mini_player_height)
|
|
||||||
val heightOfBarWithTabs = heightOfBar + dip(R.dimen.bottom_nav_height)
|
|
||||||
if (hide) {
|
|
||||||
bottomSheetBehavior.peekHeight = -windowInsets.getBottomInsets()
|
|
||||||
bottomSheetBehavior.state = STATE_COLLAPSED
|
|
||||||
libraryViewModel.setFabMargin(
|
|
||||||
this,
|
|
||||||
if (isBottomNavVisible) dip(R.dimen.bottom_nav_height) else 0
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
|
||||||
binding.slidingPanel.elevation = 0F
|
|
||||||
binding.navigationView.elevation = 5F
|
|
||||||
if (isBottomNavVisible) {
|
|
||||||
logD("List")
|
|
||||||
if (animate) {
|
|
||||||
bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs)
|
|
||||||
} else {
|
|
||||||
bottomSheetBehavior.peekHeight = heightOfBarWithTabs
|
|
||||||
}
|
|
||||||
libraryViewModel.setFabMargin(
|
|
||||||
this,
|
|
||||||
dip(R.dimen.bottom_nav_mini_player_height)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
logD("Details")
|
|
||||||
if (animate) {
|
|
||||||
bottomSheetBehavior.peekHeightAnimate(heightOfBar).doOnEnd {
|
|
||||||
binding.slidingPanel.bringToFront()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bottomSheetBehavior.peekHeight = heightOfBar
|
|
||||||
binding.slidingPanel.bringToFront()
|
|
||||||
}
|
|
||||||
libraryViewModel.setFabMargin(this, dip(R.dimen.mini_player_height))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setAllowDragging(allowDragging: Boolean) {
|
|
||||||
bottomSheetBehavior.isDraggable = allowDragging
|
|
||||||
hideBottomSheet(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun chooseFragmentForTheme() {
|
|
||||||
nowPlayingScreen = PreferenceUtil.nowPlayingScreen
|
|
||||||
|
|
||||||
val fragment: AbsPlayerFragment = when (nowPlayingScreen) {
|
|
||||||
Blur -> BlurPlayerFragment()
|
|
||||||
Adaptive -> AdaptiveFragment()
|
|
||||||
Normal -> PlayerFragment()
|
|
||||||
Card -> CardFragment()
|
|
||||||
BlurCard -> CardBlurFragment()
|
|
||||||
Fit -> FitFragment()
|
|
||||||
Flat -> FlatPlayerFragment()
|
|
||||||
Full -> FullPlayerFragment()
|
|
||||||
Plain -> PlainPlayerFragment()
|
|
||||||
Simple -> SimplePlayerFragment()
|
|
||||||
Material -> MaterialFragment()
|
|
||||||
Color -> ColorFragment()
|
|
||||||
Gradient -> GradientPlayerFragment()
|
|
||||||
Tiny -> TinyPlayerFragment()
|
|
||||||
Peek -> PeekPlayerFragment()
|
|
||||||
Circle -> CirclePlayerFragment()
|
|
||||||
Classic -> ClassicPlayerFragment()
|
|
||||||
MD3 -> MD3PlayerFragment()
|
|
||||||
else -> PlayerFragment()
|
|
||||||
} // must extend AbsPlayerFragment
|
|
||||||
supportFragmentManager.commit {
|
|
||||||
replace(R.id.playerFragmentContainer, fragment)
|
|
||||||
}
|
|
||||||
supportFragmentManager.executePendingTransactions()
|
|
||||||
playerFragment = whichFragment(R.id.playerFragmentContainer)
|
|
||||||
miniPlayerFragment = whichFragment<MiniPlayerFragment>(R.id.miniPlayerFragment)
|
|
||||||
miniPlayerFragment?.view?.setOnClickListener { expandPanel() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,69 +1,47 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities.base
|
package code.name.monkey.retromusic.activities.base
|
||||||
|
|
||||||
import android.content.Context
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import android.view.View
|
||||||
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
|
import android.view.WindowManager
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import code.name.monkey.appthemehelper.ATH
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.TintHelper
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.extensions.*
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.theme.getNightMode
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import code.name.monkey.retromusic.util.theme.getThemeResValue
|
import code.name.monkey.retromusic.util.ThemeManager
|
||||||
|
|
||||||
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
||||||
|
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
updateLocale()
|
setTheme(ThemeManager.getThemeResValue(this))
|
||||||
updateTheme()
|
|
||||||
hideStatusBar()
|
hideStatusBar()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setEdgeToEdgeOrImmersive()
|
//MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this)
|
||||||
maybeSetScreenOn()
|
|
||||||
setLightNavigationBarAuto()
|
changeBackgroundShape()
|
||||||
setLightStatusBarAuto(surfaceColor())
|
setImmersiveFullscreen()
|
||||||
if (VersionUtils.hasQ()) {
|
registerSystemUiVisibility()
|
||||||
window.decorView.isForceDarkAllowed = false
|
toggleScreenOn()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTheme() {
|
private fun toggleScreenOn() {
|
||||||
setTheme(getThemeResValue())
|
if (PreferenceUtil.getInstance(this).isScreenOnEnabled) {
|
||||||
if (PreferenceUtil.materialYou) {
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
setDefaultNightMode(getNightMode())
|
} else {
|
||||||
}
|
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
|
||||||
if (PreferenceUtil.isCustomFont) {
|
|
||||||
setTheme(R.style.FontThemeOverlay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateLocale() {
|
|
||||||
val localeCode = PreferenceUtil.languageCode
|
|
||||||
if (PreferenceUtil.isLocaleAutoStorageEnabled) {
|
|
||||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(localeCode))
|
|
||||||
PreferenceUtil.isLocaleAutoStorageEnabled = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +56,132 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hideStatusBar() {
|
||||||
|
hideStatusBar(PreferenceUtil.getInstance(this).fullScreenMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideStatusBar(fullscreen: Boolean) {
|
||||||
|
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||||
|
if (statusBar != null) {
|
||||||
|
statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun changeBackgroundShape() {
|
||||||
|
var background: Drawable? = if (PreferenceUtil.getInstance(this).isRoundCorners)
|
||||||
|
ContextCompat.getDrawable(this, R.drawable.round_window)
|
||||||
|
else
|
||||||
|
ContextCompat.getDrawable(this, R.drawable.square_window)
|
||||||
|
background = TintHelper.createTintedDrawable(background, ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
|
window.setBackgroundDrawable(background)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDrawUnderStatusBar() {
|
||||||
|
RetroUtil.setAllowDrawUnderStatusBar(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDrawUnderNavigationBar() {
|
||||||
|
RetroUtil.setAllowDrawUnderNavigationBar(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On
|
||||||
|
* Lollipop if no such view is found it will set the statusbar color using the native method.
|
||||||
|
*
|
||||||
|
* @param color the new statusbar color (will be shifted down on Lollipop and above)
|
||||||
|
*/
|
||||||
|
fun setStatusbarColor(color: Int) {
|
||||||
|
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||||
|
if (statusBar != null) {
|
||||||
|
when {
|
||||||
|
VersionUtils.hasMarshmallow() -> window.statusBarColor = color
|
||||||
|
VersionUtils.hasLollipop() -> statusBar.setBackgroundColor(ColorUtil.darkenColor(color))
|
||||||
|
else -> statusBar.setBackgroundColor(color)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when {
|
||||||
|
VersionUtils.hasMarshmallow() -> window.statusBarColor = color
|
||||||
|
else -> window.statusBarColor = ColorUtil.darkenColor(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLightStatusbarAuto(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setStatusbarColorAuto() {
|
||||||
|
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
|
||||||
|
setStatusbarColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun setTaskDescriptionColor(@ColorInt color: Int) {
|
||||||
|
ATH.setTaskDescriptionColor(this, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTaskDescriptionColorAuto() {
|
||||||
|
setTaskDescriptionColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun setNavigationbarColor(color: Int) {
|
||||||
|
if (ThemeStore.coloredNavigationBar(this)) {
|
||||||
|
ATH.setNavigationbarColor(this, color)
|
||||||
|
} else {
|
||||||
|
ATH.setNavigationbarColor(this, Color.BLACK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun setNavigationBarColorPrimary() {
|
||||||
|
ATH.setNavigationbarColor(this, ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNavigationbarColorAuto() {
|
||||||
|
setNavigationbarColor(ATHUtil.resolveColor(this, R.attr.colorSecondary))
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun setLightStatusbar(enabled: Boolean) {
|
||||||
|
ATH.setLightStatusbar(this, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLightStatusbarAuto(bgColor: Int) {
|
||||||
|
setLightStatusbar(ColorUtil.isColorLight(bgColor))
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun setLightNavigationBar(enabled: Boolean) {
|
||||||
|
if (!ATHUtil.isWindowBackgroundDark(this) and ThemeStore.coloredNavigationBar(this)) {
|
||||||
|
ATH.setLightNavigationbar(this, enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerSystemUiVisibility() {
|
||||||
|
val decorView = window.decorView
|
||||||
|
decorView.setOnSystemUiVisibilityChangeListener { visibility ->
|
||||||
|
if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
|
||||||
|
setImmersiveFullscreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unregisterSystemUiVisibility() {
|
||||||
|
val decorView = window.decorView
|
||||||
|
decorView.setOnSystemUiVisibilityChangeListener(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setImmersiveFullscreen() {
|
||||||
|
val flags = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
|
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
|
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
or View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
|
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
|
||||||
|
|
||||||
|
if (PreferenceUtil.getInstance(this).fullScreenMode) {
|
||||||
|
window.decorView.systemUiVisibility = flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exitFullscreen() {
|
||||||
|
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
setImmersiveFullscreen()
|
setImmersiveFullscreen()
|
||||||
}
|
}
|
||||||
|
@ -89,18 +193,17 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
||||||
|
|
||||||
public override fun onDestroy() {
|
public override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
unregisterSystemUiVisibility()
|
||||||
exitFullscreen()
|
exitFullscreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||||
handler.removeCallbacks(this)
|
handler.removeCallbacks(this)
|
||||||
handler.postDelayed(this, 500)
|
handler.postDelayed(this, 500)
|
||||||
}
|
}
|
||||||
return super.onKeyDown(keyCode, event)
|
return super.onKeyDown(keyCode, event)
|
||||||
}
|
|
||||||
|
|
||||||
override fun attachBaseContext(newBase: Context?) {
|
|
||||||
super.attachBaseContext(newBase)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,91 +1,314 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities.bugreport
|
package code.name.monkey.retromusic.activities.bugreport
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Dialog
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.TextUtils
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.core.content.getSystemService
|
import android.view.inputmethod.EditorInfo
|
||||||
import androidx.core.net.toUri
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.StringDef
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
import code.name.monkey.appthemehelper.util.TintHelper
|
import code.name.monkey.appthemehelper.util.TintHelper
|
||||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
||||||
import code.name.monkey.retromusic.activities.bugreport.model.DeviceInfo
|
import code.name.monkey.retromusic.activities.bugreport.model.DeviceInfo
|
||||||
import code.name.monkey.retromusic.databinding.ActivityBugReportBinding
|
import code.name.monkey.retromusic.activities.bugreport.model.Report
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo
|
||||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubLogin
|
||||||
import code.name.monkey.retromusic.extensions.showToast
|
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubTarget
|
||||||
|
import code.name.monkey.retromusic.misc.DialogAsyncTask
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.callbacks.onCancel
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import kotlinx.android.synthetic.main.activity_bug_report.*
|
||||||
|
import kotlinx.android.synthetic.main.bug_report_card_device_info.*
|
||||||
|
import kotlinx.android.synthetic.main.bug_report_card_report.*
|
||||||
|
import org.eclipse.egit.github.core.Issue
|
||||||
|
import org.eclipse.egit.github.core.client.GitHubClient
|
||||||
|
import org.eclipse.egit.github.core.client.RequestException
|
||||||
|
import org.eclipse.egit.github.core.service.IssueService
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
private const val RESULT_SUCCESS = "RESULT_OK"
|
||||||
|
private const val RESULT_BAD_CREDENTIALS = "RESULT_BAD_CREDENTIALS"
|
||||||
|
private const val RESULT_INVALID_TOKEN = "RESULT_INVALID_TOKEN"
|
||||||
|
private const val RESULT_ISSUES_NOT_ENABLED = "RESULT_ISSUES_NOT_ENABLED"
|
||||||
|
private const val RESULT_UNKNOWN = "RESULT_UNKNOWN"
|
||||||
|
|
||||||
|
@StringDef(RESULT_SUCCESS, RESULT_BAD_CREDENTIALS, RESULT_INVALID_TOKEN, RESULT_ISSUES_NOT_ENABLED, RESULT_UNKNOWN)
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
private annotation class Result
|
||||||
|
|
||||||
open class BugReportActivity : AbsThemeActivity() {
|
open class BugReportActivity : AbsThemeActivity() {
|
||||||
|
|
||||||
private lateinit var binding: ActivityBugReportBinding
|
|
||||||
private var deviceInfo: DeviceInfo? = null
|
private var deviceInfo: DeviceInfo? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityBugReportBinding.inflate(layoutInflater)
|
setContentView(R.layout.activity_bug_report)
|
||||||
setContentView(binding.root)
|
|
||||||
|
setStatusbarColorAuto()
|
||||||
|
setNavigationbarColorAuto()
|
||||||
setTaskDescriptionColorAuto()
|
setTaskDescriptionColorAuto()
|
||||||
|
|
||||||
initViews()
|
initViews()
|
||||||
|
|
||||||
if (title.isNullOrEmpty()) setTitle(R.string.report_an_issue)
|
if (TextUtils.isEmpty(title))
|
||||||
|
setTitle(R.string.report_an_issue)
|
||||||
|
|
||||||
deviceInfo = DeviceInfo(this)
|
deviceInfo = DeviceInfo(this)
|
||||||
binding.cardDeviceInfo.airTextDeviceInfo.text = deviceInfo.toString()
|
airTextDeviceInfo.text = deviceInfo.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initViews() {
|
private fun initViews() {
|
||||||
val accentColor = accentColor()
|
val accentColor = ThemeStore.accentColor(this)
|
||||||
setSupportActionBar(binding.toolbar)
|
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorPrimary)
|
||||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
toolbar.setBackgroundColor(primaryColor)
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
TintHelper.setTintAuto(optionUseAccount, accentColor, false)
|
||||||
|
optionUseAccount?.setOnClickListener {
|
||||||
|
inputTitle.isEnabled = true
|
||||||
|
inputDescription.isEnabled = true
|
||||||
|
inputUsername.isEnabled = true
|
||||||
|
inputPassword.isEnabled = true
|
||||||
|
|
||||||
binding.cardDeviceInfo.airTextDeviceInfo.setOnClickListener { copyDeviceInfoToClipBoard() }
|
optionAnonymous.isChecked = false
|
||||||
|
sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||||
|
override fun onHidden(fab: FloatingActionButton?) {
|
||||||
|
super.onHidden(fab)
|
||||||
|
sendFab.setImageResource(R.drawable.ic_send_white_24dp)
|
||||||
|
sendFab.show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TintHelper.setTintAuto(optionAnonymous, accentColor, false)
|
||||||
|
optionAnonymous.setOnClickListener {
|
||||||
|
inputTitle.isEnabled = false
|
||||||
|
inputDescription.isEnabled = false
|
||||||
|
inputUsername.isEnabled = false
|
||||||
|
inputPassword.isEnabled = false
|
||||||
|
|
||||||
TintHelper.setTintAuto(binding.sendFab, accentColor, true)
|
optionUseAccount.isChecked = false
|
||||||
binding.sendFab.setOnClickListener { reportIssue() }
|
sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||||
|
override fun onHidden(fab: FloatingActionButton?) {
|
||||||
|
super.onHidden(fab)
|
||||||
|
sendFab.setImageResource(R.drawable.ic_open_in_browser_white_24dp)
|
||||||
|
sendFab.show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
inputPassword.setOnEditorActionListener { _, actionId, _ ->
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
||||||
|
reportIssue()
|
||||||
|
return@setOnEditorActionListener true
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
airTextDeviceInfo.setOnClickListener { copyDeviceInfoToClipBoard() }
|
||||||
|
|
||||||
|
TintHelper.setTintAuto(sendFab, accentColor, true)
|
||||||
|
sendFab.setOnClickListener { reportIssue() }
|
||||||
|
|
||||||
|
MaterialUtil.setTint(inputLayoutTitle, false)
|
||||||
|
MaterialUtil.setTint(inputLayoutDescription, false)
|
||||||
|
MaterialUtil.setTint(inputLayoutUsername, false)
|
||||||
|
MaterialUtil.setTint(inputLayoutPassword, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportIssue() {
|
private fun reportIssue() {
|
||||||
|
if (optionUseAccount.isChecked) {
|
||||||
|
if (!validateInput()) return
|
||||||
|
val username = inputUsername.text.toString()
|
||||||
|
val password = inputPassword.text.toString()
|
||||||
|
sendBugReport(GithubLogin(username, password))
|
||||||
|
} else {
|
||||||
copyDeviceInfoToClipBoard()
|
copyDeviceInfoToClipBoard()
|
||||||
|
|
||||||
val i = Intent(Intent.ACTION_VIEW)
|
val i = Intent(Intent.ACTION_VIEW)
|
||||||
i.data = ISSUE_TRACKER_LINK.toUri()
|
i.data = Uri.parse(ISSUE_TRACKER_LINK)
|
||||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
startActivity(i)
|
startActivity(i)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun copyDeviceInfoToClipBoard() {
|
private fun copyDeviceInfoToClipBoard() {
|
||||||
val clipboard = getSystemService<ClipboardManager>()
|
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo?.toMarkdown())
|
val clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo?.toMarkdown())
|
||||||
clipboard?.setPrimaryClip(clip)
|
clipboard.primaryClip = clip
|
||||||
showToast(R.string.copied_device_info_to_clipboard)
|
Toast.makeText(this@BugReportActivity, R.string.copied_device_info_to_clipboard, Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun validateInput(): Boolean {
|
||||||
|
var hasErrors = false
|
||||||
|
|
||||||
|
if (optionUseAccount.isChecked) {
|
||||||
|
if (TextUtils.isEmpty(inputUsername.text)) {
|
||||||
|
setError(inputLayoutUsername, R.string.bug_report_no_username)
|
||||||
|
hasErrors = true
|
||||||
|
} else {
|
||||||
|
removeError(inputLayoutUsername)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(inputPassword.text)) {
|
||||||
|
setError(inputLayoutPassword, R.string.bug_report_no_password)
|
||||||
|
hasErrors = true
|
||||||
|
} else {
|
||||||
|
removeError(inputLayoutPassword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(inputTitle.text)) {
|
||||||
|
setError(inputLayoutTitle, R.string.bug_report_no_title)
|
||||||
|
hasErrors = true
|
||||||
|
} else {
|
||||||
|
removeError(inputLayoutTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(inputDescription.text)) {
|
||||||
|
setError(inputLayoutDescription, R.string.bug_report_no_description)
|
||||||
|
hasErrors = true
|
||||||
|
} else {
|
||||||
|
removeError(inputLayoutDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
return !hasErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setError(editTextLayout: TextInputLayout, @StringRes errorRes: Int) {
|
||||||
|
editTextLayout.error = getString(errorRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeError(editTextLayout: TextInputLayout) {
|
||||||
|
editTextLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendBugReport(login: GithubLogin) {
|
||||||
|
if (!validateInput()) return
|
||||||
|
|
||||||
|
val bugTitle = inputTitle.text.toString()
|
||||||
|
val bugDescription = inputDescription.text.toString()
|
||||||
|
|
||||||
|
val extraInfo = ExtraInfo()
|
||||||
|
onSaveExtraInfo()
|
||||||
|
|
||||||
|
val report = Report(bugTitle, bugDescription, deviceInfo, extraInfo)
|
||||||
|
val target = GithubTarget("h4h13", "RetroMusicPlayer")
|
||||||
|
|
||||||
|
ReportIssueAsyncTask.report(this, report, target, login)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onSaveExtraInfo() {}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
if (item.itemId == android.R.id.home) {
|
if (item.itemId == android.R.id.home) {
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressed()
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ReportIssueAsyncTask private constructor(activity: Activity, private val report: Report, private val target: GithubTarget,
|
||||||
|
private val login: GithubLogin) : DialogAsyncTask<Void, Void, String>(activity) {
|
||||||
|
override fun createDialog(context: Context): Dialog {
|
||||||
|
return AlertDialog.Builder(context)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Result
|
||||||
|
override fun doInBackground(vararg params: Void): String {
|
||||||
|
val client: GitHubClient = if (login.shouldUseApiToken()) {
|
||||||
|
GitHubClient().setOAuth2Token(login.apiToken)
|
||||||
|
} else {
|
||||||
|
GitHubClient().setCredentials(login.username, login.password)
|
||||||
|
}
|
||||||
|
|
||||||
|
val issue = Issue().setTitle(report.title).setBody(report.description)
|
||||||
|
try {
|
||||||
|
IssueService(client).createIssue(target.username, target.repository, issue)
|
||||||
|
return RESULT_SUCCESS
|
||||||
|
} catch (e: RequestException) {
|
||||||
|
return when (e.status) {
|
||||||
|
STATUS_BAD_CREDENTIALS -> {
|
||||||
|
if (login.shouldUseApiToken()) RESULT_INVALID_TOKEN else RESULT_BAD_CREDENTIALS
|
||||||
|
}
|
||||||
|
STATUS_ISSUES_NOT_ENABLED -> RESULT_ISSUES_NOT_ENABLED
|
||||||
|
else -> {
|
||||||
|
e.printStackTrace()
|
||||||
|
RESULT_UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
return RESULT_UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostExecute(@Result result: String) {
|
||||||
|
super.onPostExecute(result)
|
||||||
|
|
||||||
|
val context = context ?: return
|
||||||
|
|
||||||
|
when (result) {
|
||||||
|
RESULT_SUCCESS -> tryToFinishActivity()
|
||||||
|
RESULT_BAD_CREDENTIALS -> MaterialDialog(context).show {
|
||||||
|
title(R.string.bug_report_failed)
|
||||||
|
message(R.string.bug_report_failed_wrong_credentials)
|
||||||
|
positiveButton(android.R.string.ok)
|
||||||
|
}
|
||||||
|
RESULT_INVALID_TOKEN -> MaterialDialog(context).show {
|
||||||
|
title(R.string.bug_report_failed)
|
||||||
|
message(R.string.bug_report_failed_invalid_token)
|
||||||
|
positiveButton(android.R.string.ok)
|
||||||
|
}
|
||||||
|
RESULT_ISSUES_NOT_ENABLED -> MaterialDialog(context).show {
|
||||||
|
title(R.string.bug_report_failed)
|
||||||
|
message(R.string.bug_report_failed_issues_not_available)
|
||||||
|
positiveButton(android.R.string.ok)
|
||||||
|
}
|
||||||
|
else -> MaterialDialog(context).show {
|
||||||
|
title(R.string.bug_report_failed)
|
||||||
|
message(R.string.bug_report_failed_unknown)
|
||||||
|
positiveButton(android.R.string.ok) { tryToFinishActivity() }
|
||||||
|
onCancel { tryToFinishActivity() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryToFinishActivity() {
|
||||||
|
val context = context
|
||||||
|
if (context is Activity && !context.isFinishing) {
|
||||||
|
context.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ISSUE_TRACKER_LINK =
|
|
||||||
"https://github.com/MuntashirAkon/Metro/issues/new"
|
fun report(activity: Activity, report: Report, target: GithubTarget, login: GithubLogin) {
|
||||||
|
ReportIssueAsyncTask(activity, report, target, login).execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val STATUS_BAD_CREDENTIALS = 401
|
||||||
|
private const val STATUS_ISSUES_NOT_ENABLED = 410
|
||||||
|
private const val ISSUE_TRACKER_LINK = "https://github.com/h4h13/RetroMusicPlayer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.bugreport.model.DeviceInfo
import kotlinx.android.synthetic.main.activity_error_handler.*
class ErrorHandlerActivity : AppCompatActivity() {
private var deviceInfo: DeviceInfo? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_error_handler)
deviceInfo = DeviceInfo(this)
clearAppData.setOnClickListener {
try {
val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.parse("package:$packageName")
startActivity(intent)
} catch (e: ActivityNotFoundException) {
val intent = Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS)
startActivity(intent)
}
}
sendCrashLog.setOnClickListener {
val sendIntent = Intent(Intent.ACTION_SEND)
val subject = "Error report"
val body = intent.getStringExtra("error") + "\n" + deviceInfo!!.toString()
sendIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf("monkeycodeapp@gmail.com"))
sendIntent.putExtra(Intent.EXTRA_TEXT, body)
sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject)
sendIntent.type = "message/rfc822"
startActivity(Intent.createChooser(sendIntent, "Send crash log"))
deleteFile("stack.trace")
}
showCrashError.text = String.format("%s", intent.getStringExtra("error"))
showCrashError.append(deviceInfo!!.toString())
}
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import androidx.annotation.IntRange;
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||||
|
|
||||||
|
public class DeviceInfo {
|
||||||
|
private final int versionCode;
|
||||||
|
private final String versionName;
|
||||||
|
private final String buildVersion = Build.VERSION.INCREMENTAL;
|
||||||
|
private final String releaseVersion = Build.VERSION.RELEASE;
|
||||||
|
@IntRange(from = 0)
|
||||||
|
private final int sdkVersion = Build.VERSION.SDK_INT;
|
||||||
|
private final String buildID = Build.DISPLAY;
|
||||||
|
private final String brand = Build.BRAND;
|
||||||
|
private final String manufacturer = Build.MANUFACTURER;
|
||||||
|
private final String device = Build.DEVICE;
|
||||||
|
private final String model = Build.MODEL;
|
||||||
|
private final String product = Build.PRODUCT;
|
||||||
|
private final String hardware = Build.HARDWARE;
|
||||||
|
private final String baseTheme;
|
||||||
|
private final String nowPlayingTheme;
|
||||||
|
private final boolean isAdaptive;
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private final String[] abis = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||||
|
Build.SUPPORTED_ABIS : new String[]{Build.CPU_ABI, Build.CPU_ABI2};
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
private final String[] abis32Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||||
|
Build.SUPPORTED_32_BIT_ABIS : null;
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
private final String[] abis64Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||||
|
Build.SUPPORTED_64_BIT_ABIS : null;
|
||||||
|
|
||||||
|
public DeviceInfo(Context context) {
|
||||||
|
PackageInfo packageInfo;
|
||||||
|
try {
|
||||||
|
packageInfo = context.getPackageManager()
|
||||||
|
.getPackageInfo(context.getPackageName(), 0);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
packageInfo = null;
|
||||||
|
}
|
||||||
|
if (packageInfo != null) {
|
||||||
|
versionCode = packageInfo.versionCode;
|
||||||
|
versionName = packageInfo.versionName;
|
||||||
|
} else {
|
||||||
|
versionCode = -1;
|
||||||
|
versionName = null;
|
||||||
|
}
|
||||||
|
baseTheme = PreferenceUtil.getInstance(context).getBaseTheme();
|
||||||
|
nowPlayingTheme = context.getString(PreferenceUtil.getInstance(context).getNowPlayingScreen().getTitleRes());
|
||||||
|
isAdaptive = PreferenceUtil.getInstance(context).getAdaptiveColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toMarkdown() {
|
||||||
|
return "Device info:\n"
|
||||||
|
+ "---\n"
|
||||||
|
+ "<table>\n"
|
||||||
|
+ "<tr><td>App version</td><td>" + versionName + "</td></tr>\n"
|
||||||
|
+ "<tr><td>App version code</td><td>" + versionCode + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Android build version</td><td>" + buildVersion + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Android release version</td><td>" + releaseVersion + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Android SDK version</td><td>" + sdkVersion + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Android build ID</td><td>" + buildID + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Device brand</td><td>" + brand + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Device manufacturer</td><td>" + manufacturer + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Device name</td><td>" + device + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Device model</td><td>" + model + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Device product name</td><td>" + product + "</td></tr>\n"
|
||||||
|
+ "<tr><td>Device hardware name</td><td>" + hardware + "</td></tr>\n"
|
||||||
|
+ "<tr><td>ABIs</td><td>" + Arrays.toString(abis) + "</td></tr>\n"
|
||||||
|
+ "<tr><td>ABIs (32bit)</td><td>" + Arrays.toString(abis32Bits) + "</td></tr>\n"
|
||||||
|
+ "<tr><td>ABIs (64bit)</td><td>" + Arrays.toString(abis64Bits) + "</td></tr>\n"
|
||||||
|
+ "</table>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "App version: " + versionName + "\n"
|
||||||
|
+ "App version code: " + versionCode + "\n"
|
||||||
|
+ "Android build version: " + buildVersion + "\n"
|
||||||
|
+ "Android release version: " + releaseVersion + "\n"
|
||||||
|
+ "Android SDK version: " + sdkVersion + "\n"
|
||||||
|
+ "Android build ID: " + buildID + "\n"
|
||||||
|
+ "Device brand: " + brand + "\n"
|
||||||
|
+ "Device manufacturer: " + manufacturer + "\n"
|
||||||
|
+ "Device name: " + device + "\n"
|
||||||
|
+ "Device model: " + model + "\n"
|
||||||
|
+ "Device product name: " + product + "\n"
|
||||||
|
+ "Device hardware name: " + hardware + "\n"
|
||||||
|
+ "ABIs: " + Arrays.toString(abis) + "\n"
|
||||||
|
+ "ABIs (32bit): " + Arrays.toString(abis32Bits) + "\n"
|
||||||
|
+ "ABIs (64bit): " + Arrays.toString(abis64Bits) + "\n"
|
||||||
|
+ "Base theme: " + baseTheme + "\n"
|
||||||
|
+ "Now playing theme: " + nowPlayingTheme + "\n"
|
||||||
|
+ "Adaptive: " + isAdaptive;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,111 +0,0 @@
|
||||||
package code.name.monkey.retromusic.activities.bugreport.model
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.IntRange
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.core.content.pm.PackageInfoCompat
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil.isAdaptiveColor
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil.nowPlayingScreen
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class DeviceInfo(context: Context) {
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private val abis = Build.SUPPORTED_ABIS
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private val abis32Bits = Build.SUPPORTED_32_BIT_ABIS
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private val abis64Bits = Build.SUPPORTED_64_BIT_ABIS
|
|
||||||
private val baseTheme: String
|
|
||||||
private val brand = Build.BRAND
|
|
||||||
private val buildID = Build.DISPLAY
|
|
||||||
private val buildVersion = Build.VERSION.INCREMENTAL
|
|
||||||
private val device = Build.DEVICE
|
|
||||||
private val hardware = Build.HARDWARE
|
|
||||||
private val isAdaptive: Boolean
|
|
||||||
private val manufacturer = Build.MANUFACTURER
|
|
||||||
private val model = Build.MODEL
|
|
||||||
private val nowPlayingTheme: String
|
|
||||||
private val product = Build.PRODUCT
|
|
||||||
private val releaseVersion = Build.VERSION.RELEASE
|
|
||||||
|
|
||||||
@IntRange(from = 0)
|
|
||||||
private val sdkVersion = Build.VERSION.SDK_INT
|
|
||||||
private var versionCode = 0L
|
|
||||||
private var versionName: String? = null
|
|
||||||
private val selectedLang: String
|
|
||||||
fun toMarkdown(): String {
|
|
||||||
return """
|
|
||||||
Device info:
|
|
||||||
---
|
|
||||||
<table>
|
|
||||||
<tr><td><b>App version</b></td><td>$versionName</td></tr>
|
|
||||||
<tr><td>App version code</td><td>$versionCode</td></tr>
|
|
||||||
<tr><td>Android build version</td><td>$buildVersion</td></tr>
|
|
||||||
<tr><td>Android release version</td><td>$releaseVersion</td></tr>
|
|
||||||
<tr><td>Android SDK version</td><td>$sdkVersion</td></tr>
|
|
||||||
<tr><td>Android build ID</td><td>$buildID</td></tr>
|
|
||||||
<tr><td>Device brand</td><td>$brand</td></tr>
|
|
||||||
<tr><td>Device manufacturer</td><td>$manufacturer</td></tr>
|
|
||||||
<tr><td>Device name</td><td>$device</td></tr>
|
|
||||||
<tr><td>Device model</td><td>$model</td></tr>
|
|
||||||
<tr><td>Device product name</td><td>$product</td></tr>
|
|
||||||
<tr><td>Device hardware name</td><td>$hardware</td></tr>
|
|
||||||
<tr><td>ABIs</td><td>${Arrays.toString(abis)}</td></tr>
|
|
||||||
<tr><td>ABIs (32bit)</td><td>${Arrays.toString(abis32Bits)}</td></tr>
|
|
||||||
<tr><td>ABIs (64bit)</td><td>${Arrays.toString(abis64Bits)}</td></tr>
|
|
||||||
<tr><td>Language</td><td>$selectedLang</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return """
|
|
||||||
App version: $versionName
|
|
||||||
App version code: $versionCode
|
|
||||||
Android build version: $buildVersion
|
|
||||||
Android release version: $releaseVersion
|
|
||||||
Android SDK version: $sdkVersion
|
|
||||||
Android build ID: $buildID
|
|
||||||
Device brand: $brand
|
|
||||||
Device manufacturer: $manufacturer
|
|
||||||
Device name: $device
|
|
||||||
Device model: $model
|
|
||||||
Device product name: $product
|
|
||||||
Device hardware name: $hardware
|
|
||||||
ABIs: ${Arrays.toString(abis)}
|
|
||||||
ABIs (32bit): ${Arrays.toString(abis32Bits)}
|
|
||||||
ABIs (64bit): ${Arrays.toString(abis64Bits)}
|
|
||||||
Base theme: $baseTheme
|
|
||||||
Now playing theme: $nowPlayingTheme
|
|
||||||
Adaptive: $isAdaptive
|
|
||||||
System language: ${Locale.getDefault().toLanguageTag()}
|
|
||||||
In-App Language: $selectedLang
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
val packageInfo = try {
|
|
||||||
context.packageManager.getPackageInfo(context.packageName, 0)
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
if (packageInfo != null) {
|
|
||||||
versionCode = PackageInfoCompat.getLongVersionCode(packageInfo)
|
|
||||||
versionName = packageInfo.versionName
|
|
||||||
} else {
|
|
||||||
versionCode = -1
|
|
||||||
versionName = null
|
|
||||||
}
|
|
||||||
baseTheme = PreferenceUtil.baseTheme
|
|
||||||
nowPlayingTheme = context.getString(nowPlayingScreen.titleRes)
|
|
||||||
isAdaptive = isAdaptiveColor
|
|
||||||
selectedLang = AppCompatDelegate.getApplicationLocales().toLanguageTags()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model;
|
||||||
|
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo;
|
||||||
|
|
||||||
|
public class Report {
|
||||||
|
private final String title;
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
private final DeviceInfo deviceInfo;
|
||||||
|
|
||||||
|
private final ExtraInfo extraInfo;
|
||||||
|
|
||||||
|
public Report(String title, String description, DeviceInfo deviceInfo, ExtraInfo extraInfo) {
|
||||||
|
this.title = title;
|
||||||
|
this.description = description;
|
||||||
|
this.deviceInfo = deviceInfo;
|
||||||
|
this.extraInfo = extraInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description + "\n\n"
|
||||||
|
+ "-\n\n"
|
||||||
|
+ deviceInfo.toMarkdown() + "\n\n"
|
||||||
|
+ extraInfo.toMarkdown();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ExtraInfo {
|
||||||
|
private final Map<String, String> extraInfo = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public void put(String key, String value) {
|
||||||
|
extraInfo.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String key, boolean value) {
|
||||||
|
extraInfo.put(key, Boolean.toString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String key, double value) {
|
||||||
|
extraInfo.put(key, Double.toString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String key, float value) {
|
||||||
|
extraInfo.put(key, Float.toString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String key, long value) {
|
||||||
|
extraInfo.put(key, Long.toString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String key, int value) {
|
||||||
|
extraInfo.put(key, Integer.toString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String key, Object value) {
|
||||||
|
extraInfo.put(key, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(String key) {
|
||||||
|
extraInfo.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toMarkdown() {
|
||||||
|
if (extraInfo.isEmpty()) return "";
|
||||||
|
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
output.append("Extra info:\n"
|
||||||
|
+ "---\n"
|
||||||
|
+ "<table>\n");
|
||||||
|
for (String key : extraInfo.keySet()) {
|
||||||
|
output.append("<tr><td>")
|
||||||
|
.append(key)
|
||||||
|
.append("</td><td>")
|
||||||
|
.append(extraInfo.get(key))
|
||||||
|
.append("</td></tr>\n");
|
||||||
|
}
|
||||||
|
output.append("</table>\n");
|
||||||
|
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
public class GithubLogin {
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
private final String apiToken;
|
||||||
|
|
||||||
|
public GithubLogin(String username, String password) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.apiToken = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GithubLogin(String apiToken) {
|
||||||
|
this.username = null;
|
||||||
|
this.password = null;
|
||||||
|
this.apiToken = apiToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldUseApiToken() {
|
||||||
|
return TextUtils.isEmpty(username) || TextUtils.isEmpty(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApiToken() {
|
||||||
|
return apiToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||||
|
|
||||||
|
public class GithubTarget {
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
private final String repository;
|
||||||
|
|
||||||
|
public GithubTarget(String username, String repository) {
|
||||||
|
this.username = username;
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRepository() {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,9 +24,10 @@ import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
|
||||||
|
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
|
|
||||||
/** Created by hemanths on 2019-07-31. */
|
/**
|
||||||
|
* Created by hemanths on 2019-07-31.
|
||||||
|
*/
|
||||||
public class SAFGuideActivity extends IntroActivity {
|
public class SAFGuideActivity extends IntroActivity {
|
||||||
|
|
||||||
public static final int REQUEST_CODE_SAF_GUIDE = 98;
|
public static final int REQUEST_CODE_SAF_GUIDE = 98;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -39,37 +40,30 @@ public class SAFGuideActivity extends IntroActivity {
|
||||||
|
|
||||||
setButtonCtaTintMode(BUTTON_CTA_TINT_MODE_TEXT);
|
setButtonCtaTintMode(BUTTON_CTA_TINT_MODE_TEXT);
|
||||||
|
|
||||||
String title =
|
String title = String.format(getString(R.string.saf_guide_slide1_title), getString(R.string.app_name));
|
||||||
String.format(getString(R.string.saf_guide_slide1_title), getString(R.string.app_name));
|
|
||||||
|
|
||||||
addSlide(
|
addSlide(new SimpleSlide.Builder()
|
||||||
new SimpleSlide.Builder()
|
|
||||||
.title(title)
|
.title(title)
|
||||||
.description(
|
.description(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 ? R.string.saf_guide_slide1_description_before_o : R.string.saf_guide_slide1_description)
|
||||||
Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
|
|
||||||
? R.string.saf_guide_slide1_description_before_o
|
|
||||||
: R.string.saf_guide_slide1_description)
|
|
||||||
.image(R.drawable.saf_guide_1)
|
.image(R.drawable.saf_guide_1)
|
||||||
.background(code.name.monkey.appthemehelper.R.color.md_deep_purple_300)
|
.background(R.color.md_deep_purple_300)
|
||||||
.backgroundDark(code.name.monkey.appthemehelper.R.color.md_deep_purple_400)
|
.backgroundDark(R.color.md_deep_purple_400)
|
||||||
.layout(R.layout.fragment_simple_slide_large_image)
|
.layout(R.layout.fragment_simple_slide_large_image)
|
||||||
.build());
|
.build());
|
||||||
addSlide(
|
addSlide(new SimpleSlide.Builder()
|
||||||
new SimpleSlide.Builder()
|
|
||||||
.title(R.string.saf_guide_slide2_title)
|
.title(R.string.saf_guide_slide2_title)
|
||||||
.description(R.string.saf_guide_slide2_description)
|
.description(R.string.saf_guide_slide2_description)
|
||||||
.image(R.drawable.saf_guide_2)
|
.image(R.drawable.saf_guide_2)
|
||||||
.background(code.name.monkey.appthemehelper.R.color.md_deep_purple_500)
|
.background(R.color.md_deep_purple_500)
|
||||||
.backgroundDark(code.name.monkey.appthemehelper.R.color.md_deep_purple_600)
|
.backgroundDark(R.color.md_deep_purple_600)
|
||||||
.layout(R.layout.fragment_simple_slide_large_image)
|
.layout(R.layout.fragment_simple_slide_large_image)
|
||||||
.build());
|
.build());
|
||||||
addSlide(
|
addSlide(new SimpleSlide.Builder()
|
||||||
new SimpleSlide.Builder()
|
|
||||||
.title(R.string.saf_guide_slide3_title)
|
.title(R.string.saf_guide_slide3_title)
|
||||||
.description(R.string.saf_guide_slide3_description)
|
.description(R.string.saf_guide_slide3_description)
|
||||||
.image(R.drawable.saf_guide_3)
|
.image(R.drawable.saf_guide_3)
|
||||||
.background(code.name.monkey.appthemehelper.R.color.md_deep_purple_700)
|
.background(R.color.md_deep_purple_700)
|
||||||
.backgroundDark(code.name.monkey.appthemehelper.R.color.md_deep_purple_800)
|
.backgroundDark(R.color.md_deep_purple_800)
|
||||||
.layout(R.layout.fragment_simple_slide_large_image)
|
.layout(R.layout.fragment_simple_slide_large_image)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 Bartlomiej Uliasz.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it under
|
|
||||||
* the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities.saf
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity.REQUEST_CODE_SAF_GUIDE
|
|
||||||
import code.name.monkey.retromusic.util.SAFUtil
|
|
||||||
|
|
||||||
/** Created by buliasz on 2021-02-07. */
|
|
||||||
class SAFRequestActivity : Activity() {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
val intent = Intent(this, code.name.monkey.retromusic.activities.saf.SAFGuideActivity::class.java)
|
|
||||||
startActivityForResult(intent, REQUEST_CODE_SAF_GUIDE)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, intent)
|
|
||||||
when (requestCode) {
|
|
||||||
REQUEST_CODE_SAF_GUIDE -> {
|
|
||||||
SAFUtil.openTreePicker(this)
|
|
||||||
}
|
|
||||||
SAFUtil.REQUEST_SAF_PICK_TREE -> {
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
SAFUtil.saveTreeUri(this, intent)
|
|
||||||
}
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,288 +1,250 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities.tageditor
|
package code.name.monkey.retromusic.activities.tageditor
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
import android.view.animation.OvershootInterpolator
|
import android.view.animation.OvershootInterpolator
|
||||||
import android.widget.ImageView
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import androidx.activity.result.IntentSenderRequest
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
import androidx.activity.result.PickVisualMediaRequest
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import code.name.monkey.appthemehelper.util.TintHelper
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.viewbinding.ViewBinding
|
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||||
import code.name.monkey.retromusic.extensions.accentColor
|
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
|
||||||
import code.name.monkey.retromusic.extensions.colorButtons
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import code.name.monkey.retromusic.extensions.hideSoftKeyboard
|
import code.name.monkey.retromusic.util.SAFUtil
|
||||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
import com.afollestad.materialdialogs.list.listItems
|
||||||
import code.name.monkey.retromusic.model.AudioTagInfo
|
|
||||||
import code.name.monkey.retromusic.repository.Repository
|
|
||||||
import code.name.monkey.retromusic.util.logD
|
|
||||||
import code.name.monkey.retromusic.util.logE
|
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.jaudiotagger.audio.AudioFile
|
import org.jaudiotagger.audio.AudioFile
|
||||||
import org.jaudiotagger.audio.AudioFileIO
|
import org.jaudiotagger.audio.AudioFileIO
|
||||||
import org.jaudiotagger.tag.FieldKey
|
import org.jaudiotagger.tag.FieldKey
|
||||||
import org.koin.android.ext.android.inject
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|
||||||
abstract val editorImage: ImageView
|
|
||||||
val repository by inject<Repository>()
|
|
||||||
|
|
||||||
lateinit var saveFab: MaterialButton
|
abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||||
protected var id: Long = 0
|
|
||||||
|
|
||||||
|
protected var id: Int = 0
|
||||||
private set
|
private set
|
||||||
private var paletteColorPrimary: Int = 0
|
private var paletteColorPrimary: Int = 0
|
||||||
|
private var isInNoImageMode: Boolean = false
|
||||||
private var songPaths: List<String>? = null
|
private var songPaths: List<String>? = null
|
||||||
|
lateinit var saveFab: MaterialButton
|
||||||
|
|
||||||
private var savedSongPaths: List<String>? = null
|
private var savedSongPaths: List<String>? = null
|
||||||
private val currentSongPath: String? = null
|
private val currentSongPath: String? = null
|
||||||
private var savedTags: Map<FieldKey, String>? = null
|
private var savedTags: Map<FieldKey, String>? = null
|
||||||
private var savedArtworkInfo: ArtworkInfo? = null
|
private var savedArtworkInfo: ArtworkInfo? = null
|
||||||
private var _binding: VB? = null
|
|
||||||
protected val binding: VB get() = _binding!!
|
|
||||||
private var cacheFiles = listOf<File>()
|
|
||||||
|
|
||||||
abstract val bindingInflater: (LayoutInflater) -> VB
|
protected val show: MaterialDialog
|
||||||
|
get() = MaterialDialog(this@AbsTagEditorActivity).show {
|
||||||
private lateinit var launcher: ActivityResultLauncher<IntentSenderRequest>
|
title(code.name.monkey.retromusic.R.string.update_image)
|
||||||
|
listItems(items = items) { _, position, _ ->
|
||||||
protected abstract fun loadImageFromFile(selectedFile: Uri?)
|
|
||||||
|
|
||||||
protected val show: AlertDialog
|
|
||||||
get() =
|
|
||||||
MaterialAlertDialogBuilder(this)
|
|
||||||
.setTitle(R.string.update_image)
|
|
||||||
.setItems(items.toTypedArray()) { _, position ->
|
|
||||||
when (position) {
|
when (position) {
|
||||||
0 -> startImagePicker()
|
0 -> getImageFromLastFM()
|
||||||
1 -> searchImageOnWeb()
|
1 -> startImagePicker()
|
||||||
2 -> deleteImage()
|
2 -> searchImageOnWeb()
|
||||||
|
3 -> deleteImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.action_cancel, null)
|
}
|
||||||
.show()
|
protected abstract val contentViewLayout: Int
|
||||||
.colorButtons()
|
|
||||||
|
|
||||||
internal val albumArtist: String?
|
internal val albumArtist: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault
|
||||||
} catch (e: Exception) {
|
.getFirst(FieldKey.ALBUM_ARTIST)
|
||||||
logE(e)
|
} catch (ignored: Exception) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val songTitle: String?
|
protected val songTitle: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE)
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
logE(e)
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
protected val composer: String?
|
protected val composer: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER)
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
logE(e)
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val albumTitle: String?
|
protected val albumTitle: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM)
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
logE(e)
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val artistName: String?
|
protected val artistName: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST)
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
logE(e)
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val albumArtistName: String?
|
protected val albumArtistName: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault
|
||||||
} catch (e: Exception) {
|
.getFirst(FieldKey.ALBUM_ARTIST)
|
||||||
logE(e)
|
} catch (ignored: Exception) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val genreName: String?
|
protected val genreName: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE)
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
logE(e)
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val songYear: String?
|
protected val songYear: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR)
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
logE(e)
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val trackNumber: String?
|
protected val trackNumber: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK)
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
logE(e)
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected val discNumber: String?
|
|
||||||
get() {
|
|
||||||
return try {
|
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.DISC_NO)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logE(e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val lyrics: String?
|
protected val lyrics: String?
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS)
|
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS)
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
logE(e)
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected val albumArt: Bitmap?
|
protected val albumArt: Bitmap?
|
||||||
get() {
|
get() {
|
||||||
try {
|
try {
|
||||||
val artworkTag = getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.firstArtwork
|
val artworkTag = getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault
|
||||||
|
.firstArtwork
|
||||||
if (artworkTag != null) {
|
if (artworkTag != null) {
|
||||||
val artworkBinaryData = artworkTag.binaryData
|
val artworkBinaryData = artworkTag.binaryData
|
||||||
return BitmapFactory.decodeByteArray(
|
return BitmapFactory.decodeByteArray(artworkBinaryData, 0, artworkBinaryData.size)
|
||||||
artworkBinaryData,
|
|
||||||
0,
|
|
||||||
artworkBinaryData.size
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
} catch (e: Exception) {
|
} catch (ignored: Exception) {
|
||||||
logE(e)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private val pickArtworkImage =
|
|
||||||
registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
|
|
||||||
loadImageFromFile(uri)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
_binding = bindingInflater.invoke(layoutInflater)
|
setContentView(contentViewLayout)
|
||||||
setContentView(binding.root)
|
|
||||||
setTaskDescriptionColorAuto()
|
|
||||||
|
|
||||||
saveFab = findViewById( R.id.saveTags)
|
saveFab = findViewById( R.id.saveTags)
|
||||||
getIntentExtras()
|
getIntentExtras()
|
||||||
|
|
||||||
songPaths = getSongPaths()
|
songPaths = getSongPaths()
|
||||||
logD(songPaths?.size)
|
|
||||||
if (songPaths!!.isEmpty()) {
|
if (songPaths!!.isEmpty()) {
|
||||||
finish()
|
finish()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpViews()
|
setUpViews()
|
||||||
launcher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
|
|
||||||
if (it.resultCode == Activity.RESULT_OK) {
|
|
||||||
writeToFiles(getSongUris(), cacheFiles)
|
setNavigationbarColorAuto()
|
||||||
}
|
setTaskDescriptionColorAuto()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpViews() {
|
private fun setUpViews() {
|
||||||
|
setUpScrollView()
|
||||||
setUpFab()
|
setUpFab()
|
||||||
setUpImageView()
|
setUpImageView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setUpScrollView() {
|
||||||
|
//observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var items: List<String>
|
private lateinit var items: List<String>
|
||||||
|
|
||||||
private fun setUpImageView() {
|
private fun setUpImageView() {
|
||||||
loadCurrentImage()
|
loadCurrentImage()
|
||||||
items = listOf(
|
items = listOf(getString(code.name.monkey.retromusic.R.string.download_from_last_fm), getString(code.name.monkey.retromusic.R.string.pick_from_local_storage), getString(code.name.monkey.retromusic.R.string.web_search), getString(code.name.monkey.retromusic.R.string.remove_cover))
|
||||||
getString(R.string.pick_from_local_storage),
|
|
||||||
getString(R.string.web_search),
|
|
||||||
getString(R.string.remove_cover)
|
|
||||||
)
|
|
||||||
editorImage.setOnClickListener { show }
|
editorImage.setOnClickListener { show }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startImagePicker() {
|
private fun startImagePicker() {
|
||||||
pickArtworkImage.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
|
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||||
|
intent.type = "image/*"
|
||||||
|
startActivityForResult(Intent.createChooser(intent, getString(code.name.monkey.retromusic.R.string.pick_from_local_storage)), REQUEST_CODE_SELECT_IMAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun loadCurrentImage()
|
protected abstract fun loadCurrentImage()
|
||||||
|
|
||||||
|
protected abstract fun getImageFromLastFM()
|
||||||
|
|
||||||
protected abstract fun searchImageOnWeb()
|
protected abstract fun searchImageOnWeb()
|
||||||
|
|
||||||
protected abstract fun deleteImage()
|
protected abstract fun deleteImage()
|
||||||
|
|
||||||
private fun setUpFab() {
|
private fun setUpFab() {
|
||||||
saveFab.accentColor()
|
saveFab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||||
|
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||||
|
saveFab.setTextColor(this)
|
||||||
|
saveFab.iconTint = this
|
||||||
|
}
|
||||||
saveFab.apply {
|
saveFab.apply {
|
||||||
scaleX = 0f
|
scaleX = 0f
|
||||||
scaleY = 0f
|
scaleY = 0f
|
||||||
isEnabled = false
|
isEnabled = false
|
||||||
setOnClickListener { save() }
|
setOnClickListener { save() }
|
||||||
|
TintHelper.setTintAuto(this, ThemeStore.accentColor(this@AbsTagEditorActivity), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,14 +253,12 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
||||||
private fun getIntentExtras() {
|
private fun getIntentExtras() {
|
||||||
val intentExtras = intent.extras
|
val intentExtras = intent.extras
|
||||||
if (intentExtras != null) {
|
if (intentExtras != null) {
|
||||||
id = intentExtras.getLong(EXTRA_ID)
|
id = intentExtras.getInt(EXTRA_ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun getSongPaths(): List<String>
|
protected abstract fun getSongPaths(): List<String>
|
||||||
|
|
||||||
protected abstract fun getSongUris(): List<Uri>
|
|
||||||
|
|
||||||
protected fun searchWebFor(vararg keys: String) {
|
protected fun searchWebFor(vararg keys: String) {
|
||||||
val stringBuilder = StringBuilder()
|
val stringBuilder = StringBuilder()
|
||||||
for (key in keys) {
|
for (key in keys) {
|
||||||
|
@ -313,34 +273,52 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
val id = item.itemId
|
||||||
|
when (id) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
onBackPressedDispatcher.onBackPressed()
|
super.onBackPressed()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun setNoImageMode() {
|
||||||
|
isInNoImageMode = true
|
||||||
|
imageContainer!!.visibility = View.GONE
|
||||||
|
editorImage.visibility = View.GONE
|
||||||
|
editorImage.isEnabled = false
|
||||||
|
|
||||||
|
setColors(intent.getIntExtra(EXTRA_PALETTE, ATHUtil.resolveColor(this, R.attr.colorPrimary)))
|
||||||
|
}
|
||||||
|
|
||||||
protected fun dataChanged() {
|
protected fun dataChanged() {
|
||||||
showFab()
|
showFab()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showFab() {
|
private fun showFab() {
|
||||||
saveFab.animate().setDuration(500).setInterpolator(OvershootInterpolator()).scaleX(1f)
|
saveFab.animate()
|
||||||
.scaleY(1f).start()
|
.setDuration(500)
|
||||||
|
.setInterpolator(OvershootInterpolator())
|
||||||
|
.scaleX(1f)
|
||||||
|
.scaleY(1f)
|
||||||
|
.start()
|
||||||
saveFab.isEnabled = true
|
saveFab.isEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hideFab() {
|
private fun hideFab() {
|
||||||
saveFab.animate().setDuration(500).setInterpolator(OvershootInterpolator()).scaleX(0.0f)
|
saveFab.animate()
|
||||||
.scaleY(0.0f).start()
|
.setDuration(500)
|
||||||
|
.setInterpolator(OvershootInterpolator())
|
||||||
|
.scaleX(0.0f)
|
||||||
|
.scaleY(0.0f)
|
||||||
|
.start()
|
||||||
saveFab.isEnabled = false
|
saveFab.isEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
|
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
editorImage.setImageResource(R.drawable.default_audio_art)
|
editorImage.setImageResource(code.name.monkey.retromusic.R.drawable.default_album_art)
|
||||||
} else {
|
} else {
|
||||||
editorImage.setImageBitmap(bitmap)
|
editorImage.setImageBitmap(bitmap)
|
||||||
}
|
}
|
||||||
|
@ -351,104 +329,73 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
||||||
paletteColorPrimary = color
|
paletteColorPrimary = color
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun writeValuesToFiles(
|
protected fun writeValuesToFiles(fieldKeyValueMap: Map<FieldKey, String>,
|
||||||
fieldKeyValueMap: Map<FieldKey, String>,
|
artworkInfo: ArtworkInfo?) {
|
||||||
artworkInfo: ArtworkInfo?
|
RetroUtil.hideSoftKeyboard(this)
|
||||||
) {
|
|
||||||
hideSoftKeyboard()
|
|
||||||
|
|
||||||
hideFab()
|
hideFab()
|
||||||
logD(fieldKeyValueMap)
|
|
||||||
GlobalScope.launch {
|
|
||||||
if (VersionUtils.hasR()) {
|
|
||||||
cacheFiles = TagWriter.writeTagsToFilesR(
|
|
||||||
this@AbsTagEditorActivity, AudioTagInfo(
|
|
||||||
songPaths,
|
|
||||||
fieldKeyValueMap,
|
|
||||||
artworkInfo
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (cacheFiles.isNotEmpty()) {
|
savedSongPaths = getSongPaths()
|
||||||
val pendingIntent =
|
savedTags = fieldKeyValueMap
|
||||||
MediaStore.createWriteRequest(contentResolver, getSongUris())
|
savedArtworkInfo = artworkInfo
|
||||||
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
|
|
||||||
}
|
if (!SAFUtil.isSAFRequired(savedSongPaths)) {
|
||||||
|
writeTags(savedSongPaths)
|
||||||
} else {
|
} else {
|
||||||
TagWriter.writeTagsToFiles(
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
this@AbsTagEditorActivity, AudioTagInfo(
|
if (SAFUtil.isSDCardAccessGranted(this)) {
|
||||||
songPaths,
|
writeTags(savedSongPaths)
|
||||||
fieldKeyValueMap,
|
} else {
|
||||||
artworkInfo
|
startActivityForResult(Intent(this, SAFGuideActivity::class.java), SAFGuideActivity.REQUEST_CODE_SAF_GUIDE)
|
||||||
)
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeTags(paths: List<String>?) {
|
private fun writeTags(paths: List<String>?) {
|
||||||
GlobalScope.launch {
|
WriteTagsAsyncTask(this).execute(WriteTagsAsyncTask.LoadingInfo(paths, savedTags, savedArtworkInfo))
|
||||||
if (VersionUtils.hasR()) {
|
}
|
||||||
cacheFiles = TagWriter.writeTagsToFilesR(
|
|
||||||
this@AbsTagEditorActivity, AudioTagInfo(
|
|
||||||
paths,
|
|
||||||
savedTags,
|
|
||||||
savedArtworkInfo
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val pendingIntent = MediaStore.createWriteRequest(contentResolver, getSongUris())
|
|
||||||
|
|
||||||
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
|
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
||||||
} else {
|
super.onActivityResult(requestCode, resultCode, intent)
|
||||||
TagWriter.writeTagsToFiles(
|
when (requestCode) {
|
||||||
this@AbsTagEditorActivity, AudioTagInfo(
|
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
|
||||||
paths,
|
intent?.data?.let {
|
||||||
savedTags,
|
loadImageFromFile(it)
|
||||||
savedArtworkInfo
|
}
|
||||||
)
|
}
|
||||||
)
|
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE -> {
|
||||||
|
SAFUtil.openTreePicker(this)
|
||||||
|
}
|
||||||
|
SAFUtil.REQUEST_SAF_PICK_TREE -> {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
SAFUtil.saveTreeUri(this, intent)
|
||||||
|
writeTags(savedSongPaths)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SAFUtil.REQUEST_SAF_PICK_FILE -> {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
writeTags(Collections.singletonList(currentSongPath + SAFUtil.SEPARATOR + intent!!.dataString))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var audioFile: AudioFile
|
protected abstract fun loadImageFromFile(selectedFile: Uri?)
|
||||||
|
|
||||||
private fun getAudioFile(path: String): AudioFile {
|
private fun getAudioFile(path: String): AudioFile {
|
||||||
return try {
|
return try {
|
||||||
if (!this::audioFile.isInitialized) {
|
AudioFileIO.read(File(path))
|
||||||
audioFile = AudioFileIO.read(File(path))
|
|
||||||
}
|
|
||||||
audioFile
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Could not read audio file $path", e)
|
Log.e(TAG, "Could not read audio file $path", e)
|
||||||
AudioFile()
|
AudioFile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeToFiles(songUris: List<Uri>, cacheFiles: List<File>) {
|
class ArtworkInfo constructor(val albumId: Int, val artwork: Bitmap?)
|
||||||
if (cacheFiles.size == songUris.size) {
|
|
||||||
for (i in cacheFiles.indices) {
|
|
||||||
contentResolver.openOutputStream(songUris[i])?.use { output ->
|
|
||||||
cacheFiles[i].inputStream().use { input ->
|
|
||||||
input.copyTo(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lifecycleScope.launch {
|
|
||||||
TagWriter.scan(this@AbsTagEditorActivity, getSongPaths())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
// Delete Cache Files
|
|
||||||
cacheFiles.forEach { file ->
|
|
||||||
file.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val EXTRA_ID = "extra_id"
|
const val EXTRA_ID = "extra_id"
|
||||||
const val EXTRA_PALETTE = "extra_palette"
|
const val EXTRA_PALETTE = "extra_palette"
|
||||||
private val TAG = AbsTagEditorActivity::class.java.simpleName
|
private val TAG = AbsTagEditorActivity::class.java.simpleName
|
||||||
|
|
|
@ -1,17 +1,3 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities.tageditor
|
package code.name.monkey.retromusic.activities.tageditor
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
@ -22,195 +8,251 @@ import android.graphics.Color
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.transition.Slide
|
import android.text.Editable
|
||||||
import android.view.LayoutInflater
|
import android.text.TextUtils
|
||||||
import android.widget.ImageView
|
import android.text.TextWatcher
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.TintHelper
|
||||||
|
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding
|
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||||
import code.name.monkey.retromusic.extensions.*
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
|
|
||||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
import code.name.monkey.retromusic.loaders.AlbumLoader
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.rest.LastFMRestClient
|
||||||
|
import code.name.monkey.retromusic.rest.model.LastFmAlbum
|
||||||
import code.name.monkey.retromusic.util.ImageUtil
|
import code.name.monkey.retromusic.util.ImageUtil
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.LastFMUtil
|
||||||
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
||||||
import code.name.monkey.retromusic.util.logD
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.request.target.ImageViewTarget
|
import com.bumptech.glide.request.animation.GlideAnimation
|
||||||
import com.bumptech.glide.request.transition.Transition
|
import com.bumptech.glide.request.target.SimpleTarget
|
||||||
import com.google.android.material.shape.MaterialShapeDrawable
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
||||||
import org.jaudiotagger.tag.FieldKey
|
import org.jaudiotagger.tag.FieldKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBinding>() {
|
|
||||||
|
|
||||||
override val bindingInflater: (LayoutInflater) -> ActivityAlbumTagEditorBinding =
|
class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
ActivityAlbumTagEditorBinding::inflate
|
override val contentViewLayout: Int
|
||||||
|
get() = R.layout.activity_album_tag_editor
|
||||||
|
|
||||||
private fun windowEnterTransition() {
|
override fun loadImageFromFile(selectedFileUri: Uri?) {
|
||||||
val slide = Slide()
|
|
||||||
slide.excludeTarget(R.id.appBarLayout, true)
|
|
||||||
slide.excludeTarget(R.id.status_bar, true)
|
|
||||||
slide.excludeTarget(android.R.id.statusBarBackground, true)
|
|
||||||
slide.excludeTarget(android.R.id.navigationBarBackground, true)
|
|
||||||
|
|
||||||
window.enterTransition = slide
|
|
||||||
}
|
|
||||||
|
|
||||||
private var albumArtBitmap: Bitmap? = null
|
|
||||||
private var deleteAlbumArt: Boolean = false
|
|
||||||
|
|
||||||
private fun setupToolbar() {
|
|
||||||
setSupportActionBar(binding.toolbar)
|
|
||||||
binding.appBarLayout?.statusBarForeground =
|
|
||||||
MaterialShapeDrawable.createWithElevationOverlay(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
window.sharedElementsUseOverlay = true
|
|
||||||
binding.imageContainer.transitionName = getString(R.string.transition_album_art)
|
|
||||||
windowEnterTransition()
|
|
||||||
setUpViews()
|
|
||||||
setupToolbar()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpViews() {
|
|
||||||
fillViewsWithFileTags()
|
|
||||||
|
|
||||||
binding.yearContainer.setTint(false)
|
|
||||||
binding.genreContainer.setTint(false)
|
|
||||||
binding.albumTitleContainer.setTint(false)
|
|
||||||
binding.albumArtistContainer.setTint(false)
|
|
||||||
|
|
||||||
binding.albumText.appHandleColor().doAfterTextChanged { dataChanged() }
|
|
||||||
binding.albumArtistText.appHandleColor().doAfterTextChanged { dataChanged() }
|
|
||||||
binding.genreTitle.appHandleColor().doAfterTextChanged { dataChanged() }
|
|
||||||
binding.yearTitle.appHandleColor().doAfterTextChanged { dataChanged() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fillViewsWithFileTags() {
|
|
||||||
binding.albumText.setText(albumTitle)
|
|
||||||
binding.albumArtistText.setText(albumArtistName)
|
|
||||||
binding.genreTitle.setText(genreName)
|
|
||||||
binding.yearTitle.setText(songYear)
|
|
||||||
logD(albumTitle + albumArtistName)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadCurrentImage() {
|
|
||||||
val bitmap = albumArt
|
|
||||||
setImageBitmap(
|
|
||||||
bitmap,
|
|
||||||
getColor(
|
|
||||||
generatePalette(bitmap),
|
|
||||||
defaultFooterColor()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
deleteAlbumArt = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun toastLoadingFailed() {
|
|
||||||
showToast(R.string.could_not_download_album_cover)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun searchImageOnWeb() {
|
|
||||||
searchWebFor(binding.albumText.text.toString(), binding.albumArtistText.text.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deleteImage() {
|
|
||||||
setImageBitmap(
|
|
||||||
BitmapFactory.decodeResource(resources, R.drawable.default_audio_art),
|
|
||||||
defaultFooterColor()
|
|
||||||
)
|
|
||||||
deleteAlbumArt = true
|
|
||||||
dataChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
|
||||||
Glide.with(this@AlbumTagEditorActivity)
|
Glide.with(this@AlbumTagEditorActivity)
|
||||||
.asBitmapPalette()
|
.load(selectedFileUri)
|
||||||
.load(selectedFile)
|
.asBitmap()
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
.transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java)
|
||||||
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
override fun onResourceReady(
|
.skipMemoryCache(true)
|
||||||
resource: BitmapPaletteWrapper,
|
.into(object : SimpleTarget<BitmapPaletteWrapper>() {
|
||||||
transition: Transition<in BitmapPaletteWrapper>?
|
override fun onResourceReady(resource: BitmapPaletteWrapper?, glideAnimation: GlideAnimation<in BitmapPaletteWrapper>?) {
|
||||||
) {
|
RetroColorUtil.getColor(resource?.palette, Color.TRANSPARENT);
|
||||||
getColor(resource.palette, Color.TRANSPARENT)
|
albumArtBitmap = resource?.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||||
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
setImageBitmap(albumArtBitmap, RetroColorUtil.getColor(resource?.palette, ATHUtil.resolveColor(this@AlbumTagEditorActivity, R.attr.defaultFooterColor)))
|
||||||
setImageBitmap(
|
|
||||||
albumArtBitmap,
|
|
||||||
getColor(
|
|
||||||
resource.palette,
|
|
||||||
defaultFooterColor()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
deleteAlbumArt = false
|
deleteAlbumArt = false
|
||||||
dataChanged()
|
dataChanged()
|
||||||
setResult(Activity.RESULT_OK)
|
setResult(Activity.RESULT_OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||||
super.onLoadFailed(errorDrawable)
|
super.onLoadFailed(e, errorDrawable)
|
||||||
showToast(R.string.error_load_failed, Toast.LENGTH_LONG)
|
Toast.makeText(this@AlbumTagEditorActivity, e.toString(), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/*Glide.with(AlbumTagEditorActivity.this)
|
||||||
|
.load(selectedFileUri)
|
||||||
|
.asBitmap()
|
||||||
|
.transcode(new BitmapPaletteTranscoder(AlbumTagEditorActivity.this), BitmapPaletteWrapper.class)
|
||||||
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
|
.skipMemoryCache(true)
|
||||||
|
.into(new SimpleTarget<BitmapPaletteWrapper>() {
|
||||||
|
@Override
|
||||||
|
public void onLoadFailed(Exception e, Drawable errorDrawable) {
|
||||||
|
super.onLoadFailed(e, errorDrawable);
|
||||||
|
e.printStackTrace();
|
||||||
|
Toast.makeText(AlbumTagEditorActivity.this, e.toString(), Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setResource(resource: BitmapPaletteWrapper?) {}
|
@Override
|
||||||
})
|
public void onResourceReady(BitmapPaletteWrapper resource, GlideAnimation<? super BitmapPaletteWrapper> glideAnimation) {
|
||||||
|
PhonographColorUtil.getColor(resource.getPalette(), Color.TRANSPARENT);
|
||||||
|
albumArtBitmap = ImageUtil.resizeBitmap(resource.getBitmap(), 2048);
|
||||||
|
setImageBitmap(albumArtBitmap, PhonographColorUtil.getColor(resource.getPalette(), ATHUtil.resolveColor(AlbumTagEditorActivity.this, R.attr.defaultFooterColor)));
|
||||||
|
deleteAlbumArt = false;
|
||||||
|
dataChanged();
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private var albumArtBitmap: Bitmap? = null
|
||||||
|
private var deleteAlbumArt: Boolean = false
|
||||||
|
private var lastFMRestClient: LastFMRestClient? = null
|
||||||
|
private val disposable = CompositeDisposable()
|
||||||
|
|
||||||
|
private fun setupToolbar() {
|
||||||
|
toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||||
|
ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(this, toolbar, Color.TRANSPARENT)
|
||||||
|
title = null
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
TintHelper.setTintAuto(content, ATHUtil.resolveColor(this, R.attr.colorPrimary), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setDrawUnderStatusBar()
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
lastFMRestClient = LastFMRestClient(this)
|
||||||
|
|
||||||
|
setUpViews()
|
||||||
|
setupToolbar()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun setUpViews() {
|
||||||
|
fillViewsWithFileTags()
|
||||||
|
|
||||||
|
MaterialUtil.setTint(yearContainer, false)
|
||||||
|
MaterialUtil.setTint(genreContainer, false)
|
||||||
|
MaterialUtil.setTint(albumTitleContainer, false)
|
||||||
|
MaterialUtil.setTint(albumArtistContainer, false)
|
||||||
|
|
||||||
|
albumText.appHandleColor().addTextChangedListener(this)
|
||||||
|
albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||||
|
genreTitle.appHandleColor().addTextChangedListener(this)
|
||||||
|
yearTitle.appHandleColor().addTextChangedListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillViewsWithFileTags() {
|
||||||
|
albumText.setText(albumTitle)
|
||||||
|
albumArtistText.setText(albumArtistName)
|
||||||
|
genreTitle.setText(genreName)
|
||||||
|
yearTitle.setText(songYear)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadCurrentImage() {
|
||||||
|
val bitmap = albumArt
|
||||||
|
setImageBitmap(bitmap, getColor(generatePalette(bitmap), ATHUtil.resolveColor(this, R.attr.defaultFooterColor)))
|
||||||
|
deleteAlbumArt = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getImageFromLastFM() {
|
||||||
|
val albumTitleStr = albumText.text.toString()
|
||||||
|
val albumArtistNameStr = albumArtistText.text.toString()
|
||||||
|
if (albumArtistNameStr.trim { it <= ' ' } == "" || albumTitleStr.trim { it <= ' ' } == "") {
|
||||||
|
Toast.makeText(this, resources.getString(R.string.album_or_artist_empty), Toast.LENGTH_SHORT).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.add(lastFMRestClient!!.apiService
|
||||||
|
.getAlbumInfo(albumTitleStr, albumArtistNameStr, null)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribeOn(Schedulers.computation())
|
||||||
|
.subscribe { this.extractDetails(it) })
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
disposable.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractDetails(lastFmAlbum: LastFmAlbum) {
|
||||||
|
if (lastFmAlbum.album != null) {
|
||||||
|
|
||||||
|
val url = LastFMUtil.getLargestAlbumImageUrl(lastFmAlbum.album.image)
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(url) && url.trim { it <= ' ' }.isNotEmpty()) {
|
||||||
|
Glide.with(this@AlbumTagEditorActivity)
|
||||||
|
.load(url)
|
||||||
|
.asBitmap()
|
||||||
|
.transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java)
|
||||||
|
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
|
||||||
|
.error(R.drawable.default_album_art)
|
||||||
|
.into(object : SimpleTarget<BitmapPaletteWrapper>() {
|
||||||
|
override fun onLoadFailed(e: java.lang.Exception?, errorDrawable: Drawable?) {
|
||||||
|
super.onLoadFailed(e, errorDrawable)
|
||||||
|
Toast.makeText(this@AlbumTagEditorActivity, e.toString(), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResourceReady(resource: BitmapPaletteWrapper?, glideAnimation: GlideAnimation<in BitmapPaletteWrapper>?) {
|
||||||
|
albumArtBitmap = resource?.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||||
|
setImageBitmap(albumArtBitmap, RetroColorUtil.getColor(resource?.palette, ATHUtil.resolveColor(this@AlbumTagEditorActivity, R.attr.defaultFooterColor)))
|
||||||
|
deleteAlbumArt = false
|
||||||
|
dataChanged()
|
||||||
|
setResult(RESULT_OK)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (lastFmAlbum.album.tags.tag.size > 0) {
|
||||||
|
genreTitle.setText(lastFmAlbum.album.tags.tag[0].name)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
toastLoadingFailed()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toastLoadingFailed() {
|
||||||
|
Toast.makeText(this@AlbumTagEditorActivity, R.string.could_not_download_album_cover, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchImageOnWeb() {
|
||||||
|
searchWebFor(albumText.text.toString(), albumArtistText.text.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteImage() {
|
||||||
|
setImageBitmap(BitmapFactory.decodeResource(resources, R.drawable.default_album_art), ATHUtil.resolveColor(this, R.attr.defaultFooterColor))
|
||||||
|
deleteAlbumArt = true
|
||||||
|
dataChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun save() {
|
override fun save() {
|
||||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||||
fieldKeyValueMap[FieldKey.ALBUM] = binding.albumText.text.toString()
|
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
||||||
//android seems not to recognize album_artist field so we additionally write the normal artist field
|
//android seems not to recognize album_artist field so we additionally write the normal artist field
|
||||||
fieldKeyValueMap[FieldKey.ARTIST] = binding.albumArtistText.text.toString()
|
fieldKeyValueMap[FieldKey.ARTIST] = albumArtistText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = binding.albumArtistText.text.toString()
|
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.GENRE] = binding.genreTitle.text.toString()
|
fieldKeyValueMap[FieldKey.GENRE] = genreTitle.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.YEAR] = binding.yearTitle.text.toString()
|
fieldKeyValueMap[FieldKey.YEAR] = yearTitle.text.toString()
|
||||||
|
|
||||||
writeValuesToFiles(
|
writeValuesToFiles(fieldKeyValueMap, if (deleteAlbumArt) AbsTagEditorActivity.ArtworkInfo(id, null)
|
||||||
fieldKeyValueMap,
|
else if (albumArtBitmap == null) null else AbsTagEditorActivity.ArtworkInfo(id, albumArtBitmap!!))
|
||||||
when {
|
|
||||||
deleteAlbumArt -> ArtworkInfo(id, null)
|
|
||||||
albumArtBitmap == null -> null
|
|
||||||
else -> ArtworkInfo(id, albumArtBitmap!!)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSongPaths(): List<String> {
|
override fun getSongPaths(): List<String> {
|
||||||
return repository.albumById(id).songs
|
val songs = AlbumLoader.getAlbum(this, id).songs
|
||||||
.map(Song::data)
|
val paths = ArrayList<String>(songs!!.size)
|
||||||
|
for (song in songs) {
|
||||||
|
paths.add(song.data)
|
||||||
|
}
|
||||||
|
return paths
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSongUris(): List<Uri> = repository.albumById(id).songs.map {
|
|
||||||
MusicUtil.getSongFileUri(it.id)
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
dataChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setColors(color: Int) {
|
override fun setColors(color: Int) {
|
||||||
super.setColors(color)
|
super.setColors(color)
|
||||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
|
||||||
ColorStateList.valueOf(
|
|
||||||
MaterialValueHelper.getPrimaryTextColor(
|
|
||||||
this,
|
|
||||||
color.isColorLight
|
|
||||||
)
|
|
||||||
).also {
|
|
||||||
saveFab.iconTint = it
|
|
||||||
saveFab.setTextColor(it)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override val editorImage: ImageView
|
|
||||||
get() = binding.editorImage
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
|
|
@ -1,214 +1,137 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.activities.tageditor
|
package code.name.monkey.retromusic.activities.tageditor
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.text.Editable
|
||||||
import android.widget.ImageView
|
import android.text.TextWatcher
|
||||||
import android.widget.Toast
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.TintHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding
|
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||||
import code.name.monkey.retromusic.extensions.*
|
import code.name.monkey.retromusic.loaders.SongLoader
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
|
import kotlinx.android.synthetic.main.activity_song_tag_editor.*
|
||||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
|
||||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
|
||||||
import code.name.monkey.retromusic.repository.SongRepository
|
|
||||||
import code.name.monkey.retromusic.util.ImageUtil
|
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
|
||||||
import code.name.monkey.retromusic.util.logD
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
|
||||||
import com.bumptech.glide.request.target.ImageViewTarget
|
|
||||||
import com.bumptech.glide.request.transition.Transition
|
|
||||||
import com.google.android.material.shape.MaterialShapeDrawable
|
|
||||||
import org.jaudiotagger.tag.FieldKey
|
import org.jaudiotagger.tag.FieldKey
|
||||||
import org.koin.android.ext.android.inject
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>() {
|
|
||||||
|
|
||||||
override val bindingInflater: (LayoutInflater) -> ActivitySongTagEditorBinding =
|
class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||||
ActivitySongTagEditorBinding::inflate
|
|
||||||
|
|
||||||
|
override val contentViewLayout: Int
|
||||||
|
get() = R.layout.activity_song_tag_editor
|
||||||
|
|
||||||
private val songRepository by inject<SongRepository>()
|
private fun setupToolbar() {
|
||||||
|
appBarLayout.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
|
||||||
private var albumArtBitmap: Bitmap? = null
|
toolbar.apply {
|
||||||
private var deleteAlbumArt: Boolean = false
|
setBackgroundColor(ATHUtil.resolveColor(this@SongTagEditorActivity, R.attr.colorPrimary))
|
||||||
|
navigationIcon = TintHelper.createTintedDrawable(ContextCompat.getDrawable(context, R.drawable.ic_keyboard_backspace_black_24dp), ThemeStore.textColorPrimary(context))
|
||||||
|
setNavigationOnClickListener { onBackPressed() }
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
}
|
||||||
|
title = null
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
setNoImageMode()
|
||||||
setUpViews()
|
setUpViews()
|
||||||
setSupportActionBar(binding.toolbar)
|
setupToolbar()
|
||||||
binding.appBarLayout?.statusBarForeground =
|
setStatusbarColorAuto()
|
||||||
MaterialShapeDrawable.createWithElevationOverlay(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
private fun setUpViews() {
|
private fun setUpViews() {
|
||||||
fillViewsWithFileTags()
|
fillViewsWithFileTags()
|
||||||
binding.songTextContainer.setTint(false)
|
MaterialUtil.setTint(songTextContainer, false)
|
||||||
binding.composerContainer.setTint(false)
|
MaterialUtil.setTint(composerContainer, false)
|
||||||
binding.albumTextContainer.setTint(false)
|
MaterialUtil.setTint(albumTextContainer, false)
|
||||||
binding.artistContainer.setTint(false)
|
MaterialUtil.setTint(artistContainer, false)
|
||||||
binding.albumArtistContainer.setTint(false)
|
MaterialUtil.setTint(albumArtistContainer, false)
|
||||||
binding.yearContainer.setTint(false)
|
MaterialUtil.setTint(yearContainer, false)
|
||||||
binding.genreContainer.setTint(false)
|
MaterialUtil.setTint(genreContainer, false)
|
||||||
binding.trackNumberContainer.setTint(false)
|
MaterialUtil.setTint(trackNumberContainer, false)
|
||||||
binding.discNumberContainer.setTint(false)
|
MaterialUtil.setTint(lyricsContainer, false)
|
||||||
binding.lyricsContainer.setTint(false)
|
|
||||||
|
|
||||||
binding.songText.appHandleColor().doAfterTextChanged { dataChanged() }
|
songText.appHandleColor().addTextChangedListener(this)
|
||||||
binding.albumText.appHandleColor().doAfterTextChanged { dataChanged() }
|
albumText.appHandleColor().addTextChangedListener(this)
|
||||||
binding.albumArtistText.appHandleColor().doAfterTextChanged { dataChanged() }
|
albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||||
binding.artistText.appHandleColor().doAfterTextChanged { dataChanged() }
|
artistText.appHandleColor().addTextChangedListener(this)
|
||||||
binding.genreText.appHandleColor().doAfterTextChanged { dataChanged() }
|
genreText.appHandleColor().addTextChangedListener(this)
|
||||||
binding.yearText.appHandleColor().doAfterTextChanged { dataChanged() }
|
yearText.appHandleColor().addTextChangedListener(this)
|
||||||
binding.trackNumberText.appHandleColor().doAfterTextChanged { dataChanged() }
|
trackNumberText.appHandleColor().addTextChangedListener(this)
|
||||||
binding.discNumberText.appHandleColor().doAfterTextChanged { dataChanged() }
|
lyricsText.appHandleColor().addTextChangedListener(this)
|
||||||
binding.lyricsText.appHandleColor().doAfterTextChanged { dataChanged() }
|
songComposerText.appHandleColor().addTextChangedListener(this)
|
||||||
binding.songComposerText.appHandleColor().doAfterTextChanged { dataChanged() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillViewsWithFileTags() {
|
private fun fillViewsWithFileTags() {
|
||||||
binding.songText.setText(songTitle)
|
songText.setText(songTitle)
|
||||||
binding.albumArtistText.setText(albumArtist)
|
albumArtistText.setText(albumArtist)
|
||||||
binding.albumText.setText(albumTitle)
|
albumText.setText(albumTitle)
|
||||||
binding.artistText.setText(artistName)
|
artistText.setText(artistName)
|
||||||
binding.genreText.setText(genreName)
|
genreText.setText(genreName)
|
||||||
binding.yearText.setText(songYear)
|
yearText.setText(songYear)
|
||||||
binding.trackNumberText.setText(trackNumber)
|
trackNumberText.setText(trackNumber)
|
||||||
binding.discNumberText.setText(discNumber)
|
lyricsText.setText(lyrics)
|
||||||
binding.lyricsText.setText(lyrics)
|
songComposerText.setText(composer)
|
||||||
binding.songComposerText.setText(composer)
|
|
||||||
logD(songTitle + songYear)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadCurrentImage() {
|
override fun loadCurrentImage() {
|
||||||
val bitmap = albumArt
|
|
||||||
setImageBitmap(
|
}
|
||||||
bitmap,
|
|
||||||
RetroColorUtil.getColor(
|
override fun getImageFromLastFM() {
|
||||||
RetroColorUtil.generatePalette(bitmap),
|
|
||||||
defaultFooterColor()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
deleteAlbumArt = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchImageOnWeb() {
|
override fun searchImageOnWeb() {
|
||||||
searchWebFor(binding.songText.text.toString(), binding.artistText.text.toString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteImage() {
|
override fun deleteImage() {
|
||||||
setImageBitmap(
|
|
||||||
BitmapFactory.decodeResource(resources, R.drawable.default_audio_art),
|
|
||||||
defaultFooterColor()
|
|
||||||
)
|
|
||||||
deleteAlbumArt = true
|
|
||||||
dataChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setColors(color: Int) {
|
|
||||||
super.setColors(color)
|
|
||||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
|
||||||
ColorStateList.valueOf(
|
|
||||||
MaterialValueHelper.getPrimaryTextColor(
|
|
||||||
this,
|
|
||||||
color.isColorLight
|
|
||||||
)
|
|
||||||
).also {
|
|
||||||
saveFab.iconTint = it
|
|
||||||
saveFab.setTextColor(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun save() {
|
override fun save() {
|
||||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||||
fieldKeyValueMap[FieldKey.TITLE] = binding.songText.text.toString()
|
fieldKeyValueMap[FieldKey.TITLE] = songText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.ALBUM] = binding.albumText.text.toString()
|
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.ARTIST] = binding.artistText.text.toString()
|
fieldKeyValueMap[FieldKey.ARTIST] = artistText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.GENRE] = binding.genreText.text.toString()
|
fieldKeyValueMap[FieldKey.GENRE] = genreText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.YEAR] = binding.yearText.text.toString()
|
fieldKeyValueMap[FieldKey.YEAR] = yearText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.TRACK] = binding.trackNumberText.text.toString()
|
fieldKeyValueMap[FieldKey.TRACK] = trackNumberText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.DISC_NO] = binding.discNumberText.text.toString()
|
fieldKeyValueMap[FieldKey.LYRICS] = lyricsText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.LYRICS] = binding.lyricsText.text.toString()
|
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = binding.albumArtistText.text.toString()
|
fieldKeyValueMap[FieldKey.COMPOSER] = songComposerText.text.toString()
|
||||||
fieldKeyValueMap[FieldKey.COMPOSER] = binding.songComposerText.text.toString()
|
writeValuesToFiles(fieldKeyValueMap, null)
|
||||||
writeValuesToFiles(
|
|
||||||
fieldKeyValueMap, when {
|
|
||||||
deleteAlbumArt -> ArtworkInfo(id, null)
|
|
||||||
albumArtBitmap == null -> null
|
|
||||||
else -> ArtworkInfo(id, albumArtBitmap!!)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSongPaths(): List<String> = listOf(songRepository.song(id).data)
|
override fun getSongPaths(): List<String> {
|
||||||
|
val paths = ArrayList<String>(1)
|
||||||
override fun getSongUris(): List<Uri> = listOf(MusicUtil.getSongFileUri(id))
|
paths.add(SongLoader.getSong(this, id).data)
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||||
Glide.with(this@SongTagEditorActivity)
|
|
||||||
.asBitmapPalette()
|
}
|
||||||
.load(selectedFile)
|
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||||
.skipMemoryCache(true)
|
|
||||||
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
|
}
|
||||||
override fun onResourceReady(
|
|
||||||
resource: BitmapPaletteWrapper,
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
transition: Transition<in BitmapPaletteWrapper>?
|
|
||||||
) {
|
}
|
||||||
RetroColorUtil.getColor(resource.palette, Color.TRANSPARENT)
|
|
||||||
albumArtBitmap = resource.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
override fun afterTextChanged(s: Editable) {
|
||||||
setImageBitmap(
|
|
||||||
albumArtBitmap,
|
|
||||||
RetroColorUtil.getColor(
|
|
||||||
resource.palette,
|
|
||||||
defaultFooterColor()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
deleteAlbumArt = false
|
|
||||||
dataChanged()
|
dataChanged()
|
||||||
setResult(Activity.RESULT_OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
|
||||||
super.onLoadFailed(errorDrawable)
|
|
||||||
showToast(R.string.error_load_failed, Toast.LENGTH_LONG)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setResource(resource: BitmapPaletteWrapper?) {}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = SongTagEditorActivity::class.java.simpleName
|
val TAG: String = SongTagEditorActivity::class.java.simpleName
|
||||||
}
|
}
|
||||||
|
|
||||||
override val editorImage: ImageView
|
|
||||||
get() = binding.editorImage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,204 +0,0 @@
|
||||||
package code.name.monkey.retromusic.activities.tageditor
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.media.MediaScannerConnection
|
|
||||||
import android.os.Build
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.extensions.showToast
|
|
||||||
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener
|
|
||||||
import code.name.monkey.retromusic.model.AudioTagInfo
|
|
||||||
import code.name.monkey.retromusic.util.MusicUtil.createAlbumArtFile
|
|
||||||
import code.name.monkey.retromusic.util.MusicUtil.deleteAlbumArt
|
|
||||||
import code.name.monkey.retromusic.util.MusicUtil.insertAlbumArt
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.jaudiotagger.audio.AudioFileIO
|
|
||||||
import org.jaudiotagger.audio.exceptions.CannotReadException
|
|
||||||
import org.jaudiotagger.audio.exceptions.CannotWriteException
|
|
||||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
|
|
||||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
|
|
||||||
import org.jaudiotagger.tag.FieldDataInvalidException
|
|
||||||
import org.jaudiotagger.tag.TagException
|
|
||||||
import org.jaudiotagger.tag.images.AndroidArtwork
|
|
||||||
import org.jaudiotagger.tag.images.Artwork
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
class TagWriter {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
suspend fun scan(context: Context, toBeScanned: List<String?>?) {
|
|
||||||
if (toBeScanned.isNullOrEmpty()) {
|
|
||||||
Log.i("scan", "scan: Empty")
|
|
||||||
context.showToast("Scan file from folder")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
MediaScannerConnection.scanFile(
|
|
||||||
context,
|
|
||||||
toBeScanned.toTypedArray(),
|
|
||||||
null,
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
if (context is Activity) UpdateToastMediaScannerCompletionListener(
|
|
||||||
context, toBeScanned
|
|
||||||
) else null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
var artwork: Artwork? = null
|
|
||||||
var albumArtFile: File? = null
|
|
||||||
if (info.artworkInfo?.artwork != null) {
|
|
||||||
try {
|
|
||||||
albumArtFile = createAlbumArtFile(context).canonicalFile
|
|
||||||
info.artworkInfo.artwork.compress(
|
|
||||||
Bitmap.CompressFormat.JPEG,
|
|
||||||
100,
|
|
||||||
albumArtFile.outputStream()
|
|
||||||
)
|
|
||||||
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var wroteArtwork = false
|
|
||||||
var deletedArtwork = false
|
|
||||||
for (filePath in info.filePaths!!) {
|
|
||||||
try {
|
|
||||||
val audioFile = AudioFileIO.read(File(filePath))
|
|
||||||
val tag = audioFile.tagOrCreateAndSetDefault
|
|
||||||
if (info.fieldKeyValueMap != null) {
|
|
||||||
for ((key, value) in info.fieldKeyValueMap) {
|
|
||||||
try {
|
|
||||||
tag.setField(key, value)
|
|
||||||
} catch (e: FieldDataInvalidException) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
context.showToast(R.string.could_not_write_tags_to_file)
|
|
||||||
}
|
|
||||||
return@withContext listOf<File>()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (info.artworkInfo != null) {
|
|
||||||
if (info.artworkInfo.artwork == null) {
|
|
||||||
tag.deleteArtworkField()
|
|
||||||
deletedArtwork = true
|
|
||||||
} else if (artwork != null) {
|
|
||||||
tag.deleteArtworkField()
|
|
||||||
tag.setField(artwork)
|
|
||||||
wroteArtwork = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
audioFile.commit()
|
|
||||||
} catch (e: CannotReadException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: CannotWriteException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: TagException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: ReadOnlyFileException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: InvalidAudioFrameException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (wroteArtwork) {
|
|
||||||
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
|
|
||||||
} else if (deletedArtwork) {
|
|
||||||
deleteAlbumArt(context, info.artworkInfo!!.albumId)
|
|
||||||
}
|
|
||||||
scan(context, info.filePaths)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.R)
|
|
||||||
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> =
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val cacheFiles = mutableListOf<File>()
|
|
||||||
var artwork: Artwork? = null
|
|
||||||
var albumArtFile: File? = null
|
|
||||||
if (info.artworkInfo?.artwork != null) {
|
|
||||||
try {
|
|
||||||
albumArtFile = createAlbumArtFile(context).canonicalFile
|
|
||||||
info.artworkInfo.artwork.compress(
|
|
||||||
Bitmap.CompressFormat.JPEG,
|
|
||||||
100,
|
|
||||||
albumArtFile.outputStream()
|
|
||||||
)
|
|
||||||
artwork = AndroidArtwork.createArtworkFromFile(albumArtFile)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var wroteArtwork = false
|
|
||||||
var deletedArtwork = false
|
|
||||||
for (filePath in info.filePaths!!) {
|
|
||||||
try {
|
|
||||||
val originFile = File(filePath)
|
|
||||||
val cacheFile = File(context.cacheDir, originFile.name)
|
|
||||||
cacheFiles.add(cacheFile)
|
|
||||||
originFile.inputStream().use { input ->
|
|
||||||
cacheFile.outputStream().use { output ->
|
|
||||||
input.copyTo(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val audioFile = AudioFileIO.read(cacheFile)
|
|
||||||
val tag = audioFile.tagOrCreateAndSetDefault
|
|
||||||
if (info.fieldKeyValueMap != null) {
|
|
||||||
for ((key, value) in info.fieldKeyValueMap) {
|
|
||||||
try {
|
|
||||||
tag.setField(key, value)
|
|
||||||
} catch (e: FieldDataInvalidException) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
context.showToast(R.string.could_not_write_tags_to_file)
|
|
||||||
}
|
|
||||||
return@withContext listOf<File>()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (info.artworkInfo != null) {
|
|
||||||
if (info.artworkInfo.artwork == null) {
|
|
||||||
tag.deleteArtworkField()
|
|
||||||
deletedArtwork = true
|
|
||||||
} else if (artwork != null) {
|
|
||||||
tag.deleteArtworkField()
|
|
||||||
tag.setField(artwork)
|
|
||||||
wroteArtwork = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
audioFile.commit()
|
|
||||||
} catch (e: CannotReadException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: CannotWriteException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: TagException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: ReadOnlyFileException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: InvalidAudioFrameException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (wroteArtwork) {
|
|
||||||
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
|
|
||||||
} else if (deletedArtwork) {
|
|
||||||
deleteAlbumArt(context, info.artworkInfo!!.albumId)
|
|
||||||
}
|
|
||||||
cacheFiles
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
package code.name.monkey.retromusic.activities.tageditor;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.media.MediaScannerConnection;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
|
import org.jaudiotagger.audio.AudioFile;
|
||||||
|
import org.jaudiotagger.audio.AudioFileIO;
|
||||||
|
import org.jaudiotagger.tag.FieldKey;
|
||||||
|
import org.jaudiotagger.tag.Tag;
|
||||||
|
import org.jaudiotagger.tag.images.Artwork;
|
||||||
|
import org.jaudiotagger.tag.images.ArtworkFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.misc.DialogAsyncTask;
|
||||||
|
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
|
||||||
|
import code.name.monkey.retromusic.util.MusicUtil;
|
||||||
|
import code.name.monkey.retromusic.util.SAFUtil;
|
||||||
|
|
||||||
|
public class WriteTagsAsyncTask extends
|
||||||
|
DialogAsyncTask<WriteTagsAsyncTask.LoadingInfo, Integer, String[]> {
|
||||||
|
|
||||||
|
private WeakReference<Activity> activity;
|
||||||
|
|
||||||
|
public WriteTagsAsyncTask(@NonNull Activity activity) {
|
||||||
|
super(activity);
|
||||||
|
this.activity = new WeakReference<>(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String[] doInBackground(LoadingInfo... params) {
|
||||||
|
try {
|
||||||
|
LoadingInfo info = params[0];
|
||||||
|
|
||||||
|
Artwork artwork = null;
|
||||||
|
File albumArtFile = null;
|
||||||
|
if (info.artworkInfo != null && info.artworkInfo.getArtwork() != null) {
|
||||||
|
try {
|
||||||
|
albumArtFile = MusicUtil.createAlbumArtFile().getCanonicalFile();
|
||||||
|
info.artworkInfo.getArtwork()
|
||||||
|
.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
|
||||||
|
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
boolean wroteArtwork = false;
|
||||||
|
boolean deletedArtwork = false;
|
||||||
|
for (String filePath : info.filePaths) {
|
||||||
|
publishProgress(++counter, info.filePaths.size());
|
||||||
|
try {
|
||||||
|
Uri safUri = null;
|
||||||
|
if (filePath.contains(SAFUtil.SEPARATOR)) {
|
||||||
|
String[] fragments = filePath.split(SAFUtil.SEPARATOR);
|
||||||
|
filePath = fragments[0];
|
||||||
|
safUri = Uri.parse(fragments[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioFile audioFile = AudioFileIO.read(new File(filePath));
|
||||||
|
Tag tag = audioFile.getTagOrCreateAndSetDefault();
|
||||||
|
|
||||||
|
if (info.fieldKeyValueMap != null) {
|
||||||
|
for (Map.Entry<FieldKey, String> entry : info.fieldKeyValueMap.entrySet()) {
|
||||||
|
try {
|
||||||
|
tag.setField(entry.getKey(), entry.getValue());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.artworkInfo != null) {
|
||||||
|
if (info.artworkInfo.getArtwork() == null) {
|
||||||
|
tag.deleteArtworkField();
|
||||||
|
deletedArtwork = true;
|
||||||
|
} else if (artwork != null) {
|
||||||
|
tag.deleteArtworkField();
|
||||||
|
tag.setField(artwork);
|
||||||
|
wroteArtwork = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
SAFUtil.write(activity, audioFile, safUri);
|
||||||
|
|
||||||
|
} catch (@NonNull Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context context = getContext();
|
||||||
|
if (context != null) {
|
||||||
|
if (wroteArtwork) {
|
||||||
|
MusicUtil.insertAlbumArt(context, info.artworkInfo.getAlbumId(), albumArtFile.getPath());
|
||||||
|
} else if (deletedArtwork) {
|
||||||
|
MusicUtil.deleteAlbumArt(context, info.artworkInfo.getAlbumId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<String> paths = info.filePaths;
|
||||||
|
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
|
||||||
|
paths = new ArrayList<>(info.filePaths.size());
|
||||||
|
for (String path : info.filePaths) {
|
||||||
|
if (path.contains(SAFUtil.SEPARATOR))
|
||||||
|
path = path.split(SAFUtil.SEPARATOR)[0];
|
||||||
|
paths.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths.toArray(new String[paths.size()]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String[] toBeScanned) {
|
||||||
|
super.onPostExecute(toBeScanned);
|
||||||
|
scan(toBeScanned);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled(String[] toBeScanned) {
|
||||||
|
super.onCancelled(toBeScanned);
|
||||||
|
scan(toBeScanned);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scan(String[] toBeScanned) {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
if (activity != null) {
|
||||||
|
MediaScannerConnection.scanFile(activity, toBeScanned, null, new UpdateToastMediaScannerCompletionListener(activity, toBeScanned));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Dialog createDialog(@NonNull Context context) {
|
||||||
|
return new MaterialAlertDialogBuilder(context)
|
||||||
|
.setTitle(R.string.saving_changes)
|
||||||
|
.setCancelable(false)
|
||||||
|
.setView(R.layout.loading)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) {
|
||||||
|
super.onProgressUpdate(dialog, values);
|
||||||
|
//((MaterialDialog) dialog).setMaxProgress(values[1]);
|
||||||
|
//((MaterialDialog) dialog).setProgress(values[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LoadingInfo {
|
||||||
|
|
||||||
|
final Collection<String> filePaths;
|
||||||
|
@Nullable
|
||||||
|
final Map<FieldKey, String> fieldKeyValueMap;
|
||||||
|
@Nullable
|
||||||
|
private AbsTagEditorActivity.ArtworkInfo artworkInfo;
|
||||||
|
|
||||||
|
public LoadingInfo(Collection<String> filePaths,
|
||||||
|
@Nullable Map<FieldKey, String> fieldKeyValueMap,
|
||||||
|
@Nullable AbsTagEditorActivity.ArtworkInfo artworkInfo) {
|
||||||
|
this.filePaths = filePaths;
|
||||||
|
this.fieldKeyValueMap = fieldKeyValueMap;
|
||||||
|
this.artworkInfo = artworkInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Hemanth Savarala.
|
||||||
|
*
|
||||||
|
* Licensed under the GNU General Public License v3
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package code.name.monkey.retromusic.adapter;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.checkbox.MaterialCheckBox;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import code.name.monkey.appthemehelper.ThemeStore;
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.model.CategoryInfo;
|
||||||
|
import code.name.monkey.retromusic.util.SwipeAndDragHelper;
|
||||||
|
|
||||||
|
public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapter.ViewHolder> implements SwipeAndDragHelper.ActionCompletionContract {
|
||||||
|
private List<CategoryInfo> categoryInfos;
|
||||||
|
private ItemTouchHelper touchHelper;
|
||||||
|
|
||||||
|
public CategoryInfoAdapter(@NonNull List<CategoryInfo> categoryInfos) {
|
||||||
|
this.categoryInfos = categoryInfos;
|
||||||
|
SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this);
|
||||||
|
touchHelper = new ItemTouchHelper(swipeAndDragHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public CategoryInfoAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.preference_dialog_library_categories_listitem, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull CategoryInfoAdapter.ViewHolder holder, int position) {
|
||||||
|
CategoryInfo categoryInfo = categoryInfos.get(position);
|
||||||
|
|
||||||
|
holder.checkBox.setChecked(categoryInfo.visible);
|
||||||
|
holder.title.setText(holder.title.getResources().getString(categoryInfo.category.stringRes));
|
||||||
|
|
||||||
|
holder.itemView.setOnClickListener(v -> {
|
||||||
|
if (!(categoryInfo.visible && isLastCheckedCategory(categoryInfo))) {
|
||||||
|
categoryInfo.visible = !categoryInfo.visible;
|
||||||
|
holder.checkBox.setChecked(categoryInfo.visible);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(holder.itemView.getContext(), R.string.you_have_to_select_at_least_one_category, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
holder.dragView.setOnTouchListener((view, event) -> {
|
||||||
|
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||||
|
touchHelper.startDrag(holder);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return categoryInfos.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewMoved(int oldPosition, int newPosition) {
|
||||||
|
CategoryInfo categoryInfo = categoryInfos.get(oldPosition);
|
||||||
|
categoryInfos.remove(oldPosition);
|
||||||
|
categoryInfos.add(newPosition, categoryInfo);
|
||||||
|
notifyItemMoved(oldPosition, newPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attachToRecyclerView(RecyclerView recyclerView) {
|
||||||
|
touchHelper.attachToRecyclerView(recyclerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public List<CategoryInfo> getCategoryInfos() {
|
||||||
|
return categoryInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategoryInfos(@NonNull List<CategoryInfo> categoryInfos) {
|
||||||
|
this.categoryInfos = categoryInfos;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLastCheckedCategory(CategoryInfo categoryInfo) {
|
||||||
|
if (categoryInfo.visible) {
|
||||||
|
for (CategoryInfo c : categoryInfos) {
|
||||||
|
if (c != categoryInfo && c.visible) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
MaterialCheckBox checkBox;
|
||||||
|
TextView title;
|
||||||
|
View dragView;
|
||||||
|
|
||||||
|
ViewHolder(View view) {
|
||||||
|
super(view);
|
||||||
|
checkBox = view.findViewById(R.id.checkbox);
|
||||||
|
checkBox.setButtonTintList(ColorStateList.valueOf(ThemeStore.Companion.accentColor(checkBox.getContext())));
|
||||||
|
title = view.findViewById(R.id.title);
|
||||||
|
dragView = view.findViewById(R.id.drag_view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 Hemanth Savarala.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it under
|
|
||||||
* the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.adapter
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.databinding.PreferenceDialogLibraryCategoriesListitemBinding
|
|
||||||
import code.name.monkey.retromusic.extensions.showToast
|
|
||||||
import code.name.monkey.retromusic.model.CategoryInfo
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
|
||||||
import code.name.monkey.retromusic.util.SwipeAndDragHelper
|
|
||||||
import code.name.monkey.retromusic.util.SwipeAndDragHelper.ActionCompletionContract
|
|
||||||
|
|
||||||
class CategoryInfoAdapter : RecyclerView.Adapter<code.name.monkey.retromusic.adapter.CategoryInfoAdapter.ViewHolder>(),
|
|
||||||
ActionCompletionContract {
|
|
||||||
var categoryInfos: MutableList<CategoryInfo> =
|
|
||||||
PreferenceUtil.libraryCategory.toMutableList()
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
private val touchHelper: ItemTouchHelper
|
|
||||||
fun attachToRecyclerView(recyclerView: RecyclerView?) {
|
|
||||||
touchHelper.attachToRecyclerView(recyclerView)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
|
||||||
return categoryInfos.size
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
||||||
val categoryInfo = categoryInfos[position]
|
|
||||||
holder.binding.checkbox.isChecked = categoryInfo.visible
|
|
||||||
holder.binding.title.text =
|
|
||||||
holder.binding.title.resources.getString(categoryInfo.category.stringRes)
|
|
||||||
holder.itemView.setOnClickListener {
|
|
||||||
if (!(categoryInfo.visible && isLastCheckedCategory(categoryInfo))) {
|
|
||||||
categoryInfo.visible = !categoryInfo.visible
|
|
||||||
holder.binding.checkbox.isChecked = categoryInfo.visible
|
|
||||||
} else {
|
|
||||||
holder.itemView.context.showToast(R.string.you_have_to_select_at_least_one_category)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
holder.binding.dragView.setOnTouchListener { _: View?, event: MotionEvent ->
|
|
||||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
|
||||||
touchHelper.startDrag(holder)
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
|
||||||
parent: ViewGroup, viewType: Int
|
|
||||||
): ViewHolder {
|
|
||||||
return ViewHolder(
|
|
||||||
PreferenceDialogLibraryCategoriesListitemBinding.inflate(
|
|
||||||
LayoutInflater.from(
|
|
||||||
parent.context
|
|
||||||
), parent, false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewMoved(oldPosition: Int, newPosition: Int) {
|
|
||||||
val categoryInfo = categoryInfos[oldPosition]
|
|
||||||
categoryInfos.removeAt(oldPosition)
|
|
||||||
categoryInfos.add(newPosition, categoryInfo)
|
|
||||||
notifyItemMoved(oldPosition, newPosition)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isLastCheckedCategory(categoryInfo: CategoryInfo): Boolean {
|
|
||||||
if (categoryInfo.visible) {
|
|
||||||
for (c in categoryInfos) {
|
|
||||||
if (c !== categoryInfo && c.visible) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewHolder(val binding: PreferenceDialogLibraryCategoriesListitemBinding) :
|
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
binding.checkbox.buttonTintList =
|
|
||||||
ColorStateList.valueOf(accentColor(binding.checkbox.context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
val swipeAndDragHelper = SwipeAndDragHelper(this)
|
|
||||||
touchHelper = ItemTouchHelper(swipeAndDragHelper)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package code.name.monkey.retromusic.adapter
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.model.Contributor
|
||||||
|
import code.name.monkey.retromusic.util.RetroUtil.openUrl
|
||||||
|
import code.name.monkey.retromusic.views.CircularImageView
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
|
||||||
|
class ContributorAdapter(
|
||||||
|
private var contributors: List<Contributor>
|
||||||
|
) : RecyclerView.Adapter<ContributorAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
return if (viewType == HEADER) {
|
||||||
|
ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_contributor_header, parent, false))
|
||||||
|
} else
|
||||||
|
ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_contributor, parent, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val HEADER: Int = 0
|
||||||
|
const val ITEM: Int = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return if (position == 0) {
|
||||||
|
HEADER
|
||||||
|
} else {
|
||||||
|
ITEM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val contributor = contributors[position]
|
||||||
|
holder.bindData(contributor)
|
||||||
|
holder.itemView.setOnClickListener {
|
||||||
|
openUrl(it?.context as Activity, contributors[position].link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return contributors.size
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
val title: TextView = itemView.findViewById(R.id.title)
|
||||||
|
val text: TextView = itemView.findViewById(R.id.text)
|
||||||
|
val image: CircularImageView = itemView.findViewById(R.id.icon)
|
||||||
|
|
||||||
|
internal fun bindData(contributor: Contributor) {
|
||||||
|
title.text = contributor.name
|
||||||
|
text.text = contributor.summary
|
||||||
|
println(contributor.profileImage)
|
||||||
|
Glide.with(image.context)
|
||||||
|
.load(contributor.profileImage)
|
||||||
|
.error(R.drawable.ic_account_white_24dp)
|
||||||
|
.placeholder(R.drawable.ic_account_white_24dp)
|
||||||
|
.dontAnimate()
|
||||||
|
.into(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,37 +1,14 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.adapter
|
package code.name.monkey.retromusic.adapter
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.app.Activity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewOutlineProvider
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.databinding.ItemGenreBinding
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
|
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
|
||||||
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
|
||||||
import code.name.monkey.retromusic.model.Genre
|
import code.name.monkey.retromusic.model.Genre
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,74 +16,48 @@ import java.util.*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class GenreAdapter(
|
class GenreAdapter(
|
||||||
private val activity: FragmentActivity,
|
private val mActivity: Activity,
|
||||||
var dataSet: List<Genre>,
|
dataSet: ArrayList<Genre>,
|
||||||
private val listener: IGenreClickListener
|
private val mItemLayoutRes: Int
|
||||||
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
|
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
|
||||||
|
var dataSet = ArrayList<Genre>()
|
||||||
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.setHasStableIds(true)
|
this.dataSet = dataSet
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long {
|
|
||||||
return dataSet[position].id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
return ViewHolder(ItemGenreBinding.inflate(LayoutInflater.from(activity), parent, false))
|
return ViewHolder(LayoutInflater.from(mActivity).inflate(mItemLayoutRes, parent, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val genre = dataSet[position]
|
val genre = dataSet[position]
|
||||||
holder.binding.title.text = genre.name
|
if (holder.title != null) {
|
||||||
holder.binding.text.text = String.format(
|
holder.title!!.text = genre.name
|
||||||
Locale.getDefault(),
|
|
||||||
"%d %s",
|
|
||||||
genre.songCount,
|
|
||||||
if (genre.songCount > 1) activity.getString(R.string.songs) else activity.getString(R.string.song)
|
|
||||||
)
|
|
||||||
loadGenreImage(genre, holder)
|
|
||||||
}
|
}
|
||||||
|
if (holder.text != null) {
|
||||||
private fun loadGenreImage(genre: Genre, holder: GenreAdapter.ViewHolder) {
|
holder.text!!.text = String.format(Locale.getDefault(), "%d %s", genre.songCount, if (genre.songCount > 1)
|
||||||
val genreSong = MusicUtil.songByGenre(genre.id)
|
mActivity.getString(R.string.songs)
|
||||||
Glide.with(activity)
|
else
|
||||||
.asBitmapPalette()
|
mActivity.getString(R.string.song))
|
||||||
.songCoverOptions(genreSong)
|
|
||||||
.load(RetroGlideExtension.getSongModel(genreSong))
|
|
||||||
.into(object : RetroMusicColoredTarget(holder.binding.image) {
|
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
|
||||||
setColors(holder, colors)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
// Just for a bit of shadow around image
|
|
||||||
holder.binding.image.outlineProvider = ViewOutlineProvider.BOUNDS
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setColors(holder: ViewHolder, color: MediaNotificationProcessor) {
|
|
||||||
holder.binding.imageContainerCard.setCardBackgroundColor(color.backgroundColor)
|
|
||||||
holder.binding.title.setTextColor(color.primaryTextColor)
|
|
||||||
holder.binding.text.setTextColor(color.secondaryTextColor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return dataSet.size
|
return dataSet.size
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
fun swapDataSet(list: ArrayList<Genre>) {
|
||||||
fun swapDataSet(list: List<Genre>) {
|
|
||||||
dataSet = list
|
dataSet = list
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(val binding: ItemGenreBinding) : RecyclerView.ViewHolder(binding.root),
|
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||||
View.OnClickListener {
|
|
||||||
override fun onClick(v: View?) {
|
override fun onClick(v: View?) {
|
||||||
listener.onClickGenre(dataSet[layoutPosition], itemView)
|
super.onClick(v)
|
||||||
}
|
val genre = dataSet[adapterPosition]
|
||||||
|
NavigationUtil.goToGenre(mActivity, genre)
|
||||||
init {
|
|
||||||
itemView.setOnClickListener(this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,124 +1,72 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.adapter
|
package code.name.monkey.retromusic.adapter
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.util.DisplayMetrics
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.IntDef
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.fragment.app.findFragment
|
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import code.name.monkey.retromusic.*
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.album.AlbumAdapter
|
import code.name.monkey.retromusic.adapter.album.AlbumFullWidthAdapter
|
||||||
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
||||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||||
import code.name.monkey.retromusic.fragments.home.HomeFragment
|
import code.name.monkey.retromusic.extensions.show
|
||||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
|
||||||
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.model.Home
|
import code.name.monkey.retromusic.model.Home
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Playlist
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import com.google.android.material.textview.MaterialTextView
|
||||||
|
|
||||||
class HomeAdapter(private val activity: AppCompatActivity) :
|
|
||||||
RecyclerView.Adapter<RecyclerView.ViewHolder>(), IArtistClickListener, IAlbumClickListener {
|
|
||||||
|
|
||||||
private var list = listOf<Home>()
|
class HomeAdapter(
|
||||||
|
private val activity: AppCompatActivity,
|
||||||
|
private val displayMetrics: DisplayMetrics
|
||||||
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
private var list = ArrayList<Home>()
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
return list[position].homeSection
|
return list[position].homeSection
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val layout =
|
val layout = LayoutInflater.from(activity).inflate(R.layout.section_recycler_view, parent, false)
|
||||||
LayoutInflater.from(activity).inflate(R.layout.section_recycler_view, parent, false)
|
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
|
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
|
||||||
FAVOURITES -> PlaylistViewHolder(layout)
|
PLAYLISTS -> PlaylistViewHolder(layout)
|
||||||
TOP_ALBUMS, RECENT_ALBUMS -> AlbumViewHolder(layout)
|
|
||||||
else -> {
|
else -> {
|
||||||
ArtistViewHolder(layout)
|
AlbumViewHolder(LayoutInflater.from(activity).inflate(R.layout.metal_section_recycler_view, parent, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
val home = list[position]
|
println("ViewType ${getItemViewType(position)}")
|
||||||
when (getItemViewType(position)) {
|
when (getItemViewType(position)) {
|
||||||
RECENT_ALBUMS -> {
|
RECENT_ALBUMS -> {
|
||||||
val viewHolder = holder as AlbumViewHolder
|
val viewHolder = holder as AlbumViewHolder
|
||||||
viewHolder.bindView(home)
|
viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.recent_albums, R.string.recent_added_albums)
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
|
||||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.detailListFragment,
|
|
||||||
bundleOf("type" to RECENT_ALBUMS)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TOP_ALBUMS -> {
|
TOP_ALBUMS -> {
|
||||||
val viewHolder = holder as AlbumViewHolder
|
val viewHolder = holder as AlbumViewHolder
|
||||||
viewHolder.bindView(home)
|
viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.top_albums, R.string.most_played_albums)
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
|
||||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.detailListFragment,
|
|
||||||
bundleOf("type" to TOP_ALBUMS)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RECENT_ARTISTS -> {
|
RECENT_ARTISTS -> {
|
||||||
val viewHolder = holder as ArtistViewHolder
|
val viewHolder = holder as ArtistViewHolder
|
||||||
viewHolder.bindView(home)
|
viewHolder.bindView(list[position].arrayList.toArtists(), R.string.recent_artists, R.string.recent_added_artists)
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
|
||||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.detailListFragment,
|
|
||||||
bundleOf("type" to RECENT_ARTISTS)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TOP_ARTISTS -> {
|
TOP_ARTISTS -> {
|
||||||
val viewHolder = holder as ArtistViewHolder
|
val viewHolder = holder as ArtistViewHolder
|
||||||
viewHolder.bindView(home)
|
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
viewHolder.bindView(list[position].arrayList.toArtists(), R.string.top_artists, R.string.most_played_artists)
|
||||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.detailListFragment,
|
|
||||||
bundleOf("type" to TOP_ARTISTS)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
PLAYLISTS -> {
|
||||||
FAVOURITES -> {
|
|
||||||
val viewHolder = holder as PlaylistViewHolder
|
val viewHolder = holder as PlaylistViewHolder
|
||||||
viewHolder.bindView(home)
|
viewHolder.bindView(list[position].arrayList.toPlaylist(), R.string.favorites, R.string.favorites_songs)
|
||||||
viewHolder.clickableArea.setOnClickListener {
|
|
||||||
it.findFragment<HomeFragment>().setSharedAxisXTransitions()
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.detailListFragment,
|
|
||||||
bundleOf("type" to FAVOURITES)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,87 +75,107 @@ class HomeAdapter(private val activity: AppCompatActivity) :
|
||||||
return list.size
|
return list.size
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
fun swapData(sections: ArrayList<Home>) {
|
||||||
fun swapData(sections: List<Home>) {
|
|
||||||
list = sections
|
list = sections
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
companion object {
|
||||||
|
|
||||||
|
@IntDef(RECENT_ALBUMS, TOP_ALBUMS, RECENT_ARTISTS, TOP_ARTISTS, PLAYLISTS)
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
annotation class HomeSection
|
||||||
|
|
||||||
|
const val RECENT_ALBUMS = 3
|
||||||
|
const val TOP_ALBUMS = 1
|
||||||
|
const val RECENT_ARTISTS = 2
|
||||||
|
const val TOP_ARTISTS = 0
|
||||||
|
const val PLAYLISTS = 4
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) {
|
private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||||
fun bindView(home: Home) {
|
fun bindView(list: ArrayList<Album>, titleRes: Int, subtitleRes: Int) {
|
||||||
title.setText(home.titleRes)
|
if (list.isNotEmpty()) {
|
||||||
recyclerView.apply {
|
recyclerView.apply {
|
||||||
adapter = albumAdapter(home.arrayList as List<Album>)
|
show()
|
||||||
layoutManager = gridLayoutManager()
|
adapter = AlbumFullWidthAdapter(activity, list, displayMetrics)
|
||||||
|
}
|
||||||
|
titleContainer.show()
|
||||||
|
title.text = activity.getString(titleRes)
|
||||||
|
text.text = activity.getString(subtitleRes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||||
|
fun bindView(list: ArrayList<Artist>, titleRes: Int, subtitleRes: Int) {
|
||||||
|
if (list.isNotEmpty()) {
|
||||||
|
recyclerView.apply {
|
||||||
|
show()
|
||||||
|
layoutManager = GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false)
|
||||||
|
val artistAdapter = ArtistAdapter(activity, list,
|
||||||
|
PreferenceUtil.getInstance(activity).getHomeGridStyle(activity), false, null)
|
||||||
|
adapter = artistAdapter
|
||||||
|
}
|
||||||
|
titleContainer.show()
|
||||||
|
title.text = activity.getString(titleRes)
|
||||||
|
text.text = activity.getString(subtitleRes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) {
|
|
||||||
fun bindView(home: Home) {
|
|
||||||
title.setText(home.titleRes)
|
|
||||||
recyclerView.apply {
|
|
||||||
layoutManager = linearLayoutManager()
|
|
||||||
adapter = artistsAdapter(home.arrayList as List<Artist>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
|
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||||
fun bindView(home: Home) {
|
fun bindView(arrayList: ArrayList<Playlist>, titleRes: Int, subtitleRes: Int) {
|
||||||
title.setText(home.titleRes)
|
if (arrayList.isNotEmpty()) {
|
||||||
|
val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0])
|
||||||
|
if (songs.isNotEmpty()) {
|
||||||
recyclerView.apply {
|
recyclerView.apply {
|
||||||
val songAdapter = SongAdapter(
|
show()
|
||||||
activity,
|
val songAdapter = SongAdapter(activity, songs, R.layout.item_album_card, false, null)
|
||||||
home.arrayList as MutableList<Song>,
|
layoutManager = GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false)
|
||||||
R.layout.item_favourite_card
|
|
||||||
)
|
|
||||||
layoutManager = linearLayoutManager()
|
|
||||||
adapter = songAdapter
|
adapter = songAdapter
|
||||||
|
|
||||||
|
}
|
||||||
|
titleContainer.show()
|
||||||
|
title.text = activity.getString(titleRes)
|
||||||
|
text.text = activity.getString(subtitleRes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
open inner class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView)
|
val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView)
|
||||||
val title: AppCompatTextView = itemView.findViewById(R.id.title)
|
val titleContainer: View = itemView.findViewById(R.id.titleContainer)
|
||||||
val clickableArea: ViewGroup = itemView.findViewById(R.id.clickable_area)
|
val title: MaterialTextView = itemView.findViewById(R.id.title)
|
||||||
}
|
val text: MaterialTextView = itemView.findViewById(R.id.text)
|
||||||
|
|
||||||
private fun artistsAdapter(artists: List<Artist>) =
|
|
||||||
ArtistAdapter(activity, artists, PreferenceUtil.homeArtistGridStyle, this)
|
|
||||||
|
|
||||||
private fun albumAdapter(albums: List<Album>) =
|
|
||||||
AlbumAdapter(activity, albums, PreferenceUtil.homeAlbumGridStyle, this)
|
|
||||||
|
|
||||||
private fun gridLayoutManager() =
|
|
||||||
GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false)
|
|
||||||
|
|
||||||
private fun linearLayoutManager() =
|
|
||||||
LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
|
|
||||||
|
|
||||||
override fun onArtist(artistId: Long, view: View) {
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.artistDetailsFragment,
|
|
||||||
bundleOf(EXTRA_ARTIST_ID to artistId),
|
|
||||||
null,
|
|
||||||
FragmentNavigatorExtras(
|
|
||||||
view to artistId.toString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAlbumClick(albumId: Long, view: View) {
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.albumDetailsFragment,
|
|
||||||
bundleOf(EXTRA_ALBUM_ID to albumId),
|
|
||||||
null,
|
|
||||||
FragmentNavigatorExtras(
|
|
||||||
view to albumId.toString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun <E> ArrayList<E>.toAlbums(): ArrayList<Album> {
|
||||||
|
val arrayList = ArrayList<Album>()
|
||||||
|
for (x in this) {
|
||||||
|
arrayList.add(x as Album)
|
||||||
|
}
|
||||||
|
return arrayList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <E> ArrayList<E>.toArtists(): ArrayList<Artist> {
|
||||||
|
val arrayList = ArrayList<Artist>()
|
||||||
|
for (x in this) {
|
||||||
|
arrayList.add(x as Artist)
|
||||||
|
}
|
||||||
|
return arrayList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <E> ArrayList<E>.toPlaylist(): ArrayList<Playlist> {
|
||||||
|
val arrayList = ArrayList<Playlist>()
|
||||||
|
for (x in this) {
|
||||||
|
arrayList.add(x as Playlist)
|
||||||
|
}
|
||||||
|
return arrayList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,176 +1,91 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.adapter
|
package code.name.monkey.retromusic.adapter
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.app.ActivityOptions
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.os.bundleOf
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.core.view.isInvisible
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.retromusic.*
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.albumCoverOptions
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.artistImageOptions
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
|
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.model.Genre
|
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import java.util.*
|
import android.util.Pair as UtilPair
|
||||||
|
|
||||||
class SearchAdapter(
|
class SearchAdapter(
|
||||||
private val activity: FragmentActivity,
|
private val activity: AppCompatActivity,
|
||||||
private var dataSet: List<Any>
|
private var dataSet: List<Any>?
|
||||||
) : RecyclerView.Adapter<SearchAdapter.ViewHolder>() {
|
) : RecyclerView.Adapter<SearchAdapter.ViewHolder>() {
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
fun swapDataSet(dataSet: MutableList<Any>) {
|
||||||
fun swapDataSet(dataSet: List<Any>) {
|
|
||||||
this.dataSet = dataSet
|
this.dataSet = dataSet
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
if (dataSet[position] is Album) return ALBUM
|
if (dataSet!![position] is Album) return ALBUM
|
||||||
if (dataSet[position] is Artist) return if ((dataSet[position] as Artist).isAlbumArtist) ALBUM_ARTIST else ARTIST
|
if (dataSet!![position] is Artist) return ARTIST
|
||||||
if (dataSet[position] is Genre) return GENRE
|
return if (dataSet!![position] is Song) SONG else HEADER
|
||||||
if (dataSet[position] is PlaylistWithSongs) return PLAYLIST
|
|
||||||
return if (dataSet[position] is Song) SONG else HEADER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
return when (viewType) {
|
return if (viewType == HEADER) ViewHolder(LayoutInflater.from(activity).inflate(R.layout.sub_header, parent, false), viewType) else ViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false), viewType)
|
||||||
HEADER -> ViewHolder(
|
|
||||||
LayoutInflater.from(activity).inflate(
|
|
||||||
R.layout.sub_header,
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
), viewType
|
|
||||||
)
|
|
||||||
|
|
||||||
ALBUM, ARTIST, ALBUM_ARTIST -> ViewHolder(
|
|
||||||
LayoutInflater.from(activity).inflate(
|
|
||||||
R.layout.item_list_big,
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
), viewType
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> ViewHolder(
|
|
||||||
LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false),
|
|
||||||
viewType
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
when (getItemViewType(position)) {
|
when (getItemViewType(position)) {
|
||||||
ALBUM -> {
|
ALBUM -> {
|
||||||
holder.imageTextContainer?.isVisible = true
|
val album = dataSet?.get(position) as Album
|
||||||
val album = dataSet[position] as Album
|
|
||||||
holder.title?.text = album.title
|
holder.title?.text = album.title
|
||||||
holder.text?.text = album.artistName
|
holder.text?.text = album.artistName
|
||||||
Glide.with(activity).asDrawable().albumCoverOptions(album.safeGetFirstSong())
|
SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||||
.load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
|
.checkIgnoreMediaStore(activity).build()
|
||||||
.into(holder.image!!)
|
.into(holder.image)
|
||||||
}
|
}
|
||||||
|
|
||||||
ARTIST -> {
|
ARTIST -> {
|
||||||
holder.imageTextContainer?.isVisible = true
|
val artist = dataSet?.get(position) as Artist
|
||||||
val artist = dataSet[position] as Artist
|
|
||||||
holder.title?.text = artist.name
|
holder.title?.text = artist.name
|
||||||
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
|
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
|
||||||
Glide.with(activity).asDrawable().artistImageOptions(artist).load(
|
ArtistGlideRequest.Builder.from(Glide.with(activity), artist)
|
||||||
RetroGlideExtension.getArtistModel(artist)
|
.build().into(holder.image)
|
||||||
).into(holder.image!!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SONG -> {
|
SONG -> {
|
||||||
holder.imageTextContainer?.isVisible = true
|
val song = dataSet?.get(position) as Song
|
||||||
val song = dataSet[position] as Song
|
|
||||||
holder.title?.text = song.title
|
holder.title?.text = song.title
|
||||||
holder.text?.text = song.albumName
|
holder.text?.text = song.albumName
|
||||||
Glide.with(activity).asDrawable().songCoverOptions(song)
|
|
||||||
.load(RetroGlideExtension.getSongModel(song)).into(holder.image!!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GENRE -> {
|
|
||||||
val genre = dataSet[position] as Genre
|
|
||||||
holder.title?.text = genre.name
|
|
||||||
holder.text?.text = String.format(
|
|
||||||
Locale.getDefault(),
|
|
||||||
"%d %s",
|
|
||||||
genre.songCount,
|
|
||||||
if (genre.songCount > 1) activity.getString(R.string.songs) else activity.getString(
|
|
||||||
R.string.song
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
PLAYLIST -> {
|
|
||||||
val playlist = dataSet[position] as PlaylistWithSongs
|
|
||||||
holder.title?.text = playlist.playlistEntity.playlistName
|
|
||||||
//holder.text?.text = MusicUtil.playlistInfoString(activity, playlist.songs)
|
|
||||||
}
|
|
||||||
|
|
||||||
ALBUM_ARTIST -> {
|
|
||||||
holder.imageTextContainer?.isVisible = true
|
|
||||||
val artist = dataSet[position] as Artist
|
|
||||||
holder.title?.text = artist.name
|
|
||||||
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
|
|
||||||
Glide.with(activity).asDrawable().artistImageOptions(artist).load(
|
|
||||||
RetroGlideExtension.getArtistModel(artist)
|
|
||||||
).into(holder.image!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
holder.title?.text = dataSet[position].toString()
|
holder.title?.text = dataSet?.get(position).toString()
|
||||||
holder.title?.setTextColor(ThemeStore.accentColor(activity))
|
holder.title?.setTextColor(ThemeStore.accentColor(activity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return dataSet.size
|
return dataSet!!.size
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View, itemViewType: Int) : MediaEntryViewHolder(itemView) {
|
inner class ViewHolder(itemView: View, itemViewType: Int) : MediaEntryViewHolder(itemView) {
|
||||||
init {
|
init {
|
||||||
itemView.setOnLongClickListener(null)
|
itemView.setOnLongClickListener(null)
|
||||||
imageTextContainer?.isInvisible = true
|
|
||||||
if (itemViewType == SONG) {
|
if (itemViewType == SONG) {
|
||||||
imageTextContainer?.isGone = true
|
menu?.visibility = View.VISIBLE
|
||||||
menu?.isVisible = true
|
|
||||||
menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) {
|
menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) {
|
||||||
override val song: Song
|
override val song: Song
|
||||||
get() = dataSet[layoutPosition] as Song
|
get() = dataSet!![adapterPosition] as Song
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
menu?.isVisible = false
|
menu?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
when (itemViewType) {
|
when (itemViewType) {
|
||||||
|
@ -178,52 +93,28 @@ class SearchAdapter(
|
||||||
ARTIST -> setImageTransitionName(activity.getString(R.string.transition_artist_image))
|
ARTIST -> setImageTransitionName(activity.getString(R.string.transition_artist_image))
|
||||||
else -> {
|
else -> {
|
||||||
val container = itemView.findViewById<View>(R.id.imageContainer)
|
val container = itemView.findViewById<View>(R.id.imageContainer)
|
||||||
container?.isVisible = false
|
container?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View?) {
|
override fun onClick(v: View?) {
|
||||||
val item = dataSet[layoutPosition]
|
val item = dataSet!![adapterPosition]
|
||||||
when (itemViewType) {
|
when (itemViewType) {
|
||||||
ALBUM -> {
|
ALBUM -> {
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
val options = ActivityOptions.makeSceneTransitionAnimation(activity,
|
||||||
R.id.albumDetailsFragment,
|
UtilPair.create(image, activity.getString(R.string.transition_album_art)))
|
||||||
bundleOf(EXTRA_ALBUM_ID to (item as Album).id)
|
NavigationUtil.goToAlbumOptions(activity, (item as Album).id, options)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ARTIST -> {
|
ARTIST -> {
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
val options = ActivityOptions.makeSceneTransitionAnimation(activity,
|
||||||
R.id.artistDetailsFragment,
|
UtilPair.create(image, activity.getString(R.string.transition_artist_image)))
|
||||||
bundleOf(EXTRA_ARTIST_ID to (item as Artist).id)
|
NavigationUtil.goToArtistOptions(activity, (item as Artist).id, options)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ALBUM_ARTIST -> {
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.albumArtistDetailsFragment,
|
|
||||||
bundleOf(EXTRA_ARTIST_NAME to (item as Artist).name)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
GENRE -> {
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.genreDetailsFragment,
|
|
||||||
bundleOf(EXTRA_GENRE to (item as Genre))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
PLAYLIST -> {
|
|
||||||
activity.findNavController(R.id.fragment_container).navigate(
|
|
||||||
R.id.playlistDetailsFragment,
|
|
||||||
bundleOf(EXTRA_PLAYLIST_ID to (item as PlaylistWithSongs).playlistEntity.playListId)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
SONG -> {
|
SONG -> {
|
||||||
MusicPlayerRemote.playNext(item as Song)
|
val playList = ArrayList<Song>()
|
||||||
MusicPlayerRemote.playNextSong()
|
playList.add(item as Song)
|
||||||
|
MusicPlayerRemote.openQueue(playList, 0, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,8 +125,5 @@ class SearchAdapter(
|
||||||
private const val ALBUM = 1
|
private const val ALBUM = 1
|
||||||
private const val ARTIST = 2
|
private const val ARTIST = 2
|
||||||
private const val SONG = 3
|
private const val SONG = 3
|
||||||
private const val GENRE = 4
|
|
||||||
private const val PLAYLIST = 5
|
|
||||||
private const val ALBUM_ARTIST = 6
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,3 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.adapter
|
package code.name.monkey.retromusic.adapter
|
||||||
|
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
|
@ -20,43 +6,42 @@ import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||||
import code.name.monkey.retromusic.extensions.getTintedDrawable
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|
||||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
||||||
import code.name.monkey.retromusic.interfaces.ICallbacks
|
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.RetroUtil
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.signature.MediaStoreSignature
|
import com.bumptech.glide.signature.MediaStoreSignature
|
||||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
|
import java.util.*
|
||||||
import kotlin.math.log10
|
import kotlin.math.log10
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
class SongFileAdapter(
|
class SongFileAdapter(
|
||||||
override val activity: AppCompatActivity,
|
private val activity: AppCompatActivity,
|
||||||
private var dataSet: List<File>,
|
private var dataSet: List<File>?,
|
||||||
private val itemLayoutRes: Int,
|
private val itemLayoutRes: Int,
|
||||||
private val iCallbacks: ICallbacks?
|
private val callbacks: Callbacks?,
|
||||||
) : AbsMultiSelectAdapter<SongFileAdapter.ViewHolder, File>(
|
cabHolder: CabHolder?
|
||||||
activity, R.menu.menu_media_selection
|
) : AbsMultiSelectAdapter<SongFileAdapter.ViewHolder, File>(activity, cabHolder, R.menu.menu_media_selection), FastScrollRecyclerView.SectionedAdapter {
|
||||||
), PopupTextProvider {
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.setHasStableIds(true)
|
this.setHasStableIds(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
return if (dataSet[position].isDirectory) FOLDER else FILE
|
return if (dataSet!![position].isDirectory) FOLDER else FILE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long {
|
override fun getItemId(position: Int): Long {
|
||||||
return dataSet[position].hashCode().toLong()
|
return dataSet!![position].hashCode().toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun swapDataSet(songFiles: List<File>) {
|
fun swapDataSet(songFiles: List<File>) {
|
||||||
|
@ -69,14 +54,14 @@ class SongFileAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, index: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, index: Int) {
|
||||||
val file = dataSet[index]
|
val file = dataSet!![index]
|
||||||
holder.itemView.isActivated = isChecked(file)
|
holder.itemView.isActivated = isChecked(file)
|
||||||
holder.title?.text = getFileTitle(file)
|
holder.title?.text = getFileTitle(file)
|
||||||
if (holder.text != null) {
|
if (holder.text != null) {
|
||||||
if (holder.itemViewType == FILE) {
|
if (holder.itemViewType == FILE) {
|
||||||
holder.text?.text = getFileText(file)
|
holder.text!!.text = getFileText(file)
|
||||||
} else {
|
} else {
|
||||||
holder.text?.isVisible = false
|
holder.text!!.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,64 +79,64 @@ class SongFileAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFileImage(file: File, holder: ViewHolder) {
|
private fun loadFileImage(file: File, holder: ViewHolder) {
|
||||||
val iconColor = ATHUtil.resolveColor(activity, androidx.appcompat.R.attr.colorControlNormal)
|
val iconColor = ATHUtil.resolveColor(activity, R.attr.iconColor)
|
||||||
if (file.isDirectory) {
|
if (file.isDirectory) {
|
||||||
holder.image?.let {
|
holder.image?.let {
|
||||||
it.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN)
|
it.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN)
|
||||||
it.setImageResource(R.drawable.ic_folder)
|
it.setImageResource(R.drawable.ic_folder_white_24dp)
|
||||||
}
|
}
|
||||||
holder.imageTextContainer?.setCardBackgroundColor(
|
holder.imageTextContainer?.setCardBackgroundColor(ATHUtil.resolveColor(activity, R.attr.colorPrimary))
|
||||||
ATHUtil.resolveColor(
|
|
||||||
activity,
|
|
||||||
com.google.android.material.R.attr.colorSurface
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
val error = activity.getTintedDrawable(R.drawable.ic_audio_file, iconColor)
|
val error = RetroUtil.getTintedVectorDrawable(activity, R.drawable.ic_file_music_white_24dp, iconColor)
|
||||||
Glide.with(activity)
|
Glide.with(activity)
|
||||||
.load(AudioFileCover(file.path))
|
.load(AudioFileCover(file.path))
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.error(error)
|
.error(error)
|
||||||
.placeholder(error)
|
.placeholder(error)
|
||||||
.transition(RetroGlideExtension.getDefaultTransition())
|
.animate(android.R.anim.fade_in)
|
||||||
.signature(MediaStoreSignature("", file.lastModified(), 0))
|
.signature(MediaStoreSignature("", file.lastModified(), 0))
|
||||||
.into(holder.image!!)
|
.into(holder.image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return dataSet.size
|
return dataSet!!.size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIdentifier(position: Int): File {
|
override fun getIdentifier(position: Int): File? {
|
||||||
return dataSet[position]
|
return dataSet!![position]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getName(model: File): String {
|
override fun getName(`object`: File): String {
|
||||||
return getFileTitle(model)
|
return getFileTitle(`object`)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<File>) {
|
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<File>) {
|
||||||
if (iCallbacks == null) return
|
if (callbacks == null) return
|
||||||
iCallbacks.onMultipleItemAction(menuItem, selection as ArrayList<File>)
|
callbacks.onMultipleItemAction(menuItem, selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPopupText(position: Int): String {
|
override fun getSectionName(position: Int): String {
|
||||||
return if (position >= dataSet.lastIndex) "" else getSectionName(position)
|
return dataSet!![position].name[0].toString().toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSectionName(position: Int): String {
|
interface Callbacks {
|
||||||
return MusicUtil.getSectionName(dataSet[position].name)
|
fun onFileSelected(file: File)
|
||||||
|
|
||||||
|
fun onFileMenuClicked(file: File, view: View)
|
||||||
|
|
||||||
|
fun onMultipleItemAction(item: MenuItem, files: ArrayList<File>)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View) : code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder(itemView) {
|
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (menu != null && iCallbacks != null) {
|
if (menu != null && callbacks != null) {
|
||||||
menu?.setOnClickListener { v ->
|
menu!!.setOnClickListener { v ->
|
||||||
val position = layoutPosition
|
val position = adapterPosition
|
||||||
if (isPositionInRange(position)) {
|
if (isPositionInRange(position)) {
|
||||||
iCallbacks.onFileMenuClicked(dataSet[position], v)
|
callbacks.onFileMenuClicked(dataSet!![position], v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,23 +146,23 @@ class SongFileAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View?) {
|
override fun onClick(v: View?) {
|
||||||
val position = layoutPosition
|
val position = adapterPosition
|
||||||
if (isPositionInRange(position)) {
|
if (isPositionInRange(position)) {
|
||||||
if (isInQuickSelectMode) {
|
if (isInQuickSelectMode) {
|
||||||
toggleChecked(position)
|
toggleChecked(position)
|
||||||
} else {
|
} else {
|
||||||
iCallbacks?.onFileSelected(dataSet[position])
|
callbacks?.onFileSelected(dataSet!![position])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View?): Boolean {
|
override fun onLongClick(v: View?): Boolean {
|
||||||
val position = layoutPosition
|
val position = adapterPosition
|
||||||
return isPositionInRange(position) && toggleChecked(position)
|
return isPositionInRange(position) && toggleChecked(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isPositionInRange(position: Int): Boolean {
|
private fun isPositionInRange(position: Int): Boolean {
|
||||||
return position >= 0 && position < dataSet.size
|
return position >= 0 && position < dataSet!!.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
package code.name.monkey.retromusic.adapter
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class StorageAdapter(
|
|
||||||
val storageList: List<Storage>,
|
|
||||||
val storageClickListener: StorageClickListener
|
|
||||||
) :
|
|
||||||
RecyclerView.Adapter<StorageAdapter.ViewHolder>() {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
|
||||||
return ViewHolder(
|
|
||||||
LayoutInflater.from(parent.context).inflate(
|
|
||||||
R.layout.item_storage,
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
||||||
holder.bindData(storageList[position])
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
|
||||||
return storageList.size
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
|
||||||
val title: TextView = itemView.findViewById(R.id.title)
|
|
||||||
|
|
||||||
fun bindData(storage: Storage) {
|
|
||||||
title.text = storage.title
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
itemView.setOnClickListener { storageClickListener.onStorageClicked(storageList[bindingAdapterPosition]) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StorageClickListener {
|
|
||||||
fun onStorageClicked(storage: Storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Storage {
|
|
||||||
lateinit var title: String
|
|
||||||
lateinit var file: File
|
|
||||||
}
|
|
|
@ -1,59 +1,62 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.adapter.album
|
package code.name.monkey.retromusic.adapter.album
|
||||||
|
|
||||||
|
import android.app.ActivityOptions
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.isVisible
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.albumCoverOptions
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
|
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import code.name.monkey.retromusic.helper.SortOrder
|
import code.name.monkey.retromusic.helper.SortOrder
|
||||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
|
||||||
|
|
||||||
|
|
||||||
|
open class AlbumAdapter(protected val activity: AppCompatActivity,
|
||||||
|
dataSet: ArrayList<Album>,
|
||||||
|
@param:LayoutRes protected var itemLayoutRes: Int,
|
||||||
|
usePalette: Boolean,
|
||||||
|
cabHolder: CabHolder?) : AbsMultiSelectAdapter<AlbumAdapter.ViewHolder, Album>(activity, cabHolder, code.name.monkey.retromusic.R.menu.menu_media_selection), FastScrollRecyclerView.SectionedAdapter {
|
||||||
|
var dataSet: ArrayList<Album>
|
||||||
|
protected set
|
||||||
|
|
||||||
|
protected var usePalette = false
|
||||||
|
|
||||||
open class AlbumAdapter(
|
|
||||||
override val activity: FragmentActivity,
|
|
||||||
var dataSet: List<Album>,
|
|
||||||
var itemLayoutRes: Int,
|
|
||||||
val listener: IAlbumClickListener?
|
|
||||||
) : AbsMultiSelectAdapter<AlbumAdapter.ViewHolder, Album>(
|
|
||||||
activity,
|
|
||||||
R.menu.menu_media_selection
|
|
||||||
), PopupTextProvider {
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
this.dataSet = dataSet
|
||||||
|
this.usePalette = usePalette
|
||||||
this.setHasStableIds(true)
|
this.setHasStableIds(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun swapDataSet(dataSet: List<Album>) {
|
fun useItemLayout(itemLayoutRes: Int) {
|
||||||
|
this.itemLayoutRes = itemLayoutRes
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun usePalette(usePalette: Boolean) {
|
||||||
|
this.usePalette = usePalette
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun swapDataSet(dataSet: ArrayList<Album>) {
|
||||||
this.dataSet = dataSet
|
this.dataSet = dataSet
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
@ -67,18 +70,12 @@ open class AlbumAdapter(
|
||||||
return ViewHolder(view)
|
return ViewHolder(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAlbumTitle(album: Album): String {
|
private fun getAlbumTitle(album: Album): String? {
|
||||||
return album.title
|
return album.title
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun getAlbumText(album: Album): String? {
|
protected open fun getAlbumText(album: Album): String? {
|
||||||
return album.albumArtist.let {
|
return album.artistName
|
||||||
if (it.isNullOrEmpty()) {
|
|
||||||
album.artistName
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
@ -87,39 +84,37 @@ open class AlbumAdapter(
|
||||||
holder.itemView.isActivated = isChecked
|
holder.itemView.isActivated = isChecked
|
||||||
holder.title?.text = getAlbumTitle(album)
|
holder.title?.text = getAlbumTitle(album)
|
||||||
holder.text?.text = getAlbumText(album)
|
holder.text?.text = getAlbumText(album)
|
||||||
// Check if imageContainer exists so we can have a smooth transition without
|
holder.playSongs?.setOnClickListener { album.songs?.let { songs -> MusicPlayerRemote.openQueue(songs, 0, true) } }
|
||||||
// CardView clipping, if it doesn't exist in current layout set transition name to image instead.
|
|
||||||
if (holder.imageContainer != null) {
|
|
||||||
holder.imageContainer?.transitionName = album.id.toString()
|
|
||||||
} else {
|
|
||||||
holder.image?.transitionName = album.id.toString()
|
|
||||||
}
|
|
||||||
loadAlbumCover(album, holder)
|
loadAlbumCover(album, holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
|
protected open fun setColors(color: Int, holder: ViewHolder) {
|
||||||
if (holder.paletteColorContainer != null) {
|
if (holder.paletteColorContainer != null) {
|
||||||
holder.title?.setTextColor(color.primaryTextColor)
|
holder.title?.setTextColor(
|
||||||
holder.text?.setTextColor(color.secondaryTextColor)
|
MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color)))
|
||||||
holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor)
|
holder.text?.setTextColor(MaterialValueHelper.getSecondaryTextColor(activity, ColorUtil.isColorLight(color)))
|
||||||
|
holder.paletteColorContainer?.setBackgroundColor(color)
|
||||||
}
|
}
|
||||||
holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor)
|
|
||||||
holder.imageContainerCard?.setCardBackgroundColor(color.backgroundColor)
|
holder.mask?.backgroundTintList = ColorStateList.valueOf(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
protected open fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
||||||
if (holder.image == null) {
|
if (holder.image == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val song = album.safeGetFirstSong()
|
|
||||||
Glide.with(activity)
|
SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||||
.asBitmapPalette()
|
.checkIgnoreMediaStore(activity)
|
||||||
.albumCoverOptions(song)
|
.generatePalette(activity).build()
|
||||||
//.checkIgnoreMediaStore()
|
|
||||||
.load(RetroGlideExtension.getSongModel(song))
|
|
||||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onLoadCleared(placeholder: Drawable?) {
|
||||||
setColors(colors, holder)
|
super.onLoadCleared(placeholder)
|
||||||
|
setColors(defaultFooterColor, holder)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onColorReady(color: Int) {
|
||||||
|
setColors(color, holder)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -129,69 +124,63 @@ open class AlbumAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long {
|
override fun getItemId(position: Int): Long {
|
||||||
return dataSet[position].id
|
return dataSet[position].id.toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIdentifier(position: Int): Album? {
|
override fun getIdentifier(position: Int): Album? {
|
||||||
return dataSet[position]
|
return dataSet[position]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getName(model: Album): String {
|
override fun getName(album: Album): String {
|
||||||
return model.title
|
return album.title!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(
|
override fun onMultipleItemAction(menuItem: MenuItem,
|
||||||
menuItem: MenuItem,
|
selection: ArrayList<Album>) {
|
||||||
selection: List<Album>
|
|
||||||
) {
|
|
||||||
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongList(albums: List<Album>): List<Song> {
|
private fun getSongList(albums: List<Album>): ArrayList<Song> {
|
||||||
val songs = ArrayList<Song>()
|
val songs = ArrayList<Song>()
|
||||||
for (album in albums) {
|
for (album in albums) {
|
||||||
songs.addAll(album.songs)
|
songs.addAll(album.songs!!)
|
||||||
}
|
}
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPopupText(position: Int): String {
|
override fun getSectionName(position: Int): String {
|
||||||
return getSectionName(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getSectionName(position: Int): String {
|
|
||||||
var sectionName: String? = null
|
var sectionName: String? = null
|
||||||
when (PreferenceUtil.albumSortOrder) {
|
when (PreferenceUtil.getInstance(activity).albumSortOrder) {
|
||||||
SortOrder.AlbumSortOrder.ALBUM_A_Z, SortOrder.AlbumSortOrder.ALBUM_Z_A -> sectionName =
|
SortOrder.AlbumSortOrder.ALBUM_A_Z, SortOrder.AlbumSortOrder.ALBUM_Z_A -> sectionName = dataSet[position].title
|
||||||
dataSet[position].title
|
SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].artistName
|
||||||
|
SortOrder.AlbumSortOrder.ALBUM_YEAR -> return MusicUtil.getYearString(dataSet[position].year)
|
||||||
SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].albumArtist
|
|
||||||
SortOrder.AlbumSortOrder.ALBUM_YEAR -> return MusicUtil.getYearString(
|
|
||||||
dataSet[position].year
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return MusicUtil.getSectionName(sectionName)
|
return MusicUtil.getSectionName(sectionName)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
menu?.isVisible = false
|
setImageTransitionName(activity.getString(code.name.monkey.retromusic.R.string.transition_album_art))
|
||||||
|
if (menu != null) {
|
||||||
|
menu!!.visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View?) {
|
override fun onClick(v: View?) {
|
||||||
super.onClick(v)
|
super.onClick(v)
|
||||||
if (isInQuickSelectMode) {
|
if (isInQuickSelectMode) {
|
||||||
toggleChecked(layoutPosition)
|
toggleChecked(adapterPosition)
|
||||||
} else {
|
} else {
|
||||||
image?.let {
|
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(activity, image, activity.getString(R.string.transition_album_art))
|
||||||
listener?.onAlbumClick(dataSet[layoutPosition].id, imageContainer ?: it)
|
NavigationUtil.goToAlbumOptions(activity, dataSet[adapterPosition].id, activityOptions)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View?): Boolean {
|
override fun onLongClick(v: View?): Boolean {
|
||||||
return toggleChecked(layoutPosition)
|
toggleChecked(adapterPosition)
|
||||||
|
return super.onLongClick(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,3 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.adapter.album
|
package code.name.monkey.retromusic.adapter.album
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -19,36 +5,21 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.core.os.BundleCompat
|
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.activities.MainActivity
|
|
||||||
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
|
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
|
||||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
|
||||||
import code.name.monkey.retromusic.fragments.base.goToLyrics
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.songCoverOptions
|
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||||
import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter
|
import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
import java.util.*
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
class AlbumCoverPagerAdapter(
|
|
||||||
fragmentManager: FragmentManager,
|
class AlbumCoverPagerAdapter(fm: FragmentManager, private val dataSet: ArrayList<Song>) : CustomFragmentStatePagerAdapter(fm) {
|
||||||
private val dataSet: List<Song>
|
|
||||||
) : CustomFragmentStatePagerAdapter(fragmentManager) {
|
|
||||||
|
|
||||||
private var currentColorReceiver: AlbumCoverFragment.ColorReceiver? = null
|
private var currentColorReceiver: AlbumCoverFragment.ColorReceiver? = null
|
||||||
private var currentColorReceiverPosition = -1
|
private var currentColorReceiverPosition = -1
|
||||||
|
@ -88,99 +59,71 @@ class AlbumCoverPagerAdapter(
|
||||||
|
|
||||||
class AlbumCoverFragment : Fragment() {
|
class AlbumCoverFragment : Fragment() {
|
||||||
|
|
||||||
|
lateinit var albumCover: ImageView
|
||||||
private var isColorReady: Boolean = false
|
private var isColorReady: Boolean = false
|
||||||
private lateinit var color: MediaNotificationProcessor
|
private var color: Int = 0
|
||||||
private lateinit var song: Song
|
private lateinit var song: Song
|
||||||
private var colorReceiver: ColorReceiver? = null
|
private var colorReceiver: ColorReceiver? = null
|
||||||
private var request: Int = 0
|
private var request: Int = 0
|
||||||
private val mainActivity get() = activity as MainActivity
|
|
||||||
|
private val layout: Int
|
||||||
|
get() {
|
||||||
|
return when (PreferenceUtil.getInstance(activity).albumCoverStyle) {
|
||||||
|
AlbumCoverStyle.NORMAL -> R.layout.fragment_album_cover
|
||||||
|
AlbumCoverStyle.FLAT -> R.layout.fragment_album_flat_cover
|
||||||
|
AlbumCoverStyle.CIRCLE -> R.layout.fragment_album_circle_cover
|
||||||
|
AlbumCoverStyle.CARD -> R.layout.fragment_album_card_cover
|
||||||
|
AlbumCoverStyle.MATERIAL -> R.layout.fragment_album_material_cover
|
||||||
|
AlbumCoverStyle.FULL -> R.layout.fragment_album_full_cover
|
||||||
|
AlbumCoverStyle.FULL_CARD -> R.layout.fragment_album_full_card_cover
|
||||||
|
else -> R.layout.fragment_album_cover
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
if (arguments != null) {
|
if (arguments != null) {
|
||||||
song = BundleCompat.getParcelable(requireArguments(), SONG_ARG, Song::class.java)!!
|
song = arguments!!.getParcelable(SONG_ARG)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
inflater: LayoutInflater,
|
val finalLayout = when {
|
||||||
container: ViewGroup?,
|
PreferenceUtil.getInstance(activity).carouselEffect() -> R.layout.fragment_album_carousel_cover
|
||||||
savedInstanceState: Bundle?
|
else -> layout
|
||||||
): View? {
|
|
||||||
val view = inflater.inflate(getLayoutWithPlayerTheme(), container, false)
|
|
||||||
view.setOnClickListener {
|
|
||||||
if (mainActivity.getBottomSheetBehavior().state == STATE_EXPANDED) {
|
|
||||||
showLyricsDialog()
|
|
||||||
}
|
}
|
||||||
|
val view = inflater.inflate(finalLayout, container, false)
|
||||||
|
albumCover = view.findViewById(R.id.player_image)
|
||||||
|
albumCover.setOnClickListener {
|
||||||
|
|
||||||
|
NavigationUtil.goToLyrics(requireActivity())
|
||||||
}
|
}
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showLyricsDialog() {
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
val data: String? = MusicUtil.getLyrics(song)
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
MaterialAlertDialogBuilder(
|
|
||||||
requireContext(),
|
|
||||||
com.google.android.material.R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
|
||||||
).apply {
|
|
||||||
setTitle(song.title)
|
|
||||||
setMessage(if (data.isNullOrEmpty()) "No lyrics found" else data)
|
|
||||||
setNegativeButton(R.string.synced_lyrics) { _, _ ->
|
|
||||||
goToLyrics(requireActivity())
|
|
||||||
}
|
|
||||||
show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLayoutWithPlayerTheme(): Int {
|
|
||||||
return when (PreferenceUtil.nowPlayingScreen) {
|
|
||||||
Card, Fit, Tiny, Classic, Gradient, Full -> R.layout.fragment_album_full_cover
|
|
||||||
Peek -> R.layout.fragment_peek_album_cover
|
|
||||||
else -> {
|
|
||||||
if (PreferenceUtil.isCarouselEffect) {
|
|
||||||
R.layout.fragment_album_carousel_cover
|
|
||||||
} else {
|
|
||||||
when (PreferenceUtil.albumCoverStyle) {
|
|
||||||
AlbumCoverStyle.Normal -> R.layout.fragment_album_cover
|
|
||||||
AlbumCoverStyle.Flat -> R.layout.fragment_album_flat_cover
|
|
||||||
AlbumCoverStyle.Circle -> R.layout.fragment_album_circle_cover
|
|
||||||
AlbumCoverStyle.Card -> R.layout.fragment_album_card_cover
|
|
||||||
AlbumCoverStyle.Full -> R.layout.fragment_album_full_cover
|
|
||||||
AlbumCoverStyle.FullCard -> R.layout.fragment_album_full_card_cover
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
loadAlbumCover(albumCover = view.findViewById(R.id.player_image))
|
loadAlbumCover()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
colorReceiver = null
|
colorReceiver = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadAlbumCover(albumCover: ImageView) {
|
private fun loadAlbumCover() {
|
||||||
Glide.with(this)
|
SongGlideRequest.Builder.from(Glide.with(requireContext()), song)
|
||||||
.asBitmapPalette()
|
.checkIgnoreMediaStore(activity)
|
||||||
.songCoverOptions(song)
|
.generatePalette(activity).build()
|
||||||
//.checkIgnoreMediaStore()
|
|
||||||
.load(RetroGlideExtension.getSongModel(song))
|
|
||||||
.dontAnimate()
|
|
||||||
.into(object : RetroMusicColoredTarget(albumCover) {
|
.into(object : RetroMusicColoredTarget(albumCover) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onColorReady(color: Int) {
|
||||||
setColor(colors)
|
setColor(color)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setColor(color: MediaNotificationProcessor) {
|
private fun setColor(color: Int) {
|
||||||
this.color = color
|
this.color = color
|
||||||
isColorReady = true
|
isColorReady = true
|
||||||
if (colorReceiver != null) {
|
if (colorReceiver != null) {
|
||||||
|
@ -199,7 +142,7 @@ class AlbumCoverPagerAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ColorReceiver {
|
interface ColorReceiver {
|
||||||
fun onColorReady(color: MediaNotificationProcessor, request: Int)
|
fun onColorReady(color: Int, request: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -208,7 +151,9 @@ class AlbumCoverPagerAdapter(
|
||||||
|
|
||||||
fun newInstance(song: Song): AlbumCoverFragment {
|
fun newInstance(song: Song): AlbumCoverFragment {
|
||||||
val frag = AlbumCoverFragment()
|
val frag = AlbumCoverFragment()
|
||||||
frag.arguments = bundleOf(SONG_ARG to song)
|
val args = Bundle()
|
||||||
|
args.putParcelable(SONG_ARG, song)
|
||||||
|
frag.arguments = args
|
||||||
return frag
|
return frag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,3 +163,4 @@ class AlbumCoverPagerAdapter(
|
||||||
val TAG: String = AlbumCoverPagerAdapter::class.java.simpleName
|
val TAG: String = AlbumCoverPagerAdapter::class.java.simpleName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017. Alexander Bilchuk <a.bilchuk@sandrlab.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package code.name.monkey.retromusic.adapter.album
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.ActivityOptions
|
||||||
|
import android.util.DisplayMetrics
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import code.name.monkey.retromusic.R
|
||||||
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||||
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
import code.name.monkey.retromusic.model.Album
|
||||||
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
|
import code.name.monkey.retromusic.views.MetalRecyclerViewPager
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
|
||||||
|
class AlbumFullWidthAdapter(private val activity: Activity, private val dataSet: ArrayList<Album>, metrics: DisplayMetrics) :
|
||||||
|
MetalRecyclerViewPager.MetalAdapter<AlbumFullWidthAdapter.FullMetalViewHolder>(metrics) {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FullMetalViewHolder {
|
||||||
|
return FullMetalViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.pager_item, parent, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: FullMetalViewHolder, position: Int) {
|
||||||
|
// don't forget about calling supper.onBindViewHolder!
|
||||||
|
super.onBindViewHolder(holder, position)
|
||||||
|
val album = dataSet[position]
|
||||||
|
holder.title?.text = getAlbumTitle(album)
|
||||||
|
holder.text?.text = getAlbumText(album)
|
||||||
|
holder.playSongs?.setOnClickListener { album.songs?.let { songs -> MusicPlayerRemote.openQueue(songs, 0, true) } }
|
||||||
|
loadAlbumCover(album, holder)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAlbumTitle(album: Album): String? {
|
||||||
|
return album.title
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAlbumText(album: Album): String? {
|
||||||
|
return album.artistName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadAlbumCover(album: Album, holder: FullMetalViewHolder) {
|
||||||
|
if (holder.image == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||||
|
.checkIgnoreMediaStore(activity)
|
||||||
|
.generatePalette(activity).build()
|
||||||
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
|
override fun onColorReady(color: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return dataSet.size
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class FullMetalViewHolder(itemView: View) : MetalRecyclerViewPager.MetalViewHolder(itemView) {
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(activity, image, activity.getString(R.string.transition_album_art))
|
||||||
|
NavigationUtil.goToAlbumOptions(activity, dataSet[adapterPosition].id, activityOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,39 +1,32 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.adapter.album
|
package code.name.monkey.retromusic.adapter.album
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.albumCoverOptions
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
|
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
|
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||||
import code.name.monkey.retromusic.helper.HorizontalAdapterHelper
|
import code.name.monkey.retromusic.helper.HorizontalAdapterHelper
|
||||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||||
import code.name.monkey.retromusic.model.Album
|
import code.name.monkey.retromusic.model.Album
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class HorizontalAlbumAdapter(
|
class HorizontalAlbumAdapter(
|
||||||
activity: FragmentActivity,
|
activity: AppCompatActivity,
|
||||||
dataSet: List<Album>,
|
dataSet: ArrayList<Album>,
|
||||||
albumClickListener: IAlbumClickListener
|
usePalette: Boolean,
|
||||||
|
cabHolder: CabHolder?
|
||||||
) : AlbumAdapter(
|
) : AlbumAdapter(
|
||||||
activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, albumClickListener
|
activity,
|
||||||
|
dataSet,
|
||||||
|
HorizontalAdapterHelper.LAYOUT_RES,
|
||||||
|
usePalette,
|
||||||
|
cabHolder
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun createViewHolder(view: View, viewType: Int): ViewHolder {
|
override fun createViewHolder(view: View, viewType: Int): ViewHolder {
|
||||||
|
@ -42,30 +35,38 @@ class HorizontalAlbumAdapter(
|
||||||
return ViewHolder(view)
|
return ViewHolder(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
|
override fun setColors(color: Int, holder: ViewHolder) {
|
||||||
// holder.title?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorPrimary))
|
holder.title?.setTextColor(MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color)))
|
||||||
// holder.text?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorSecondary))
|
holder.text?.setTextColor(MaterialValueHelper.getSecondaryTextColor(activity, ColorUtil.isColorLight(color)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
override fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
||||||
if (holder.image == null) return
|
if (holder.image == null) return
|
||||||
Glide.with(activity)
|
|
||||||
.asBitmapPalette()
|
SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||||
.albumCoverOptions(album.safeGetFirstSong())
|
.checkIgnoreMediaStore(activity)
|
||||||
.load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
|
.generatePalette(activity).build()
|
||||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onLoadCleared(placeholder: Drawable?) {
|
||||||
setColors(colors, holder)
|
super.onLoadCleared(placeholder)
|
||||||
|
setColors(albumArtistFooterColor, holder)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onColorReady(color: Int) {
|
||||||
|
if (usePalette)
|
||||||
|
setColors(color, holder)
|
||||||
|
else
|
||||||
|
setColors(albumArtistFooterColor, holder)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAlbumText(album: Album): String {
|
override fun getAlbumText(album: Album): String? {
|
||||||
return MusicUtil.getYearString(album.year)
|
return MusicUtil.getYearString(album.year)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
return HorizontalAdapterHelper.getItemViewType(position, itemCount)
|
return HorizontalAdapterHelper.getItemViewtype(position, itemCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
|
|
|
@ -1,79 +1,55 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Hemanth Savarla.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU General Public License v3
|
|
||||||
*
|
|
||||||
* This is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package code.name.monkey.retromusic.adapter.artist
|
package code.name.monkey.retromusic.adapter.artist
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.app.ActivityOptions
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.content.res.Resources
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.isVisible
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||||
import code.name.monkey.retromusic.extensions.hide
|
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.artistImageOptions
|
|
||||||
import code.name.monkey.retromusic.glide.RetroGlideExtension.asBitmapPalette
|
|
||||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||||
import code.name.monkey.retromusic.interfaces.IAlbumArtistClickListener
|
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||||
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
|
||||||
import code.name.monkey.retromusic.model.Artist
|
import code.name.monkey.retromusic.model.Artist
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.util.MusicUtil
|
import code.name.monkey.retromusic.util.MusicUtil
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.NavigationUtil
|
||||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class ArtistAdapter(
|
|
||||||
override val activity: FragmentActivity,
|
|
||||||
var dataSet: List<Artist>,
|
|
||||||
var itemLayoutRes: Int,
|
|
||||||
val IArtistClickListener: IArtistClickListener,
|
|
||||||
val IAlbumArtistClickListener: IAlbumArtistClickListener? = null
|
|
||||||
) : AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist>(activity, R.menu.menu_media_selection),
|
|
||||||
PopupTextProvider {
|
|
||||||
|
|
||||||
var albumArtistsOnly = false
|
class ArtistAdapter(val activity: AppCompatActivity,
|
||||||
|
var dataSet: ArrayList<Artist>,
|
||||||
|
@LayoutRes var itemLayoutRes: Int,
|
||||||
|
var usePalette: Boolean,
|
||||||
|
cabHolder: CabHolder?
|
||||||
|
) : AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist>(activity, cabHolder, R.menu.menu_media_selection), FastScrollRecyclerView.SectionedAdapter {
|
||||||
|
|
||||||
init {
|
fun swapDataSet(dataSet: ArrayList<Artist>) {
|
||||||
this.setHasStableIds(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
|
||||||
fun swapDataSet(dataSet: List<Artist>) {
|
|
||||||
this.dataSet = dataSet
|
this.dataSet = dataSet
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
albumArtistsOnly = PreferenceUtil.albumArtistsOnly
|
}
|
||||||
|
|
||||||
|
fun usePalette(usePalette: Boolean) {
|
||||||
|
this.usePalette = usePalette
|
||||||
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long {
|
override fun getItemId(position: Int): Long {
|
||||||
return dataSet[position].id
|
return dataSet[position].id.toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val view =
|
val view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)
|
||||||
try {
|
|
||||||
LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)
|
|
||||||
} catch (e: Resources.NotFoundException) {
|
|
||||||
LayoutInflater.from(activity).inflate(R.layout.item_grid_circle, parent, false)
|
|
||||||
}
|
|
||||||
return createViewHolder(view)
|
return createViewHolder(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,38 +62,33 @@ class ArtistAdapter(
|
||||||
val isChecked = isChecked(artist)
|
val isChecked = isChecked(artist)
|
||||||
holder.itemView.isActivated = isChecked
|
holder.itemView.isActivated = isChecked
|
||||||
holder.title?.text = artist.name
|
holder.title?.text = artist.name
|
||||||
holder.text?.hide()
|
holder.text?.visibility = View.GONE
|
||||||
val transitionName =
|
|
||||||
if (albumArtistsOnly) artist.name else artist.id.toString()
|
|
||||||
if (holder.imageContainer != null) {
|
|
||||||
holder.imageContainer?.transitionName = transitionName
|
|
||||||
} else {
|
|
||||||
holder.image?.transitionName = transitionName
|
|
||||||
}
|
|
||||||
loadArtistImage(artist, holder)
|
loadArtistImage(artist, holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setColors(processor: MediaNotificationProcessor, holder: ViewHolder) {
|
fun setColors(color: Int, holder: ViewHolder) {
|
||||||
holder.mask?.backgroundTintList = ColorStateList.valueOf(processor.primaryTextColor)
|
|
||||||
if (holder.paletteColorContainer != null) {
|
if (holder.paletteColorContainer != null) {
|
||||||
holder.paletteColorContainer?.setBackgroundColor(processor.backgroundColor)
|
holder.paletteColorContainer?.setBackgroundColor(color)
|
||||||
holder.title?.setTextColor(processor.primaryTextColor)
|
holder.title?.setTextColor(MaterialValueHelper.getPrimaryTextColor(activity, ColorUtil.isColorLight(color)))
|
||||||
}
|
}
|
||||||
holder.imageContainerCard?.setCardBackgroundColor(processor.backgroundColor)
|
|
||||||
|
holder.mask?.backgroundTintList = ColorStateList.valueOf(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadArtistImage(artist: Artist, holder: ViewHolder) {
|
private fun loadArtistImage(artist: Artist, holder: ViewHolder) {
|
||||||
if (holder.image == null) {
|
if (holder.image == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Glide.with(activity)
|
ArtistGlideRequest.Builder.from(Glide.with(activity), artist)
|
||||||
.asBitmapPalette()
|
.generatePalette(activity).build()
|
||||||
.artistImageOptions(artist)
|
|
||||||
.load(RetroGlideExtension.getArtistModel(artist))
|
|
||||||
.transition(RetroGlideExtension.getDefaultTransition())
|
|
||||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
override fun onLoadCleared(placeholder: Drawable?) {
|
||||||
setColors(colors, holder)
|
super.onLoadCleared(placeholder)
|
||||||
|
setColors(defaultFooterColor, holder)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onColorReady(color: Int) {
|
||||||
|
setColors(color, holder)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -126,22 +97,20 @@ class ArtistAdapter(
|
||||||
return dataSet.size
|
return dataSet.size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIdentifier(position: Int): Artist {
|
override fun getIdentifier(position: Int): Artist? {
|
||||||
return dataSet[position]
|
return dataSet[position]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getName(model: Artist): String {
|
override fun getName(artist: Artist): String {
|
||||||
return model.name
|
return artist.name
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMultipleItemAction(
|
override fun onMultipleItemAction(menuItem: MenuItem,
|
||||||
menuItem: MenuItem,
|
selection: ArrayList<Artist>) {
|
||||||
selection: List<Artist>
|
|
||||||
) {
|
|
||||||
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSongList(artists: List<Artist>): List<Song> {
|
private fun getSongList(artists: List<Artist>): ArrayList<Song> {
|
||||||
val songs = ArrayList<Song>()
|
val songs = ArrayList<Song>()
|
||||||
for (artist in artists) {
|
for (artist in artists) {
|
||||||
songs.addAll(artist.songs) // maybe async in future?
|
songs.addAll(artist.songs) // maybe async in future?
|
||||||
|
@ -149,38 +118,30 @@ class ArtistAdapter(
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPopupText(position: Int): String {
|
override fun getSectionName(position: Int): String {
|
||||||
return getSectionName(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getSectionName(position: Int): String {
|
|
||||||
return MusicUtil.getSectionName(dataSet[position].name)
|
return MusicUtil.getSectionName(dataSet[position].name)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(itemView: View) : code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder(itemView) {
|
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
menu?.isVisible = false
|
setImageTransitionName(activity.getString(code.name.monkey.retromusic.R.string.transition_artist_image))
|
||||||
|
menu?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View?) {
|
override fun onClick(v: View?) {
|
||||||
super.onClick(v)
|
super.onClick(v)
|
||||||
if (isInQuickSelectMode) {
|
if (isInQuickSelectMode) {
|
||||||
toggleChecked(layoutPosition)
|
toggleChecked(adapterPosition)
|
||||||
} else {
|
} else {
|
||||||
val artist = dataSet[layoutPosition]
|
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(activity, image, activity.getString(R.string.transition_artist_image))
|
||||||
image?.let {
|
NavigationUtil.goToArtistOptions(activity, dataSet[adapterPosition].id, activityOptions)
|
||||||
if (albumArtistsOnly && IAlbumArtistClickListener != null) {
|
|
||||||
IAlbumArtistClickListener.onAlbumArtist(artist.name, imageContainer ?: it)
|
|
||||||
} else {
|
|
||||||
IArtistClickListener.onArtist(artist.id, imageContainer ?: it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View?): Boolean {
|
override fun onLongClick(v: View?): Boolean {
|
||||||
return toggleChecked(layoutPosition)
|
toggleChecked(adapterPosition)
|
||||||
|
return super.onLongClick(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
package code.name.monkey.retromusic.adapter.backup
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.appcompat.widget.PopupMenu
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.databinding.ItemListBackupBinding
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
|
|
||||||
class BackupAdapter(
|
|
||||||
val activity: FragmentActivity,
|
|
||||||
var dataSet: MutableList<File>,
|
|
||||||
val backupClickedListener: BackupClickedListener
|
|
||||||
) : RecyclerView.Adapter<BackupAdapter.ViewHolder>() {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
|
||||||
return ViewHolder(
|
|
||||||
ItemListBackupBinding.inflate(LayoutInflater.from(activity), parent, false)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
||||||
holder.binding.title.text = dataSet[position].nameWithoutExtension
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = dataSet.size
|
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
|
||||||
fun swapDataset(dataSet: List<File>) {
|
|
||||||
this.dataSet = ArrayList(dataSet)
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ViewHolder(val binding: ItemListBackupBinding) :
|
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
binding.menu.setOnClickListener { view ->
|
|
||||||
val popupMenu = PopupMenu(activity, view)
|
|
||||||
popupMenu.inflate(R.menu.menu_backup)
|
|
||||||
popupMenu.setOnMenuItemClickListener { menuItem ->
|
|
||||||
return@setOnMenuItemClickListener backupClickedListener.onBackupMenuClicked(
|
|
||||||
dataSet[bindingAdapterPosition],
|
|
||||||
menuItem
|
|
||||||
)
|
|
||||||
}
|
|
||||||
popupMenu.show()
|
|
||||||
}
|
|
||||||
itemView.setOnClickListener {
|
|
||||||
backupClickedListener.onBackupClicked(dataSet[bindingAdapterPosition])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackupClickedListener {
|
|
||||||
fun onBackupClicked(file: File)
|
|
||||||
|
|
||||||
fun onBackupMenuClicked(file: File, menuItem: MenuItem): Boolean
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
package code.name.monkey.retromusic.adapter.base;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.annotation.MenuRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import com.afollestad.materialcab.MaterialCab;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.R;
|
||||||
|
import code.name.monkey.retromusic.interfaces.CabHolder;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class AbsMultiSelectAdapter<VH extends RecyclerView.ViewHolder, I> extends RecyclerView.Adapter<VH> implements MaterialCab.Callback {
|
||||||
|
@Nullable
|
||||||
|
private final CabHolder cabHolder;
|
||||||
|
private final Context context;
|
||||||
|
private MaterialCab cab;
|
||||||
|
private ArrayList<I> checked;
|
||||||
|
private int menuRes;
|
||||||
|
|
||||||
|
public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) {
|
||||||
|
this.cabHolder = cabHolder;
|
||||||
|
checked = new ArrayList<>();
|
||||||
|
this.menuRes = menuRes;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setMultiSelectMenuRes(@MenuRes int menuRes) {
|
||||||
|
this.menuRes = menuRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean toggleChecked(final int position) {
|
||||||
|
if (cabHolder != null) {
|
||||||
|
I identifier = getIdentifier(position);
|
||||||
|
if (identifier == null) return false;
|
||||||
|
|
||||||
|
if (!checked.remove(identifier)) checked.add(identifier);
|
||||||
|
|
||||||
|
notifyItemChanged(position);
|
||||||
|
updateCab();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkAll() {
|
||||||
|
if (cabHolder != null) {
|
||||||
|
checked.clear();
|
||||||
|
for (int i = 0; i < getItemCount(); i++) {
|
||||||
|
I identifier = getIdentifier(i);
|
||||||
|
if (identifier != null) {
|
||||||
|
checked.add(identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
updateCab();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCab() {
|
||||||
|
if (cabHolder != null) {
|
||||||
|
if (cab == null || !cab.isActive()) {
|
||||||
|
cab = cabHolder.openCab(menuRes, this);
|
||||||
|
}
|
||||||
|
final int size = checked.size();
|
||||||
|
if (size <= 0) cab.finish();
|
||||||
|
else if (size == 1) cab.setTitle(getName(checked.get(0)));
|
||||||
|
else cab.setTitle(context.getString(R.string.x_selected, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearChecked() {
|
||||||
|
checked.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isChecked(I identifier) {
|
||||||
|
return checked.contains(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isInQuickSelectMode() {
|
||||||
|
return cab != null && cab.isActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCabCreated(MaterialCab materialCab, Menu menu) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCabItemClicked(MenuItem menuItem) {
|
||||||
|
if (menuItem.getItemId() == R.id.action_multi_select_adapter_check_all) {
|
||||||
|
checkAll();
|
||||||
|
} else {
|
||||||
|
onMultipleItemAction(menuItem, new ArrayList<>(checked));
|
||||||
|
cab.finish();
|
||||||
|
clearChecked();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCabFinished(MaterialCab materialCab) {
|
||||||
|
clearChecked();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getName(I object) {
|
||||||
|
return object.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected abstract I getIdentifier(int position);
|
||||||
|
|
||||||
|
protected abstract void onMultipleItemAction(MenuItem menuItem, ArrayList<I> selection);
|
||||||
|
}
|
|
@ -1,131 +0,0 @@
|
||||||
package code.name.monkey.retromusic.adapter.base
|
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.view.ActionMode
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
|
||||||
import androidx.activity.OnBackPressedCallback
|
|
||||||
import androidx.annotation.MenuRes
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.databinding.NumberRollViewBinding
|
|
||||||
import code.name.monkey.retromusic.views.NumberRollView
|
|
||||||
|
|
||||||
abstract class AbsMultiSelectAdapter<V : RecyclerView.ViewHolder?, I>(
|
|
||||||
open val activity: FragmentActivity, @MenuRes menuRes: Int,
|
|
||||||
) : RecyclerView.Adapter<V>(), ActionMode.Callback {
|
|
||||||
var actionMode: ActionMode? = null
|
|
||||||
private val checked: MutableList<I>
|
|
||||||
private var menuRes: Int
|
|
||||||
|
|
||||||
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
||||||
val inflater = mode?.menuInflater
|
|
||||||
inflater?.inflate(menuRes, menu)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
|
||||||
if (item?.itemId == R.id.action_multi_select_adapter_check_all) {
|
|
||||||
checkAll()
|
|
||||||
} else {
|
|
||||||
onMultipleItemAction(item!!, ArrayList(checked))
|
|
||||||
actionMode?.finish()
|
|
||||||
clearChecked()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyActionMode(mode: ActionMode?) {
|
|
||||||
clearChecked()
|
|
||||||
activity.window.statusBarColor = when {
|
|
||||||
VersionUtils.hasMarshmallow() -> Color.TRANSPARENT
|
|
||||||
else -> Color.BLACK
|
|
||||||
}
|
|
||||||
actionMode = null
|
|
||||||
onBackPressedCallback.remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkAll() {
|
|
||||||
if (actionMode != null) {
|
|
||||||
checked.clear()
|
|
||||||
for (i in 0 until itemCount) {
|
|
||||||
val identifier = getIdentifier(i)
|
|
||||||
if (identifier != null) {
|
|
||||||
checked.add(identifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notifyDataSetChanged()
|
|
||||||
updateCab()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract fun getIdentifier(position: Int): I?
|
|
||||||
|
|
||||||
protected abstract fun getName(model: I): String?
|
|
||||||
|
|
||||||
protected fun isChecked(identifier: I): Boolean {
|
|
||||||
return checked.contains(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected val isInQuickSelectMode: Boolean
|
|
||||||
get() = actionMode != null
|
|
||||||
|
|
||||||
protected abstract fun onMultipleItemAction(menuItem: MenuItem, selection: List<I>)
|
|
||||||
protected fun setMultiSelectMenuRes(@MenuRes menuRes: Int) {
|
|
||||||
this.menuRes = menuRes
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun toggleChecked(position: Int): Boolean {
|
|
||||||
val identifier = getIdentifier(position) ?: return false
|
|
||||||
if (!checked.remove(identifier)) {
|
|
||||||
checked.add(identifier)
|
|
||||||
}
|
|
||||||
notifyItemChanged(position)
|
|
||||||
updateCab()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun clearChecked() {
|
|
||||||
checked.clear()
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateCab() {
|
|
||||||
if (actionMode == null) {
|
|
||||||
actionMode = activity.startActionMode(this)?.apply {
|
|
||||||
customView = NumberRollViewBinding.inflate(activity.layoutInflater).root
|
|
||||||
}
|
|
||||||
activity.onBackPressedDispatcher.addCallback(onBackPressedCallback)
|
|
||||||
}
|
|
||||||
val size = checked.size
|
|
||||||
when {
|
|
||||||
size <= 0 -> {
|
|
||||||
actionMode?.finish()
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
actionMode?.customView?.findViewById<NumberRollView>(R.id.selection_mode_number)
|
|
||||||
?.setNumber(size, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
checked = ArrayList()
|
|
||||||
this.menuRes = menuRes
|
|
||||||
}
|
|
||||||
|
|
||||||
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
|
|
||||||
override fun handleOnBackPressed() {
|
|
||||||
if (actionMode != null) {
|
|
||||||
actionMode?.finish()
|
|
||||||
remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,115 +14,104 @@
|
||||||
|
|
||||||
package code.name.monkey.retromusic.adapter.base;
|
package code.name.monkey.retromusic.adapter.base;
|
||||||
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.FrameLayout;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.google.android.material.card.MaterialCardView;
|
import com.google.android.material.card.MaterialCardView;
|
||||||
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder;
|
|
||||||
|
|
||||||
|
import code.name.monkey.appthemehelper.util.ATHUtil;
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
|
|
||||||
public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHolder
|
public class MediaEntryViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener {
|
||||||
implements View.OnLongClickListener, View.OnClickListener {
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public View dragView;
|
public TextView title;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public View dummyContainer;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public ImageView image;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public MaterialCardView imageContainerCard;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public FrameLayout imageContainer;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public TextView imageText;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public MaterialCardView imageTextContainer;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public View mask;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public AppCompatImageView menu;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public View paletteColorContainer;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public TextView text;
|
public TextView text;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public TextView text2;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public TextView time;
|
public TextView time;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public TextView title;
|
public TextView imageText;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ViewGroup imageContainer;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public MaterialCardView imageContainerCard;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public View menu;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public View dragView;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public View paletteColorContainer;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public RecyclerView recyclerView;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ImageButton playSongs;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public View mask;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public MaterialCardView imageTextContainer;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ImageView image;
|
||||||
|
|
||||||
public MediaEntryViewHolder(@NonNull View itemView) {
|
public MediaEntryViewHolder(@NonNull View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
title = itemView.findViewById(R.id.title);
|
title = itemView.findViewById(R.id.title);
|
||||||
text = itemView.findViewById(R.id.text);
|
text = itemView.findViewById(R.id.text);
|
||||||
text2 = itemView.findViewById(R.id.text2);
|
|
||||||
|
|
||||||
image = itemView.findViewById(R.id.image);
|
image = itemView.findViewById(R.id.image);
|
||||||
time = itemView.findViewById(R.id.time);
|
time = itemView.findViewById(R.id.time);
|
||||||
|
|
||||||
imageText = itemView.findViewById(R.id.imageText);
|
imageText = itemView.findViewById(R.id.imageText);
|
||||||
|
imageContainer = itemView.findViewById(R.id.imageContainer);
|
||||||
imageTextContainer = itemView.findViewById(R.id.imageTextContainer);
|
imageTextContainer = itemView.findViewById(R.id.imageTextContainer);
|
||||||
imageContainerCard = itemView.findViewById(R.id.imageContainerCard);
|
imageContainerCard = itemView.findViewById(R.id.imageContainerCard);
|
||||||
imageContainer = itemView.findViewById(R.id.imageContainer);
|
|
||||||
|
|
||||||
menu = itemView.findViewById(R.id.menu);
|
menu = itemView.findViewById(R.id.menu);
|
||||||
dragView = itemView.findViewById(R.id.drag_view);
|
dragView = itemView.findViewById(R.id.drag_view);
|
||||||
paletteColorContainer = itemView.findViewById(R.id.paletteColorContainer);
|
paletteColorContainer = itemView.findViewById(R.id.paletteColorContainer);
|
||||||
|
recyclerView = itemView.findViewById(R.id.recycler_view);
|
||||||
mask = itemView.findViewById(R.id.mask);
|
mask = itemView.findViewById(R.id.mask);
|
||||||
dummyContainer = itemView.findViewById(R.id.dummy_view);
|
playSongs = itemView.findViewById(R.id.playSongs);
|
||||||
|
|
||||||
if (imageContainerCard != null) {
|
if (imageContainerCard != null) {
|
||||||
imageContainerCard.setCardBackgroundColor(Color.TRANSPARENT);
|
imageContainerCard.setCardBackgroundColor(ATHUtil.INSTANCE.resolveColor(itemView.getContext(), R.attr.colorPrimary));
|
||||||
}
|
}
|
||||||
itemView.setOnClickListener(this);
|
itemView.setOnClickListener(this);
|
||||||
itemView.setOnLongClickListener(this);
|
itemView.setOnLongClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public View getSwipeableContainerView() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View v) {
|
public boolean onLongClick(View v) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setImageTransitionName(@NonNull String transitionName) {
|
@Override
|
||||||
itemView.setTransitionName(transitionName);
|
public void onClick(View v) {
|
||||||
/* if (imageContainerCard != null) {
|
|
||||||
imageContainerCard.setTransitionName(transitionName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setImageTransitionName(@NonNull String transitionName) {
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
image.setTransitionName(transitionName);
|
image.setTransitionName(transitionName);
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|