Browse Source

Merge develop into feature/fga/dagger_setup

feature/bma/flipper
ganfra 2 years ago
parent
commit
4c88d8e3c2
  1. 901
      .editorconfig
  2. 33
      .github/workflows/build.yml
  3. 20
      .github/workflows/danger.yml
  4. 22
      .github/workflows/dependabot.yml
  5. 46
      .github/workflows/quality.yml
  6. 25
      .github/workflows/tests.yml
  7. 2
      .github/workflows/triage-labelled.yml
  8. 125
      .idea/codeStyles/Project.xml
  9. 5
      .idea/codeStyles/codeStyleConfig.xml
  10. 3
      README.md
  11. 27
      app/build.gradle.kts
  12. 2
      app/src/main/java/io/element/android/x/ElementRootModule.kt
  13. 2
      app/src/main/java/io/element/android/x/ElementXApplication.kt
  14. 17
      app/src/main/java/io/element/android/x/MainActivity.kt
  15. 2
      app/src/main/java/io/element/android/x/MainViewModel.kt
  16. 9
      app/src/main/java/io/element/android/x/Navigation.kt
  17. 170
      app/src/main/res/drawable/ic_launcher_background.xml
  18. 27
      app/src/main/res/drawable/ic_launcher_foreground.xml
  19. 4
      app/src/main/res/values/ic_launcher_background.xml
  20. 72
      build.gradle.kts
  21. 14
      docs/usefulLinks.md
  22. 4
      features/login/build.gradle.kts
  23. 8
      features/login/src/androidTest/java/io/element/android/x/features/login/ExampleInstrumentedTest.kt
  24. 4
      features/login/src/main/AndroidManifest.xml
  25. 34
      features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt
  26. 2
      features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt
  27. 3
      features/login/src/main/java/io/element/android/x/features/login/LoginViewState.kt
  28. 30
      features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerScreen.kt
  29. 2
      features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerViewModel.kt
  30. 2
      features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerViewState.kt
  31. 2
      features/login/src/main/java/io/element/android/x/features/login/error/ErrorFormatter.kt
  32. 22
      features/login/src/main/res/drawable/element_logo_green.xml
  33. 13
      features/login/src/main/res/drawable/ic_baseline_dataset_24.xml
  34. 5
      features/login/src/test/java/io/element/android/x/features/login/ExampleUnitTest.kt
  35. 1
      features/messages/build.gradle.kts
  36. 8
      features/messages/src/androidTest/java/io/element/android/x/features/messages/ExampleInstrumentedTest.kt
  37. 4
      features/messages/src/main/AndroidManifest.xml
  38. 14
      features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateFactory.kt
  39. 99
      features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt
  40. 2
      features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt
  41. 16
      features/messages/src/main/java/io/element/android/x/features/messages/components/MessageEventBubble.kt
  42. 4
      features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesReactionsView.kt
  43. 83
      features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemActionsSheet.kt
  44. 2
      features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemEncryptedView.kt
  45. 9
      features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemImageView.kt
  46. 2
      features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemInformativeView.kt
  47. 2
      features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemRedactedView.kt
  48. 6
      features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemTextView.kt
  49. 2
      features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemUnknownView.kt
  50. 103
      features/messages/src/main/java/io/element/android/x/features/messages/components/html/HtmlDocument.kt
  51. 1
      features/messages/src/main/java/io/element/android/x/features/messages/diff/CacheInvalidator.kt
  52. 3
      features/messages/src/main/java/io/element/android/x/features/messages/diff/MatrixTimelineItemsDiffCallback.kt
  53. 2
      features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemAction.kt
  54. 3
      features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemGroupPosition.kt
  55. 3
      features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemReactionState.kt
  56. 6
      features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt
  57. 4
      features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemContent.kt
  58. 2
      features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEmoteContent.kt
  59. 2
      features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEncryptedContent.kt
  60. 2
      features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemImageContent.kt
  61. 2
      features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemNoticeContent.kt
  62. 2
      features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemRedactedContent.kt
  63. 2
      features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextBasedContent.kt
  64. 2
      features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextContent.kt
  65. 2
      features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemUnknownContent.kt
  66. 2
      features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerViewModel.kt
  67. 2
      features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerViewState.kt
  68. 2
      features/messages/src/main/java/io/element/android/x/features/messages/util/MutableListExt.kt
  69. 5
      features/messages/src/test/java/io/element/android/x/features/messages/ExampleUnitTest.kt
  70. 3
      features/onboarding/build.gradle.kts
  71. 8
      features/onboarding/src/androidTest/java/io/element/android/x/features/login/ExampleInstrumentedTest.kt
  72. 4
      features/onboarding/src/main/AndroidManifest.xml
  73. 32
      features/onboarding/src/main/java/io/element/android/x/features/onboarding/OnBoardingScreen.kt
  74. 2
      features/onboarding/src/main/java/io/element/android/x/features/onboarding/OnBoardingViewModel.kt
  75. 10
      features/onboarding/src/main/java/io/element/android/x/features/onboarding/SplashCarouselState.kt
  76. 2
      features/onboarding/src/main/java/io/element/android/x/features/onboarding/SplashCarouselStateFactory.kt
  77. 5
      features/onboarding/src/test/java/io/element/android/x/features/login/ExampleUnitTest.kt
  78. 3
      features/roomlist/build.gradle.kts
  79. 8
      features/roomlist/src/androidTest/java/io/element/android/x/features/roomlist/ExampleInstrumentedTest.kt
  80. 4
      features/roomlist/src/main/AndroidManifest.xml
  81. 17
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/LastMessageFormatter.kt
  82. 21
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt
  83. 11
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt
  84. 36
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt
  85. 20
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomSummaryRow.kt
  86. 3
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummary.kt
  87. 3
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummaryPlaceholders.kt
  88. 8
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/stubbed.kt
  89. 5
      features/roomlist/src/test/java/io/element/android/x/features/roomlist/ExampleUnitTest.kt
  90. 3
      gradle.properties
  91. 15
      gradle/libs.versions.toml
  92. 9
      libraries/core/src/main/java/io/element/android/x/core/compose/LogCompositions.kt
  93. 4
      libraries/core/src/main/java/io/element/android/x/core/compose/OnLifecycleEvent.kt
  94. 0
      libraries/core/src/main/java/io/element/android/x/core/compose/PairCombinedPreviewParameter.kt
  95. 3
      libraries/core/src/main/java/io/element/android/x/core/compose/TextFieldLocalState.kt
  96. 2
      libraries/core/src/main/java/io/element/android/x/core/coroutine/TimingOperators.kt
  97. 4
      libraries/core/src/main/java/io/element/android/x/core/coroutine/pmap.kt
  98. 4
      libraries/core/src/main/java/io/element/android/x/core/data/Try.kt
  99. 12
      libraries/core/src/main/java/io/element/android/x/core/ui/DimensionConverter.kt
  100. 8
      libraries/core/src/main/java/io/element/android/x/core/ui/View.kt
  101. Some files were not shown because too many files have changed in this diff Show More

901
.editorconfig

@ -0,0 +1,901 @@ @@ -0,0 +1,901 @@
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
max_line_length = 160
tab_width = 4
ij_continuation_indent_size = 4
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_visual_guides = none
ij_wrap_on_typing = false
[*.java]
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = false
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_extends_list = false
ij_java_align_multiline_for = true
ij_java_align_multiline_method_parentheses = false
ij_java_align_multiline_parameters = true
ij_java_align_multiline_parameters_in_calls = false
ij_java_align_multiline_parenthesized_expression = false
ij_java_align_multiline_records = true
ij_java_align_multiline_resources = true
ij_java_align_multiline_ternary_operation = false
ij_java_align_multiline_text_blocks = false
ij_java_align_multiline_throws_list = false
ij_java_align_subsequent_simple_methods = false
ij_java_align_throws_keyword = false
ij_java_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_builder_methods = none
ij_java_call_parameters_new_line_after_left_paren = false
ij_java_call_parameters_right_paren_on_new_line = false
ij_java_call_parameters_wrap = off
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = false
ij_java_class_annotation_wrap = split_into_lines
ij_java_class_brace_style = end_of_line
ij_java_class_count_to_use_import_on_demand = 99
ij_java_class_names_in_javadoc = 1
ij_java_do_not_indent_top_level_class_members = false
ij_java_do_not_wrap_after_single_annotation = false
ij_java_do_while_brace_force = never
ij_java_doc_add_blank_line_after_description = true
ij_java_doc_add_blank_line_after_param_comments = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = true
ij_java_doc_align_exception_comments = true
ij_java_doc_align_param_comments = true
ij_java_doc_do_not_wrap_if_one_line = false
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = true
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = false
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = false
ij_java_enum_constants_wrap = off
ij_java_extends_keyword_wrap = off
ij_java_extends_list_wrap = off
ij_java_field_annotation_wrap = split_into_lines
ij_java_finally_on_new_line = false
ij_java_for_brace_force = never
ij_java_for_statement_new_line_after_left_paren = false
ij_java_for_statement_right_paren_on_new_line = false
ij_java_for_statement_wrap = off
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_if_brace_force = never
ij_java_imports_layout = $android.**, $androidx.**, $com.**, $junit.**, $net.**, $org.**, $java.**, $javax.**, $*, |, android.**, |, androidx.**, |, com.**, |, junit.**, |, net.**, |, org.**, |, java.**, |, javax.**, |, *, |
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 2
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
ij_java_keep_blank_lines_in_code = 2
ij_java_keep_blank_lines_in_declarations = 2
ij_java_keep_builder_methods_indents = false
ij_java_keep_control_statement_in_one_line = true
ij_java_keep_first_column_comment = true
ij_java_keep_indents_on_empty_lines = false
ij_java_keep_line_breaks = true
ij_java_keep_multiple_expressions_in_one_line = false
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = false
ij_java_keep_simple_lambdas_in_one_line = false
ij_java_keep_simple_methods_in_one_line = false
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = false
ij_java_line_comment_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_new_line_after_lparen_in_record_header = false
ij_java_parameter_annotation_wrap = off
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = false
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_record_components_wrap = normal
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = false
ij_java_replace_null_check = true
ij_java_replace_sum_lambda_with_method_ref = true
ij_java_resource_list_new_line_after_left_paren = false
ij_java_resource_list_right_paren_on_new_line = false
ij_java_resource_list_wrap = off
ij_java_rparen_on_new_line_in_record_header = false
ij_java_space_after_closing_angle_bracket_in_type_argument = false
ij_java_space_after_colon = true
ij_java_space_after_comma = true
ij_java_space_after_comma_in_type_arguments = true
ij_java_space_after_for_semicolon = true
ij_java_space_after_quest = true
ij_java_space_after_type_cast = true
ij_java_space_before_annotation_array_initializer_left_brace = false
ij_java_space_before_annotation_parameter_list = false
ij_java_space_before_array_initializer_left_brace = false
ij_java_space_before_catch_keyword = true
ij_java_space_before_catch_left_brace = true
ij_java_space_before_catch_parentheses = true
ij_java_space_before_class_left_brace = true
ij_java_space_before_colon = true
ij_java_space_before_colon_in_foreach = true
ij_java_space_before_comma = false
ij_java_space_before_do_left_brace = true
ij_java_space_before_else_keyword = true
ij_java_space_before_else_left_brace = true
ij_java_space_before_finally_keyword = true
ij_java_space_before_finally_left_brace = true
ij_java_space_before_for_left_brace = true
ij_java_space_before_for_parentheses = true
ij_java_space_before_for_semicolon = false
ij_java_space_before_if_left_brace = true
ij_java_space_before_if_parentheses = true
ij_java_space_before_method_call_parentheses = false
ij_java_space_before_method_left_brace = true
ij_java_space_before_method_parentheses = false
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
ij_java_space_before_quest = true
ij_java_space_before_switch_left_brace = true
ij_java_space_before_switch_parentheses = true
ij_java_space_before_synchronized_left_brace = true
ij_java_space_before_synchronized_parentheses = true
ij_java_space_before_try_left_brace = true
ij_java_space_before_try_parentheses = true
ij_java_space_before_type_parameter_list = false
ij_java_space_before_while_keyword = true
ij_java_space_before_while_left_brace = true
ij_java_space_before_while_parentheses = true
ij_java_space_inside_one_line_enum_braces = false
ij_java_space_within_empty_array_initializer_braces = false
ij_java_space_within_empty_method_call_parentheses = false
ij_java_space_within_empty_method_parentheses = false
ij_java_spaces_around_additive_operators = true
ij_java_spaces_around_assignment_operators = true
ij_java_spaces_around_bitwise_operators = true
ij_java_spaces_around_equality_operators = true
ij_java_spaces_around_lambda_arrow = true
ij_java_spaces_around_logical_operators = true
ij_java_spaces_around_method_ref_dbl_colon = false
ij_java_spaces_around_multiplicative_operators = true
ij_java_spaces_around_relational_operators = true
ij_java_spaces_around_shift_operators = true
ij_java_spaces_around_type_bounds_in_type_parameters = true
ij_java_spaces_around_unary_operator = false
ij_java_spaces_within_angle_brackets = false
ij_java_spaces_within_annotation_parentheses = false
ij_java_spaces_within_array_initializer_braces = false
ij_java_spaces_within_braces = false
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = false
ij_java_spaces_within_for_parentheses = false
ij_java_spaces_within_if_parentheses = false
ij_java_spaces_within_method_call_parentheses = false
ij_java_spaces_within_method_parentheses = false
ij_java_spaces_within_parentheses = false
ij_java_spaces_within_record_header = false
ij_java_spaces_within_switch_parentheses = false
ij_java_spaces_within_synchronized_parentheses = false
ij_java_spaces_within_try_parentheses = false
ij_java_spaces_within_while_parentheses = false
ij_java_special_else_if_treatment = true
ij_java_subclass_name_suffix = Impl
ij_java_ternary_operation_signs_on_next_line = false
ij_java_ternary_operation_wrap = off
ij_java_test_name_suffix = Test
ij_java_throws_keyword_wrap = off
ij_java_throws_list_wrap = off
ij_java_use_external_annotations = false
ij_java_use_fq_class_names = false
ij_java_use_relative_indents = false
ij_java_use_single_class_imports = true
ij_java_variable_annotation_wrap = off
ij_java_visibility = public
ij_java_while_brace_force = never
ij_java_while_on_new_line = false
ij_java_wrap_comments = false
ij_java_wrap_first_method_in_call_chain = false
ij_java_wrap_long_lines = false
[*.properties]
ij_properties_align_group_field_declarations = false
ij_properties_keep_blank_lines = false
ij_properties_key_value_delimiter = equals
ij_properties_spaces_around_key_value_delimiter = false
[.editorconfig]
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
ij_continuation_indent_size = 4
ij_xml_align_attributes = false
ij_xml_align_text = false
ij_xml_attribute_wrap = normal
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 2
ij_xml_keep_indents_on_empty_lines = false
ij_xml_keep_line_breaks = false
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = false
ij_xml_line_comment_at_first_column = true
ij_xml_space_after_tag_name = false
ij_xml_space_around_equals_in_attribute = false
ij_xml_space_inside_empty_tag = true
ij_xml_text_wrap = normal
ij_xml_use_custom_settings = true
[{*.bash,*.sh,*.zsh}]
indent_size = 2
tab_width = 2
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
ij_shell_use_unix_line_separator = true
[{*.c,*.c++,*.cc,*.cp,*.cpp,*.cu,*.cuh,*.cxx,*.h,*.h++,*.hh,*.hp,*.hpp,*.hxx,*.i,*.icc,*.ii,*.inl,*.ino,*.ipp,*.m,*.mm,*.pch,*.tcc,*.tpp}]
ij_c_add_brief_tag = false
ij_c_add_getter_prefix = true
ij_c_add_setter_prefix = true
ij_c_align_dictionary_pair_values = false
ij_c_align_group_field_declarations = false
ij_c_align_init_list_in_columns = true
ij_c_align_multiline_array_initializer_expression = true
ij_c_align_multiline_assignment = true
ij_c_align_multiline_binary_operation = true
ij_c_align_multiline_chained_methods = false
ij_c_align_multiline_for = true
ij_c_align_multiline_ternary_operation = true
ij_c_array_initializer_comma_on_next_line = false
ij_c_array_initializer_new_line_after_left_brace = false
ij_c_array_initializer_right_brace_on_new_line = false
ij_c_array_initializer_wrap = normal
ij_c_assignment_wrap = off
ij_c_binary_operation_sign_on_next_line = false
ij_c_binary_operation_wrap = normal
ij_c_blank_lines_after_class_header = 0
ij_c_blank_lines_after_imports = 1
ij_c_blank_lines_around_class = 1
ij_c_blank_lines_around_field = 0
ij_c_blank_lines_around_field_in_interface = 0
ij_c_blank_lines_around_method = 1
ij_c_blank_lines_around_method_in_interface = 1
ij_c_blank_lines_around_namespace = 0
ij_c_blank_lines_around_properties_in_declaration = 0
ij_c_blank_lines_around_properties_in_interface = 0
ij_c_blank_lines_before_imports = 1
ij_c_blank_lines_before_method_body = 0
ij_c_block_brace_placement = end_of_line
ij_c_block_brace_style = end_of_line
ij_c_block_comment_at_first_column = true
ij_c_catch_on_new_line = false
ij_c_class_brace_style = end_of_line
ij_c_class_constructor_init_list_align_multiline = true
ij_c_class_constructor_init_list_comma_on_next_line = false
ij_c_class_constructor_init_list_new_line_after_colon = never
ij_c_class_constructor_init_list_new_line_before_colon = if_long
ij_c_class_constructor_init_list_wrap = normal
ij_c_copy_is_deep = false
ij_c_create_interface_for_categories = true
ij_c_declare_generated_methods = true
ij_c_description_include_member_names = true
ij_c_discharged_short_ternary_operator = false
ij_c_do_not_add_breaks = false
ij_c_do_while_brace_force = never
ij_c_else_on_new_line = false
ij_c_enum_constants_comma_on_next_line = false
ij_c_enum_constants_wrap = on_every_item
ij_c_for_brace_force = never
ij_c_for_statement_new_line_after_left_paren = false
ij_c_for_statement_right_paren_on_new_line = false
ij_c_for_statement_wrap = off
ij_c_function_brace_placement = end_of_line
ij_c_function_call_arguments_align_multiline = true
ij_c_function_call_arguments_align_multiline_pars = false
ij_c_function_call_arguments_comma_on_next_line = false
ij_c_function_call_arguments_new_line_after_lpar = false
ij_c_function_call_arguments_new_line_before_rpar = false
ij_c_function_call_arguments_wrap = normal
ij_c_function_non_top_after_return_type_wrap = normal
ij_c_function_parameters_align_multiline = true
ij_c_function_parameters_align_multiline_pars = false
ij_c_function_parameters_comma_on_next_line = false
ij_c_function_parameters_new_line_after_lpar = false
ij_c_function_parameters_new_line_before_rpar = false
ij_c_function_parameters_wrap = normal
ij_c_function_top_after_return_type_wrap = normal
ij_c_generate_additional_eq_operators = true
ij_c_generate_additional_rel_operators = true
ij_c_generate_class_constructor = true
ij_c_generate_comparison_operators_use_std_tie = false
ij_c_generate_instance_variables_for_properties = ask
ij_c_generate_operators_as_members = true
ij_c_header_guard_style_pattern = ${PROJECT_NAME}_${FILE_NAME}_${EXT}
ij_c_if_brace_force = never
ij_c_in_line_short_ternary_operator = true
ij_c_indent_block_comment = true
ij_c_indent_c_struct_members = 4
ij_c_indent_case_from_switch = true
ij_c_indent_class_members = 4
ij_c_indent_directive_as_code = false
ij_c_indent_implementation_members = 0
ij_c_indent_inside_code_block = 4
ij_c_indent_interface_members = 0
ij_c_indent_interface_members_except_ivars_block = false
ij_c_indent_namespace_members = 4
ij_c_indent_preprocessor_directive = 0
ij_c_indent_visibility_keywords = 0
ij_c_insert_override = true
ij_c_insert_virtual_with_override = false
ij_c_introduce_auto_vars = false
ij_c_introduce_const_params = false
ij_c_introduce_const_vars = false
ij_c_introduce_generate_property = false
ij_c_introduce_generate_synthesize = true
ij_c_introduce_globals_to_header = true
ij_c_introduce_prop_to_private_category = false
ij_c_introduce_static_consts = true
ij_c_introduce_use_ns_types = false
ij_c_ivars_prefix = _
ij_c_keep_blank_lines_before_end = 2
ij_c_keep_blank_lines_before_right_brace = 2
ij_c_keep_blank_lines_in_code = 2
ij_c_keep_blank_lines_in_declarations = 2
ij_c_keep_case_expressions_in_one_line = false
ij_c_keep_control_statement_in_one_line = true
ij_c_keep_directive_at_first_column = true
ij_c_keep_first_column_comment = true
ij_c_keep_line_breaks = true
ij_c_keep_nested_namespaces_in_one_line = false
ij_c_keep_simple_blocks_in_one_line = true
ij_c_keep_simple_methods_in_one_line = true
ij_c_keep_structures_in_one_line = false
ij_c_lambda_capture_list_align_multiline = false
ij_c_lambda_capture_list_align_multiline_bracket = false
ij_c_lambda_capture_list_comma_on_next_line = false
ij_c_lambda_capture_list_new_line_after_lbracket = false
ij_c_lambda_capture_list_new_line_before_rbracket = false
ij_c_lambda_capture_list_wrap = off
ij_c_line_comment_add_space = false
ij_c_line_comment_at_first_column = true
ij_c_method_brace_placement = end_of_line
ij_c_method_call_arguments_align_by_colons = true
ij_c_method_call_arguments_align_multiline = false
ij_c_method_call_arguments_special_dictionary_pairs_treatment = true
ij_c_method_call_arguments_wrap = off
ij_c_method_call_chain_wrap = off
ij_c_method_parameters_align_by_colons = true
ij_c_method_parameters_align_multiline = false
ij_c_method_parameters_wrap = off
ij_c_namespace_brace_placement = end_of_line
ij_c_parentheses_expression_new_line_after_left_paren = false
ij_c_parentheses_expression_right_paren_on_new_line = false
ij_c_place_assignment_sign_on_next_line = false
ij_c_property_nonatomic = true
ij_c_put_ivars_to_implementation = true
ij_c_refactor_compatibility_aliases_and_classes = true
ij_c_refactor_properties_and_ivars = true
ij_c_release_style = ivar
ij_c_retain_object_parameters_in_constructor = true
ij_c_semicolon_after_method_signature = false
ij_c_shift_operation_align_multiline = true
ij_c_shift_operation_wrap = normal
ij_c_show_non_virtual_functions = false
ij_c_space_after_colon = true
ij_c_space_after_colon_in_selector = false
ij_c_space_after_comma = true
ij_c_space_after_cup_in_blocks = false
ij_c_space_after_dictionary_literal_colon = true
ij_c_space_after_for_semicolon = true
ij_c_space_after_init_list_colon = true
ij_c_space_after_method_parameter_type_parentheses = false
ij_c_space_after_method_return_type_parentheses = false
ij_c_space_after_pointer_in_declaration = false
ij_c_space_after_quest = true
ij_c_space_after_reference_in_declaration = false
ij_c_space_after_reference_in_rvalue = false
ij_c_space_after_structures_rbrace = true
ij_c_space_after_superclass_colon = true
ij_c_space_after_type_cast = true
ij_c_space_after_visibility_sign_in_method_declaration = true
ij_c_space_before_autorelease_pool_lbrace = true
ij_c_space_before_catch_keyword = true
ij_c_space_before_catch_left_brace = true
ij_c_space_before_catch_parentheses = true
ij_c_space_before_category_parentheses = true
ij_c_space_before_chained_send_message = true
ij_c_space_before_class_left_brace = true
ij_c_space_before_colon = true
ij_c_space_before_comma = false
ij_c_space_before_dictionary_literal_colon = false
ij_c_space_before_do_left_brace = true
ij_c_space_before_else_keyword = true
ij_c_space_before_else_left_brace = true
ij_c_space_before_for_left_brace = true
ij_c_space_before_for_parentheses = true
ij_c_space_before_for_semicolon = false
ij_c_space_before_if_left_brace = true
ij_c_space_before_if_parentheses = true
ij_c_space_before_init_list = false
ij_c_space_before_init_list_colon = true
ij_c_space_before_method_call_parentheses = false
ij_c_space_before_method_left_brace = true
ij_c_space_before_method_parentheses = false
ij_c_space_before_namespace_lbrace = true
ij_c_space_before_pointer_in_declaration = true
ij_c_space_before_property_attributes_parentheses = false
ij_c_space_before_protocols_brackets = true
ij_c_space_before_quest = true
ij_c_space_before_reference_in_declaration = true
ij_c_space_before_superclass_colon = true
ij_c_space_before_switch_left_brace = true
ij_c_space_before_switch_parentheses = true
ij_c_space_before_template_call_lt = false
ij_c_space_before_template_declaration_lt = false
ij_c_space_before_try_left_brace = true
ij_c_space_before_while_keyword = true
ij_c_space_before_while_left_brace = true
ij_c_space_before_while_parentheses = true
ij_c_space_between_adjacent_brackets = false
ij_c_space_between_operator_and_punctuator = false
ij_c_space_within_empty_array_initializer_braces = false
ij_c_spaces_around_additive_operators = true
ij_c_spaces_around_assignment_operators = true
ij_c_spaces_around_bitwise_operators = true
ij_c_spaces_around_equality_operators = true
ij_c_spaces_around_lambda_arrow = true
ij_c_spaces_around_logical_operators = true
ij_c_spaces_around_multiplicative_operators = true
ij_c_spaces_around_pm_operators = false
ij_c_spaces_around_relational_operators = true
ij_c_spaces_around_shift_operators = true
ij_c_spaces_around_unary_operator = false
ij_c_spaces_within_array_initializer_braces = false
ij_c_spaces_within_braces = true
ij_c_spaces_within_brackets = false
ij_c_spaces_within_cast_parentheses = false
ij_c_spaces_within_catch_parentheses = false
ij_c_spaces_within_category_parentheses = false
ij_c_spaces_within_empty_braces = false
ij_c_spaces_within_empty_function_call_parentheses = false
ij_c_spaces_within_empty_function_declaration_parentheses = false
ij_c_spaces_within_empty_lambda_capture_list_bracket = false
ij_c_spaces_within_empty_template_call_ltgt = false
ij_c_spaces_within_empty_template_declaration_ltgt = false
ij_c_spaces_within_for_parentheses = false
ij_c_spaces_within_function_call_parentheses = false
ij_c_spaces_within_function_declaration_parentheses = false
ij_c_spaces_within_if_parentheses = false
ij_c_spaces_within_lambda_capture_list_bracket = false
ij_c_spaces_within_method_parameter_type_parentheses = false
ij_c_spaces_within_method_return_type_parentheses = false
ij_c_spaces_within_parentheses = false
ij_c_spaces_within_property_attributes_parentheses = false
ij_c_spaces_within_protocols_brackets = false
ij_c_spaces_within_send_message_brackets = false
ij_c_spaces_within_switch_parentheses = false
ij_c_spaces_within_template_call_ltgt = false
ij_c_spaces_within_template_declaration_ltgt = false
ij_c_spaces_within_template_double_gt = true
ij_c_spaces_within_while_parentheses = false
ij_c_special_else_if_treatment = true
ij_c_superclass_list_after_colon = never
ij_c_superclass_list_align_multiline = true
ij_c_superclass_list_before_colon = if_long
ij_c_superclass_list_comma_on_next_line = false
ij_c_superclass_list_wrap = on_every_item
ij_c_tag_prefix_of_block_comment = at
ij_c_tag_prefix_of_line_comment = back_slash
ij_c_template_call_arguments_align_multiline = false
ij_c_template_call_arguments_align_multiline_pars = false
ij_c_template_call_arguments_comma_on_next_line = false
ij_c_template_call_arguments_new_line_after_lt = false
ij_c_template_call_arguments_new_line_before_gt = false
ij_c_template_call_arguments_wrap = off
ij_c_template_declaration_function_body_indent = false
ij_c_template_declaration_function_wrap = split_into_lines
ij_c_template_declaration_struct_body_indent = false
ij_c_template_declaration_struct_wrap = split_into_lines
ij_c_template_parameters_align_multiline = false
ij_c_template_parameters_align_multiline_pars = false
ij_c_template_parameters_comma_on_next_line = false
ij_c_template_parameters_new_line_after_lt = false
ij_c_template_parameters_new_line_before_gt = false
ij_c_template_parameters_wrap = off
ij_c_ternary_operation_signs_on_next_line = true
ij_c_ternary_operation_wrap = normal
ij_c_type_qualifiers_placement = before
ij_c_use_modern_casts = true
ij_c_use_setters_in_constructor = true
ij_c_while_brace_force = never
ij_c_while_on_new_line = false
ij_c_wrap_property_declaration = off
[{*.cmake,CMakeLists.txt}]
ij_cmake_align_multiline_parameters_in_calls = false
ij_cmake_force_commands_case = 2
ij_cmake_keep_blank_lines_in_code = 2
ij_cmake_space_before_for_parentheses = true
ij_cmake_space_before_if_parentheses = true
ij_cmake_space_before_method_call_parentheses = false
ij_cmake_space_before_method_parentheses = false
ij_cmake_space_before_while_parentheses = true
ij_cmake_spaces_within_for_parentheses = false
ij_cmake_spaces_within_if_parentheses = false
ij_cmake_spaces_within_method_call_parentheses = false
ij_cmake_spaces_within_method_parentheses = false
ij_cmake_spaces_within_while_parentheses = false
[{*.gant,*.gradle,*.groovy,*.gy}]
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
ij_groovy_align_multiline_assignment = false
ij_groovy_align_multiline_binary_operation = false
ij_groovy_align_multiline_chained_methods = false
ij_groovy_align_multiline_extends_list = false
ij_groovy_align_multiline_for = true
ij_groovy_align_multiline_list_or_map = true
ij_groovy_align_multiline_method_parentheses = false
ij_groovy_align_multiline_parameters = true
ij_groovy_align_multiline_parameters_in_calls = false
ij_groovy_align_multiline_resources = true
ij_groovy_align_multiline_ternary_operation = false
ij_groovy_align_multiline_throws_list = false
ij_groovy_align_named_args_in_map = true
ij_groovy_align_throws_keyword = false
ij_groovy_array_initializer_new_line_after_left_brace = false
ij_groovy_array_initializer_right_brace_on_new_line = false
ij_groovy_array_initializer_wrap = off
ij_groovy_assert_statement_wrap = off
ij_groovy_assignment_wrap = off
ij_groovy_binary_operation_wrap = off
ij_groovy_blank_lines_after_class_header = 0
ij_groovy_blank_lines_after_imports = 1
ij_groovy_blank_lines_after_package = 1
ij_groovy_blank_lines_around_class = 1
ij_groovy_blank_lines_around_field = 0
ij_groovy_blank_lines_around_field_in_interface = 0
ij_groovy_blank_lines_around_method = 1
ij_groovy_blank_lines_around_method_in_interface = 1
ij_groovy_blank_lines_before_imports = 1
ij_groovy_blank_lines_before_method_body = 0
ij_groovy_blank_lines_before_package = 0
ij_groovy_block_brace_style = end_of_line
ij_groovy_block_comment_at_first_column = true
ij_groovy_call_parameters_new_line_after_left_paren = false
ij_groovy_call_parameters_right_paren_on_new_line = false
ij_groovy_call_parameters_wrap = off
ij_groovy_catch_on_new_line = false
ij_groovy_class_annotation_wrap = split_into_lines
ij_groovy_class_brace_style = end_of_line
ij_groovy_class_count_to_use_import_on_demand = 5
ij_groovy_do_while_brace_force = never
ij_groovy_else_on_new_line = false
ij_groovy_enum_constants_wrap = off
ij_groovy_extends_keyword_wrap = off
ij_groovy_extends_list_wrap = off
ij_groovy_field_annotation_wrap = split_into_lines
ij_groovy_finally_on_new_line = false
ij_groovy_for_brace_force = never
ij_groovy_for_statement_new_line_after_left_paren = false
ij_groovy_for_statement_right_paren_on_new_line = false
ij_groovy_for_statement_wrap = off
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_imports_layout = *, |, javax.**, java.**, |, $*
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
ij_groovy_keep_blank_lines_before_right_brace = 2
ij_groovy_keep_blank_lines_in_code = 2
ij_groovy_keep_blank_lines_in_declarations = 2
ij_groovy_keep_control_statement_in_one_line = true
ij_groovy_keep_first_column_comment = true
ij_groovy_keep_indents_on_empty_lines = false
ij_groovy_keep_line_breaks = true
ij_groovy_keep_multiple_expressions_in_one_line = false
ij_groovy_keep_simple_blocks_in_one_line = false
ij_groovy_keep_simple_classes_in_one_line = true
ij_groovy_keep_simple_lambdas_in_one_line = true
ij_groovy_keep_simple_methods_in_one_line = true
ij_groovy_label_indent_absolute = false
ij_groovy_label_indent_size = 0
ij_groovy_lambda_brace_style = end_of_line
ij_groovy_layout_static_imports_separately = true
ij_groovy_line_comment_add_space = false
ij_groovy_line_comment_at_first_column = true
ij_groovy_method_annotation_wrap = split_into_lines
ij_groovy_method_brace_style = end_of_line
ij_groovy_method_call_chain_wrap = off
ij_groovy_method_parameters_new_line_after_left_paren = false
ij_groovy_method_parameters_right_paren_on_new_line = false
ij_groovy_method_parameters_wrap = off
ij_groovy_modifier_list_wrap = false
ij_groovy_names_count_to_use_import_on_demand = 3
ij_groovy_parameter_annotation_wrap = off
ij_groovy_parentheses_expression_new_line_after_left_paren = false
ij_groovy_parentheses_expression_right_paren_on_new_line = false
ij_groovy_prefer_parameters_wrap = false
ij_groovy_resource_list_new_line_after_left_paren = false
ij_groovy_resource_list_right_paren_on_new_line = false
ij_groovy_resource_list_wrap = off
ij_groovy_space_after_assert_separator = true
ij_groovy_space_after_colon = true
ij_groovy_space_after_comma = true
ij_groovy_space_after_comma_in_type_arguments = true
ij_groovy_space_after_for_semicolon = true
ij_groovy_space_after_quest = true
ij_groovy_space_after_type_cast = true
ij_groovy_space_before_annotation_parameter_list = false
ij_groovy_space_before_array_initializer_left_brace = false
ij_groovy_space_before_assert_separator = false
ij_groovy_space_before_catch_keyword = true
ij_groovy_space_before_catch_left_brace = true
ij_groovy_space_before_catch_parentheses = true
ij_groovy_space_before_class_left_brace = true
ij_groovy_space_before_closure_left_brace = true
ij_groovy_space_before_colon = true
ij_groovy_space_before_comma = false
ij_groovy_space_before_do_left_brace = true
ij_groovy_space_before_else_keyword = true
ij_groovy_space_before_else_left_brace = true
ij_groovy_space_before_finally_keyword = true
ij_groovy_space_before_finally_left_brace = true
ij_groovy_space_before_for_left_brace = true
ij_groovy_space_before_for_parentheses = true
ij_groovy_space_before_for_semicolon = false
ij_groovy_space_before_if_left_brace = true
ij_groovy_space_before_if_parentheses = true
ij_groovy_space_before_method_call_parentheses = false
ij_groovy_space_before_method_left_brace = true
ij_groovy_space_before_method_parentheses = false
ij_groovy_space_before_quest = true
ij_groovy_space_before_switch_left_brace = true
ij_groovy_space_before_switch_parentheses = true
ij_groovy_space_before_synchronized_left_brace = true
ij_groovy_space_before_synchronized_parentheses = true
ij_groovy_space_before_try_left_brace = true
ij_groovy_space_before_try_parentheses = true
ij_groovy_space_before_while_keyword = true
ij_groovy_space_before_while_left_brace = true
ij_groovy_space_before_while_parentheses = true
ij_groovy_space_in_named_argument = true
ij_groovy_space_in_named_argument_before_colon = false
ij_groovy_space_within_empty_array_initializer_braces = false
ij_groovy_space_within_empty_method_call_parentheses = false
ij_groovy_spaces_around_additive_operators = true
ij_groovy_spaces_around_assignment_operators = true
ij_groovy_spaces_around_bitwise_operators = true
ij_groovy_spaces_around_equality_operators = true
ij_groovy_spaces_around_lambda_arrow = true
ij_groovy_spaces_around_logical_operators = true
ij_groovy_spaces_around_multiplicative_operators = true
ij_groovy_spaces_around_regex_operators = true
ij_groovy_spaces_around_relational_operators = true
ij_groovy_spaces_around_shift_operators = true
ij_groovy_spaces_within_annotation_parentheses = false
ij_groovy_spaces_within_array_initializer_braces = false
ij_groovy_spaces_within_braces = true
ij_groovy_spaces_within_brackets = false
ij_groovy_spaces_within_cast_parentheses = false
ij_groovy_spaces_within_catch_parentheses = false
ij_groovy_spaces_within_for_parentheses = false
ij_groovy_spaces_within_gstring_injection_braces = false
ij_groovy_spaces_within_if_parentheses = false
ij_groovy_spaces_within_list_or_map = false
ij_groovy_spaces_within_method_call_parentheses = false
ij_groovy_spaces_within_method_parentheses = false
ij_groovy_spaces_within_parentheses = false
ij_groovy_spaces_within_switch_parentheses = false
ij_groovy_spaces_within_synchronized_parentheses = false
ij_groovy_spaces_within_try_parentheses = false
ij_groovy_spaces_within_tuple_expression = false
ij_groovy_spaces_within_while_parentheses = false
ij_groovy_special_else_if_treatment = true
ij_groovy_ternary_operation_wrap = off
ij_groovy_throws_keyword_wrap = off
ij_groovy_throws_list_wrap = off
ij_groovy_use_flying_geese_braces = false
ij_groovy_use_fq_class_names = false
ij_groovy_use_fq_class_names_in_javadoc = true
ij_groovy_use_relative_indents = false
ij_groovy_use_single_class_imports = true
ij_groovy_variable_annotation_wrap = off
ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_long_lines = false
[{*.gradle.kts,*.kt,*.kts,*.main.kts}]
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = false
ij_kotlin_assignment_wrap = off
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = false
ij_kotlin_call_parameters_right_paren_on_new_line = false
ij_kotlin_call_parameters_wrap = off
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = off
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_continuation_indent_for_chained_calls = true
ij_kotlin_continuation_indent_for_expression_bodies = true
ij_kotlin_continuation_indent_in_argument_lists = true
ij_kotlin_continuation_indent_in_elvis = true
ij_kotlin_continuation_indent_in_if_conditions = true
ij_kotlin_continuation_indent_in_parameter_lists = true
ij_kotlin_continuation_indent_in_supertype_lists = true
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = off
ij_kotlin_field_annotation_wrap = normal
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = false
ij_kotlin_import_nested_classes = false
ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 0
ij_kotlin_keep_blank_lines_in_code = 1
ij_kotlin_keep_blank_lines_in_declarations = 1
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 = off
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 = off
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ij_kotlin_packages_to_use_import_on_demand = kotlinx.android.synthetic.**
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_use_custom_formatting_for_modifiers = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 0
ij_kotlin_wrap_first_method_in_call_chain = false
[{*.har,*.json}]
indent_size = 2
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = true
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
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
ij_html_uniform_ident = false
[{*.yaml,*.yml}]
indent_size = 2
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
ij_yaml_indent_sequence_value = true
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_sequence_on_new_line = false
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true

33
.github/workflows/build.yml

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
name: APK Build
on:
pull_request: { }
push:
branches: [ main, develop ]
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon
jobs:
debug:
name: Build debug APKs
runs-on: ubuntu-latest
if: github.ref != 'refs/heads/main'
strategy:
fail-fast: false
# Allow all jobs on develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}', github.sha) || format('build-debug-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- name: Assemble debug APK
run: ./gradlew assembleDebug $CI_GRADLE_ARG_PROPERTIES
- name: Upload debug APKs
uses: actions/upload-artifact@v3
with:
name: elementx-debug
path: |
app/build/outputs/apk/debug/app-debug.apk

20
.github/workflows/danger.yml

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
name: Danger CI
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
name: Danger
steps:
- uses: actions/checkout@v3
- run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger
uses: danger/danger-js@11.2.0
with:
args: "--dangerfile ./tools/danger/dangerfile.js"
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
# Fallback for forks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

22
.github/workflows/dependabot.yml

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Updates for Github Actions used in the repo
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
reviewers:
- "vector-im/element-x-android-reviewers"
# Updates for Gradle dependencies used in the app
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 200
reviewers:
- "vector-im/element-x-android-reviewers"

46
.github/workflows/quality.yml

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
name: Code Quality Checks
on:
pull_request: { }
push:
branches: [ main, develop ]
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -XX:MaxPermSize=512m -Dkotlin.daemon.jvm.options="-Xmx2g" -Dkotlin.incremental=false
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon
jobs:
check:
name: Project Check Suite
runs-on: ubuntu-latest
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-develop-{0}', github.sha) || format('check-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- name: Run code quality check suite
run: ./gradlew check $CI_GRADLE_ARG_PROPERTIES
- name: Upload reports
if: always()
uses: actions/upload-artifact@v3
with:
name: linting-report
path: |
*/build/reports/**/*.*
- name: Prepare Danger
if: always()
run: |
npm install --save-dev @babel/core
npm install --save-dev @babel/plugin-transform-flow-strip-types
yarn add danger-plugin-lint-report --dev
- name: Danger lint
if: always()
uses: danger/danger-js@11.2.0
with:
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
# Fallback for forks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

25
.github/workflows/tests.yml

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
name: Test
on:
pull_request: { }
push:
branches: [ main, develop ]
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 4 --no-daemon
jobs:
tests:
name: Runs unit tests
runs-on: ubuntu-latest
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- name: Run tests
run: ./gradlew test $CI_GRADLE_ARG_PROPERTIES

2
.github/workflows/triage-labelled.yml

@ -2,7 +2,7 @@ name: Move labelled issues to correct boards and columns @@ -2,7 +2,7 @@ name: Move labelled issues to correct boards and columns
on:
issues:
types: [labeled]
types: [ labeled ]
jobs:
move_element_x_issues:

125
.idea/codeStyles/Project.xml

@ -0,0 +1,125 @@ @@ -0,0 +1,125 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

3
README.md

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
# element-x-android-poc
Proof Of Concept to run a Matrix client on Android devices using the Matrix Rust Sdk and Jetpack compose.
The plan is [here](https://github.com/vector-im/element-x-android-poc/issues/1)!
The plan is [here](https://github.com/vector-im/element-x-android-poc/issues/1)!

27
app/build.gradle.kts

@ -5,6 +5,7 @@ plugins { @@ -5,6 +5,7 @@ plugins {
alias(libs.plugins.anvil)
alias(libs.plugins.kapt)
id("com.google.firebase.appdistribution") version "3.0.2"
id("org.jetbrains.kotlinx.knit") version "0.4.0"
}
android {
@ -12,7 +13,7 @@ android { @@ -12,7 +13,7 @@ android {
defaultConfig {
applicationId = "io.element.android.x"
targetSdk = 33
targetSdk = 33 // TODO Use Versions.targetSdk
versionCode = 1
versionName = "1.0"
@ -81,7 +82,6 @@ android { @@ -81,7 +82,6 @@ android {
appId = "1:912726360885:android:e17435e0beb0303000427c"
}
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
@ -111,6 +111,26 @@ android { @@ -111,6 +111,26 @@ android {
}
}
// Knit
apply {
plugin("kotlinx-knit")
}
knit {
files = fileTree(project.rootDir) {
include(
"**/*.md",
"**/*.kt",
"*/*.kts",
)
exclude(
"**/build/**",
"*/.gradle/**",
"*/towncrier/template.md",
"**/CHANGES.md",
)
}
}
dependencies {
implementation(project(":libraries:designsystem"))
@ -134,7 +154,6 @@ dependencies { @@ -134,7 +154,6 @@ dependencies {
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.startup)
implementation(libs.coil)
implementation(libs.timber)
implementation(libs.mavericks.compose)
implementation(libs.dagger)
@ -142,4 +161,4 @@ dependencies { @@ -142,4 +161,4 @@ dependencies {
implementation(libs.showkase)
ksp(libs.showkase.processor)
}
}

2
app/src/main/java/io/element/android/x/Showkase.kt → app/src/main/java/io/element/android/x/ElementRootModule.kt

@ -4,4 +4,4 @@ import com.airbnb.android.showkase.annotation.ShowkaseRoot @@ -4,4 +4,4 @@ import com.airbnb.android.showkase.annotation.ShowkaseRoot
import com.airbnb.android.showkase.annotation.ShowkaseRootModule
@ShowkaseRoot
class ElementRootModule : ShowkaseRootModule
class ElementRootModule : ShowkaseRootModule

2
app/src/main/java/io/element/android/x/ElementXApplication.kt

@ -30,6 +30,4 @@ class ElementXApplication : Application(), DaggerComponentOwner { @@ -30,6 +30,4 @@ class ElementXApplication : Application(), DaggerComponentOwner {
initializeComponent(MavericksInitializer::class.java)
}
}
}

17
app/src/main/java/io/element/android/x/MainActivity.kt

@ -19,8 +19,17 @@ import androidx.compose.foundation.layout.padding @@ -19,8 +19,17 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
@ -109,7 +118,6 @@ class MainActivity : ComponentActivity() { @@ -109,7 +118,6 @@ class MainActivity : ComponentActivity() {
OnLifecycleEvent { _, event ->
Timber.v("OnLifecycleEvent: $event")
}
}
@Composable
@ -171,5 +179,4 @@ class MainActivity : ComponentActivity() { @@ -171,5 +179,4 @@ class MainActivity : ComponentActivity() {
fun MainContentPreview() {
MainContent(startRoute = OnBoardingScreenNavigationDestination)
}
}
}

2
app/src/main/java/io/element/android/x/MainViewModel.kt

@ -50,4 +50,4 @@ class MainViewModel @AssistedInject constructor( @@ -50,4 +50,4 @@ class MainViewModel @AssistedInject constructor(
matrixClient.startSync()
}
}
}
}

9
app/src/main/java/io/element/android/x/Navigation.kt

@ -7,7 +7,11 @@ import com.ramcosta.composedestinations.annotation.RootNavGraph @@ -7,7 +7,11 @@ import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.popUpTo
import io.element.android.x.core.di.bindings
import io.element.android.x.destinations.*
import io.element.android.x.destinations.ChangeServerScreenNavigationDestination
import io.element.android.x.destinations.LoginScreenNavigationDestination
import io.element.android.x.destinations.MessagesScreenNavigationDestination
import io.element.android.x.destinations.OnBoardingScreenNavigationDestination
import io.element.android.x.destinations.RoomListScreenNavigationDestination
import io.element.android.x.di.AppBindings
import io.element.android.x.features.login.LoginScreen
import io.element.android.x.features.login.changeserver.ChangeServerScreen
@ -84,6 +88,3 @@ fun RoomListScreenNavigation(navigator: DestinationsNavigator) { @@ -84,6 +88,3 @@ fun RoomListScreenNavigation(navigator: DestinationsNavigator) {
fun MessagesScreenNavigation(roomId: String, navigator: DestinationsNavigator) {
MessagesScreen(roomId = roomId, onBackPressed = navigator::navigateUp)
}

170
app/src/main/res/drawable/ic_launcher_background.xml

@ -1,170 +0,0 @@ @@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

27
app/src/main/res/drawable/ic_launcher_foreground.xml

@ -1,27 +0,0 @@ @@ -1,27 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group android:scaleX="0.675"
android:scaleY="0.675"
android:translateX="32.4"
android:translateY="32.4">
<path
android:pathData="M23.04,3.84C23.04,1.7192 24.7593,0 26.88,0C41.0185,0 52.48,11.4615 52.48,25.6C52.48,27.7208 50.7608,29.44 48.64,29.44C46.5193,29.44 44.8,27.7208 44.8,25.6C44.8,15.7031 36.777,7.68 26.88,7.68C24.7593,7.68 23.04,5.9608 23.04,3.84Z"
android:fillColor="#FFF"
android:fillType="evenOdd"/>
<path
android:pathData="M40.96,60.16C40.96,62.2808 39.2407,64 37.12,64C22.9815,64 11.52,52.5385 11.52,38.4C11.52,36.2792 13.2392,34.56 15.36,34.56C17.4807,34.56 19.2,36.2792 19.2,38.4C19.2,48.2969 27.223,56.32 37.12,56.32C39.2407,56.32 40.96,58.0392 40.96,60.16Z"
android:fillColor="#FFF"
android:fillType="evenOdd"/>
<path
android:pathData="M3.84,40.96C1.7192,40.96 -0,39.2407 -0,37.12C-0,22.9815 11.4615,11.52 25.6,11.52C27.7208,11.52 29.44,13.2392 29.44,15.36C29.44,17.4807 27.7208,19.2 25.6,19.2C15.7031,19.2 7.68,27.223 7.68,37.12C7.68,39.2407 5.9608,40.96 3.84,40.96Z"
android:fillColor="#FFF"
android:fillType="evenOdd"/>
<path
android:pathData="M60.16,23.04C62.2808,23.04 64,24.7593 64,26.88C64,41.0185 52.5385,52.48 38.4,52.48C36.2792,52.48 34.56,50.7608 34.56,48.64C34.56,46.5193 36.2792,44.8 38.4,44.8C48.2969,44.8 56.32,36.777 56.32,26.88C56.32,24.7593 58.0392,23.04 60.16,23.04Z"
android:fillColor="#FFF"
android:fillType="evenOdd"/>
</group>
</vector>

4
app/src/main/res/values/ic_launcher_background.xml

@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#000000</color>
</resources>

72
build.gradle.kts

@ -7,8 +7,78 @@ plugins { @@ -7,8 +7,78 @@ plugins {
alias(libs.plugins.anvil) apply false
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kapt) apply false
alias(libs.plugins.detekt)
alias(libs.plugins.ktlint)
}
tasks.register<Delete>("clean").configure {
delete(rootProject.buildDir)
}
}
allprojects {
// Detekt
apply {
plugin("io.gitlab.arturbosch.detekt")
}
detekt {
// preconfigure defaults
buildUponDefaultConfig = true
// activate all available (even unstable) rules.
allRules = true
// point to your custom config defining rules to run, overwriting default behavior
config = files("$rootDir/tools/detekt/detekt.yml")
}
dependencies {
detektPlugins("com.twitter.compose.rules:detekt:0.0.26")
}
// KtLint
apply {
plugin("org.jlleitschuh.gradle.ktlint")
}
// See https://github.com/JLLeitschuh/ktlint-gradle#configuration
configure<org.jlleitschuh.gradle.ktlint.KtlintExtension> {
// See https://github.com/pinterest/ktlint/releases/
// TODO 0.47.1 is available
version.set("0.45.1")
android.set(true)
ignoreFailures.set(false)
enableExperimentalRules.set(true)
// display the corresponding rule
verbose.set(true)
reporters {
reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.PLAIN)
// To have XML report for Danger
reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.CHECKSTYLE)
}
filter {
exclude { element -> element.file.path.contains("$buildDir/generated/") }
}
disabledRules.set(
setOf(
// TODO Re-enable these 4 rules after reformatting project
"indent",
"experimental:argument-list-wrapping",
"max-line-length",
"parameter-list-wrapping",
"spacing-between-declarations-with-comments",
"no-multi-spaces",
"experimental:spacing-between-declarations-with-annotations",
"experimental:annotation",
// - Missing newline after "("
// - Missing newline before ")"
"wrapping",
// - Unnecessary trailing comma before ")"
"experimental:trailing-comma",
// - A block comment in between other elements on the same line is disallowed
"experimental:comment-wrapping",
// - A KDoc comment after any other element on the same line must be separated by a new line
"experimental:kdoc-wrapping",
// Ignore error "Redundant curly braces", since we use it to fix false positives, for instance in "elementLogs.${i}.txt"
"string-template",
)
)
}
}

14
docs/usefulLinks.md

@ -1,14 +1,22 @@ @@ -1,14 +1,22 @@
### VersionCatalog
<!--- TOC -->
* [VersionCatalog](#versioncatalog)
* [Jetpack Compose](#jetpack-compose)
<!--- END -->
### VersionCatalog
https://docs.gradle.org/current/userguide/platforms.html
### Jetpack Compose
https://developer.android.com/jetpack/compose/mental-model
https://developer.android.com/jetpack/compose/libraries
https://developer.android.com/jetpack/compose/modifiers-list
https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md#api-guidelines-for-jetpack-compose
Preview
https://alexzh.com/jetpack-compose-preview/
https://github.com/airbnb/Showkase
https://github.com/airbnb/Showkase

4
features/login/build.gradle.kts

@ -23,8 +23,6 @@ dependencies { @@ -23,8 +23,6 @@ dependencies {
implementation(project(":libraries:elementresources"))
implementation(libs.mavericks.compose)
ksp(libs.showkase.processor)
implementation(libs.timber)
testImplementation(libs.test.junit)
androidTestImplementation(libs.test.junitext)
}
}

8
features/login/src/androidTest/java/io/element/android/x/features/login/ExampleInstrumentedTest.kt

@ -1,13 +1,11 @@ @@ -1,13 +1,11 @@
package io.element.android.x.features.login
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
@ -21,4 +19,4 @@ class ExampleInstrumentedTest { @@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.element.android.x.features.login.test", appContext.packageName)
}
}
}

4
features/login/src/main/AndroidManifest.xml

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
</manifest>
</manifest>

34
features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt

@ -2,7 +2,13 @@ @@ -2,7 +2,13 @@
package io.element.android.x.features.login
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
@ -10,8 +16,21 @@ import androidx.compose.foundation.verticalScroll @@ -10,8 +16,21 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
@ -56,19 +75,22 @@ fun LoginScreen( @@ -56,19 +75,22 @@ fun LoginScreen(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoginContent(
state: LoginViewState,
formState: LoginFormState,
modifier: Modifier = Modifier,
onChangeServer: () -> Unit = {},
onLoginChanged: (String) -> Unit = {},
onPasswordChanged: (String) -> Unit = {},
onSubmitClicked: () -> Unit = {},
onLoginWithSuccess: (MatrixClient) -> Unit = {},
) {
Surface(color = MaterialTheme.colorScheme.background) {
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.background,
) {
Box(
modifier = Modifier
.fillMaxSize()
@ -96,7 +118,7 @@ fun LoginContent( @@ -96,7 +118,7 @@ fun LoginContent(
)
// Form
Column(
//modifier = Modifier.weight(1f),
// modifier = Modifier.weight(1f),
) {
Box(
modifier = Modifier.fillMaxWidth()

2
features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt

@ -66,4 +66,4 @@ class LoginViewModel @AssistedInject constructor( @@ -66,4 +66,4 @@ class LoginViewModel @AssistedInject constructor(
formState.value = formState.value.copy(login = name)
setState { copy(loggedInClient = Uninitialized) }
}
}
}

3
features/login/src/main/java/io/element/android/x/features/login/LoginViewState.kt

@ -23,5 +23,4 @@ data class LoginFormState( @@ -23,5 +23,4 @@ data class LoginFormState(
companion object {
val Default = LoginFormState("", "")
}
}
}

30
features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerScreen.kt

@ -3,13 +3,27 @@ @@ -3,13 +3,27 @@
package io.element.android.x.features.login.changeserver
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@ -45,15 +59,18 @@ fun ChangeServerScreen( @@ -45,15 +59,18 @@ fun ChangeServerScreen(
)
}
@Composable
fun ChangeServerContent(
state: ChangeServerViewState,
modifier: Modifier = Modifier,
onChangeServer: (String) -> Unit = {},
onChangeServerSubmit: () -> Unit = {},
onChangeServerSuccess: () -> Unit = {},
) {
Surface(color = MaterialTheme.colorScheme.background) {
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.background,
) {
val scrollState = rememberScrollState()
Box(
modifier = Modifier
@ -67,8 +84,7 @@ fun ChangeServerContent( @@ -67,8 +84,7 @@ fun ChangeServerContent(
state = scrollState,
)
.padding(horizontal = 16.dp)
)
{
) {
val isError = state.changeServerAction is Fail
Box(
modifier = Modifier
@ -101,7 +117,7 @@ fun ChangeServerContent( @@ -101,7 +117,7 @@ fun ChangeServerContent(
)
Text(
text = "A server is a home for all your data.\n" +
"You choose your server and it’s easy to make one.", // TODO "Learn more.",
"You choose your server and it’s easy to make one.", // TODO "Learn more.",
modifier = Modifier
.fillMaxWidth()
.align(Alignment.CenterHorizontally)

2
features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerViewModel.kt

@ -48,4 +48,4 @@ class ChangeServerViewModel @AssistedInject constructor( @@ -48,4 +48,4 @@ class ChangeServerViewModel @AssistedInject constructor(
}
}
}
}
}

2
features/login/src/main/java/io/element/android/x/features/login/changeserver/ChangeServerViewState.kt

@ -10,4 +10,4 @@ data class ChangeServerViewState( @@ -10,4 +10,4 @@ data class ChangeServerViewState(
val changeServerAction: Async<Unit> = Uninitialized,
) : MavericksState {
val submitEnabled = homeserver.isNotEmpty() && changeServerAction !is Loading
}
}

2
features/login/src/main/java/io/element/android/x/features/login/error/ErrorFormatter.kt

@ -3,8 +3,8 @@ package io.element.android.x.features.login.error @@ -3,8 +3,8 @@ package io.element.android.x.features.login.error
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import io.element.android.x.core.uri.isValidUrl
import io.element.android.x.features.login.LoginFormState
import io.element.android.x.element.resources.R as ElementR
import io.element.android.x.features.login.LoginFormState
@Composable
fun loginError(

22
features/login/src/main/res/drawable/element_logo_green.xml

@ -1,22 +0,0 @@ @@ -1,22 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<path
android:pathData="M23.04,3.84C23.04,1.7192 24.7593,0 26.88,0C41.0185,0 52.48,11.4615 52.48,25.6C52.48,27.7208 50.7608,29.44 48.64,29.44C46.5193,29.44 44.8,27.7208 44.8,25.6C44.8,15.7031 36.777,7.68 26.88,7.68C24.7593,7.68 23.04,5.9608 23.04,3.84Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M40.96,60.16C40.96,62.2808 39.2407,64 37.12,64C22.9815,64 11.52,52.5385 11.52,38.4C11.52,36.2792 13.2392,34.56 15.36,34.56C17.4807,34.56 19.2,36.2792 19.2,38.4C19.2,48.2969 27.223,56.32 37.12,56.32C39.2407,56.32 40.96,58.0392 40.96,60.16Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M3.84,40.96C1.7192,40.96 -0,39.2407 -0,37.12C-0,22.9815 11.4615,11.52 25.6,11.52C27.7208,11.52 29.44,13.2392 29.44,15.36C29.44,17.4807 27.7208,19.2 25.6,19.2C15.7031,19.2 7.68,27.223 7.68,37.12C7.68,39.2407 5.9608,40.96 3.84,40.96Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M60.16,23.04C62.2808,23.04 64,24.7593 64,26.88C64,41.0185 52.5385,52.48 38.4,52.48C36.2792,52.48 34.56,50.7608 34.56,48.64C34.56,46.5193 36.2792,44.8 38.4,44.8C48.2969,44.8 56.32,36.777 56.32,26.88C56.32,24.7593 58.0392,23.04 60.16,23.04Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
</vector>

13
features/login/src/main/res/drawable/ic_baseline_dataset_24.xml

@ -1,5 +1,10 @@ @@ -1,5 +1,10 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM11,17H7v-4h4V17zM11,11H7V7h4V11zM17,17h-4v-4h4V17zM17,11h-4V7h4V11z"/>
<vector android:height="24dp"
android:tint="#000000"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM11,17H7v-4h4V17zM11,11H7V7h4V11zM17,17h-4v-4h4V17zM17,11h-4V7h4V11z" />
</vector>

5
features/login/src/test/java/io/element/android/x/features/login/ExampleUnitTest.kt

@ -1,9 +1,8 @@ @@ -1,9 +1,8 @@
package io.element.android.x.features.login
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
@ -14,4 +13,4 @@ class ExampleUnitTest { @@ -14,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
}

1
features/messages/build.gradle.kts

@ -23,7 +23,6 @@ dependencies { @@ -23,7 +23,6 @@ dependencies {
implementation(project(":libraries:textcomposer"))
implementation(libs.mavericks.compose)
implementation(libs.coil.compose)
implementation(libs.timber)
implementation(libs.datetime)
implementation(libs.accompanist.flowlayout)
implementation(libs.androidx.recyclerview)

8
features/messages/src/androidTest/java/io/element/android/x/features/messages/ExampleInstrumentedTest.kt

@ -1,13 +1,11 @@ @@ -1,13 +1,11 @@
package io.element.android.x.features.messages
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
@ -21,4 +19,4 @@ class ExampleInstrumentedTest { @@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.element.android.x.features.messages.test", appContext.packageName)
}
}
}

4
features/messages/src/main/AndroidManifest.xml

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
</manifest>
</manifest>

14
features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateFactory.kt

@ -9,12 +9,20 @@ import io.element.android.x.features.messages.model.AggregatedReaction @@ -9,12 +9,20 @@ import io.element.android.x.features.messages.model.AggregatedReaction
import io.element.android.x.features.messages.model.MessagesItemGroupPosition
import io.element.android.x.features.messages.model.MessagesItemReactionState
import io.element.android.x.features.messages.model.MessagesTimelineItemState
import io.element.android.x.features.messages.model.content.*
import io.element.android.x.features.messages.model.content.MessagesTimelineItemContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemEmoteContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemEncryptedContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemImageContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemNoticeContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent
import io.element.android.x.features.messages.util.invalidateLast
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.media.MediaResolver
import io.element.android.x.matrix.room.MatrixRoom
import io.element.android.x.matrix.timeline.MatrixTimelineItem
import kotlin.system.measureTimeMillis
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -28,7 +36,6 @@ import org.matrix.rustcomponents.sdk.FormattedBody @@ -28,7 +36,6 @@ import org.matrix.rustcomponents.sdk.FormattedBody
import org.matrix.rustcomponents.sdk.MessageFormat
import org.matrix.rustcomponents.sdk.MessageType
import timber.log.Timber
import kotlin.system.measureTimeMillis
class MessageTimelineItemStateFactory(
private val client: MatrixClient,
@ -83,7 +90,6 @@ class MessageTimelineItemStateFactory( @@ -83,7 +90,6 @@ class MessageTimelineItemStateFactory(
timelineItemStates.emit(newTimelineItemStates)
}
private fun calculateAndApplyDiff(newTimelineItems: List<MatrixTimelineItem>) {
val timeToDiff = measureTimeMillis {
val diffCallback =
@ -192,7 +198,6 @@ class MessageTimelineItemStateFactory( @@ -192,7 +198,6 @@ class MessageTimelineItemStateFactory(
htmlDocument = messageType.content.formatted?.toHtmlDocument()
)
else -> MessagesTimelineItemUnknownContent
}
}
@ -232,5 +237,4 @@ class MessageTimelineItemStateFactory( @@ -232,5 +237,4 @@ class MessageTimelineItemStateFactory(
.resolve(url, kind = MediaResolver.Kind.Thumbnail(size.value))
return AvatarData(name, model, size)
}
}

99
features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt

@ -6,9 +6,26 @@ @@ -6,9 +6,26 @@
package io.element.android.x.features.messages
import Avatar
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
@ -20,8 +37,24 @@ import androidx.compose.material.icons.Icons @@ -20,8 +37,24 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.End
import androidx.compose.ui.Alignment.Companion.Start
@ -41,18 +74,40 @@ import com.airbnb.mvrx.compose.mavericksViewModel @@ -41,18 +74,40 @@ import com.airbnb.mvrx.compose.mavericksViewModel
import io.element.android.x.core.compose.LogCompositions
import io.element.android.x.core.compose.PairCombinedPreviewParameter
import io.element.android.x.core.data.StableCharSequence
import io.element.android.x.designsystem.components.avatar.Avatar
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.features.messages.components.*
import io.element.android.x.features.messages.model.*
import io.element.android.x.features.messages.model.content.*
import io.element.android.x.features.messages.components.MessageEventBubble
import io.element.android.x.features.messages.components.MessagesReactionsView
import io.element.android.x.features.messages.components.MessagesTimelineItemEncryptedView
import io.element.android.x.features.messages.components.MessagesTimelineItemImageView
import io.element.android.x.features.messages.components.MessagesTimelineItemRedactedView
import io.element.android.x.features.messages.components.MessagesTimelineItemTextView
import io.element.android.x.features.messages.components.MessagesTimelineItemUnknownView
import io.element.android.x.features.messages.components.TimelineItemActionsScreen
import io.element.android.x.features.messages.model.AggregatedReaction
import io.element.android.x.features.messages.model.MessagesItemGroupPosition
import io.element.android.x.features.messages.model.MessagesItemGroupPositionProvider
import io.element.android.x.features.messages.model.MessagesItemReactionState
import io.element.android.x.features.messages.model.MessagesTimelineItemState
import io.element.android.x.features.messages.model.MessagesViewState
import io.element.android.x.features.messages.model.content.MessagesTimelineItemContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemContentProvider
import io.element.android.x.features.messages.model.content.MessagesTimelineItemEncryptedContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemImageContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent
import io.element.android.x.features.messages.textcomposer.MessageComposerViewModel
import io.element.android.x.features.messages.textcomposer.MessageComposerViewState
import io.element.android.x.textcomposer.MessageComposerMode
import io.element.android.x.textcomposer.TextComposer
import java.lang.Math.random
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import timber.log.Timber
import java.lang.Math.random
@Composable
fun MessagesScreen(
@ -61,7 +116,6 @@ fun MessagesScreen( @@ -61,7 +116,6 @@ fun MessagesScreen(
viewModel: MessagesViewModel = mavericksViewModel(argsFactory = { roomId }),
composerViewModel: MessageComposerViewModel = mavericksViewModel(argsFactory = { roomId })
) {
fun onSendMessage(textMessage: String) {
viewModel.sendMessage(textMessage)
composerViewModel.updateText("")
@ -88,7 +142,7 @@ fun MessagesScreen( @@ -88,7 +142,7 @@ fun MessagesScreen(
MessagesScreenContent(
roomTitle = roomTitle,
roomAvatar = roomAvatar,
timelineItems = timelineItems().orEmpty(),
timelineItems = timelineItems().orEmpty().toImmutableList(),
hasMoreToLoad = hasMoreToLoad,
onReachedLoadMore = viewModel::loadMore,
onBackPressed = onBackPressed,
@ -130,7 +184,7 @@ fun MessagesScreen( @@ -130,7 +184,7 @@ fun MessagesScreen(
fun MessagesScreenContent(
roomTitle: String?,
roomAvatar: AvatarData?,
timelineItems: List<MessagesTimelineItemState>,
timelineItems: ImmutableList<MessagesTimelineItemState>,
hasMoreToLoad: Boolean,
onReachedLoadMore: () -> Unit,
onBackPressed: () -> Unit,
@ -146,9 +200,11 @@ fun MessagesScreenContent( @@ -146,9 +200,11 @@ fun MessagesScreenContent(
composerCanSendMessage: Boolean,
composerText: StableCharSequence?,
snackbarHostState: SnackbarHostState,
modifier: Modifier = Modifier,
) {
LogCompositions(tag = "MessagesScreen", msg = "Content")
Scaffold(
modifier = modifier,
contentWindowInsets = WindowInsets.statusBars,
topBar = {
MessagesTopAppBar(
@ -187,7 +243,7 @@ fun MessagesScreenContent( @@ -187,7 +243,7 @@ fun MessagesScreenContent(
@Composable
fun MessagesContent(
timelineItems: List<MessagesTimelineItemState>,
timelineItems: ImmutableList<MessagesTimelineItemState>,
hasMoreToLoad: Boolean,
onReachedLoadMore: () -> Unit,
onSendMessage: (String) -> Unit,
@ -203,7 +259,6 @@ fun MessagesContent( @@ -203,7 +259,6 @@ fun MessagesContent(
composerText: StableCharSequence?,
modifier: Modifier = Modifier
) {
val lazyListState = rememberLazyListState()
Column(
modifier = modifier
@ -249,9 +304,11 @@ fun MessagesContent( @@ -249,9 +304,11 @@ fun MessagesContent(
fun MessagesTopAppBar(
roomTitle: String?,
roomAvatar: AvatarData?,
onBackPressed: () -> Unit,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
) {
TopAppBar(
modifier = modifier,
navigationIcon = {
IconButton(onClick = onBackPressed) {
Icon(
@ -282,7 +339,7 @@ fun MessagesTopAppBar( @@ -282,7 +339,7 @@ fun MessagesTopAppBar(
@Composable
fun TimelineItems(
lazyListState: LazyListState,
timelineItems: List<MessagesTimelineItemState>,
timelineItems: ImmutableList<MessagesTimelineItemState>,
highlightedEventId: String?,
modifier: Modifier = Modifier,
hasMoreToLoad: Boolean = false,
@ -322,7 +379,6 @@ fun TimelineItems( @@ -322,7 +379,6 @@ fun TimelineItems(
onLoadMore = onReachedLoadMore
)
}
}
private fun MessagesTimelineItemState.key(): String {
@ -339,7 +395,6 @@ private fun MessagesTimelineItemState.contentType(): Int { @@ -339,7 +395,6 @@ private fun MessagesTimelineItemState.contentType(): Int {
}
}
@Composable
fun TimelineItemRow(
timelineItem: MessagesTimelineItemState,
@ -426,7 +481,6 @@ fun MessageEventRow( @@ -426,7 +481,6 @@ fun MessageEventRow(
content = messageEvent.content,
modifier = contentModifier
)
else -> TODO() /* compiler issue ? */
}
}
MessagesReactionsView(
@ -471,8 +525,8 @@ private fun MessageSenderInformation( @@ -471,8 +525,8 @@ private fun MessageSenderInformation(
@Composable
internal fun BoxScope.MessagesScrollHelper(
lazyListState: LazyListState,
timelineItems: List<MessagesTimelineItemState>,
onLoadMore: () -> Unit,
timelineItems: ImmutableList<MessagesTimelineItemState>,
onLoadMore: () -> Unit = {},
) {
val coroutineScope = rememberCoroutineScope()
val firstVisibleItemIndex by remember { derivedStateOf { lazyListState.firstVisibleItemIndex } }
@ -526,7 +580,6 @@ internal fun BoxScope.MessagesScrollHelper( @@ -526,7 +580,6 @@ internal fun BoxScope.MessagesScrollHelper(
Icon(Icons.Default.ArrowDownward, "")
}
}
}
@Composable
@ -543,7 +596,6 @@ internal fun MessagesLoadingMoreIndicator() { @@ -543,7 +596,6 @@ internal fun MessagesLoadingMoreIndicator() {
color = MaterialTheme.colorScheme.primary
)
}
}
class MessagesItemGroupPositionToMessagesTimelineItemContentProvider :
@ -551,6 +603,7 @@ class MessagesItemGroupPositionToMessagesTimelineItemContentProvider : @@ -551,6 +603,7 @@ class MessagesItemGroupPositionToMessagesTimelineItemContentProvider :
MessagesItemGroupPositionProvider() to MessagesTimelineItemContentProvider()
)
@Suppress("PreviewPublic")
@Preview(showBackground = true)
@Composable
fun TimelineItemsPreview(
@ -559,7 +612,7 @@ fun TimelineItemsPreview( @@ -559,7 +612,7 @@ fun TimelineItemsPreview(
) {
TimelineItems(
lazyListState = LazyListState(),
timelineItems = listOf(
timelineItems = persistentListOf(
// 3 items (First Middle Last) with isMine = false
createMessageEvent(
isMine = false,

2
features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt

@ -218,4 +218,4 @@ class MessagesViewModel @AssistedInject constructor( @@ -218,4 +218,4 @@ class MessagesViewModel @AssistedInject constructor(
timeline.callback = null
timeline.dispose()
}
}
}

16
features/messages/src/main/java/io/element/android/x/features/messages/components/MessageEventBubble.kt

@ -13,7 +13,13 @@ import androidx.compose.ui.Modifier @@ -13,7 +13,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
import io.element.android.x.designsystem.*
import io.element.android.x.designsystem.LocalIsDarkTheme
import io.element.android.x.designsystem.MessageHighlightDark
import io.element.android.x.designsystem.MessageHighlightLight
import io.element.android.x.designsystem.SystemGrey5Dark
import io.element.android.x.designsystem.SystemGrey5Light
import io.element.android.x.designsystem.SystemGrey6Dark
import io.element.android.x.designsystem.SystemGrey6Light
import io.element.android.x.features.messages.model.MessagesItemGroupPosition
private val BUBBLE_RADIUS = 16.dp
@ -26,9 +32,9 @@ fun MessageEventBubble( @@ -26,9 +32,9 @@ fun MessageEventBubble(
interactionSource: MutableInteractionSource,
isHighlighted: Boolean,
modifier: Modifier = Modifier,
onClick: () -> Unit,
onLongClick: () -> Unit,
content: @Composable () -> Unit,
onClick: () -> Unit = {},
onLongClick: () -> Unit = {},
content: @Composable () -> Unit = {},
) {
fun bubbleShape(): Shape {
return when (groupPosition) {
@ -102,4 +108,4 @@ fun MessageEventBubble( @@ -102,4 +108,4 @@ fun MessageEventBubble(
shape = bubbleShape,
content = content
)
}
}

4
features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesReactionsView.kt

@ -24,7 +24,7 @@ fun MessagesReactionsView( @@ -24,7 +24,7 @@ fun MessagesReactionsView(
reactionsState: MessagesItemReactionState,
modifier: Modifier = Modifier,
) {
if(reactionsState.reactions.isEmpty()) return
if (reactionsState.reactions.isEmpty()) return
FlowRow(
modifier = modifier,
mainAxisSpacing = 2.dp,
@ -53,4 +53,4 @@ fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Mo @@ -53,4 +53,4 @@ fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Mo
Text(text = reaction.count, color = MaterialTheme.colorScheme.secondary, fontSize = 12.sp)
}
}
}
}

83
features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemActionsSheet.kt

@ -3,14 +3,28 @@ @@ -3,14 +3,28 @@
package io.element.android.x.features.messages.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ListItem
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.unit.dp
import com.airbnb.mvrx.compose.collectAsState
import io.element.android.x.designsystem.components.VectorIcon
@ -21,7 +35,6 @@ import io.element.android.x.features.messages.model.MessagesTimelineItemState @@ -21,7 +35,6 @@ import io.element.android.x.features.messages.model.MessagesTimelineItemState
import io.element.android.x.features.messages.model.MessagesViewState
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
import io.element.android.x.features.messages.textcomposer.MessageComposerViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@ -63,7 +76,6 @@ fun TimelineItemActionsScreen( @@ -63,7 +76,6 @@ fun TimelineItemActionsScreen(
}
}
ModalBottomSheetLayout(
modifier = modifier,
sheetState = modalBottomSheetState,
@ -71,46 +83,47 @@ fun TimelineItemActionsScreen( @@ -71,46 +83,47 @@ fun TimelineItemActionsScreen(
SheetContent(
actionsSheetState = itemActionsSheetState(),
onActionClicked = ::onItemActionClicked,
modifier = Modifier.navigationBarsPadding().imePadding()
modifier = Modifier
.navigationBarsPadding()
.imePadding()
)
}
) {}
}
@Composable
private fun SheetContent(
actionsSheetState: MessagesItemActionsSheetState?,
onActionClicked: (MessagesItemAction, MessagesTimelineItemState.MessageEvent) -> Unit,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
onActionClicked: (MessagesItemAction, MessagesTimelineItemState.MessageEvent) -> Unit = { _, _ -> },
) {
if (actionsSheetState == null || actionsSheetState.actions.isEmpty()) {
// Crashes if sheetContent size is zero
Box(modifier = modifier.size(1.dp))
return
}
LazyColumn(
modifier = modifier
.fillMaxWidth()
) {
items(actionsSheetState.actions) {
ListItem(
modifier = Modifier.clickable {
onActionClicked(it, actionsSheetState.targetItem)
},
text = {
Text(
text = it.title,
color = if (it.destructive) MaterialTheme.colors.error else Color.Unspecified,
)
},
icon = {
VectorIcon(
resourceId = it.icon,
tint = if (it.destructive) MaterialTheme.colors.error else LocalContentColor.current,
)
}
)
} else {
LazyColumn(
modifier = modifier
.fillMaxWidth()
) {
items(actionsSheetState.actions) {
ListItem(
modifier = Modifier.clickable {
onActionClicked(it, actionsSheetState.targetItem)
},
text = {
Text(
text = it.title,
color = if (it.destructive) MaterialTheme.colors.error else Color.Unspecified,
)
},
icon = {
VectorIcon(
resourceId = it.icon,
tint = if (it.destructive) MaterialTheme.colors.error else LocalContentColor.current,
)
}
)
}
}
}
}
}

2
features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemEncryptedView.kt

@ -17,4 +17,4 @@ fun MessagesTimelineItemEncryptedView( @@ -17,4 +17,4 @@ fun MessagesTimelineItemEncryptedView(
icon = Icons.Default.Warning,
modifier = modifier
)
}
}

9
features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemImageView.kt

@ -6,7 +6,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi @@ -6,7 +6,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
@ -16,7 +15,6 @@ import androidx.compose.ui.Modifier @@ -16,7 +15,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.request.ImageRequest
import io.element.android.x.features.messages.model.content.MessagesTimelineItemImageContent
@ -26,9 +24,9 @@ fun MessagesTimelineItemImageView( @@ -26,9 +24,9 @@ fun MessagesTimelineItemImageView(
content: MessagesTimelineItemImageContent,
modifier: Modifier = Modifier
) {
val widthPercent = if(content.aspectRatio > 1f){
val widthPercent = if (content.aspectRatio > 1f) {
1f
}else {
} else {
0.7f
}
Box(
@ -37,7 +35,6 @@ fun MessagesTimelineItemImageView( @@ -37,7 +35,6 @@ fun MessagesTimelineItemImageView(
.aspectRatio(content.aspectRatio),
contentAlignment = Alignment.Center,
) {
var isLoading = rememberSaveable(content.imageMeta) { mutableStateOf(true) }
val context = LocalContext.current
val model = ImageRequest.Builder(context)
@ -52,4 +49,4 @@ fun MessagesTimelineItemImageView( @@ -52,4 +49,4 @@ fun MessagesTimelineItemImageView(
onSuccess = { isLoading.value = false },
)
}
}
}

2
features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemInformativeView.kt

@ -40,4 +40,4 @@ fun MessagesTimelineItemInformativeView( @@ -40,4 +40,4 @@ fun MessagesTimelineItemInformativeView(
text = text
)
}
}
}

2
features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemRedactedView.kt

@ -17,4 +17,4 @@ fun MessagesTimelineItemRedactedView( @@ -17,4 +17,4 @@ fun MessagesTimelineItemRedactedView(
icon = Icons.Default.Delete,
modifier = modifier
)
}
}

6
features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemTextView.kt

@ -22,8 +22,8 @@ fun MessagesTimelineItemTextView( @@ -22,8 +22,8 @@ fun MessagesTimelineItemTextView(
content: MessagesTimelineItemTextBasedContent,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit,
onTextLongClicked: () -> Unit,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
val htmlDocument = content.htmlDocument
if (htmlDocument != null) {
@ -74,4 +74,4 @@ private fun String.linkify( @@ -74,4 +74,4 @@ private fun String.linkify(
end = end
)
}
}
}

2
features/messages/src/main/java/io/element/android/x/features/messages/components/MessagesTimelineItemUnknownView.kt

@ -17,4 +17,4 @@ fun MessagesTimelineItemUnknownView( @@ -17,4 +17,4 @@ fun MessagesTimelineItemUnknownView(
icon = Icons.Default.Info,
modifier = modifier
)
}
}

103
features/messages/src/main/java/io/element/android/x/features/messages/components/html/HtmlDocument.kt

@ -9,17 +9,25 @@ import androidx.compose.foundation.layout.padding @@ -9,17 +9,25 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material3.*
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.*
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.flowlayout.FlowRow
@ -27,6 +35,7 @@ import io.element.android.x.designsystem.LinkColor @@ -27,6 +35,7 @@ import io.element.android.x.designsystem.LinkColor
import io.element.android.x.designsystem.components.ClickableLinkText
import io.element.android.x.matrix.permalink.PermalinkData
import io.element.android.x.matrix.permalink.PermalinkParser
import kotlinx.collections.immutable.persistentMapOf
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.nodes.Node
@ -38,34 +47,33 @@ private const val chipId = "chip" @@ -38,34 +47,33 @@ private const val chipId = "chip"
fun HtmlDocument(
document: Document,
interactionSource: MutableInteractionSource,
onTextClicked: () -> Unit,
onTextLongClicked: () -> Unit,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
HtmlBody(
body = document.body(),
interactionSource = interactionSource,
modifier = modifier,
onTextClicked = onTextClicked,
onTextLongClicked = onTextLongClicked,
interactionSource = interactionSource
)
}
@Composable
private fun HtmlBody(
body: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit,
onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
@Composable
fun NodesFlowRode(
nodes: Iterator<Node>,
onTextClicked: () -> Unit,
onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) = FlowRow(
mainAxisSpacing = 2.dp,
crossAxisSpacing = 8.dp,
@ -106,9 +114,9 @@ private fun HtmlBody( @@ -106,9 +114,9 @@ private fun HtmlBody(
while (nodesIterator.hasNext()) {
NodesFlowRode(
nodes = nodesIterator,
interactionSource = interactionSource,
onTextClicked = onTextClicked,
onTextLongClicked = onTextLongClicked,
interactionSource = interactionSource
)
}
}
@ -125,10 +133,10 @@ private fun Element.isInline(): Boolean { @@ -125,10 +133,10 @@ private fun Element.isInline(): Boolean {
@Composable
private fun HtmlBlock(
element: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit,
onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
val blockModifier = modifier
.padding(top = 4.dp)
@ -183,10 +191,10 @@ private fun HtmlBlock( @@ -183,10 +191,10 @@ private fun HtmlBlock(
@Composable
private fun HtmlInline(
element: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit,
onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
Box(modifier) {
val styledText = buildAnnotatedString {
@ -202,7 +210,10 @@ private fun HtmlInline( @@ -202,7 +210,10 @@ private fun HtmlInline(
}
@Composable
private fun HtmlPreformatted(pre: Element, modifier: Modifier = Modifier) {
private fun HtmlPreformatted(
pre: Element,
modifier: Modifier = Modifier
) {
val isCode = pre.firstElementChild()?.normalName() == "code"
val backgroundColor =
if (isCode) MaterialTheme.colorScheme.codeBackground() else Color.Unspecified
@ -221,9 +232,10 @@ private fun HtmlPreformatted(pre: Element, modifier: Modifier = Modifier) { @@ -221,9 +232,10 @@ private fun HtmlPreformatted(pre: Element, modifier: Modifier = Modifier) {
@Composable
private fun HtmlParagraph(
paragraph: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
Box(modifier) {
val styledText = buildAnnotatedString {
@ -239,9 +251,10 @@ private fun HtmlParagraph( @@ -239,9 +251,10 @@ private fun HtmlParagraph(
@Composable
private fun HtmlBlockquote(
blockquote: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
val color = MaterialTheme.colorScheme.onBackground
Box(
@ -268,13 +281,13 @@ private fun HtmlBlockquote( @@ -268,13 +281,13 @@ private fun HtmlBlockquote(
}
}
@Composable
private fun HtmlHeading(
heading: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
val style = when (heading.normalName()) {
"h1" -> MaterialTheme.typography.headlineLarge.copy(fontSize = 30.sp)
@ -304,9 +317,10 @@ private fun HtmlHeading( @@ -304,9 +317,10 @@ private fun HtmlHeading(
@Composable
private fun HtmlMxReply(
mxReply: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
val blockquote = mxReply.childNodes().firstOrNull() ?: return
val shape = RoundedCornerShape(12.dp)
@ -356,9 +370,10 @@ private fun HtmlMxReply( @@ -356,9 +370,10 @@ private fun HtmlMxReply(
@Composable
private fun HtmlOrderedList(
orderedList: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
var number = 1
val delimiter = "."
@ -381,9 +396,10 @@ private fun HtmlOrderedList( @@ -381,9 +396,10 @@ private fun HtmlOrderedList(
@Composable
private fun HtmlUnorderedList(
unorderedList: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
) {
val marker = ""
HtmlListItems(
@ -402,14 +418,14 @@ private fun HtmlUnorderedList( @@ -402,14 +418,14 @@ private fun HtmlUnorderedList(
}
}
@Composable
private fun HtmlListItems(
list: Element,
modifier: Modifier = Modifier,
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
interactionSource: MutableInteractionSource,
content: @Composable (node: TextNode) -> Unit
modifier: Modifier = Modifier,
onTextClicked: () -> Unit = {},
onTextLongClicked: () -> Unit = {},
content: @Composable (node: TextNode) -> Unit = {}
) {
Column(modifier = modifier) {
for (node in list.children()) {
@ -420,13 +436,12 @@ private fun HtmlListItems( @@ -420,13 +436,12 @@ private fun HtmlListItems(
}
is Element -> HtmlBlock(
element = innerNode,
modifier = modifier.padding(start = 4.dp),
modifier = Modifier.padding(start = 4.dp),
onTextClicked = onTextClicked, onTextLongClicked = onTextLongClicked,
interactionSource = interactionSource
)
}
}
}
}
}
@ -439,7 +454,6 @@ private fun AnnotatedString.Builder.appendInlineChildrenElements( @@ -439,7 +454,6 @@ private fun AnnotatedString.Builder.appendInlineChildrenElements(
childNodes: List<Node>,
colors: ColorScheme
) {
for (node in childNodes) {
when (node) {
is TextNode -> {
@ -452,7 +466,6 @@ private fun AnnotatedString.Builder.appendInlineChildrenElements( @@ -452,7 +466,6 @@ private fun AnnotatedString.Builder.appendInlineChildrenElements(
}
}
private fun AnnotatedString.Builder.appendInlineElement(element: Element, colors: ColorScheme) {
when (element.normalName()) {
"br" -> {
@ -490,7 +503,6 @@ private fun AnnotatedString.Builder.appendInlineElement(element: Element, colors @@ -490,7 +503,6 @@ private fun AnnotatedString.Builder.appendInlineElement(element: Element, colors
appendInlineChildrenElements(element.childNodes(), colors)
}
}
}
private fun AnnotatedString.Builder.appendLink(link: Element) {
@ -521,13 +533,13 @@ private fun AnnotatedString.Builder.appendLink(link: Element) { @@ -521,13 +533,13 @@ private fun AnnotatedString.Builder.appendLink(link: Element) {
@Composable
private fun HtmlText(
text: AnnotatedString,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
style: TextStyle = LocalTextStyle.current,
onClick: () -> Unit,
onLongClick: () -> Unit,
interactionSource: MutableInteractionSource,
onClick: () -> Unit = {},
onLongClick: () -> Unit = {},
) {
val inlineContentMap = emptyMap<String, InlineTextContent>()
val inlineContentMap = persistentMapOf<String, InlineTextContent>()
ClickableLinkText(
text = text,
linkAnnotationTag = "URL",
@ -539,4 +551,3 @@ private fun HtmlText( @@ -539,4 +551,3 @@ private fun HtmlText(
onLongClick = onLongClick
)
}

1
features/messages/src/main/java/io/element/android/x/features/messages/diff/CacheInvalidator.kt

@ -37,5 +37,4 @@ internal class CacheInvalidator(private val itemStatesCache: MutableList<Message @@ -37,5 +37,4 @@ internal class CacheInvalidator(private val itemStatesCache: MutableList<Message
itemStatesCache.removeAt(position)
}
}
}

3
features/messages/src/main/java/io/element/android/x/features/messages/diff/MatrixTimelineItemsDiffCallback.kt

@ -31,5 +31,4 @@ internal class MatrixTimelineItemsDiffCallback( @@ -31,5 +31,4 @@ internal class MatrixTimelineItemsDiffCallback(
val newItem = newList.getOrNull(newItemPosition)
return oldItem == newItem
}
}
}

2
features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemAction.kt

@ -15,4 +15,4 @@ sealed class MessagesItemAction( @@ -15,4 +15,4 @@ sealed class MessagesItemAction(
object Redact : MessagesItemAction("Redact", VectorIcons.Delete, destructive = true)
object Reply : MessagesItemAction("Reply", VectorIcons.Reply)
object Edit : MessagesItemAction("Edit", VectorIcons.Edit)
}
}

3
features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemGroupPosition.kt

@ -12,7 +12,6 @@ sealed interface MessagesItemGroupPosition { @@ -12,7 +12,6 @@ sealed interface MessagesItemGroupPosition {
First, None -> true
else -> false
}
}
internal class MessagesItemGroupPositionProvider : PreviewParameterProvider<MessagesItemGroupPosition> {
@ -22,4 +21,4 @@ internal class MessagesItemGroupPositionProvider : PreviewParameterProvider<Mess @@ -22,4 +21,4 @@ internal class MessagesItemGroupPositionProvider : PreviewParameterProvider<Mess
MessagesItemGroupPosition.Last,
MessagesItemGroupPosition.None,
)
}
}

3
features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemReactionState.kt

@ -6,9 +6,10 @@ import androidx.compose.runtime.Stable @@ -6,9 +6,10 @@ import androidx.compose.runtime.Stable
data class MessagesItemReactionState(
val reactions: List<AggregatedReaction>
)
@Stable
data class AggregatedReaction(
val key: String,
val count: String,
val isHighlighted: Boolean = false
)
)

6
features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt

@ -23,11 +23,5 @@ sealed interface MessagesTimelineItemState { @@ -23,11 +23,5 @@ sealed interface MessagesTimelineItemState {
val showSenderInformation = groupPosition.isNew() && !isMine
val safeSenderName: String = senderDisplayName ?: senderId
}
}

4
features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemContent.kt

@ -2,8 +2,6 @@ package io.element.android.x.features.messages.model.content @@ -2,8 +2,6 @@ package io.element.android.x.features.messages.model.content
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import org.matrix.rustcomponents.sdk.EncryptedMessage
import org.matrix.rustcomponents.sdk.FormattedBody
import org.matrix.rustcomponents.sdk.MessageFormat
sealed interface MessagesTimelineItemContent
@ -28,4 +26,4 @@ class MessagesTimelineItemContentProvider : PreviewParameterProvider<MessagesTim @@ -28,4 +26,4 @@ class MessagesTimelineItemContentProvider : PreviewParameterProvider<MessagesTim
),
MessagesTimelineItemUnknownContent,
)
}
}

2
features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEmoteContent.kt

@ -5,4 +5,4 @@ import org.jsoup.nodes.Document @@ -5,4 +5,4 @@ import org.jsoup.nodes.Document
data class MessagesTimelineItemEmoteContent(
override val body: String,
override val htmlDocument: Document?
) : MessagesTimelineItemTextBasedContent
) : MessagesTimelineItemTextBasedContent

2
features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEncryptedContent.kt

@ -4,4 +4,4 @@ import org.matrix.rustcomponents.sdk.EncryptedMessage @@ -4,4 +4,4 @@ import org.matrix.rustcomponents.sdk.EncryptedMessage
data class MessagesTimelineItemEncryptedContent(
val encryptedMessage: EncryptedMessage
) : MessagesTimelineItemContent
) : MessagesTimelineItemContent

2
features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemImageContent.kt

@ -7,4 +7,4 @@ data class MessagesTimelineItemImageContent( @@ -7,4 +7,4 @@ data class MessagesTimelineItemImageContent(
val imageMeta: MediaResolver.Meta,
val blurhash: String?,
val aspectRatio: Float
) : MessagesTimelineItemContent
) : MessagesTimelineItemContent

2
features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemNoticeContent.kt

@ -5,4 +5,4 @@ import org.jsoup.nodes.Document @@ -5,4 +5,4 @@ import org.jsoup.nodes.Document
data class MessagesTimelineItemNoticeContent(
override val body: String,
override val htmlDocument: Document?
) : MessagesTimelineItemTextBasedContent
) : MessagesTimelineItemTextBasedContent

2
features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemRedactedContent.kt

@ -1,3 +1,3 @@ @@ -1,3 +1,3 @@
package io.element.android.x.features.messages.model.content
object MessagesTimelineItemRedactedContent : MessagesTimelineItemContent
object MessagesTimelineItemRedactedContent : MessagesTimelineItemContent

2
features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextBasedContent.kt

@ -5,4 +5,4 @@ import org.jsoup.nodes.Document @@ -5,4 +5,4 @@ import org.jsoup.nodes.Document
sealed interface MessagesTimelineItemTextBasedContent : MessagesTimelineItemContent {
val body: String
val htmlDocument: Document?
}
}

2
features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextContent.kt

@ -5,4 +5,4 @@ import org.jsoup.nodes.Document @@ -5,4 +5,4 @@ import org.jsoup.nodes.Document
data class MessagesTimelineItemTextContent(
override val body: String,
override val htmlDocument: Document?
) : MessagesTimelineItemTextBasedContent
) : MessagesTimelineItemTextBasedContent

2
features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemUnknownContent.kt

@ -1,3 +1,3 @@ @@ -1,3 +1,3 @@
package io.element.android.x.features.messages.model.content
object MessagesTimelineItemUnknownContent : MessagesTimelineItemContent
object MessagesTimelineItemUnknownContent : MessagesTimelineItemContent

2
features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerViewModel.kt

@ -35,4 +35,4 @@ class MessageComposerViewModel @AssistedInject constructor( @@ -35,4 +35,4 @@ class MessageComposerViewModel @AssistedInject constructor(
)
}
}
}
}

2
features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerViewState.kt

@ -16,4 +16,4 @@ data class MessageComposerViewState( @@ -16,4 +16,4 @@ data class MessageComposerViewState(
// val voiceBroadcastState: VoiceBroadcastState? = null,
val text: StableCharSequence? = null,
val isFullScreen: Boolean = false,
) : MavericksState
) : MavericksState

2
features/messages/src/main/java/io/element/android/x/features/messages/util/MutableListExt.kt

@ -5,4 +5,4 @@ internal inline fun <reified T> MutableList<T?>.invalidateLast() { @@ -5,4 +5,4 @@ internal inline fun <reified T> MutableList<T?>.invalidateLast() {
if (indexOfLast > 0) {
set(indexOfLast - 1, null)
}
}
}

5
features/messages/src/test/java/io/element/android/x/features/messages/ExampleUnitTest.kt

@ -1,9 +1,8 @@ @@ -1,9 +1,8 @@
package io.element.android.x.features.messages
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
@ -14,4 +13,4 @@ class ExampleUnitTest { @@ -14,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
}

3
features/onboarding/build.gradle.kts

@ -12,10 +12,9 @@ dependencies { @@ -12,10 +12,9 @@ dependencies {
implementation(project(":libraries:elementresources"))
implementation(project(":libraries:designsystem"))
implementation(libs.mavericks.compose)
implementation(libs.timber)
implementation(libs.accompanist.pager)
implementation(libs.accompanist.pagerindicator)
testImplementation(libs.test.junit)
androidTestImplementation(libs.test.junitext)
ksp(libs.showkase.processor)
}
}

8
features/onboarding/src/androidTest/java/io/element/android/x/features/login/ExampleInstrumentedTest.kt

@ -1,13 +1,11 @@ @@ -1,13 +1,11 @@
package io.element.android.x.features.login
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
@ -21,4 +19,4 @@ class ExampleInstrumentedTest { @@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.element.android.x.features.login.test", appContext.packageName)
}
}
}

4
features/onboarding/src/main/AndroidManifest.xml

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
</manifest>
</manifest>

32
features/onboarding/src/main/java/io/element/android/x/features/onboarding/OnBoardingScreen.kt

@ -3,12 +3,24 @@ @@ -3,12 +3,24 @@
package io.element.android.x.features.onboarding
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
@ -42,19 +54,20 @@ fun OnBoardingScreen( @@ -42,19 +54,20 @@ fun OnBoardingScreen(
)
}
@OptIn(ExperimentalPagerApi::class)
@Composable
fun OnBoardingContent(
state: OnBoardingViewState,
onPageChanged: (Int) -> Unit,
onSignUp: () -> Unit,
onSignIn: () -> Unit,
modifier: Modifier = Modifier,
onPageChanged: (Int) -> Unit = {},
onSignUp: () -> Unit = {},
onSignIn: () -> Unit = {},
) {
val carrouselState = remember { SplashCarouselStateFactory().create() }
val nbOfPages = carrouselState.items.size
var key by remember { mutableStateOf(false) }
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.background,
) {
Box(
@ -125,8 +138,11 @@ fun OnBoardingContent( @@ -125,8 +138,11 @@ fun OnBoardingContent(
@Composable
fun OnBoardingPage(
item: SplashCarouselState.Item,
modifier: Modifier = Modifier,
) {
Box {
Box(
modifier = modifier,
) {
/*
Image(
painterResource(id = item.pageBackground),
@ -164,4 +180,4 @@ fun OnBoardingPage( @@ -164,4 +180,4 @@ fun OnBoardingPage(
)
}
}
}
}

2
features/onboarding/src/main/java/io/element/android/x/features/onboarding/OnBoardingViewModel.kt

@ -12,4 +12,4 @@ class OnBoardingViewModel(initialState: OnBoardingViewState) : @@ -12,4 +12,4 @@ class OnBoardingViewModel(initialState: OnBoardingViewState) :
)
}
}
}
}

10
features/onboarding/src/main/java/io/element/android/x/features/onboarding/SplashCarouselState.kt

@ -20,12 +20,12 @@ import androidx.annotation.DrawableRes @@ -20,12 +20,12 @@ import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
data class SplashCarouselState(
val items: List<Item>
val items: List<Item>
) {
data class Item(
@StringRes val title: Int,
@StringRes val body: Int,
@DrawableRes val image: Int,
@DrawableRes val pageBackground: Int
@StringRes val title: Int,
@StringRes val body: Int,
@DrawableRes val image: Int,
@DrawableRes val pageBackground: Int
)
}

2
features/onboarding/src/main/java/io/element/android/x/features/onboarding/SplashCarouselStateFactory.kt

@ -3,7 +3,7 @@ package io.element.android.x.features.onboarding @@ -3,7 +3,7 @@ package io.element.android.x.features.onboarding
import androidx.annotation.DrawableRes
import io.element.android.x.element.resources.R as ElementR
class SplashCarouselStateFactory() {
class SplashCarouselStateFactory {
fun create(): SplashCarouselState {
val lightTheme = true
fun background(@DrawableRes lightDrawable: Int) =

5
features/onboarding/src/test/java/io/element/android/x/features/login/ExampleUnitTest.kt

@ -1,9 +1,8 @@ @@ -1,9 +1,8 @@
package io.element.android.x.features.login
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
@ -14,4 +13,4 @@ class ExampleUnitTest { @@ -14,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
}

3
features/roomlist/build.gradle.kts

@ -20,10 +20,9 @@ dependencies { @@ -20,10 +20,9 @@ dependencies {
implementation(project(":libraries:matrix"))
implementation(project(":libraries:designsystem"))
implementation(libs.mavericks.compose)
implementation(libs.timber)
implementation(libs.datetime)
implementation(libs.accompanist.placeholder)
testImplementation(libs.test.junit)
androidTestImplementation(libs.test.junitext)
ksp(libs.showkase.processor)
}
}

8
features/roomlist/src/androidTest/java/io/element/android/x/features/roomlist/ExampleInstrumentedTest.kt

@ -1,13 +1,11 @@ @@ -1,13 +1,11 @@
package io.element.android.x.features.roomlist
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
@ -21,4 +19,4 @@ class ExampleInstrumentedTest { @@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.element.android.x.features.roomlist.test", appContext.packageName)
}
}
}

4
features/roomlist/src/main/AndroidManifest.xml

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
</manifest>
</manifest>

17
features/roomlist/src/main/java/io/element/android/x/features/roomlist/LastMessageFormatter.kt

@ -2,12 +2,18 @@ package io.element.android.x.features.roomlist @@ -2,12 +2,18 @@ package io.element.android.x.features.roomlist
import android.text.format.DateFormat
import android.text.format.DateUtils
import kotlinx.datetime.*
import kotlinx.datetime.TimeZone
import java.time.Period
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.Locale
import kotlin.math.absoluteValue
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.datetime.toJavaLocalDate
import kotlinx.datetime.toJavaLocalDateTime
import kotlinx.datetime.toLocalDateTime
class LastMessageFormatter(
private val clock: Clock = Clock.System,
@ -29,7 +35,6 @@ class LastMessageFormatter( @@ -29,7 +35,6 @@ class LastMessageFormatter(
DateTimeFormatter.ofPattern(pattern)
}
fun format(timestamp: Long?): String {
if (timestamp == null) return ""
val now: Instant = clock.now()
@ -77,6 +82,4 @@ class LastMessageFormatter( @@ -77,6 +82,4 @@ class LastMessageFormatter(
DateUtils.FORMAT_SHOW_WEEKDAY
).toString()
}
}
}

21
features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt

@ -36,6 +36,8 @@ import io.element.android.x.features.roomlist.model.RoomListRoomSummary @@ -36,6 +36,8 @@ import io.element.android.x.features.roomlist.model.RoomListRoomSummary
import io.element.android.x.features.roomlist.model.RoomListViewState
import io.element.android.x.features.roomlist.model.stubbedRoomSummaries
import io.element.android.x.matrix.core.RoomId
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@Composable
fun RoomListScreen(
@ -53,7 +55,7 @@ fun RoomListScreen( @@ -53,7 +55,7 @@ fun RoomListScreen(
val roomSummaries by viewModel.collectAsState(RoomListViewState::rooms)
val matrixUser by viewModel.collectAsState(RoomListViewState::user)
RoomListContent(
roomSummaries = roomSummaries().orEmpty(),
roomSummaries = roomSummaries().orEmpty().toImmutableList(),
matrixUser = matrixUser(),
onRoomClicked = onRoomClicked,
onLogoutClicked = viewModel::logout,
@ -66,16 +68,16 @@ fun RoomListScreen( @@ -66,16 +68,16 @@ fun RoomListScreen(
@Composable
fun RoomListContent(
roomSummaries: List<RoomListRoomSummary>,
roomSummaries: ImmutableList<RoomListRoomSummary>,
matrixUser: MatrixUser?,
onRoomClicked: (RoomId) -> Unit,
filter: String,
onFilterChanged: (String) -> Unit,
onLogoutClicked: () -> Unit,
onScrollOver: (IntRange) -> Unit,
isLoginOut: Boolean,
modifier: Modifier = Modifier,
onRoomClicked: (RoomId) -> Unit = {},
onFilterChanged: (String) -> Unit = {},
onLogoutClicked: () -> Unit = {},
onScrollOver: (IntRange) -> Unit = {},
) {
fun onRoomClicked(room: RoomListRoomSummary) {
onRoomClicked(room.roomId)
}
@ -104,7 +106,7 @@ fun RoomListContent( @@ -104,7 +106,7 @@ fun RoomListContent(
}
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
RoomListTopBar(
matrixUser = matrixUser,
@ -133,7 +135,7 @@ fun RoomListContent( @@ -133,7 +135,7 @@ fun RoomListContent(
}
)
if (isLoginOut) {
ProgressDialog("Login out...")
ProgressDialog(text = "Login out...")
}
}
@ -176,4 +178,3 @@ fun PreviewableDarkRoomListContent() { @@ -176,4 +178,3 @@ fun PreviewableDarkRoomListContent() {
)
}
}

11
features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt

@ -4,17 +4,15 @@ import com.airbnb.mvrx.* @@ -4,17 +4,15 @@ import com.airbnb.mvrx.*
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.x.anvilannotations.ContributesViewModel
import io.element.android.x.core.data.parallelMap
import io.element.android.x.core.coroutine.parallelMap
import io.element.android.x.core.di.daggerMavericksViewModelFactory
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.designsystem.components.avatar.AvatarSize
import io.element.android.x.di.AppScope
import io.element.android.x.di.SessionScope
import io.element.android.x.features.roomlist.model.MatrixUser
import io.element.android.x.features.roomlist.model.RoomListRoomSummary
import io.element.android.x.features.roomlist.model.RoomListRoomSummaryPlaceholders
import io.element.android.x.features.roomlist.model.RoomListViewState
import io.element.android.x.matrix.Matrix
import io.element.android.x.matrix.MatrixClient
import io.element.android.x.matrix.media.MediaResolver
import io.element.android.x.matrix.room.RoomSummary
@ -112,8 +110,8 @@ class RoomListViewModel @AssistedInject constructor( @@ -112,8 +110,8 @@ class RoomListViewModel @AssistedInject constructor(
copy(
rooms = when {
it is Loading ||
// Note: this second case will prevent to handle correctly the empty case
(it is Success && it().isEmpty() && filter.isEmpty()) -> {
// Note: this second case will prevent to handle correctly the empty case
(it is Success && it().isEmpty() && filter.isEmpty()) -> {
// Show fake placeholders to avoid having empty screen
Loading(RoomListRoomSummaryPlaceholders.createFakeList(size = 16))
}
@ -158,5 +156,4 @@ class RoomListViewModel @AssistedInject constructor( @@ -158,5 +156,4 @@ class RoomListViewModel @AssistedInject constructor(
.resolve(url, kind = MediaResolver.Kind.Thumbnail(size.value))
return AvatarData(name, model, size)
}
}
}

36
features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt

@ -2,7 +2,6 @@ @@ -2,7 +2,6 @@
package io.element.android.x.features.roomlist.components
import Avatar
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.ContentAlpha
@ -11,9 +10,23 @@ import androidx.compose.material.icons.filled.ArrowBack @@ -11,9 +10,23 @@ import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Logout
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MediumTopAppBar
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
@ -24,6 +37,7 @@ import androidx.compose.ui.text.font.FontWeight @@ -24,6 +37,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import io.element.android.x.core.compose.LogCompositions
import io.element.android.x.core.compose.textFieldState
import io.element.android.x.designsystem.components.avatar.Avatar
import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.x.features.roomlist.model.MatrixUser
@ -35,7 +49,6 @@ fun RoomListTopBar( @@ -35,7 +49,6 @@ fun RoomListTopBar(
onLogoutClicked: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior
) {
LogCompositions(tag = "RoomListScreen", msg = "TopBar")
var searchWidgetStateIsOpened by rememberSaveable { mutableStateOf(false) }
@ -65,20 +78,20 @@ fun RoomListTopBar( @@ -65,20 +78,20 @@ fun RoomListTopBar(
scrollBehavior = scrollBehavior,
)
}
}
@Composable
fun SearchRoomListTopBar(
text: String,
onFilterChanged: (String) -> Unit,
onCloseClicked: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior
scrollBehavior: TopAppBarScrollBehavior,
modifier: Modifier = Modifier,
onFilterChanged: (String) -> Unit = {},
onCloseClicked: () -> Unit = {},
) {
var filterState by textFieldState(stateValue = text)
val focusRequester = remember { FocusRequester() }
TopAppBar(
modifier = Modifier
modifier = modifier
.nestedScroll(scrollBehavior.nestedScrollConnection),
title = {
TextField(
@ -184,10 +197,13 @@ private fun DefaultRoomListTopBar( @@ -184,10 +197,13 @@ private fun DefaultRoomListTopBar(
)
// Log out confirmation dialog
ConfirmationDialog(
openDialog,
isDisplayed = openDialog.value,
title = "Log out",
content = "Do you confirm you want to log out?",
submitText = "Log out",
onSubmitClicked = onLogoutClicked,
onDismiss = {
openDialog.value = false
}
)
}

20
features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomSummaryRow.kt

@ -1,16 +1,23 @@ @@ -1,16 +1,23 @@
package io.element.android.x.features.roomlist.components
import Avatar
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
@ -29,17 +36,17 @@ import androidx.compose.ui.unit.LayoutDirection @@ -29,17 +36,17 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.placeholder.material.placeholder
import io.element.android.x.designsystem.components.avatar.Avatar
import io.element.android.x.features.roomlist.model.RoomListRoomSummary
private val minHeight = 72.dp
@Composable
internal fun RoomSummaryRow(
modifier: Modifier = Modifier,
room: RoomListRoomSummary,
onClick: (RoomListRoomSummary) -> Unit
modifier: Modifier = Modifier,
onClick: (RoomListRoomSummary) -> Unit = {},
) {
val clickModifier = if (room.isPlaceholder) {
modifier
} else {
@ -57,7 +64,6 @@ internal fun RoomSummaryRow( @@ -57,7 +64,6 @@ internal fun RoomSummaryRow(
) {
DefaultRoomSummaryRow(room = room)
}
}
@Composable

3
features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummary.kt

@ -13,4 +13,5 @@ data class RoomListRoomSummary( @@ -13,4 +13,5 @@ data class RoomListRoomSummary(
val timestamp: String? = null,
val lastMessage: CharSequence? = null,
val avatarData: AvatarData = AvatarData(),
val isPlaceholder: Boolean = false,)
val isPlaceholder: Boolean = false,
)

3
features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummaryPlaceholders.kt

@ -2,7 +2,6 @@ package io.element.android.x.features.roomlist.model @@ -2,7 +2,6 @@ package io.element.android.x.features.roomlist.model
import io.element.android.x.designsystem.components.avatar.AvatarData
object RoomListRoomSummaryPlaceholders {
fun create(id: String): RoomListRoomSummary {
@ -23,6 +22,4 @@ object RoomListRoomSummaryPlaceholders { @@ -23,6 +22,4 @@ object RoomListRoomSummaryPlaceholders {
}
}
}
}

8
features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/stubbed.kt

@ -1,9 +1,11 @@ @@ -1,9 +1,11 @@
package io.element.android.x.features.roomlist.model
import io.element.android.x.designsystem.components.avatar.AvatarData
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
internal fun stubbedRoomSummaries(): List<RoomListRoomSummary> {
return listOf(
internal fun stubbedRoomSummaries(): ImmutableList<RoomListRoomSummary> {
return persistentListOf(
RoomListRoomSummary(
name = "Room",
hasUnread = true,
@ -22,4 +24,4 @@ internal fun stubbedRoomSummaries(): List<RoomListRoomSummary> { @@ -22,4 +24,4 @@ internal fun stubbedRoomSummaries(): List<RoomListRoomSummary> {
),
RoomListRoomSummaryPlaceholders.create("roomId2")
)
}
}

5
features/roomlist/src/test/java/io/element/android/x/features/roomlist/ExampleUnitTest.kt

@ -1,9 +1,8 @@ @@ -1,9 +1,8 @@
package io.element.android.x.features.roomlist
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
@ -14,4 +13,4 @@ class ExampleUnitTest { @@ -14,4 +13,4 @@ class ExampleUnitTest {
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
}

3
gradle.properties

@ -21,8 +21,7 @@ kotlin.code.style=official @@ -21,8 +21,7 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
# Dummy values for signing secrets / nightly
signing.element.nightly.storePassword=Secret
signing.element.nightly.keyId=Secret
signing.element.nightly.keyPassword=Secret
signing.element.nightly.keyPassword=Secret

15
gradle/libs.versions.toml

@ -1,3 +1,6 @@ @@ -1,3 +1,6 @@
# This file is referenced in ./plugins/settings.gradle.kts to generate the version catalog.
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
[versions]
# Project
android_gradle_plugin = "7.3.1"
@ -39,7 +42,6 @@ test_orchestrator = "1.4.1" @@ -39,7 +42,6 @@ test_orchestrator = "1.4.1"
#other
mavericks = "3.0.1"
timber = "5.0.1"
coil = "2.2.1"
datetime = "0.4.0"
wysiwyg = "0.7.0.1"
@ -52,6 +54,10 @@ jsoup = "1.15.3" @@ -52,6 +54,10 @@ jsoup = "1.15.3"
dagger = "2.43"
anvil = "2.4.2"
# quality
detekt = "1.22.0"
ktlint = "11.0.0"
[libraries]
# Project
android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref = "android_gradle_plugin" }
@ -102,13 +108,12 @@ test_orchestrator = { module = "androidx.test:orchestrator", version.ref = "test @@ -102,13 +108,12 @@ test_orchestrator = { module = "androidx.test:orchestrator", version.ref = "test
# Others
mavericks_compose = { module = "com.airbnb.android:mavericks-compose", version.ref = "mavericks" }
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" }
compose_destinations = { module = "io.github.raamcosta.compose-destinations:animations-core", version.ref = "compose_destinations" }
compose_destinations_processor = {module = "io.github.raamcosta.compose-destinations:ksp", version.ref = "compose_destinations"}
compose_destinations_processor = { module = "io.github.raamcosta.compose-destinations:ksp", version.ref = "compose_destinations" }
showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" }
showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" }
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
@ -133,4 +138,6 @@ kotlin_android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } @@ -133,4 +138,6 @@ kotlin_android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin_jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kapt = {id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin"}
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
anvil = {id = "com.squareup.anvil", version.ref = "anvil"}
anvil = {id = "com.squareup.anvil", version.ref = "anvil"}
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }

9
libraries/core/src/main/java/io/element/android/x/core/compose/LogCompositions.kt

@ -1,22 +1,21 @@ @@ -1,22 +1,21 @@
package io.element.android.x.core.compose
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.remember
import io.element.android.x.core.BuildConfig
import timber.log.Timber
// Note the inline function below which ensures that this function is essentially
// copied at the call site to ensure that its logging only recompositions from the
// original call site.
@Composable
inline fun LogCompositions(tag: String, msg: String) {
fun LogCompositions(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
val ref = remember { Ref(0) }
SideEffect { ref.value++ }
Log.d(tag, "Compositions: $msg ${ref.value}")
Timber.d(tag, "Compositions: $msg ${ref.value}")
}
}
class Ref(var value: Int)
class Ref(var value: Int)

4
libraries/core/src/main/java/io/element/android/x/core/compose/OnLifecycleEvent.kt

@ -12,7 +12,7 @@ import androidx.lifecycle.LifecycleOwner @@ -12,7 +12,7 @@ import androidx.lifecycle.LifecycleOwner
fun OnLifecycleEvent(onEvent: (owner: LifecycleOwner, event: Lifecycle.Event) -> Unit) {
val eventHandler = rememberUpdatedState(onEvent)
val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)
DisposableEffect(lifecycleOwner.value) {
val lifecycle = lifecycleOwner.value.lifecycle
val observer = LifecycleEventObserver { owner, event ->
@ -24,4 +24,4 @@ fun OnLifecycleEvent(onEvent: (owner: LifecycleOwner, event: Lifecycle.Event) -> @@ -24,4 +24,4 @@ fun OnLifecycleEvent(onEvent: (owner: LifecycleOwner, event: Lifecycle.Event) ->
lifecycle.removeObserver(observer)
}
}
}
}

0
libraries/core/src/main/java/io/element/android/x/core/compose/Previews.kt → libraries/core/src/main/java/io/element/android/x/core/compose/PairCombinedPreviewParameter.kt

3
libraries/core/src/main/java/io/element/android/x/core/compose/TextFieldLocalState.kt

@ -5,7 +5,6 @@ import androidx.compose.runtime.MutableState @@ -5,7 +5,6 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@Composable
public fun textFieldState(stateValue: String): MutableState<String> =
remember(stateValue) { mutableStateOf(stateValue) }
remember(stateValue) { mutableStateOf(stateValue) }

2
libraries/core/src/main/java/io/element/android/x/core/coroutine/TimingOperators.kt

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package io.element.android.x.core.data.flow
package io.element.android.x.core.coroutine
import android.os.SystemClock
import kotlinx.coroutines.CoroutineScope

4
libraries/core/src/main/java/io/element/android/x/core/coroutine/pmap.kt

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package io.element.android.x.core.data
package io.element.android.x.core.coroutine
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -7,4 +7,4 @@ import kotlinx.coroutines.coroutineScope @@ -7,4 +7,4 @@ import kotlinx.coroutines.coroutineScope
// https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/
suspend fun <A, B> Iterable<A>.parallelMap(f: suspend (A) -> B): List<B> = coroutineScope {
map { async { f(it) } }.awaitAll()
}
}

4
libraries/core/src/main/java/io/element/android/x/core/data/Try.kt

@ -1,13 +1,13 @@ @@ -1,13 +1,13 @@
package io.element.android.x.core.data
import android.util.Log
import timber.log.Timber
inline fun <A> tryOrNull(message: String? = null, operation: () -> A): A? {
return try {
operation()
} catch (any: Throwable) {
if (message != null) {
Log.e("TAG", message, any)
Timber.e("TAG", message, any)
}
null
}

12
libraries/core/src/main/java/io/element/android/x/core/ui/DimensionConverter.kt

@ -24,18 +24,18 @@ class DimensionConverter(val resources: Resources) { @@ -24,18 +24,18 @@ class DimensionConverter(val resources: Resources) {
@Px
fun dpToPx(dp: Int): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp.toFloat(),
resources.displayMetrics
TypedValue.COMPLEX_UNIT_DIP,
dp.toFloat(),
resources.displayMetrics
).toInt()
}
@Px
fun spToPx(sp: Int): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
sp.toFloat(),
resources.displayMetrics
TypedValue.COMPLEX_UNIT_SP,
sp.toFloat(),
resources.displayMetrics
).toInt()
}

8
libraries/core/src/main/java/io/element/android/x/core/ui/View.kt

@ -35,9 +35,9 @@ fun View.showKeyboard(andRequestFocus: Boolean = false) { @@ -35,9 +35,9 @@ fun View.showKeyboard(andRequestFocus: Boolean = false) {
fun View.setHorizontalPadding(padding: Int) {
setPadding(
padding,
paddingTop,
padding,
paddingBottom
padding,
paddingTop,
padding,
paddingBottom
)
}

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

Loading…
Cancel
Save