From d8bbc7858622b6d9c278469aab701ca0b609cddf Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:35:49 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- .../components/ui/autocomplete/README.md | 19 + .../components/ui/autocomplete/build.gradle | 40 + .../components/ui/autocomplete/proguard-rules.pro | 21 + .../ui/autocomplete/src/main/AndroidManifest.xml | 4 + .../ui/autocomplete/InlineAutocompleteEditText.kt | 905 +++++++++++++++++++++ .../ui/autocomplete/src/main/res/values/attrs.xml | 9 + .../autocomplete/InlineAutocompleteEditTextTest.kt | 585 +++++++++++++ .../org.mockito.plugins.MockMaker | 2 + .../src/test/resources/robolectric.properties | 1 + .../components/ui/colors/README.md | 19 + .../components/ui/colors/build.gradle | 40 + .../components/ui/colors/proguard-rules.pro | 21 + .../ui/colors/src/main/AndroidManifest.xml | 4 + .../mozilla/components/ui/colors/PhotonColors.kt | 178 ++++ .../colors/src/main/res/values/photon_colors.xml | 219 +++++ .../components/ui/fonts/README.md | 19 + .../components/ui/fonts/build.gradle | 31 + .../components/ui/fonts/proguard-rules.pro | 21 + .../ui/fonts/src/main/AndroidManifest.xml | 4 + .../ui/fonts/src/main/res/values/roboto_fonts.xml | 18 + .../components/ui/icons/README.md | 19 + .../components/ui/icons/build.gradle | 31 + .../components/ui/icons/proguard-rules.pro | 21 + .../ui/icons/src/main/AndroidManifest.xml | 4 + .../icons/src/main/assets/mozac_error_asleep.svg | 4 + .../icons/src/main/assets/mozac_error_confused.svg | 4 + .../icons/src/main/assets/mozac_error_eye_roll.svg | 4 + .../src/main/assets/mozac_error_hourglass.svg | 4 + .../icons/src/main/assets/mozac_error_inspect.svg | 4 + .../ui/icons/src/main/assets/mozac_error_lock.svg | 4 + .../src/main/assets/mozac_error_no_internet.svg | 4 + .../src/main/assets/mozac_error_question_file.svg | 4 + .../src/main/assets/mozac_error_shred_file.svg | 4 + .../src/main/assets/mozac_error_surprised.svg | 4 + .../src/main/assets/mozac_error_unplugged.svg | 4 + .../res/drawable/mozac_ic_add_to_homescreen_24.xml | 20 + .../src/main/res/drawable/mozac_ic_app_menu_24.xml | 12 + .../res/drawable/mozac_ic_app_menu_space_24.xml | 12 + .../res/drawable/mozac_ic_append_down_left_24.xml | 12 + .../res/drawable/mozac_ic_append_up_left_24.xml | 12 + .../res/drawable/mozac_ic_append_up_right_24.xml | 12 + .../res/drawable/mozac_ic_arrow_clockwise_24.xml | 12 + .../src/main/res/drawable/mozac_ic_autoplay_24.xml | 15 + .../res/drawable/mozac_ic_autoplay_slash_24.xml | 15 + .../res/drawable/mozac_ic_avatar_circle_24.xml | 18 + .../drawable/mozac_ic_avatar_circle_fill_24.xml | 16 + .../mozac_ic_avatar_info_circle_fill_24.xml | 20 + .../mozac_ic_avatar_warning_circle_fill_24.xml | 22 + .../src/main/res/drawable/mozac_ic_back_24.xml | 12 + .../src/main/res/drawable/mozac_ic_bookmark_20.xml | 16 + .../src/main/res/drawable/mozac_ic_bookmark_24.xml | 14 + .../drawable/mozac_ic_bookmark_badge_fill_20.xml | 15 + .../res/drawable/mozac_ic_bookmark_fill_20.xml | 12 + .../res/drawable/mozac_ic_bookmark_fill_24.xml | 12 + .../res/drawable/mozac_ic_bookmark_slash_24.xml | 15 + .../res/drawable/mozac_ic_bookmark_tray_24.xml | 15 + .../drawable/mozac_ic_bookmark_tray_fill_24.xml | 15 + .../src/main/res/drawable/mozac_ic_briefcase.xml | 16 + .../src/main/res/drawable/mozac_ic_broken_lock.xml | 21 + .../src/main/res/drawable/mozac_ic_camera_24.xml | 12 + .../main/res/drawable/mozac_ic_camera_slash_24.xml | 15 + .../icons/src/main/res/drawable/mozac_ic_cart.xml | 17 + .../main/res/drawable/mozac_ic_checkmark_24.xml | 12 + .../main/res/drawable/mozac_ic_chevron_down_20.xml | 12 + .../main/res/drawable/mozac_ic_chevron_down_24.xml | 12 + .../main/res/drawable/mozac_ic_chevron_down_8.xml | 12 + .../main/res/drawable/mozac_ic_chevron_left_24.xml | 12 + .../res/drawable/mozac_ic_chevron_right_24.xml | 12 + .../main/res/drawable/mozac_ic_chevron_up_20.xml | 12 + .../main/res/drawable/mozac_ic_chevron_up_24.xml | 12 + .../icons/src/main/res/drawable/mozac_ic_chill.xml | 19 + .../src/main/res/drawable/mozac_ic_circle.xml | 9 + .../main/res/drawable/mozac_ic_clipboard_24.xml | 20 + .../main/res/drawable/mozac_ic_collection_24.xml | 20 + .../res/drawable/mozac_ic_competitiveness_24.xml | 16 + .../src/main/res/drawable/mozac_ic_cookies_24.xml | 12 + .../res/drawable/mozac_ic_cookies_slash_24.xml | 15 + .../src/main/res/drawable/mozac_ic_copy_24.xml | 15 + .../main/res/drawable/mozac_ic_credit_card_24.xml | 20 + .../src/main/res/drawable/mozac_ic_critical_24.xml | 15 + .../res/drawable/mozac_ic_critical_fill_24.xml | 16 + .../src/main/res/drawable/mozac_ic_cross_20.xml | 12 + .../src/main/res/drawable/mozac_ic_cross_24.xml | 12 + .../main/res/drawable/mozac_ic_cross_circle_24.xml | 15 + .../res/drawable/mozac_ic_cross_circle_fill_20.xml | 16 + .../res/drawable/mozac_ic_cross_circle_fill_24.xml | 12 + .../main/res/drawable/mozac_ic_cryptominer_24.xml | 12 + .../res/drawable/mozac_ic_data_clearance_24.xml | 14 + .../main/res/drawable/mozac_ic_debug_drawer_24.xml | 15 + .../src/main/res/drawable/mozac_ic_delete_24.xml | 20 + .../res/drawable/mozac_ic_device_desktop_24.xml | 12 + .../drawable/mozac_ic_device_desktop_send_24.xml | 15 + .../res/drawable/mozac_ic_device_mobile_24.xml | 16 + .../src/main/res/drawable/mozac_ic_dollar.xml | 19 + .../src/main/res/drawable/mozac_ic_download_24.xml | 15 + .../main/res/drawable/mozac_ic_dropdown_arrow.xml | 12 + .../src/main/res/drawable/mozac_ic_edit_24.xml | 16 + .../drawable/mozac_ic_ellipsis_horizontal_24.xml | 12 + .../res/drawable/mozac_ic_ellipsis_vertical_24.xml | 12 + .../main/res/drawable/mozac_ic_experiments_24.xml | 28 + .../main/res/drawable/mozac_ic_extension_24.xml | 12 + .../res/drawable/mozac_ic_extension_cog_24.xml | 17 + .../res/drawable/mozac_ic_extension_fill_24.xml | 12 + .../res/drawable/mozac_ic_external_link_24.xml | 15 + .../src/main/res/drawable/mozac_ic_eye_24.xml | 15 + .../main/res/drawable/mozac_ic_eye_slash_24.xml | 24 + .../icons/src/main/res/drawable/mozac_ic_fence.xml | 13 + .../res/drawable/mozac_ic_fingerprinter_24.xml | 18 + .../src/main/res/drawable/mozac_ic_folder_24.xml | 12 + .../main/res/drawable/mozac_ic_folder_add_24.xml | 15 + .../icons/src/main/res/drawable/mozac_ic_font.xml | 14 + .../icons/src/main/res/drawable/mozac_ic_food.xml | 15 + .../src/main/res/drawable/mozac_ic_forward_24.xml | 12 + .../icons/src/main/res/drawable/mozac_ic_fruit.xml | 15 + .../icons/src/main/res/drawable/mozac_ic_gift.xml | 17 + .../src/main/res/drawable/mozac_ic_globe_24.xml | 16 + .../icons/src/main/res/drawable/mozac_ic_grid.xml | 13 + .../src/main/res/drawable/mozac_ic_grid_add_24.xml | 15 + .../main/res/drawable/mozac_ic_help_circle_24.xml | 15 + .../res/drawable/mozac_ic_help_circle_fill_24.xml | 16 + .../src/main/res/drawable/mozac_ic_history_24.xml | 15 + .../src/main/res/drawable/mozac_ic_home_24.xml | 16 + .../src/main/res/drawable/mozac_ic_image_24.xml | 15 + .../main/res/drawable/mozac_ic_image_slash_24.xml | 24 + .../main/res/drawable/mozac_ic_information_24.xml | 15 + .../res/drawable/mozac_ic_information_fill_24.xml | 16 + .../main/res/drawable/mozac_ic_lightbulb_24.xml | 12 + .../src/main/res/drawable/mozac_ic_link_24.xml | 18 + .../src/main/res/drawable/mozac_ic_location_24.xml | 15 + .../res/drawable/mozac_ic_location_slash_24.xml | 18 + .../src/main/res/drawable/mozac_ic_lock_20.xml | 16 + .../src/main/res/drawable/mozac_ic_lock_24.xml | 16 + .../main/res/drawable/mozac_ic_lock_slash_20.xml | 15 + .../main/res/drawable/mozac_ic_lock_slash_24.xml | 20 + .../main/res/drawable/mozac_ic_lock_warning_20.xml | 16 + .../main/res/drawable/mozac_ic_lock_warning_24.xml | 18 + .../src/main/res/drawable/mozac_ic_login_24.xml | 12 + .../main/res/drawable/mozac_ic_logo_chrome_24.xml | 12 + .../main/res/drawable/mozac_ic_logo_firefox_24.xml | 14 + .../main/res/drawable/mozac_ic_logo_safari_24.xml | 15 + .../main/res/drawable/mozac_ic_microphone_24.xml | 15 + .../res/drawable/mozac_ic_microphone_slash_24.xml | 12 + .../main/res/drawable/mozac_ic_more_grid_24.xml | 12 + .../src/main/res/drawable/mozac_ic_mozilla.xml | 7 + .../main/res/drawable/mozac_ic_night_mode_24.xml | 12 + .../main/res/drawable/mozac_ic_notification_24.xml | 15 + .../mozac_ic_notification_dot_badge_fill_20.xml | 15 + .../drawable/mozac_ic_notification_slash_24.xml | 24 + .../src/main/res/drawable/mozac_ic_open_in.xml | 7 + .../main/res/drawable/mozac_ic_packaging_24.xml | 16 + .../main/res/drawable/mozac_ic_page_zoom_24.xml | 15 + .../res/drawable/mozac_ic_page_zoom_fill_24.xml | 12 + .../src/main/res/drawable/mozac_ic_passkey_24.xml | 18 + .../res/drawable/mozac_ic_pause_badge_fill_16.xml | 12 + .../main/res/drawable/mozac_ic_permissions_24.xml | 20 + .../icons/src/main/res/drawable/mozac_ic_pet.xml | 17 + .../src/main/res/drawable/mozac_ic_pin_24.xml | 16 + .../res/drawable/mozac_ic_pin_badge_fill_16.xml | 15 + .../src/main/res/drawable/mozac_ic_pin_fill_24.xml | 12 + .../src/main/res/drawable/mozac_ic_pin_filled.xml | 13 + .../main/res/drawable/mozac_ic_pin_slash_24.xml | 20 + .../res/drawable/mozac_ic_pin_slash_fill_24.xml | 12 + .../res/drawable/mozac_ic_play_badge_fill_16.xml | 12 + .../src/main/res/drawable/mozac_ic_plugin_24.xml | 12 + .../src/main/res/drawable/mozac_ic_plus_24.xml | 12 + .../src/main/res/drawable/mozac_ic_preferences.xml | 14 + .../src/main/res/drawable/mozac_ic_price_24.xml | 15 + .../src/main/res/drawable/mozac_ic_print_24.xml | 12 + .../main/res/drawable/mozac_ic_private_mode_24.xml | 14 + .../mozac_ic_private_mode_circle_fill_20.xml | 17 + .../mozac_ic_private_mode_circle_fill_24.xml | 17 + .../mozac_ic_private_mode_circle_fill_48.xml | 17 + ...mozac_ic_private_mode_circle_fill_stroke_20.xml | 22 + .../src/main/res/drawable/mozac_ic_qr_code_24.xml | 12 + .../src/main/res/drawable/mozac_ic_quality_24.xml | 17 + .../main/res/drawable/mozac_ic_reader_view_24.xml | 15 + .../res/drawable/mozac_ic_reader_view_fill_24.xml | 16 + .../main/res/drawable/mozac_ic_reading_list_24.xml | 15 + .../res/drawable/mozac_ic_reading_list_add_24.xml | 15 + .../src/main/res/drawable/mozac_ic_reorder.xml | 7 + .../src/main/res/drawable/mozac_ic_rocket.xml | 17 + .../main/res/drawable/mozac_ic_rocket_filled.xml | 17 + .../src/main/res/drawable/mozac_ic_save_24.xml | 12 + .../main/res/drawable/mozac_ic_save_file_24.xml | 12 + .../src/main/res/drawable/mozac_ic_search_24.xml | 12 + .../src/main/res/drawable/mozac_ic_select_all.xml | 13 + .../src/main/res/drawable/mozac_ic_settings_24.xml | 17 + .../res/drawable/mozac_ic_share_android_24.xml | 16 + .../main/res/drawable/mozac_ic_share_apple_24.xml | 15 + .../src/main/res/drawable/mozac_ic_shield_24.xml | 12 + .../main/res/drawable/mozac_ic_shield_slash_24.xml | 15 + .../src/main/res/drawable/mozac_ic_shipping_24.xml | 16 + .../src/main/res/drawable/mozac_ic_shopping_24.xml | 15 + .../res/drawable/mozac_ic_social_tracker_24.xml | 12 + .../src/main/res/drawable/mozac_ic_sparkle_24.xml | 15 + .../main/res/drawable/mozac_ic_star_fill_20.xml | 12 + .../res/drawable/mozac_ic_star_half_fill_20.xml | 12 + .../drawable/mozac_ic_star_one_half_fill_20.xml | 15 + .../icons/src/main/res/drawable/mozac_ic_stop.xml | 13 + .../src/main/res/drawable/mozac_ic_storage_24.xml | 15 + .../res/drawable/mozac_ic_storage_slash_24.xml | 24 + .../src/main/res/drawable/mozac_ic_sync_24.xml | 12 + .../main/res/drawable/mozac_ic_sync_tabs_24.xml | 15 + .../icons/src/main/res/drawable/mozac_ic_tab.xml | 13 + .../res/drawable/mozac_ic_tab_badge_fill_20.xml | 15 + .../src/main/res/drawable/mozac_ic_tab_new.xml | 13 + .../main/res/drawable/mozac_ic_tab_number_24.xml | 17 + .../src/main/res/drawable/mozac_ic_tab_tray_24.xml | 12 + .../src/main/res/drawable/mozac_ic_themes_24.xml | 16 + .../src/main/res/drawable/mozac_ic_tool_24.xml | 12 + .../main/res/drawable/mozac_ic_translate_24.xml | 12 + .../icons/src/main/res/drawable/mozac_ic_tree.xml | 15 + .../res/drawable/mozac_ic_update_circle_24.xml | 15 + .../src/main/res/drawable/mozac_ic_vacation.xml | 26 + .../src/main/res/drawable/mozac_ic_warning_24.xml | 18 + .../main/res/drawable/mozac_ic_warning_fill_24.xml | 12 + .../mozac_ic_web_extension_default_icon.xml | 15 + .../main/res/drawable/mozac_ic_whats_new_24.xml | 16 + .../ui/icons/src/main/res/values/attrs.xml | 12 + .../ui/icons/src/main/res/values/colors.xml | 15 + .../src/main/res/values/mozac_ui_icons_strings.xml | 17 + .../components/ui/tabcounter/.gitignore | 1 + .../components/ui/tabcounter/README.md | 37 + .../components/ui/tabcounter/build.gradle | 50 ++ .../components/ui/tabcounter/proguard-rules.pro | 21 + .../ui/tabcounter/src/main/AndroidManifest.xml | 8 + .../mozilla/components/ui/tabcounter/TabCounter.kt | 348 ++++++++ .../components/ui/tabcounter/TabCounterMenu.kt | 101 +++ .../main/res/drawable/mozac_ui_tabcounter_bar.xml | 11 + .../main/res/drawable/mozac_ui_tabcounter_box.xml | 15 + .../mozac_ui_tabcounter_round_rectangle_ripple.xml | 13 + .../main/res/layout/mozac_ui_tabcounter_layout.xml | 50 ++ .../tabcounter/src/main/res/values-am/strings.xml | 17 + .../tabcounter/src/main/res/values-ar/strings.xml | 17 + .../tabcounter/src/main/res/values-ast/strings.xml | 17 + .../tabcounter/src/main/res/values-azb/strings.xml | 17 + .../tabcounter/src/main/res/values-be/strings.xml | 17 + .../tabcounter/src/main/res/values-bg/strings.xml | 17 + .../tabcounter/src/main/res/values-bn/strings.xml | 17 + .../tabcounter/src/main/res/values-br/strings.xml | 17 + .../tabcounter/src/main/res/values-bs/strings.xml | 17 + .../tabcounter/src/main/res/values-ca/strings.xml | 17 + .../tabcounter/src/main/res/values-cak/strings.xml | 17 + .../tabcounter/src/main/res/values-ceb/strings.xml | 17 + .../tabcounter/src/main/res/values-ckb/strings.xml | 17 + .../tabcounter/src/main/res/values-co/strings.xml | 17 + .../tabcounter/src/main/res/values-cs/strings.xml | 17 + .../tabcounter/src/main/res/values-cy/strings.xml | 17 + .../tabcounter/src/main/res/values-da/strings.xml | 17 + .../tabcounter/src/main/res/values-de/strings.xml | 17 + .../tabcounter/src/main/res/values-dsb/strings.xml | 17 + .../tabcounter/src/main/res/values-el/strings.xml | 17 + .../src/main/res/values-en-rCA/strings.xml | 17 + .../src/main/res/values-en-rGB/strings.xml | 17 + .../tabcounter/src/main/res/values-eo/strings.xml | 17 + .../src/main/res/values-es-rAR/strings.xml | 17 + .../src/main/res/values-es-rCL/strings.xml | 17 + .../src/main/res/values-es-rES/strings.xml | 17 + .../src/main/res/values-es-rMX/strings.xml | 17 + .../tabcounter/src/main/res/values-es/strings.xml | 17 + .../tabcounter/src/main/res/values-et/strings.xml | 17 + .../tabcounter/src/main/res/values-eu/strings.xml | 17 + .../tabcounter/src/main/res/values-fa/strings.xml | 17 + .../tabcounter/src/main/res/values-ff/strings.xml | 7 + .../tabcounter/src/main/res/values-fi/strings.xml | 17 + .../tabcounter/src/main/res/values-fr/strings.xml | 17 + .../tabcounter/src/main/res/values-fur/strings.xml | 17 + .../src/main/res/values-fy-rNL/strings.xml | 17 + .../tabcounter/src/main/res/values-gd/strings.xml | 17 + .../tabcounter/src/main/res/values-gl/strings.xml | 17 + .../tabcounter/src/main/res/values-gn/strings.xml | 17 + .../src/main/res/values-hi-rIN/strings.xml | 15 + .../tabcounter/src/main/res/values-hr/strings.xml | 17 + .../tabcounter/src/main/res/values-hsb/strings.xml | 17 + .../tabcounter/src/main/res/values-hu/strings.xml | 17 + .../src/main/res/values-hy-rAM/strings.xml | 17 + .../tabcounter/src/main/res/values-ia/strings.xml | 18 + .../tabcounter/src/main/res/values-in/strings.xml | 17 + .../tabcounter/src/main/res/values-is/strings.xml | 17 + .../tabcounter/src/main/res/values-it/strings.xml | 17 + .../tabcounter/src/main/res/values-iw/strings.xml | 17 + .../tabcounter/src/main/res/values-ja/strings.xml | 17 + .../tabcounter/src/main/res/values-ka/strings.xml | 17 + .../tabcounter/src/main/res/values-kaa/strings.xml | 17 + .../tabcounter/src/main/res/values-kab/strings.xml | 17 + .../tabcounter/src/main/res/values-kk/strings.xml | 17 + .../tabcounter/src/main/res/values-kmr/strings.xml | 17 + .../tabcounter/src/main/res/values-ko/strings.xml | 17 + .../tabcounter/src/main/res/values-lo/strings.xml | 17 + .../tabcounter/src/main/res/values-lt/strings.xml | 17 + .../tabcounter/src/main/res/values-my/strings.xml | 17 + .../src/main/res/values-nb-rNO/strings.xml | 17 + .../src/main/res/values-ne-rNP/strings.xml | 17 + .../tabcounter/src/main/res/values-nl/strings.xml | 17 + .../src/main/res/values-nn-rNO/strings.xml | 17 + .../tabcounter/src/main/res/values-oc/strings.xml | 17 + .../tabcounter/src/main/res/values-or/strings.xml | 11 + .../src/main/res/values-pa-rIN/strings.xml | 17 + .../src/main/res/values-pa-rPK/strings.xml | 17 + .../tabcounter/src/main/res/values-pl/strings.xml | 17 + .../src/main/res/values-pt-rBR/strings.xml | 17 + .../src/main/res/values-pt-rPT/strings.xml | 17 + .../tabcounter/src/main/res/values-rm/strings.xml | 17 + .../tabcounter/src/main/res/values-ro/strings.xml | 11 + .../tabcounter/src/main/res/values-ru/strings.xml | 17 + .../tabcounter/src/main/res/values-sat/strings.xml | 17 + .../tabcounter/src/main/res/values-sc/strings.xml | 17 + .../tabcounter/src/main/res/values-si/strings.xml | 17 + .../tabcounter/src/main/res/values-sk/strings.xml | 17 + .../tabcounter/src/main/res/values-skr/strings.xml | 17 + .../tabcounter/src/main/res/values-sl/strings.xml | 17 + .../tabcounter/src/main/res/values-sq/strings.xml | 17 + .../tabcounter/src/main/res/values-sr/strings.xml | 17 + .../tabcounter/src/main/res/values-su/strings.xml | 17 + .../src/main/res/values-sv-rSE/strings.xml | 17 + .../tabcounter/src/main/res/values-szl/strings.xml | 17 + .../tabcounter/src/main/res/values-te/strings.xml | 13 + .../tabcounter/src/main/res/values-tg/strings.xml | 17 + .../tabcounter/src/main/res/values-th/strings.xml | 17 + .../tabcounter/src/main/res/values-tl/strings.xml | 17 + .../tabcounter/src/main/res/values-tr/strings.xml | 17 + .../tabcounter/src/main/res/values-trs/strings.xml | 17 + .../tabcounter/src/main/res/values-tt/strings.xml | 17 + .../tabcounter/src/main/res/values-tzm/strings.xml | 9 + .../tabcounter/src/main/res/values-ug/strings.xml | 17 + .../tabcounter/src/main/res/values-uk/strings.xml | 17 + .../tabcounter/src/main/res/values-ur/strings.xml | 17 + .../tabcounter/src/main/res/values-uz/strings.xml | 17 + .../tabcounter/src/main/res/values-vi/strings.xml | 17 + .../tabcounter/src/main/res/values-yo/strings.xml | 17 + .../src/main/res/values-zh-rCN/strings.xml | 17 + .../src/main/res/values-zh-rTW/strings.xml | 17 + .../ui/tabcounter/src/main/res/values/attrs.xml | 11 + .../ui/tabcounter/src/main/res/values/colors.xml | 9 + .../ui/tabcounter/src/main/res/values/dimens.xml | 8 + .../ui/tabcounter/src/main/res/values/strings.xml | 20 + .../components/ui/tabcounter/TabCounterMenuTest.kt | 53 ++ .../components/ui/tabcounter/TabCounterTest.kt | 70 ++ .../org.mockito.plugins.MockMaker | 2 + .../src/test/resources/robolectric.properties | 1 + .../components/ui/widgets/README.md | 19 + .../components/ui/widgets/build.gradle | 51 ++ .../components/ui/widgets/proguard-rules.pro | 21 + .../ui/widgets/src/main/AndroidManifest.xml | 4 + .../mozilla/components/ui/widgets/Extentions.kt | 37 + .../components/ui/widgets/SnackbarDelegate.kt | 55 ++ .../ui/widgets/VerticalSwipeRefreshLayout.kt | 216 +++++ .../components/ui/widgets/WidgetSiteItemView.kt | 106 +++ .../ui/widgets/behavior/BrowserGestureDetector.kt | 192 +++++ .../widgets/behavior/EngineViewClippingBehavior.kt | 90 ++ .../behavior/EngineViewScrollingBehavior.kt | 237 ++++++ .../widgets/behavior/ViewYTranslationStrategy.kt | 189 +++++ .../ui/widgets/behavior/ViewYTranslator.kt | 81 ++ .../drawable/mozac_widget_favicon_background.xml | 17 + .../res/drawable/rounded_button_background.xml | 18 + .../src/main/res/layout/mozac_widget_site_item.xml | 77 ++ .../ui/widgets/src/main/res/values-am/strings.xml | 5 + .../ui/widgets/src/main/res/values-ar/strings.xml | 5 + .../ui/widgets/src/main/res/values-ast/strings.xml | 5 + .../ui/widgets/src/main/res/values-azb/strings.xml | 5 + .../ui/widgets/src/main/res/values-be/strings.xml | 5 + .../ui/widgets/src/main/res/values-bg/strings.xml | 5 + .../ui/widgets/src/main/res/values-br/strings.xml | 5 + .../ui/widgets/src/main/res/values-bs/strings.xml | 5 + .../ui/widgets/src/main/res/values-ca/strings.xml | 5 + .../ui/widgets/src/main/res/values-cak/strings.xml | 5 + .../ui/widgets/src/main/res/values-co/strings.xml | 5 + .../ui/widgets/src/main/res/values-cs/strings.xml | 5 + .../ui/widgets/src/main/res/values-cy/strings.xml | 5 + .../ui/widgets/src/main/res/values-da/strings.xml | 5 + .../ui/widgets/src/main/res/values-de/strings.xml | 5 + .../ui/widgets/src/main/res/values-dsb/strings.xml | 5 + .../ui/widgets/src/main/res/values-el/strings.xml | 5 + .../widgets/src/main/res/values-en-rCA/strings.xml | 5 + .../widgets/src/main/res/values-en-rGB/strings.xml | 5 + .../ui/widgets/src/main/res/values-eo/strings.xml | 5 + .../widgets/src/main/res/values-es-rAR/strings.xml | 5 + .../widgets/src/main/res/values-es-rCL/strings.xml | 5 + .../widgets/src/main/res/values-es-rES/strings.xml | 5 + .../widgets/src/main/res/values-es-rMX/strings.xml | 5 + .../ui/widgets/src/main/res/values-es/strings.xml | 5 + .../ui/widgets/src/main/res/values-et/strings.xml | 5 + .../ui/widgets/src/main/res/values-eu/strings.xml | 5 + .../ui/widgets/src/main/res/values-fa/strings.xml | 5 + .../ui/widgets/src/main/res/values-fi/strings.xml | 5 + .../ui/widgets/src/main/res/values-fr/strings.xml | 5 + .../ui/widgets/src/main/res/values-fur/strings.xml | 5 + .../widgets/src/main/res/values-fy-rNL/strings.xml | 5 + .../ui/widgets/src/main/res/values-gd/strings.xml | 5 + .../ui/widgets/src/main/res/values-gl/strings.xml | 5 + .../ui/widgets/src/main/res/values-gn/strings.xml | 5 + .../ui/widgets/src/main/res/values-hr/strings.xml | 5 + .../ui/widgets/src/main/res/values-hsb/strings.xml | 5 + .../ui/widgets/src/main/res/values-hu/strings.xml | 5 + .../widgets/src/main/res/values-hy-rAM/strings.xml | 5 + .../ui/widgets/src/main/res/values-ia/strings.xml | 5 + .../ui/widgets/src/main/res/values-in/strings.xml | 5 + .../ui/widgets/src/main/res/values-is/strings.xml | 5 + .../ui/widgets/src/main/res/values-it/strings.xml | 5 + .../ui/widgets/src/main/res/values-iw/strings.xml | 5 + .../ui/widgets/src/main/res/values-ja/strings.xml | 5 + .../ui/widgets/src/main/res/values-ka/strings.xml | 5 + .../ui/widgets/src/main/res/values-kaa/strings.xml | 5 + .../ui/widgets/src/main/res/values-kab/strings.xml | 5 + .../ui/widgets/src/main/res/values-kk/strings.xml | 5 + .../ui/widgets/src/main/res/values-kmr/strings.xml | 5 + .../ui/widgets/src/main/res/values-ko/strings.xml | 5 + .../ui/widgets/src/main/res/values-lo/strings.xml | 5 + .../widgets/src/main/res/values-nb-rNO/strings.xml | 5 + .../ui/widgets/src/main/res/values-nl/strings.xml | 5 + .../widgets/src/main/res/values-nn-rNO/strings.xml | 5 + .../ui/widgets/src/main/res/values-oc/strings.xml | 5 + .../widgets/src/main/res/values-pa-rIN/strings.xml | 5 + .../widgets/src/main/res/values-pa-rPK/strings.xml | 5 + .../ui/widgets/src/main/res/values-pl/strings.xml | 5 + .../widgets/src/main/res/values-pt-rBR/strings.xml | 5 + .../widgets/src/main/res/values-pt-rPT/strings.xml | 5 + .../ui/widgets/src/main/res/values-rm/strings.xml | 5 + .../ui/widgets/src/main/res/values-ro/strings.xml | 5 + .../ui/widgets/src/main/res/values-ru/strings.xml | 5 + .../ui/widgets/src/main/res/values-sat/strings.xml | 5 + .../ui/widgets/src/main/res/values-sc/strings.xml | 5 + .../ui/widgets/src/main/res/values-si/strings.xml | 5 + .../ui/widgets/src/main/res/values-sk/strings.xml | 5 + .../ui/widgets/src/main/res/values-skr/strings.xml | 5 + .../ui/widgets/src/main/res/values-sl/strings.xml | 5 + .../ui/widgets/src/main/res/values-sq/strings.xml | 5 + .../ui/widgets/src/main/res/values-sr/strings.xml | 5 + .../ui/widgets/src/main/res/values-su/strings.xml | 5 + .../widgets/src/main/res/values-sv-rSE/strings.xml | 5 + .../ui/widgets/src/main/res/values-tg/strings.xml | 5 + .../ui/widgets/src/main/res/values-th/strings.xml | 5 + .../ui/widgets/src/main/res/values-tr/strings.xml | 5 + .../ui/widgets/src/main/res/values-trs/strings.xml | 5 + .../ui/widgets/src/main/res/values-tt/strings.xml | 5 + .../ui/widgets/src/main/res/values-ug/strings.xml | 5 + .../ui/widgets/src/main/res/values-uk/strings.xml | 5 + .../ui/widgets/src/main/res/values-vi/strings.xml | 5 + .../widgets/src/main/res/values-zh-rCN/strings.xml | 5 + .../widgets/src/main/res/values-zh-rTW/strings.xml | 5 + .../ui/widgets/src/main/res/values/attrs.xml | 19 + .../ui/widgets/src/main/res/values/colors.xml | 10 + .../ui/widgets/src/main/res/values/dimens.xml | 14 + .../ui/widgets/src/main/res/values/strings.xml | 8 + .../ui/widgets/src/main/res/values/styles.xml | 63 ++ .../mozilla/components/ui/widgets/TestUtils.kt | 73 ++ .../ui/widgets/VerticalSwipeRefreshLayoutTest.kt | 430 ++++++++++ .../ui/widgets/WidgetSiteItemViewTest.kt | 93 +++ .../widgets/behavior/BrowserGestureDetectorTest.kt | 231 ++++++ .../behavior/EngineViewClippingBehaviorTest.kt | 221 +++++ .../behavior/EngineViewScrollingBehaviorTest.kt | 575 +++++++++++++ .../components/ui/widgets/behavior/TestUtils.kt | 62 ++ .../behavior/ViewYTranslationStrategyTest.kt | 712 ++++++++++++++++ .../ui/widgets/behavior/ViewYTranslatorTest.kt | 113 +++ .../org.mockito.plugins.MockMaker | 2 + .../src/test/resources/robolectric.properties | 1 + 456 files changed, 11995 insertions(+) create mode 100644 mobile/android/android-components/components/ui/autocomplete/README.md create mode 100644 mobile/android/android-components/components/ui/autocomplete/build.gradle create mode 100644 mobile/android/android-components/components/ui/autocomplete/proguard-rules.pro create mode 100644 mobile/android/android-components/components/ui/autocomplete/src/main/AndroidManifest.xml create mode 100644 mobile/android/android-components/components/ui/autocomplete/src/main/java/mozilla/components/ui/autocomplete/InlineAutocompleteEditText.kt create mode 100644 mobile/android/android-components/components/ui/autocomplete/src/main/res/values/attrs.xml create mode 100644 mobile/android/android-components/components/ui/autocomplete/src/test/java/mozilla/components/ui/autocomplete/InlineAutocompleteEditTextTest.kt create mode 100644 mobile/android/android-components/components/ui/autocomplete/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker create mode 100644 mobile/android/android-components/components/ui/autocomplete/src/test/resources/robolectric.properties create mode 100644 mobile/android/android-components/components/ui/colors/README.md create mode 100644 mobile/android/android-components/components/ui/colors/build.gradle create mode 100644 mobile/android/android-components/components/ui/colors/proguard-rules.pro create mode 100644 mobile/android/android-components/components/ui/colors/src/main/AndroidManifest.xml create mode 100644 mobile/android/android-components/components/ui/colors/src/main/java/mozilla/components/ui/colors/PhotonColors.kt create mode 100644 mobile/android/android-components/components/ui/colors/src/main/res/values/photon_colors.xml create mode 100644 mobile/android/android-components/components/ui/fonts/README.md create mode 100644 mobile/android/android-components/components/ui/fonts/build.gradle create mode 100644 mobile/android/android-components/components/ui/fonts/proguard-rules.pro create mode 100644 mobile/android/android-components/components/ui/fonts/src/main/AndroidManifest.xml create mode 100644 mobile/android/android-components/components/ui/fonts/src/main/res/values/roboto_fonts.xml create mode 100644 mobile/android/android-components/components/ui/icons/README.md create mode 100644 mobile/android/android-components/components/ui/icons/build.gradle create mode 100644 mobile/android/android-components/components/ui/icons/proguard-rules.pro create mode 100644 mobile/android/android-components/components/ui/icons/src/main/AndroidManifest.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_asleep.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_confused.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_eye_roll.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_hourglass.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_inspect.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_lock.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_no_internet.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_question_file.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_shred_file.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_surprised.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_unplugged.svg create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_add_to_homescreen_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_app_menu_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_app_menu_space_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_down_left_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_up_left_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_up_right_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_arrow_clockwise_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_autoplay_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_autoplay_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_circle_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_circle_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_info_circle_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_warning_circle_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_back_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_badge_fill_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_fill_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_tray_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_tray_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_briefcase.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_broken_lock.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_camera_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_camera_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cart.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_checkmark_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_8.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_left_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_right_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_up_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_up_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chill.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_circle.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_clipboard_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_collection_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_competitiveness_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cookies_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cookies_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_copy_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_credit_card_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_critical_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_critical_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_fill_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cryptominer_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_data_clearance_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_debug_drawer_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_delete_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_desktop_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_desktop_send_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_mobile_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_dollar.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_download_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_dropdown_arrow.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_edit_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_ellipsis_horizontal_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_ellipsis_vertical_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_experiments_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_cog_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_external_link_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_eye_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_eye_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fence.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fingerprinter_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_folder_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_folder_add_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_font.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_food.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_forward_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fruit.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_gift.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_globe_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_grid.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_grid_add_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_help_circle_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_help_circle_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_history_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_home_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_image_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_image_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_information_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_information_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lightbulb_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_link_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_location_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_location_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_slash_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_warning_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_warning_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_login_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_chrome_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_firefox_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_safari_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_microphone_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_microphone_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_more_grid_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_mozilla.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_night_mode_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_dot_badge_fill_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_open_in.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_packaging_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_page_zoom_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_page_zoom_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_passkey_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pause_badge_fill_16.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_permissions_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pet.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_badge_fill_16.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_filled.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_slash_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_play_badge_fill_16.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_plugin_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_plus_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_preferences.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_price_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_print_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_48.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_stroke_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_qr_code_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_quality_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reader_view_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reader_view_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reading_list_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reading_list_add_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reorder.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_rocket.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_rocket_filled.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_save_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_save_file_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_search_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_select_all.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_settings_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_share_android_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_share_apple_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shield_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shield_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shipping_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shopping_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_social_tracker_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sparkle_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_fill_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_half_fill_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_one_half_fill_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_stop.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_storage_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_storage_slash_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sync_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sync_tabs_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_badge_fill_20.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_new.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_number_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_tray_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_themes_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tool_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_translate_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tree.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_update_circle_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_vacation.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_warning_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_warning_fill_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_web_extension_default_icon.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_whats_new_24.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/values/attrs.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/values/colors.xml create mode 100644 mobile/android/android-components/components/ui/icons/src/main/res/values/mozac_ui_icons_strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/.gitignore create mode 100644 mobile/android/android-components/components/ui/tabcounter/README.md create mode 100644 mobile/android/android-components/components/ui/tabcounter/build.gradle create mode 100644 mobile/android/android-components/components/ui/tabcounter/proguard-rules.pro create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/AndroidManifest.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/java/mozilla/components/ui/tabcounter/TabCounter.kt create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/java/mozilla/components/ui/tabcounter/TabCounterMenu.kt create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_bar.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_box.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_round_rectangle_ripple.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/layout/mozac_ui_tabcounter_layout.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-am/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ar/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ast/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-azb/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-be/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bg/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bn/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-br/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bs/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ca/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cak/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ceb/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ckb/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-co/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cs/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cy/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-da/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-de/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-dsb/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-el/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-en-rCA/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-en-rGB/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-eo/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rAR/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rCL/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rES/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rMX/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-et/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-eu/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fa/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ff/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fi/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fr/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fur/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fy-rNL/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gd/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gl/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gn/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hi-rIN/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hr/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hsb/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hu/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hy-rAM/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ia/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-in/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-is/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-it/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-iw/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ja/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ka/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kaa/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kab/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kk/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kmr/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ko/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-lo/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-lt/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-my/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nb-rNO/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ne-rNP/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nl/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nn-rNO/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-oc/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-or/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pa-rIN/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pa-rPK/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pl/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pt-rBR/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pt-rPT/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-rm/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ro/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ru/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sat/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sc/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-si/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sk/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-skr/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sl/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sq/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sr/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-su/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sv-rSE/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-szl/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-te/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tg/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-th/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tl/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tr/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-trs/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tt/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tzm/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ug/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-uk/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ur/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-uz/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-vi/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-yo/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-zh-rCN/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values-zh-rTW/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values/attrs.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values/colors.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values/dimens.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/main/res/values/strings.xml create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/test/java/mozilla/components/ui/tabcounter/TabCounterMenuTest.kt create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/test/java/mozilla/components/ui/tabcounter/TabCounterTest.kt create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker create mode 100644 mobile/android/android-components/components/ui/tabcounter/src/test/resources/robolectric.properties create mode 100644 mobile/android/android-components/components/ui/widgets/README.md create mode 100644 mobile/android/android-components/components/ui/widgets/build.gradle create mode 100644 mobile/android/android-components/components/ui/widgets/proguard-rules.pro create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/AndroidManifest.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/Extentions.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/SnackbarDelegate.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/VerticalSwipeRefreshLayout.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/BrowserGestureDetector.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/EngineViewClippingBehavior.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/EngineViewScrollingBehavior.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/ViewYTranslationStrategy.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/ViewYTranslator.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/drawable/mozac_widget_favicon_background.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/drawable/rounded_button_background.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-am/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ar/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ast/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-azb/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-be/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-bg/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-br/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-bs/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ca/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-cak/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-co/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-cs/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-cy/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-da/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-de/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-dsb/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-el/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-en-rCA/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-en-rGB/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-eo/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rAR/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rCL/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rES/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rMX/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-es/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-et/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-eu/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-fa/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-fi/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-fr/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-fur/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-fy-rNL/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-gd/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-gl/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-gn/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-hr/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-hsb/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-hu/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-hy-rAM/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ia/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-in/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-is/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-it/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-iw/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ja/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ka/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-kaa/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-kab/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-kk/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-kmr/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ko/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-lo/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-nb-rNO/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-nl/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-nn-rNO/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-oc/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-pa-rIN/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-pa-rPK/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-pl/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-pt-rBR/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-pt-rPT/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-rm/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ro/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ru/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-sat/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-sc/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-si/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-sk/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-skr/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-sl/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-sq/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-sr/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-su/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-sv-rSE/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-tg/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-th/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-tr/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-trs/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-tt/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-ug/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-uk/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-vi/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-zh-rCN/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values-zh-rTW/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values/attrs.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values/colors.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values/dimens.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values/strings.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/main/res/values/styles.xml create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/TestUtils.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/VerticalSwipeRefreshLayoutTest.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/WidgetSiteItemViewTest.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/BrowserGestureDetectorTest.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/EngineViewClippingBehaviorTest.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/EngineViewScrollingBehaviorTest.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/TestUtils.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/ViewYTranslationStrategyTest.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/ViewYTranslatorTest.kt create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker create mode 100644 mobile/android/android-components/components/ui/widgets/src/test/resources/robolectric.properties (limited to 'mobile/android/android-components/components/ui') diff --git a/mobile/android/android-components/components/ui/autocomplete/README.md b/mobile/android/android-components/components/ui/autocomplete/README.md new file mode 100644 index 0000000000..da22f2fea5 --- /dev/null +++ b/mobile/android/android-components/components/ui/autocomplete/README.md @@ -0,0 +1,19 @@ +# [Android Components](../../../README.md) > UI > Autocomplete + +A set of components to provide autocomplete functionality. + +## Usage + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:ui-autocomplete:{latest-version}" +``` + +## License + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/ diff --git a/mobile/android/android-components/components/ui/autocomplete/build.gradle b/mobile/android/android-components/components/ui/autocomplete/build.gradle new file mode 100644 index 0000000000..16f9792a02 --- /dev/null +++ b/mobile/android/android-components/components/ui/autocomplete/build.gradle @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + namespace 'mozilla.components.ui.autocomplete' +} + +dependencies { + implementation ComponentsDependencies.androidx_appcompat + + implementation project(":support-base") + implementation project(":support-utils") + + testImplementation project(":support-test") + + testImplementation ComponentsDependencies.androidx_test_junit + testImplementation ComponentsDependencies.testing_robolectric +} + +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) diff --git a/mobile/android/android-components/components/ui/autocomplete/proguard-rules.pro b/mobile/android/android-components/components/ui/autocomplete/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/ui/autocomplete/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/ui/autocomplete/src/main/AndroidManifest.xml b/mobile/android/android-components/components/ui/autocomplete/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e16cda1d34 --- /dev/null +++ b/mobile/android/android-components/components/ui/autocomplete/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + diff --git a/mobile/android/android-components/components/ui/autocomplete/src/main/java/mozilla/components/ui/autocomplete/InlineAutocompleteEditText.kt b/mobile/android/android-components/components/ui/autocomplete/src/main/java/mozilla/components/ui/autocomplete/InlineAutocompleteEditText.kt new file mode 100644 index 0000000000..c029f226eb --- /dev/null +++ b/mobile/android/android-components/components/ui/autocomplete/src/main/java/mozilla/components/ui/autocomplete/InlineAutocompleteEditText.kt @@ -0,0 +1,905 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.autocomplete + +import android.content.ClipboardManager +import android.content.Context +import android.content.Context.INPUT_METHOD_SERVICE +import android.graphics.Rect +import android.os.Build +import android.provider.Settings.Secure.DEFAULT_INPUT_METHOD +import android.provider.Settings.Secure.getString +import android.text.Editable +import android.text.NoCopySpan +import android.text.Selection +import android.text.Spannable +import android.text.Spanned +import android.text.TextUtils +import android.text.TextWatcher +import android.text.style.BackgroundColorSpan +import android.text.style.ForegroundColorSpan +import android.util.AttributeSet +import android.view.KeyEvent +import android.view.MotionEvent +import android.view.View +import android.view.accessibility.AccessibilityEvent +import android.view.inputmethod.BaseInputConnection +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputConnection +import android.view.inputmethod.InputConnectionWrapper +import android.view.inputmethod.InputMethodManager +import androidx.annotation.VisibleForTesting +import androidx.appcompat.widget.AppCompatEditText +import mozilla.components.support.base.Component +import mozilla.components.support.base.facts.Action +import mozilla.components.support.base.facts.Fact +import mozilla.components.support.base.facts.collect +import mozilla.components.support.utils.SafeUrl +import androidx.appcompat.R as appcompatR + +typealias OnCommitListener = () -> Unit +typealias OnFilterListener = (String) -> Unit +typealias OnSearchStateChangeListener = (Boolean) -> Unit +typealias OnTextChangeListener = (String, String) -> Unit +typealias OnDispatchKeyEventPreImeListener = (KeyEvent?) -> Boolean +typealias OnKeyPreImeListener = (View, Int, KeyEvent) -> Boolean +typealias OnSelectionChangedListener = (Int, Int) -> Unit +typealias OnWindowsFocusChangeListener = (Boolean) -> Unit + +typealias TextFormatter = (String) -> String + +/** + * Aids in testing functionality which relies on some aspects of InlineAutocompleteEditText. + */ +interface AutocompleteView { + + /** + * Current text. + */ + val originalText: String + + /** + * Apply provided [result] autocomplete result. + */ + fun applyAutocompleteResult(result: InlineAutocompleteEditText.AutocompleteResult) + + /** + * Notify that there is no autocomplete result available. + */ + fun noAutocompleteResult() +} + +/** + * A UI edit text component which supports inline autocompletion. + * + * The background color of autocomplete spans can be configured using + * the custom autocompleteBackgroundColor attribute e.g. + * app:autocompleteBackgroundColor="#ffffff". + * + * A filter listener (see [setOnFilterListener]) needs to be attached to + * provide autocomplete results. It will be invoked when the input + * text changes. The listener gets direct access to this component (via its view + * parameter), so it can call {@link applyAutocompleteResult} in return. + * + * A commit listener (see [setOnCommitListener]) can be attached which is + * invoked when the user selected the result i.e. is done editing. + * + * Various other listeners can be attached to enhance default behaviour e.g. + * [setOnSelectionChangedListener] and [setOnWindowsFocusChangeListener] which + * will be invoked in response to [onSelectionChanged] and [onWindowFocusChanged] + * respectively (see also [setOnTextChangeListener], + * [setOnSelectionChangedListener], and [setOnWindowsFocusChangeListener]). + */ +@Suppress("LargeClass", "TooManyFunctions") +open class InlineAutocompleteEditText @JvmOverloads constructor( + ctx: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = appcompatR.attr.editTextStyle, +) : AppCompatEditText(ctx, attrs, defStyleAttr), AutocompleteView { + + data class AutocompleteResult( + val text: String, + val source: String, + val totalItems: Int, + ) { + fun startsWith(text: String): Boolean = this.text.startsWith(text) + } + + private var commitListener: OnCommitListener? = null + fun setOnCommitListener(l: OnCommitListener) { commitListener = l } + + private var filterListener: OnFilterListener? = null + fun setOnFilterListener(l: OnFilterListener) { filterListener = l } + fun refreshAutocompleteSuggestions() { filterListener?.invoke(originalText) } + + private var searchStateChangeListener: OnSearchStateChangeListener? = null + fun setOnSearchStateChangeListener(l: OnSearchStateChangeListener) { searchStateChangeListener = l } + + private var textChangeListener: OnTextChangeListener? = null + fun setOnTextChangeListener(l: OnTextChangeListener) { textChangeListener = l } + + private var dispatchKeyEventPreImeListener: OnDispatchKeyEventPreImeListener? = null + fun setOnDispatchKeyEventPreImeListener(l: OnDispatchKeyEventPreImeListener?) { dispatchKeyEventPreImeListener = l } + + private var keyPreImeListener: OnKeyPreImeListener? = null + fun setOnKeyPreImeListener(l: OnKeyPreImeListener) { keyPreImeListener = l } + + private var selectionChangedListener: OnSelectionChangedListener? = null + fun setOnSelectionChangedListener(l: OnSelectionChangedListener) { selectionChangedListener = l } + + private var windowFocusChangeListener: OnWindowsFocusChangeListener? = null + fun setOnWindowsFocusChangeListener(l: OnWindowsFocusChangeListener) { windowFocusChangeListener = l } + + // The previous autocomplete result returned to us + var autocompleteResult: AutocompleteResult? = null + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + set + + // Length of the user-typed portion of the result + private var autoCompletePrefixLength: Int = 0 + + // If text change is due to us setting autocomplete + private var settingAutoComplete: Boolean = false + + // Spans used for marking the autocomplete text + private var autoCompleteSpans: List? = null + + // Do not process autocomplete result + private var discardAutoCompleteResult: Boolean = false + + val nonAutocompleteText: String + get() = getNonAutocompleteText(text) + + override val originalText: String + get() = text.subSequence(0, autoCompletePrefixLength).toString() + + /** + * The background color used for the autocomplete suggestion. + */ + var autoCompleteBackgroundColor: Int = { + val a = context.obtainStyledAttributes(attrs, R.styleable.InlineAutocompleteEditText) + val color = a.getColor( + R.styleable.InlineAutocompleteEditText_autocompleteBackgroundColor, + DEFAULT_AUTOCOMPLETE_BACKGROUND_COLOR, + ) + a.recycle() + color + }() + + /** + * The Foreground color used for the autocomplete suggestion. + */ + var autoCompleteForegroundColor: Int? = null + + private val inputMethodManger get() = context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager? + + @SuppressWarnings("ReturnCount") + private val onKeyPreIme = fun (_: View, keyCode: Int, event: KeyEvent): Boolean { + // We only want to process one event per tap + if (event.action != KeyEvent.ACTION_DOWN) { + return false + } + + if (keyCode == KeyEvent.KEYCODE_ENTER) { + // If the edit text has a composition string, don't submit the text yet. + // ENTER is needed to commit the composition string. + val content = text + if (!hasCompositionString(content)) { + commitListener?.invoke() + return true + } + } + + if (keyCode == KeyEvent.KEYCODE_BACK) { + removeAutocomplete(text) + return false + } + + return false + } + + private val onKey = fun (_: View, keyCode: Int, event: KeyEvent): Boolean { + if (keyCode == KeyEvent.KEYCODE_ENTER) { + if (event.action != KeyEvent.ACTION_DOWN) { + return true + } + + commitListener?.invoke() + return true + } + + // Delete autocomplete text when backspacing or forward deleting. + return ( + ( + keyCode == KeyEvent.KEYCODE_DEL || + keyCode == KeyEvent.KEYCODE_FORWARD_DEL + ) && + removeAutocomplete(text) + ) + } + + private val onSelectionChanged = fun (selStart: Int, selEnd: Int) { + // The user has repositioned the cursor somewhere. We need to adjust + // the autocomplete text depending on where the new cursor is. + val text = text + val start = text.getSpanStart(AUTOCOMPLETE_SPAN) + + val nothingSelected = start == selStart && start == selEnd + if (settingAutoComplete || nothingSelected || start < 0) { + // Do not commit autocomplete text if there is no autocomplete text + // or if selection is still at start of autocomplete text + return + } + + if (selStart <= start && selEnd <= start) { + // The cursor is in user-typed text; remove any autocomplete text. + removeAutocomplete(text) + } else { + // The cursor is in the autocomplete text; commit it so it becomes regular text. + commitAutocomplete(text) + } + } + + private val isAmazonEchoShowKeyboard: Boolean + get() = INPUT_METHOD_AMAZON_ECHO_SHOW == getCurrentInputMethod() + + public override fun onAttachedToWindow() { + super.onAttachedToWindow() + + if (this.keyPreImeListener == null) { this.keyPreImeListener = onKeyPreIme } + if (this.selectionChangedListener == null) { this.selectionChangedListener = onSelectionChanged } + + setOnKeyListener(onKey) + addTextChangedListener(TextChangeListener()) + } + + public override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) { + super.onFocusChanged(gainFocus, direction, previouslyFocusedRect) + + // Make search icon inactive when edit toolbar search term isn't a user entered + // search term + val isActive = !TextUtils.isEmpty(text) + + searchStateChangeListener?.invoke(isActive) + + if (gainFocus) { + resetAutocompleteState() + return + } + + removeAutocomplete(text) + + try { + restartInput() + inputMethodManger?.hideSoftInputFromWindow(windowToken, 0) + } catch (ignored: NullPointerException) { + // See bug 782096 for details + } + } + + override fun setText(text: CharSequence?, type: BufferType) { + val textString = text?.toString() ?: "" + super.setText(textString, type) + + // Any autocomplete text would have been overwritten, so reset our autocomplete states. + resetAutocompleteState() + } + + /** + * Sets the text of the edit text. + * @param text The text to set. + * @param shouldAutoComplete If false, [TextChangeListener] the text watcher will be disabled for this set. + */ + fun setText(text: CharSequence?, shouldAutoComplete: Boolean = true) { + val wasSettingAutoComplete = settingAutoComplete + + // Disable listeners in order to stop auto completion + settingAutoComplete = !shouldAutoComplete + setText(text, BufferType.EDITABLE) + settingAutoComplete = wasSettingAutoComplete + } + + /** + * Appends the given text to the end of the current text. + * @param text The text to append. + * @param shouldAutoComplete If false, [TextChangeListener] text watcher will be disabled for this append. + */ + fun appendText(text: CharSequence?, shouldAutoComplete: Boolean = true) { + val wasSettingAutoComplete = settingAutoComplete + + // Disable listeners in order to stop auto completion + settingAutoComplete = !shouldAutoComplete + append(text) + settingAutoComplete = wasSettingAutoComplete + } + + override fun getText(): Editable { + return super.getText() as Editable + } + + override fun sendAccessibilityEventUnchecked(event: AccessibilityEvent) { + // We need to bypass the isShown() check in the default implementation + // for TYPE_VIEW_TEXT_SELECTION_CHANGED events so that accessibility + // services could detect a url change. + if (event.eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED && + parent != null && !isShown + ) { + onInitializeAccessibilityEvent(event) + dispatchPopulateAccessibilityEvent(event) + parent.requestSendAccessibilityEvent(this, event) + } else { + super.sendAccessibilityEventUnchecked(event) + } + } + + /** + * Mark the start of autocomplete changes so our text change + * listener does not react to changes in autocomplete text + */ + private fun beginSettingAutocomplete() { + beginBatchEdit() + settingAutoComplete = true + } + + /** + * Mark the end of autocomplete changes + */ + private fun endSettingAutocomplete() { + settingAutoComplete = false + endBatchEdit() + } + + /** + * Reset autocomplete states to their initial values + */ + private fun resetAutocompleteState() { + autoCompleteSpans = mutableListOf( + AUTOCOMPLETE_SPAN, + BackgroundColorSpan(autoCompleteBackgroundColor), + ).apply { + autoCompleteForegroundColor?.let { add(ForegroundColorSpan(it)) } + } + autocompleteResult = null + // Pretend we already autocompleted the existing text, + // so that actions like backspacing don't trigger autocompletion. + autoCompletePrefixLength = text.length + isCursorVisible = true + } + + /** + * Remove any autocomplete text + * + * @param text Current text content that may include autocomplete text + */ + private fun removeAutocomplete(text: Editable): Boolean { + val start = text.getSpanStart(AUTOCOMPLETE_SPAN) + if (start < 0) { + // No autocomplete text + return false + } + + beginSettingAutocomplete() + + // When we call delete() here, the autocomplete spans we set are removed as well. + text.delete(start, text.length) + + // Keep autoCompletePrefixLength the same because the prefix has not changed. + // Clear mAutoCompleteResult to make sure we get fresh autocomplete text next time. + autocompleteResult = null + + // Reshow the cursor. + isCursorVisible = true + + endSettingAutocomplete() + return true + } + + /** + * Convert any autocomplete text to regular text + * + * @param text Current text content that may include autocomplete text + */ + private fun commitAutocomplete(text: Editable): Boolean { + val start = text.getSpanStart(AUTOCOMPLETE_SPAN) + if (start < 0) { + // No autocomplete text + return false + } + + beginSettingAutocomplete() + + // Remove all spans here to convert from autocomplete text to regular text + for (span in autoCompleteSpans!!) { + text.removeSpan(span) + } + + // Keep mAutoCompleteResult the same because the result has not changed. + // Reset autoCompletePrefixLength because the prefix now includes the autocomplete text. + autoCompletePrefixLength = text.length + + // Reshow the cursor. + isCursorVisible = true + + endSettingAutocomplete() + + // Invoke textChangeListener manually, because previous autocomplete text is now committed + textChangeListener?.apply { + val fullText = text.toString() + invoke(fullText, fullText) + } + + return true + } + + /** + * Applies the provided result by updating the current autocomplete + * text and selection, if any. + * + * @param result the [AutocompleteResult] to apply + */ + override fun applyAutocompleteResult(result: AutocompleteResult) { + // If discardAutoCompleteResult is true, we temporarily disabled + // autocomplete (due to backspacing, etc.) and we should bail early. + if (discardAutoCompleteResult) { + return + } + + if (!isEnabled) { + autocompleteResult = null + return + } + + val text = text + val autoCompleteStart = text.getSpanStart(AUTOCOMPLETE_SPAN) + autocompleteResult = result + + if (autoCompleteStart > -1) { + // Autocomplete text already exists; we should replace existing autocomplete text. + replaceAutocompleteText(result, autoCompleteStart) + } else { + // No autocomplete text yet; we should add autocomplete text + addAutocompleteText(result) + } + + announceForAccessibility(text.toString()) + } + + private fun replaceAutocompleteText(result: AutocompleteResult, autoCompleteStart: Int) { + // Autocomplete text already exists; we should replace existing autocomplete text. + val text = text + val resultLength = result.text.length + + // If the result and the current text don't have the same prefixes, + // the result is stale and we should wait for the another result to come in. + if (!TextUtils.regionMatches(result.text, 0, text, 0, autoCompleteStart)) { + return + } + + beginSettingAutocomplete() + + // Replace the existing autocomplete text with new one. + // replace() preserves the autocomplete spans that we set before. + text.replace(autoCompleteStart, text.length, result.text, autoCompleteStart, resultLength) + + // Reshow the cursor if there is no longer any autocomplete text. + if (autoCompleteStart == resultLength) { + isCursorVisible = true + } + + endSettingAutocomplete() + } + + private fun addAutocompleteText(result: AutocompleteResult) { + // No autocomplete text yet; we should add autocomplete text + val text = text + val textLength = text.length + val resultLength = result.text.length + + // If the result prefix doesn't match the current text, + // the result is stale and we should wait for the another result to come in. + if (resultLength <= textLength || !TextUtils.regionMatches(result.text, 0, text, 0, textLength)) { + return + } + + val spans = text.getSpans(textLength, textLength, Any::class.java) + val spanStarts = IntArray(spans.size) + val spanEnds = IntArray(spans.size) + val spanFlags = IntArray(spans.size) + + // Save selection/composing span bounds so we can restore them later. + for (i in spans.indices) { + val span = spans[i] + val spanFlag = text.getSpanFlags(span) + + // We don't care about spans that are not selection or composing spans. + // For those spans, spanFlag[i] will be 0 and we don't restore them. + if (spanFlag and Spanned.SPAN_COMPOSING == 0 && + span !== Selection.SELECTION_START && + span !== Selection.SELECTION_END + ) { + continue + } + + spanStarts[i] = text.getSpanStart(span) + spanEnds[i] = text.getSpanEnd(span) + spanFlags[i] = spanFlag + } + + beginSettingAutocomplete() + + // First add trailing text. + text.append(result.text, textLength, resultLength) + + // Restore selection/composing spans. + for (i in spans.indices) { + val spanFlag = spanFlags[i] + if (spanFlag == 0) { + // Skip if the span was ignored before. + continue + } + text.setSpan(spans[i], spanStarts[i], spanEnds[i], spanFlag) + } + + // Mark added text as autocomplete text. + for (span in autoCompleteSpans!!) { + text.setSpan(span, textLength, resultLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + + // Hide the cursor. + isCursorVisible = false + + // Make sure the autocomplete text is visible. If the autocomplete text is too + // long, it would appear the cursor will be scrolled out of view. However, this + // is not the case in practice, because EditText still makes sure the cursor is + // still in view. + bringPointIntoView(resultLength) + + endSettingAutocomplete() + } + + override fun noAutocompleteResult() { + removeAutocomplete(text) + } + + /** + * Code to handle deleting autocomplete first when backspacing. + * If there is no autocomplete text, both removeAutocomplete() and commitAutocomplete() + * are no-ops and return false. Therefore we can use them here without checking explicitly + * if we have autocomplete text or not. + * + * Also turns off text prediction for private mode tabs. + */ + @SuppressWarnings("ComplexMethod") + override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? { + val ic = super.onCreateInputConnection(outAttrs) ?: return null + + return object : InputConnectionWrapper(ic, false) { + override fun deleteSurroundingText(beforeLength: Int, afterLength: Int): Boolean { + if (removeAutocomplete(text)) { + // If we have autocomplete text, the cursor is at the boundary between + // regular and autocomplete text. So regardless of which direction we + // are deleting, we should delete the autocomplete text first. + // + // On Amazon Echo Show devices, restarting input prevents us from backspacing + // the last few characters of autocomplete: #911. However, on non-Echo devices, + // not restarting input will cause the keyboard to desync when backspacing: #1489. + if (!isAmazonEchoShowKeyboard) { + restartInput() + } + return false + } + return super.deleteSurroundingText(beforeLength, afterLength) + } + + // available on API level 24+ + override fun deleteSurroundingTextInCodePoints(beforeLength: Int, afterLength: Int): Boolean { + if (removeAutocomplete(text)) { + restartInput() + return false + } + return super.deleteSurroundingTextInCodePoints(beforeLength, afterLength) + } + + /** + * Optionally remove the current autocompletion depending on the new [text]. + * + * Cases in which the autocompletion will be removed: + * - if the user pressed the backspace to remove the autocompletion or + * - if the user modified their input such that the autocompletion does not apply anymore. + * + * @return `true` if this method consumed the user input, `false` otherwise. + */ + @Suppress("ComplexCondition") + private fun removeAutocompleteOnComposing(text: CharSequence): Boolean { + val editable = getText() + + // Remove the autocomplete text as soon as possible if not applicable anymore. + if (!editableText.startsWith(text) && removeAutocomplete(editable)) { + return false // If the user modified their input then allow the new text to be set. + } + + val composingStart = BaseInputConnection.getComposingSpanStart(editable) + val composingEnd = BaseInputConnection.getComposingSpanEnd(editable) + // We only delete the autocomplete text when the user is backspacing, + // i.e. when the composing text is getting shorter. + if (composingStart >= 0 && + composingEnd >= 0 && + composingEnd - composingStart > text.length && + removeAutocomplete(editable) + ) { + finishComposingText() + // Make the IME aware that we interrupted the setComposingText call, + // by calling restartInput() + restartInput() + return true + } + return false + } + + override fun commitText(text: CharSequence, newCursorPosition: Int): Boolean { + return if (removeAutocompleteOnComposing(text)) { + false + } else { + super.commitText(text, newCursorPosition) + } + } + + override fun setComposingText(text: CharSequence, newCursorPosition: Int): Boolean { + return if (removeAutocompleteOnComposing(text)) { + false + } else { + super.setComposingText(text, newCursorPosition) + } + } + } + } + + private fun restartInput() { + inputMethodManger?.restartInput(this) + } + + private fun getCurrentInputMethod(): String { + val inputMethod = getString(context.contentResolver, DEFAULT_INPUT_METHOD) + return inputMethod ?: "" + } + + /** + * This class watches for text changes and adds or removes autocomplete text accordingly. + * Using this class is preferred when making text changes as it will not interfere + * with any composing text at the same time as custom keyboards. + * + * Known issue: autocomplete will not be added when replacing the current text with one + * that has a text length equal to the one being replaced minus 1. + * */ + private inner class TextChangeListener : TextWatcher { + + /** + * Holds the value of the non-autocomplete text before any changes have been made. + * */ + private var beforeChangedTextNonAutocomplete: String = "" + + /** + * The number of characters that have been changed in [onTextChanged]. + * When using keyboards that do not have their own text correction enabled + * and the user is pressing backspace this value will be 0. + * */ + private var textChangedCount: Int = 0 + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + if (!isEnabled || settingAutoComplete) return + beforeChangedTextNonAutocomplete = getNonAutocompleteText(text) + } + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + if (settingAutoComplete) return + + // In this initial implementation, we do not include the changed text to minimize PII. + Fact(Component.UI_AUTOCOMPLETE, Action.IMPLEMENTATION_DETAIL, "onTextChanged", "InlineAutocompleteEditText") + .collect() + + if (!isEnabled) return + + textChangedCount = count + } + + override fun afterTextChanged(editable: Editable) { + if (!isEnabled || settingAutoComplete) return + + val afterNonAutocompleteText = getNonAutocompleteText(editable) + + val hasTextShortenedByOne: Boolean = + beforeChangedTextNonAutocomplete.length == afterNonAutocompleteText.length + 1 + + // Covers both keyboards with text correction activated and those without. + val hasBackspaceBeenPressed = + textChangedCount == 0 || hasTextShortenedByOne + + // No autocompleting when typing a search query + val afterTextIsSearch = afterNonAutocompleteText.contains(" ") + + val hasTextBeenAdded: Boolean = + ( + afterNonAutocompleteText.contains(beforeChangedTextNonAutocomplete) || + beforeChangedTextNonAutocomplete.isEmpty() + ) && + afterNonAutocompleteText.length > beforeChangedTextNonAutocomplete.length + + var shouldAddAutocomplete: Boolean = hasTextBeenAdded || (!afterTextIsSearch && !hasBackspaceBeenPressed) + + autoCompletePrefixLength = afterNonAutocompleteText.length + + // If we are not autocompleting, we set discardAutoCompleteResult to true + // to discard any autocomplete results that are in-flight, and vice versa. + discardAutoCompleteResult = !shouldAddAutocomplete + + if (!shouldAddAutocomplete) { + // Remove the old autocomplete text until any new autocomplete text gets added. + removeAutocomplete(editable) + } else { + // If this text already matches our autocomplete text, autocomplete likely + // won't change. Just reuse the old autocomplete value. + autocompleteResult?.takeIf { it.startsWith(afterNonAutocompleteText) }?.let { + applyAutocompleteResult(it) + shouldAddAutocomplete = false + } + } + + // Update search icon with an active state since user is typing + searchStateChangeListener?.invoke(afterNonAutocompleteText.isNotEmpty()) + + if (shouldAddAutocomplete) { + filterListener?.invoke(afterNonAutocompleteText) + } + + textChangeListener?.invoke(afterNonAutocompleteText, text.toString()) + } + } + + override fun dispatchKeyEventPreIme(event: KeyEvent?): Boolean { + return event?.let { + dispatchKeyEventPreImeListener?.invoke(it) ?: onKeyPreIme(it.keyCode, it) + } ?: super.dispatchKeyEventPreIme(event) + } + + override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean { + return keyPreImeListener?.invoke(this, keyCode, event) ?: false + } + + public override fun onSelectionChanged(selStart: Int, selEnd: Int) { + selectionChangedListener?.invoke(selStart, selEnd) + super.onSelectionChanged(selStart, selEnd) + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + windowFocusChangeListener?.invoke(hasFocus) + } + + override fun onTextContextMenuItem(id: Int): Boolean { + // Ensure more control over what gets pasted from the framework floating menu. + // Behavior closely following the default implementation from TextView#onTextContextMenuItem(). + if (id == android.R.id.paste || id == android.R.id.pasteAsPlainText) { + val selectionStart = selectionStart + val selectionEnd = selectionEnd + + val min = 0.coerceAtLeast(selectionStart.coerceAtMost(selectionEnd)) + val max = 0.coerceAtLeast(selectionStart.coerceAtLeast(selectionEnd)) + + if (id == android.R.id.pasteAsPlainText || + (id == android.R.id.paste && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + ) { + paste(min, max, false) + } else { + paste(min, max, true) + } + + return true // action was performed + } + + return callOnTextContextMenuItemSuper(id) + } + + @Suppress("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { + return if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M && + event.actionMasked == MotionEvent.ACTION_UP + ) { + // Android 6 occasionally throws a NullPointerException inside Editor.onTouchEvent() + // for ACTION_UP when attempting to display (uninitialised) text handles. The Editor + // and TextView IME interactions are quite complex, so I don't know how to properly + // work around this issue, but we can at least catch the NPE to prevent crashing + // the whole app. + // (Editor tries to make both selection handles visible, but in certain cases they haven't + // been initialised yet, causing the NPE. It doesn't bother to check the selection handle + // state, and making some other calls to ensure the handles exist doesn't seem like a + // clean solution either since I don't understand most of the selection logic. This implementation + // only seems to exist in Android 6, both Android 5 and 7 have different implementations.) + try { + super.onTouchEvent(event) + } catch (ignored: NullPointerException) { + // Ignore this (see above) - since we're now in an unknown state let's clear all selection + // (which is still better than an arbitrary crash that we can't control): + clearFocus() + true + } + } else { + super.onTouchEvent(event) + } + } + + @VisibleForTesting + internal fun callOnTextContextMenuItemSuper(id: Int) = super.onTextContextMenuItem(id) + + /** + * Paste clipboard content between min and max positions. + * + * Method matching TextView#paste() but which also strips unwanted schemes before actually pasting. + */ + @Suppress("NestedBlockDepth") + @VisibleForTesting + internal fun paste(min: Int, max: Int, withFormatting: Boolean) { + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = clipboard.primaryClip + + if (clip != null) { + var didFirst = false + for (i in 0 until clip.itemCount) { + val textToBePasted: CharSequence? + textToBePasted = if (withFormatting) { + clip.getItemAt(i).coerceToStyledText(context) + } else { + // Get an item as text and remove all spans by toString(). + val text = clip.getItemAt(i).coerceToText(context) + (text as? Spanned)?.toString() ?: text + } + + // Actually stripping unwanted schemes + val safeTextToBePasted = SafeUrl.stripUnsafeUrlSchemes(context, textToBePasted) + + if (safeTextToBePasted != null) { + if (!didFirst) { + Selection.setSelection(editableText as Spannable?, max) + editableText.replace(min, max, safeTextToBePasted) + didFirst = true + } else { + editableText.insert(selectionEnd, "\n") + editableText.insert(selectionEnd, safeTextToBePasted) + } + } + } + } + } + + companion object { + internal val AUTOCOMPLETE_SPAN = NoCopySpan.Concrete() + internal const val DEFAULT_AUTOCOMPLETE_BACKGROUND_COLOR = 0xffb5007f.toInt() + + // The Echo Show IME does not conflict with Fire TV: com.amazon.tv.ime/.FireTVIME + // However, it may be used by other Amazon keyboards. In theory, if they have the same IME + // ID, they should have similar behavior. + const val INPUT_METHOD_AMAZON_ECHO_SHOW = "com.amazon.bluestone.keyboard/.DictationIME" + + /** + * Get the portion of text that is not marked as autocomplete text. + * + * @param text Current text content that may include autocomplete text + */ + private fun getNonAutocompleteText(text: Editable): String { + val start = text.getSpanStart(AUTOCOMPLETE_SPAN) + return if (start < 0) { + // No autocomplete text; return the whole string. + text.toString() + } else { + // Only return the portion that's not autocomplete text + TextUtils.substring(text, 0, start) + } + } + + private fun hasCompositionString(content: Editable): Boolean { + val spans = content.getSpans(0, content.length, Any::class.java) + return spans.any { span -> content.getSpanFlags(span) and Spanned.SPAN_COMPOSING != 0 } + } + } +} diff --git a/mobile/android/android-components/components/ui/autocomplete/src/main/res/values/attrs.xml b/mobile/android/android-components/components/ui/autocomplete/src/main/res/values/attrs.xml new file mode 100644 index 0000000000..2b23026264 --- /dev/null +++ b/mobile/android/android-components/components/ui/autocomplete/src/main/res/values/attrs.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/autocomplete/src/test/java/mozilla/components/ui/autocomplete/InlineAutocompleteEditTextTest.kt b/mobile/android/android-components/components/ui/autocomplete/src/test/java/mozilla/components/ui/autocomplete/InlineAutocompleteEditTextTest.kt new file mode 100644 index 0000000000..d455b0a28e --- /dev/null +++ b/mobile/android/android-components/components/ui/autocomplete/src/test/java/mozilla/components/ui/autocomplete/InlineAutocompleteEditTextTest.kt @@ -0,0 +1,585 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.autocomplete + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.os.Build +import android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE +import android.view.KeyEvent +import android.view.ViewParent +import android.view.accessibility.AccessibilityEvent +import android.view.inputmethod.BaseInputConnection +import android.view.inputmethod.EditorInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.ui.autocomplete.InlineAutocompleteEditText.AutocompleteResult +import mozilla.components.ui.autocomplete.InlineAutocompleteEditText.Companion.AUTOCOMPLETE_SPAN +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.robolectric.Robolectric.buildAttributeSet +import org.robolectric.annotation.Config + +@RunWith(AndroidJUnit4::class) +class InlineAutocompleteEditTextTest { + + private val attributes = buildAttributeSet().build() + + @Test + fun autoCompleteResult() { + val result = AutocompleteResult("testText", "testSource", 1) + assertEquals("testText", result.text) + assertEquals("testSource", result.source) + assertEquals(1, result.totalItems) + } + + @Test + fun getNonAutocompleteText() { + val et = InlineAutocompleteEditText(testContext) + et.setText("Test") + assertEquals("Test", et.nonAutocompleteText) + + et.text.setSpan(AUTOCOMPLETE_SPAN, 2, 3, SPAN_EXCLUSIVE_EXCLUSIVE) + assertEquals("Te", et.nonAutocompleteText) + + et.text.setSpan(AUTOCOMPLETE_SPAN, 0, 3, SPAN_EXCLUSIVE_EXCLUSIVE) + assertEquals("", et.nonAutocompleteText) + } + + @Test + fun getOriginalText() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.setText("Test") + assertEquals("Test", et.originalText) + + et.text.setSpan(AUTOCOMPLETE_SPAN, 2, 3, SPAN_EXCLUSIVE_EXCLUSIVE) + assertEquals("Test", et.originalText) + + et.text.setSpan(AUTOCOMPLETE_SPAN, 0, 3, SPAN_EXCLUSIVE_EXCLUSIVE) + assertEquals("Test", et.originalText) + } + + @Test + fun onFocusChange() { + val et = InlineAutocompleteEditText(testContext, attributes) + val searchStates = mutableListOf() + + et.setOnSearchStateChangeListener { b: Boolean -> searchStates.add(searchStates.size, b) } + et.onFocusChanged(false, 0, null) + + et.setText("text") + et.text.setSpan(AUTOCOMPLETE_SPAN, 0, 3, SPAN_EXCLUSIVE_EXCLUSIVE) + et.onFocusChanged(false, 0, null) + assertTrue(et.text.isEmpty()) + + et.setText("text") + et.text.setSpan(AUTOCOMPLETE_SPAN, 0, 3, SPAN_EXCLUSIVE_EXCLUSIVE) + et.onFocusChanged(true, 0, null) + assertFalse(et.text.isEmpty()) + assertEquals(listOf(false, true, true), searchStates) + } + + @Test + fun sendAccessibilityEventUnchecked() { + val et = spy(InlineAutocompleteEditText(testContext, attributes)) + doReturn(false).`when`(et).isShown + doReturn(mock(ViewParent::class.java)).`when`(et).parent + + val event = AccessibilityEvent() + event.eventType = AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED + et.sendAccessibilityEventUnchecked(event) + + verify(et).onInitializeAccessibilityEvent(event) + verify(et).dispatchPopulateAccessibilityEvent(event) + verify(et.parent).requestSendAccessibilityEvent(et, event) + } + + @Test + fun onAutocompleteSetsEmptyResult() { + val et = spy(InlineAutocompleteEditText(testContext, attributes)) + + doReturn(false).`when`(et).isEnabled + et.applyAutocompleteResult(AutocompleteResult("text", "source", 1)) + assertNull(et.autocompleteResult) + } + + @Test + fun onAutocompleteDiscardsStaleResult() { + val et = spy(InlineAutocompleteEditText(testContext, attributes)) + doReturn(true).`when`(et).isEnabled + et.setText("text") + + et.applyAutocompleteResult(AutocompleteResult("stale result", "source", 1)) + assertEquals("text", et.text.toString()) + + et.text.setSpan(AUTOCOMPLETE_SPAN, 1, 3, SPAN_EXCLUSIVE_EXCLUSIVE) + et.applyAutocompleteResult(AutocompleteResult("stale result", "source", 1)) + assertEquals("text", et.text.toString()) + } + + @Test + fun onAutocompleteReplacesExistingAutocompleteText() { + val et = spy(InlineAutocompleteEditText(testContext, attributes)) + doReturn(true).`when`(et).isEnabled + + et.setText("text") + et.text.setSpan(AUTOCOMPLETE_SPAN, 1, 3, SPAN_EXCLUSIVE_EXCLUSIVE) + et.applyAutocompleteResult(AutocompleteResult("text completed", "source", 1)) + assertEquals("text completed", et.text.toString()) + } + + @Test + fun onAutocompleteAppendsExistingText() { + val et = spy(InlineAutocompleteEditText(testContext, attributes)) + doReturn(true).`when`(et).isEnabled + + et.setText("text") + et.applyAutocompleteResult(AutocompleteResult("text completed", "source", 1)) + assertEquals("text completed", et.text.toString()) + } + + @Test + fun onAutocompleteSetsSpan() { + val et = spy(InlineAutocompleteEditText(testContext, attributes)) + doReturn(true).`when`(et).isEnabled + + et.setText("text") + et.applyAutocompleteResult(AutocompleteResult("text completed", "source", 1)) + + assertEquals(4, et.text.getSpanStart(AUTOCOMPLETE_SPAN)) + assertEquals(14, et.text.getSpanEnd(AUTOCOMPLETE_SPAN)) + assertEquals(SPAN_EXCLUSIVE_EXCLUSIVE, et.text.getSpanFlags(AUTOCOMPLETE_SPAN)) + } + + @Test + fun onKeyPreImeListenerInvocation() { + val et = InlineAutocompleteEditText(testContext, attributes) + var invokedWithParams: List? = null + et.setOnKeyPreImeListener { p1, p2, p3 -> + invokedWithParams = listOf(p1, p2, p3) + true + } + val event = mock(KeyEvent::class.java) + et.onKeyPreIme(1, event) + assertEquals(listOf(et, 1, event), invokedWithParams) + } + + @Test + fun onSelectionChangedListenerInvocation() { + val et = InlineAutocompleteEditText(testContext, attributes) + var invokedWithParams: List? = null + et.setOnSelectionChangedListener { p1, p2 -> + invokedWithParams = listOf(p1, p2) + } + et.onSelectionChanged(0, 1) + assertEquals(listOf(0, 1), invokedWithParams) + } + + @Test + fun onSelectionChangedCommitsResult() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.onAttachedToWindow() + + et.setText("text") + et.applyAutocompleteResult(AutocompleteResult("text completed", "source", 1)) + assertEquals(4, et.text.getSpanStart(AUTOCOMPLETE_SPAN)) + + et.onSelectionChanged(4, 14) + assertEquals(-1, et.text.getSpanStart(AUTOCOMPLETE_SPAN)) + } + + @Test + fun onWindowFocusChangedListenerInvocation() { + val et = InlineAutocompleteEditText(testContext, attributes) + var invokedWithParams: List? = null + et.setOnWindowsFocusChangeListener { p1 -> + invokedWithParams = listOf(p1) + } + et.onWindowFocusChanged(true) + assertEquals(listOf(true), invokedWithParams) + } + + @Test + fun onCommitListenerInvocation() { + val et = InlineAutocompleteEditText(testContext, attributes) + var invoked = false + et.setOnCommitListener { invoked = true } + et.onAttachedToWindow() + + et.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)) + assertTrue(invoked) + } + + @Test + fun onTextChangeListenerInvocation() { + val et = InlineAutocompleteEditText(testContext, attributes) + var invokedWithParams: List? = null + et.setOnTextChangeListener { p1, p2 -> + invokedWithParams = listOf(p1, p2) + } + et.onAttachedToWindow() + + et.setText("text") + assertEquals(listOf("text", "text"), invokedWithParams) + } + + @Test + fun onSearchStateChangeListenerInvocation() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.onAttachedToWindow() + + var invokedWithParams: List? = null + et.setOnSearchStateChangeListener { p1 -> + invokedWithParams = listOf(p1) + } + + et.setText("") + assertEquals(listOf(false), invokedWithParams) + + et.setText("text") + assertEquals(listOf(true), invokedWithParams) + } + + @Test + fun onFilterListenerInvocation() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.onAttachedToWindow() + + var lastInvokedWithText: String? = null + var invokedCounter = 0 + et.setOnFilterListener { p1 -> + lastInvokedWithText = p1 + invokedCounter++ + } + + // Already have an autocomplete result, and setting a text to the same value as the result. + et.applyAutocompleteResult(AutocompleteResult("text", "source", 1)) + et.setText("text") + // Autocomplete filter shouldn't have been called, because we already have a matching result. + assertEquals(0, invokedCounter) + + et.setText("text") + assertEquals(1, invokedCounter) + assertEquals("text", lastInvokedWithText) + + // Test backspace. We don't expect autocomplete to have been called. + et.setText("tex") + assertEquals(1, invokedCounter) + + // Presence of a space is counted as a 'search query', we don't autocomplete those. + et.setText("search term") + assertEquals(1, invokedCounter) + + // Empty text isn't autocompleted either. + et.setText("") + assertEquals(1, invokedCounter) + + // Autocomplete for the first letter + et.setText("t") + assertEquals(2, invokedCounter) + et.applyAutocompleteResult(AutocompleteResult("text", "source", 1)) + + // Autocomplete should be called for the next letter that doesn't match the result + et.setText("ta") + assertEquals(3, invokedCounter) + } + + @Test + fun `GIVEN an autocomplete listener WHEN asked to refresh autocomplete suggestions THEN restart the autocomplete functionality with the curret text`() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.onAttachedToWindow() + et.setText("Test") + var lastInvokedWithText: String? = null + var invokedCounter = 0 + et.setOnFilterListener { p1 -> + lastInvokedWithText = p1 + invokedCounter++ + } + + et.refreshAutocompleteSuggestions() + + assertEquals("Test", lastInvokedWithText) + assertEquals(1, invokedCounter) + } + + @Test + fun onCreateInputConnection() { + val et = spy(InlineAutocompleteEditText(testContext, attributes)) + val icw = et.onCreateInputConnection(mock(EditorInfo::class.java)) + doReturn(true).`when`(et).isEnabled + + et.setText("text") + et.applyAutocompleteResult(AutocompleteResult("text completed", "source", 1)) + assertEquals("text completed", et.text.toString()) + + icw?.deleteSurroundingText(0, 1) + assertNull(et.autocompleteResult) + assertEquals("text", et.text.toString()) + + et.applyAutocompleteResult(AutocompleteResult("text completed", "source", 1)) + assertEquals("text completed", et.text.toString()) + + BaseInputConnection.setComposingSpans(et.text) + icw?.commitText("text", 4) + assertNull(et.autocompleteResult) + assertEquals("text", et.text.toString()) + + et.applyAutocompleteResult(AutocompleteResult("text completed", "source", 1)) + assertEquals("text completed", et.text.toString()) + + BaseInputConnection.setComposingSpans(et.text) + icw?.setComposingText("text", 4) + assertNull(et.autocompleteResult) + assertEquals("text", et.text.toString()) + } + + @Test + fun removeAutocompleteOnComposing() { + val et = InlineAutocompleteEditText(testContext, attributes) + val ic = et.onCreateInputConnection(mock(EditorInfo::class.java)) + + ic?.setComposingText("text", 1) + assertEquals("text", et.text.toString()) + + et.applyAutocompleteResult(AutocompleteResult("text completed", "source", 1)) + assertEquals("text completed", et.text.toString()) + + // Simulating a backspace which should remove the autocomplete and leave original text + ic?.setComposingText("tex", 1) + assertEquals("text", et.text.toString()) + + // Verify that we finished composing + assertEquals(-1, BaseInputConnection.getComposingSpanStart(et.text)) + assertEquals(-1, BaseInputConnection.getComposingSpanEnd(et.text)) + } + + @Test + fun `GIVEN the current text contains an autocompletion WHEN a new character does not match the autocompletion THEN remove the autocompletion`() { + val et = InlineAutocompleteEditText(testContext, attributes) + val ic = et.onCreateInputConnection(mock(EditorInfo::class.java)) + + ic?.setComposingText("mo", 1) + assertEquals("mo", et.text.toString()) + + et.applyAutocompleteResult(AutocompleteResult("mozilla", "source", 1)) + assertEquals("mozilla", et.text.toString()) + + // Simulating the user entering a new character which makes the current autocomplete invalid + ic?.setComposingText("mod", 1) + assertEquals("mod", et.text.toString()) + + // Verify that autocompletion works for the new text + et.applyAutocompleteResult(AutocompleteResult("moderator", "source", 1)) + assertEquals("moderator", et.text.toString()) + } + + @Test + fun `GIVEN empty edit field WHEN text 'g' added THEN autocomplete to google`() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.setText("") + et.onAttachedToWindow() + + et.autocompleteResult = AutocompleteResult( + text = "google.com", + source = "test-source", + totalItems = 100, + ) + + et.setText("g") + assertEquals("google.com", "${et.text}") + } + + @Test + fun `GIVEN empty edit field WHEN text 'g ' added THEN don't autocomplete to google`() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.setText("") + et.onAttachedToWindow() + + et.autocompleteResult = AutocompleteResult( + text = "google.com", + source = "test-source", + totalItems = 100, + ) + + et.setText("g ") + assertEquals("g ", "${et.text}") + } + + @Test + fun `GIVEN field with 'google' WHEN backspacing THEN doesn't autocomplete`() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.setText("google") + et.onAttachedToWindow() + + et.autocompleteResult = AutocompleteResult( + text = "google.com", + source = "test-source", + totalItems = 100, + ) + + et.setText("googl") + assertEquals("googl", "${et.text}") + } + + @Test + fun `GIVEN field with selected text WHEN text 'g' added THEN autocomplete to google`() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.setText("testestest") + et.selectAll() + et.onAttachedToWindow() + et.autocompleteResult = AutocompleteResult( + text = "google.com", + source = "test-source", + totalItems = 100, + ) + + et.setText("g") + assertEquals("google.com", "${et.text}") + } + + @Test + fun `GIVEN field with selected text 'google ' WHEN text 'g' added THEN autocomplete to google`() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.setText("https://www.google.com/") + et.selectAll() + et.onAttachedToWindow() + et.autocompleteResult = AutocompleteResult( + text = "google.com", + source = "test-source", + totalItems = 100, + ) + + et.setText("g") + assertEquals("google.com", "${et.text}") + } + + @Test + fun `WHEN setting text THEN isEnabled is never modified`() { + val et = spy(InlineAutocompleteEditText(testContext, attributes)) + et.setText("", shouldAutoComplete = false) + // assigning here so it verifies the setter, not the getter + verify(et, never()).isEnabled = true + } + + @Test + fun `WHEN onTextContextMenuItem is called for options other than paste THEN we should not paste() and just call super`() { + val editText = spy(InlineAutocompleteEditText(testContext, attributes)) + + editText.onTextContextMenuItem(android.R.id.copy) + editText.onTextContextMenuItem(android.R.id.shareText) + editText.onTextContextMenuItem(android.R.id.cut) + editText.onTextContextMenuItem(android.R.id.selectAll) + + verify(editText, never()).paste(anyInt(), anyInt(), anyBoolean()) + verify(editText, times(4)).callOnTextContextMenuItemSuper(anyInt()) + } + + @Test + fun `WHEN onTextContextMenuItem is called for paste THEN we should paste() and not call super`() { + val editText = spy(InlineAutocompleteEditText(testContext, attributes)) + + editText.onTextContextMenuItem(android.R.id.paste) + + verify(editText).paste(anyInt(), anyInt(), anyBoolean()) + verify(editText, never()).callOnTextContextMenuItemSuper(anyInt()) + } + + @Test + fun `WHEN onTextContextMenuItem is called for pasteAsPlainText THEN we should paste() and not call super`() { + val editText = spy(InlineAutocompleteEditText(testContext, attributes)) + + editText.onTextContextMenuItem(android.R.id.pasteAsPlainText) + + verify(editText).paste(anyInt(), anyInt(), anyBoolean()) + verify(editText, never()).callOnTextContextMenuItemSuper(anyInt()) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.LOLLIPOP, Build.VERSION_CODES.LOLLIPOP_MR1]) + fun `GIVEN an Android L device, WHEN onTextContextMenuItem is called for paste THEN we should paste() with formatting`() { + val editText = spy(InlineAutocompleteEditText(testContext, attributes)) + + editText.onTextContextMenuItem(android.R.id.paste) + + verify(editText).paste(anyInt(), anyInt(), eq(true)) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.M, Build.VERSION_CODES.N, Build.VERSION_CODES.O, Build.VERSION_CODES.P]) + fun `GIVEN an Android M device, WHEN onTextContextMenuItem is called for paste THEN we should paste() without formatting`() { + val editText = spy(InlineAutocompleteEditText(testContext, attributes)) + + editText.onTextContextMenuItem(android.R.id.paste) + + verify(editText).paste(anyInt(), anyInt(), eq(false)) + } + + @Test + fun `GIVEN no previous text WHEN paste is selected THEN paste() should be called with 0,0`() { + val editText = spy(InlineAutocompleteEditText(testContext, attributes)) + + editText.onTextContextMenuItem(android.R.id.paste) + + verify(editText).paste(eq(0), eq(0), eq(false)) + } + + @Test + fun `GIVEN 5 chars previous text WHEN paste is selected THEN paste() should be called with 0,5`() { + val editText = spy(InlineAutocompleteEditText(testContext, attributes)) + editText.setText("chars") + editText.selectAll() + + editText.onTextContextMenuItem(android.R.id.paste) + + verify(editText).paste(eq(0), eq(5), eq(false)) + } + + @Test + fun `WHEN paste() is called with new text THEN we will display the new text`() { + val editText = spy(InlineAutocompleteEditText(testContext, attributes)) + (testContext.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).apply { + setPrimaryClip(ClipData.newPlainText("Test", "test")) + } + + assertEquals("", editText.text.toString()) + + editText.paste(0, 0, false) + + assertEquals("test", editText.text.toString()) + } + + fun `WHEN committing autocomplete THEN textChangedListener is invoked`() { + val et = InlineAutocompleteEditText(testContext, attributes) + et.setText("") + + et.onAttachedToWindow() + et.autocompleteResult = AutocompleteResult( + text = "google.com", + source = "test-source", + totalItems = 100, + ) + et.setText("g") + var callbackInvoked = false + et.setOnTextChangeListener { _, _ -> + callbackInvoked = true + } + et.setSelection(3) + assertTrue(callbackInvoked) + } +} diff --git a/mobile/android/android-components/components/ui/autocomplete/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/mobile/android/android-components/components/ui/autocomplete/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..cf1c399ea8 --- /dev/null +++ b/mobile/android/android-components/components/ui/autocomplete/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,2 @@ +mock-maker-inline +// This allows mocking final classes (classes are final by default in Kotlin) diff --git a/mobile/android/android-components/components/ui/autocomplete/src/test/resources/robolectric.properties b/mobile/android/android-components/components/ui/autocomplete/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..932b01b9eb --- /dev/null +++ b/mobile/android/android-components/components/ui/autocomplete/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 diff --git a/mobile/android/android-components/components/ui/colors/README.md b/mobile/android/android-components/components/ui/colors/README.md new file mode 100644 index 0000000000..2b7855fee9 --- /dev/null +++ b/mobile/android/android-components/components/ui/colors/README.md @@ -0,0 +1,19 @@ +# [Android Components](../../../README.md) > UI > Colors + +The standard set of [Photon](https://design.firefox.com/photon/) colors. + +## Usage + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:ui-colors:{latest-version}" +``` + +## License + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/ diff --git a/mobile/android/android-components/components/ui/colors/build.gradle b/mobile/android/android-components/components/ui/colors/build.gradle new file mode 100644 index 0000000000..865b113e50 --- /dev/null +++ b/mobile/android/android-components/components/ui/colors/build.gradle @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + buildFeatures { + compose true + } + + composeOptions { + kotlinCompilerExtensionVersion = Versions.compose_compiler + } + + namespace 'mozilla.components.ui.colors' +} + +dependencies { + implementation platform(ComponentsDependencies.androidx_compose_bom) + implementation ComponentsDependencies.androidx_compose_ui +} + +apply from: '../../../android-lint.gradle' +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/colors/proguard-rules.pro b/mobile/android/android-components/components/ui/colors/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/ui/colors/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/ui/colors/src/main/AndroidManifest.xml b/mobile/android/android-components/components/ui/colors/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e16cda1d34 --- /dev/null +++ b/mobile/android/android-components/components/ui/colors/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + diff --git a/mobile/android/android-components/components/ui/colors/src/main/java/mozilla/components/ui/colors/PhotonColors.kt b/mobile/android/android-components/components/ui/colors/src/main/java/mozilla/components/ui/colors/PhotonColors.kt new file mode 100644 index 0000000000..214070deed --- /dev/null +++ b/mobile/android/android-components/components/ui/colors/src/main/java/mozilla/components/ui/colors/PhotonColors.kt @@ -0,0 +1,178 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.colors + +import androidx.compose.ui.graphics.Color + +/** + * Colors from the [Photon Design System](https://design.firefox.com/photon/visuals/color.html). + * + * _"Firefox colors are bold, vibrant and attractive. They enhance the experience by providing visual + * clues and by bringing attention to primary actions."_ + */ +object PhotonColors { + // Firefox Blue is one of our primary colors. We use blue as accent color for highlighting buttons, + // links and active states like the current tab in Firefox for desktop. + val Blue05 = Color(0xFFAAF2FF) + val Blue10 = Color(0xFF80EBFF) + val Blue20 = Color(0xFF00DDFF) + val Blue30 = Color(0xFF00B3F4) + val Blue40 = Color(0xFF0090ED) + val Blue50 = Color(0xFF0060DF) + val Blue50A44 = Blue50.copy(alpha = 0.44f) + val Blue50A80 = Blue50.copy(alpha = 0.8f) + val Blue60 = Color(0xFF0250BB) + val Blue70 = Color(0xFF054096) + val Blue80 = Color(0xFF073072) + val Blue90 = Color(0xFF09204D) + + // Green is primarily used for positive / success / update status in user interface. + val Green05 = Color(0xFFE3FFF3) + val Green10 = Color(0xFFD1FFEE) + val Green20 = Color(0xFFB3FFE3) + val Green30 = Color(0xFF87FFD1) + val Green40 = Color(0xFF54FFBD) + val Green50 = Color(0xFF3FE1B0) + val Green60 = Color(0xFF2AC3A2) + val Green70 = Color(0xFF008787) + val Green80 = Color(0xFF005E5E) + val Green90 = Color(0xFF08403F) + + // Red is primarily used for attention / error status or a destructive action in user interface. + val Red05 = Color(0xFFFFDFE7) + val Red10 = Color(0xFFFFBDC5) + val Red20 = Color(0xFFFF9AA2) + val Red30 = Color(0xFFFF848B) + val Red40 = Color(0xFFFF6A75) + val Red50 = Color(0xFFFF4F5E) + val Red60 = Color(0xFFE22850) + val Red70 = Color(0xFFC50042) + val Red80 = Color(0xFF810220) + val Red90 = Color(0xFF440306) + + // Yellow is primarily used for attention / caution / warning status in user interface. + val Yellow05 = Color(0xFFFFFFCC) + val Yellow10 = Color(0xFFFFFF98) + val Yellow20 = Color(0xFFFFEA80) + val Yellow30 = Color(0xFFFFD567) + val Yellow40 = Color(0xFFFFBD4F) + val Yellow40A41 = Yellow40.copy(alpha = .41f) + val Yellow50 = Color(0xFFFFA436) + val Yellow60 = Color(0xFFE27F2E) + val Yellow60A40 = Yellow60.copy(alpha = 0.4f) + val Yellow70 = Color(0xFFC45A27) + val Yellow70A77 = Yellow70.copy(alpha = 0.77f) + val Yellow80 = Color(0xFFA7341F) + val Yellow90 = Color(0xFF960E18) + + // Purple is commonly used to indicate privacy. + val Purple05 = Color(0xFFF7E2FF) + val Purple10 = Color(0xFFF6B8FF) + val Purple20 = Color(0xFFF68FFF) + val Purple30 = Color(0xFFF770FF) + val Purple40 = Color(0xFFD74CF0) + val Purple50 = Color(0xFFB833E1) + val Purple60 = Color(0xFF952BB9) + val Purple70 = Color(0xFF722291) + val Purple80 = Color(0xFF4E1A69) + val Purple90 = Color(0xFF2B1141) + + // Firefox Orange is only used for branding. Do not use it otherwise! + val Orange05 = Color(0xFFFFF4DE) + val Orange10 = Color(0xFFFFD5B2) + val Orange20 = Color(0xFFFFB587) + val Orange30 = Color(0xFFFFA266) + val Orange40 = Color(0xFFFF8A50) + val Orange50 = Color(0xFFFF7139) + val Orange60 = Color(0xFFE25920) + val Orange70 = Color(0xFFCC3D00) + val Orange80 = Color(0xFF9E280B) + val Orange90 = Color(0xFF7C1504) + + // Pink is only used for Firefox Focus. Do not use it otherwise! + val Pink05 = Color(0xFFFFDEF0) + val Pink10 = Color(0xFFFFB4DB) + val Pink20 = Color(0xFFFF8AC5) + val Pink30 = Color(0xFFFF6BBA) + val Pink40 = Color(0xFFFF4AA2) + val Pink50 = Color(0xFFFF298A) + val Pink60 = Color(0xFFE21587) + val Pink70 = Color(0xFFC60084) + val Pink70A69 = Pink70.copy(alpha = 0.69f) + val Pink80 = Color(0xFF7F145B) + val Pink90 = Color(0xff50134b) + + // Firefox Ink is commonly used for Firefox branding and new product websites. + val Ink05 = Color(0xFF393473) + val Ink10 = Color(0xFF342F6D) + val Ink20 = Color(0xFF312A64) + val Ink20A48 = Color(0x7A312A64) + val Ink20A50 = Color(0x80312A64) + val Ink30 = Color(0xFF2E255D) + val Ink40 = Color(0xFF2B2156) + val Ink50 = Color(0xFF291D4F) + val Ink60 = Color(0xFF271948) + val Ink70 = Color(0xFF241541) + val Ink80 = Color(0xFF20123A) + val Ink80A96 = Color(0xF520123A) + val Ink90 = Color(0xFF1D1133) + + // Light grey should primarily be used for the Light Theme and secondary buttons. + val LightGrey05 = Color(0xFFFBFBFE) + val LightGrey05A40 = Color(0x66FBFBFE) + val LightGrey05A60 = Color(0x99FBFBFE) + val LightGrey10 = Color(0xFFF9F9FB) + val LightGrey20 = Color(0xFFF0F0F4) + val LightGrey30 = Color(0xFFE0E0E6) + val LightGrey40 = Color(0xFFCFCFD8) + val LightGrey50 = Color(0xFFBFBFC9) + val LightGrey60 = Color(0xFFAFAFBA) + val LightGrey70 = Color(0xFF9F9FAD) + val LightGrey80 = Color(0xFF8F8F9D) + val LightGrey90 = Color(0xFF80808E) + + // Dark grey should primarily be used for the Dark theme and secondary buttons. + val DarkGrey05 = Color(0xFF5B5B66) + val DarkGrey05A45 = Color(0x735B5B66) + val DarkGrey10 = Color(0xFF52525E) + val DarkGrey20 = Color(0xFF4A4A55) + val DarkGrey30 = Color(0xFF42414D) + val DarkGrey30A95 = Color(0xF242414D) + val DarkGrey30A96 = Color(0xF542414D) + val DarkGrey40 = Color(0xFF3A3944) + val DarkGrey50 = Color(0xFF32313C) + val DarkGrey60 = Color(0xFF2B2A33) + val DarkGrey70 = Color(0xFF23222B) + val DarkGrey80 = Color(0xFF1C1B22) + val DarkGrey90 = Color(0xFF15141A) + val DarkGrey90A40 = Color(0x6615141A) + val DarkGrey90A60 = Color(0x9915141A) + val DarkGrey90A95 = Color(0xF215141A) + val DarkGrey90A96 = Color(0xF515141A) + + // Violet + val Violet05 = Color(0xFFE7DFFF) + val Violet10 = Color(0xFFD9BFFF) + val Violet20 = Color(0xFFCB9EFF) + val Violet20A60 = Color(0x99CB9EFF) + val Violet30 = Color(0xFFC689FF) + val Violet40 = Color(0xFFAB71FF) + val Violet40A12 = Color(0x1FAB71FF) + val Violet40A30 = Color(0x4DAB71FF) + val Violet50 = Color(0xFF9059FF) + val Violet50A32 = Color(0x529059FF) + val Violet50A48 = Color(0x7A9059FF) + val Violet60 = Color(0xFF7542E5) + val Violet60A50 = Color(0x807542E5) + val Violet70 = Color(0xFF592ACB) + val Violet70A12 = Color(0x1F592ACB) + val Violet70A80 = Color(0xCC592ACB) + val Violet80 = Color(0xFF45278D) + val Violet90 = Color(0xFF321C64) + val Violet90A20 = Color(0x33321C64) + + val White = Color(0xFFFFFFFF) + val Black = Color(0xFF000000) +} diff --git a/mobile/android/android-components/components/ui/colors/src/main/res/values/photon_colors.xml b/mobile/android/android-components/components/ui/colors/src/main/res/values/photon_colors.xml new file mode 100644 index 0000000000..c873639d17 --- /dev/null +++ b/mobile/android/android-components/components/ui/colors/src/main/res/values/photon_colors.xml @@ -0,0 +1,219 @@ + + + + + + + #ffaaf2ff + #ff80ebff + #ff00ddff + #ff00b3f4 + #ff0090ed + #ff0060df + #700060df + #cc0060df + #ff0250bb + #ff054096 + #ff073072 + #ff09204d + + + #ffff1ad9 + #ffed00b5 + #ffb5007f + #ff7d004f + #ff440027 + + + #ffffdef0 + #ffffb4db + #ffff8ac5 + #ffff6bba + #ffff4aa2 + #ffff298a + #ffe21587 + #ffc60084 + #b0c60084 + #ff7f145b + #ff50134b + + + #ffe3fff3 + #ffd1ffee + #ffb3ffe3 + #ff87ffd1 + #ff54ffbd + #ff3fe1b0 + #ff2ac3a2 + #ff008787 + #ff005e5e + #ff08403f + + + #ffffdfe7 + #ffffbdc5 + #ffff9aa2 + #ffff848b + #ffff6a75 + #ffff4f5e + #ffe22850 + #ffc50042 + #ff810220 + #ff440306 + + + #ffffffcc + #ffffff98 + #ffffea80 + #ffffd567 + #ffffbd4f + #69ffbd4f + #ffffa436 + #ffe27f2e + #66e27f2e + #ffc45a27 + #c4c45a27 + #ffa7341f + #ff960e18 + + + #00feff + #00c8d7 + #008ea4 + #005a71 + #002d3e + + + #fff7e2ff + #fff6b8ff + #fff68fff + #fff770ff + #ffd74cf0 + #ffb833e1 + #ff952bb9 + #ff722291 + #ff4e1a69 + #ff2b1141 + + + #fffff4de + #ffffd5b2 + #ffffb587 + #ffffa266 + #ffff8a50 + #ffff7139 + #ffe25920 + #ffcc3d00 + #ff9e280b + #ff7c1504 + + + #ff393473 + #ff342f6d + #ff312a64 + #7A312a64 + #80312A64 + #ff2e255d + #ff2b2156 + #ff291d4f + #ff271948 + #ff241541 + #ff20123a + #f520123a + #ff1d1133 + + + #f9f9fa + #ededf0 + #d7d7db + #b1b1b3 + #737373 + #4a4a4f + #38383d + #2a2a2e + #0c0c0d + + + #fffbfbfe + #66fbfbfe + #99fbfbfe + #fff9f9fb + #fff0f0f4 + #ffe0e0e6 + #ffcfcfd8 + #ffbfbfc9 + #ffafafba + #ff9f9fad + #ff8f8f9d + #ff80808e + + + #ff5b5b66 + #735b5b66 + #ff52525e + #ff4a4a55 + #ff42414d + #f242414d + #f542414d + #ff3a3944 + #ff32313c + #ff2b2a33 + #ff23222b + #ff1c1b22 + #ff15141a + #6615141a + #9915141a + #f215141a + #f515141a + + + #ffE7DFFF + #ffd9bfff + #ffcb9eFF + #99cb9eFF + #ffc689FF + #ffab71ff + #1fab71ff + #4dab71ff + #ff9059FF + #529059FF + #7A9059FF + #ff7542e5 + #807542E5 + #ff592ACB + #1F592ACB + #CC592ACB + #ff45278d + #ff321c64 + #33321c64 + + + #ffffff + + + #000000 + diff --git a/mobile/android/android-components/components/ui/fonts/README.md b/mobile/android/android-components/components/ui/fonts/README.md new file mode 100644 index 0000000000..33b94590e0 --- /dev/null +++ b/mobile/android/android-components/components/ui/fonts/README.md @@ -0,0 +1,19 @@ +# [Android Components](../../../README.md) > UI > Fonts + +The standard set of fonts used by Mozilla Android products. + +## Usage + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:ui-fonts:{latest-version}" +``` + +## License + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/ diff --git a/mobile/android/android-components/components/ui/fonts/build.gradle b/mobile/android/android-components/components/ui/fonts/build.gradle new file mode 100644 index 0000000000..f3eb06ce5e --- /dev/null +++ b/mobile/android/android-components/components/ui/fonts/build.gradle @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + namespace 'mozilla.components.ui.fonts' +} + +dependencies { + // None +} + +apply from: '../../../android-lint.gradle' +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/fonts/proguard-rules.pro b/mobile/android/android-components/components/ui/fonts/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/ui/fonts/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/ui/fonts/src/main/AndroidManifest.xml b/mobile/android/android-components/components/ui/fonts/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e16cda1d34 --- /dev/null +++ b/mobile/android/android-components/components/ui/fonts/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + diff --git a/mobile/android/android-components/components/ui/fonts/src/main/res/values/roboto_fonts.xml b/mobile/android/android-components/components/ui/fonts/src/main/res/values/roboto_fonts.xml new file mode 100644 index 0000000000..eeb5222aab --- /dev/null +++ b/mobile/android/android-components/components/ui/fonts/src/main/res/values/roboto_fonts.xml @@ -0,0 +1,18 @@ + + + + + + sans-serif + sans-serif-light + sans-serif-condensed + sans-serif-black + sans-serif-thin + sans-serif-medium + + diff --git a/mobile/android/android-components/components/ui/icons/README.md b/mobile/android/android-components/components/ui/icons/README.md new file mode 100644 index 0000000000..47c4e989c0 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/README.md @@ -0,0 +1,19 @@ +# [Android Components](../../../README.md) > UI > Icons + +A collection of often used browser icons. + +## Usage + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:ui-icons:{latest-version}" +``` + +## License + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/ diff --git a/mobile/android/android-components/components/ui/icons/build.gradle b/mobile/android/android-components/components/ui/icons/build.gradle new file mode 100644 index 0000000000..d49b47c2f8 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/build.gradle @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + namespace 'mozilla.components.ui.icons' +} + +dependencies { + // None +} + +apply from: '../../../android-lint.gradle' +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/proguard-rules.pro b/mobile/android/android-components/components/ui/icons/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/ui/icons/src/main/AndroidManifest.xml b/mobile/android/android-components/components/ui/icons/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e16cda1d34 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_asleep.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_asleep.svg new file mode 100644 index 0000000000..8829f8a102 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_asleep.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_2 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_confused.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_confused.svg new file mode 100644 index 0000000000..0bed690133 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_confused.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_4 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_eye_roll.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_eye_roll.svg new file mode 100644 index 0000000000..3f8a939693 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_eye_roll.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_7 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_hourglass.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_hourglass.svg new file mode 100644 index 0000000000..62d88c9e19 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_hourglass.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_8 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_inspect.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_inspect.svg new file mode 100644 index 0000000000..b64c203ae7 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_inspect.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_1 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_lock.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_lock.svg new file mode 100644 index 0000000000..fc72ea7b8e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_lock.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_5 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_no_internet.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_no_internet.svg new file mode 100644 index 0000000000..cdb183fb97 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_no_internet.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_3 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_question_file.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_question_file.svg new file mode 100644 index 0000000000..9bc4e60044 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_question_file.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_12 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_shred_file.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_shred_file.svg new file mode 100644 index 0000000000..a7df64add3 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_shred_file.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_13 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_surprised.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_surprised.svg new file mode 100644 index 0000000000..94889f53d0 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_surprised.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_9 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_unplugged.svg b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_unplugged.svg new file mode 100644 index 0000000000..719986efbf --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/assets/mozac_error_unplugged.svg @@ -0,0 +1,4 @@ + +fx-fenix_error_6 \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_add_to_homescreen_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_add_to_homescreen_24.xml new file mode 100644 index 0000000000..c9ab6e5d53 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_add_to_homescreen_24.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_app_menu_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_app_menu_24.xml new file mode 100644 index 0000000000..6e30f173e1 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_app_menu_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_app_menu_space_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_app_menu_space_24.xml new file mode 100644 index 0000000000..35f9896179 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_app_menu_space_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_down_left_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_down_left_24.xml new file mode 100644 index 0000000000..6ff8a72414 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_down_left_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_up_left_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_up_left_24.xml new file mode 100644 index 0000000000..a3e053f9fc --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_up_left_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_up_right_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_up_right_24.xml new file mode 100644 index 0000000000..ec4ac86fed --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_append_up_right_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_arrow_clockwise_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_arrow_clockwise_24.xml new file mode 100644 index 0000000000..f618ea5a92 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_arrow_clockwise_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_autoplay_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_autoplay_24.xml new file mode 100644 index 0000000000..d5fd8e11e5 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_autoplay_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_autoplay_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_autoplay_slash_24.xml new file mode 100644 index 0000000000..2b90a56673 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_autoplay_slash_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_circle_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_circle_24.xml new file mode 100644 index 0000000000..0bf6b500f1 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_circle_24.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_circle_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_circle_fill_24.xml new file mode 100644 index 0000000000..b4726ed624 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_circle_fill_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_info_circle_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_info_circle_fill_24.xml new file mode 100644 index 0000000000..c2bc674121 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_info_circle_fill_24.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_warning_circle_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_warning_circle_fill_24.xml new file mode 100644 index 0000000000..f0787da169 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_avatar_warning_circle_fill_24.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_back_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_back_24.xml new file mode 100644 index 0000000000..e1e62043a9 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_back_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_20.xml new file mode 100644 index 0000000000..47030805a0 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_20.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_24.xml new file mode 100644 index 0000000000..360c5809c2 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_24.xml @@ -0,0 +1,14 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_badge_fill_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_badge_fill_20.xml new file mode 100644 index 0000000000..aa602c8e62 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_badge_fill_20.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_fill_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_fill_20.xml new file mode 100644 index 0000000000..61a9bfe15b --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_fill_20.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_fill_24.xml new file mode 100644 index 0000000000..e5a17775e3 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_fill_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_slash_24.xml new file mode 100644 index 0000000000..5527aceeef --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_slash_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_tray_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_tray_24.xml new file mode 100644 index 0000000000..2b35f1e963 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_tray_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_tray_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_tray_fill_24.xml new file mode 100644 index 0000000000..2b35f1e963 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_bookmark_tray_fill_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_briefcase.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_briefcase.xml new file mode 100644 index 0000000000..748b164535 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_briefcase.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_broken_lock.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_broken_lock.xml new file mode 100644 index 0000000000..1f670e0029 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_broken_lock.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_camera_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_camera_24.xml new file mode 100644 index 0000000000..e244e12aca --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_camera_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_camera_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_camera_slash_24.xml new file mode 100644 index 0000000000..fb59ea92ff --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_camera_slash_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cart.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cart.xml new file mode 100644 index 0000000000..c29759e94d --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cart.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_checkmark_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_checkmark_24.xml new file mode 100644 index 0000000000..e3e97451ef --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_checkmark_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_20.xml new file mode 100644 index 0000000000..f362622f33 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_20.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_24.xml new file mode 100644 index 0000000000..995c2045df --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_8.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_8.xml new file mode 100644 index 0000000000..9ce948fafe --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_down_8.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_left_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_left_24.xml new file mode 100644 index 0000000000..2bf58b14ee --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_left_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_right_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_right_24.xml new file mode 100644 index 0000000000..038db30bf7 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_right_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_up_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_up_20.xml new file mode 100644 index 0000000000..e313fba3c1 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_up_20.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_up_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_up_24.xml new file mode 100644 index 0000000000..b50e8547aa --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chevron_up_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chill.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chill.xml new file mode 100644 index 0000000000..36017ece6b --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_chill.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_circle.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_circle.xml new file mode 100644 index 0000000000..e3f75f3aff --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_circle.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_clipboard_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_clipboard_24.xml new file mode 100644 index 0000000000..f8207ab859 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_clipboard_24.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_collection_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_collection_24.xml new file mode 100644 index 0000000000..c71981eae3 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_collection_24.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_competitiveness_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_competitiveness_24.xml new file mode 100644 index 0000000000..8de4dda69f --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_competitiveness_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cookies_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cookies_24.xml new file mode 100644 index 0000000000..3955146580 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cookies_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cookies_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cookies_slash_24.xml new file mode 100644 index 0000000000..9b9adcdcf5 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cookies_slash_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_copy_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_copy_24.xml new file mode 100644 index 0000000000..7fb92271da --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_copy_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_credit_card_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_credit_card_24.xml new file mode 100644 index 0000000000..69721f0c35 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_credit_card_24.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_critical_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_critical_24.xml new file mode 100644 index 0000000000..364f898f93 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_critical_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_critical_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_critical_fill_24.xml new file mode 100644 index 0000000000..e81b1bd08a --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_critical_fill_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_20.xml new file mode 100644 index 0000000000..07a4a4f9cb --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_20.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_24.xml new file mode 100644 index 0000000000..6af42311d3 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_24.xml new file mode 100644 index 0000000000..98e4a06d50 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_fill_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_fill_20.xml new file mode 100644 index 0000000000..937bd3d312 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_fill_20.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_fill_24.xml new file mode 100644 index 0000000000..b7255c0764 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cross_circle_fill_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cryptominer_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cryptominer_24.xml new file mode 100644 index 0000000000..aa6e3d7453 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_cryptominer_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_data_clearance_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_data_clearance_24.xml new file mode 100644 index 0000000000..a6c54e16b8 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_data_clearance_24.xml @@ -0,0 +1,14 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_debug_drawer_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_debug_drawer_24.xml new file mode 100644 index 0000000000..cf9df6537b --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_debug_drawer_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_delete_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_delete_24.xml new file mode 100644 index 0000000000..295ed43ac7 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_delete_24.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_desktop_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_desktop_24.xml new file mode 100644 index 0000000000..a0d1de4e7c --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_desktop_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_desktop_send_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_desktop_send_24.xml new file mode 100644 index 0000000000..3668554e2a --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_desktop_send_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_mobile_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_mobile_24.xml new file mode 100644 index 0000000000..30b9f1eac2 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_device_mobile_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_dollar.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_dollar.xml new file mode 100644 index 0000000000..b0ddfbf563 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_dollar.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_download_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_download_24.xml new file mode 100644 index 0000000000..c79a08a8ba --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_download_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_dropdown_arrow.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_dropdown_arrow.xml new file mode 100644 index 0000000000..11e35191b8 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_dropdown_arrow.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_edit_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_edit_24.xml new file mode 100644 index 0000000000..4cc97d5cf7 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_edit_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_ellipsis_horizontal_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_ellipsis_horizontal_24.xml new file mode 100644 index 0000000000..a75ad8f3e2 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_ellipsis_horizontal_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_ellipsis_vertical_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_ellipsis_vertical_24.xml new file mode 100644 index 0000000000..e5d6a89b19 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_ellipsis_vertical_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_experiments_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_experiments_24.xml new file mode 100644 index 0000000000..949626f212 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_experiments_24.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_24.xml new file mode 100644 index 0000000000..67a37d7624 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_cog_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_cog_24.xml new file mode 100644 index 0000000000..596a85a3e4 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_cog_24.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_fill_24.xml new file mode 100644 index 0000000000..52b50249fa --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_extension_fill_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_external_link_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_external_link_24.xml new file mode 100644 index 0000000000..79e00bceb8 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_external_link_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_eye_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_eye_24.xml new file mode 100644 index 0000000000..9cba339370 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_eye_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_eye_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_eye_slash_24.xml new file mode 100644 index 0000000000..2d0c82ea6c --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_eye_slash_24.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fence.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fence.xml new file mode 100644 index 0000000000..ccd01bd3c7 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fence.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fingerprinter_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fingerprinter_24.xml new file mode 100644 index 0000000000..a1eccfd3a8 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fingerprinter_24.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_folder_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_folder_24.xml new file mode 100644 index 0000000000..d63d873480 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_folder_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_folder_add_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_folder_add_24.xml new file mode 100644 index 0000000000..c6938d1006 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_folder_add_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_font.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_font.xml new file mode 100644 index 0000000000..9e5d320ba6 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_font.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_food.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_food.xml new file mode 100644 index 0000000000..d82801ad65 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_food.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_forward_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_forward_24.xml new file mode 100644 index 0000000000..8b66c40787 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_forward_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fruit.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fruit.xml new file mode 100644 index 0000000000..250b848431 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_fruit.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_gift.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_gift.xml new file mode 100644 index 0000000000..8bb994b81f --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_gift.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_globe_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_globe_24.xml new file mode 100644 index 0000000000..2bedff87fb --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_globe_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_grid.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_grid.xml new file mode 100644 index 0000000000..ddd9e4b881 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_grid.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_grid_add_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_grid_add_24.xml new file mode 100644 index 0000000000..ca83cc4f1d --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_grid_add_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_help_circle_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_help_circle_24.xml new file mode 100644 index 0000000000..352515dbe6 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_help_circle_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_help_circle_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_help_circle_fill_24.xml new file mode 100644 index 0000000000..a4f9cf8aec --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_help_circle_fill_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_history_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_history_24.xml new file mode 100644 index 0000000000..328727849e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_history_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_home_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_home_24.xml new file mode 100644 index 0000000000..43b4264c5f --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_home_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_image_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_image_24.xml new file mode 100644 index 0000000000..1d12948178 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_image_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_image_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_image_slash_24.xml new file mode 100644 index 0000000000..49ed64618e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_image_slash_24.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_information_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_information_24.xml new file mode 100644 index 0000000000..24be636c52 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_information_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_information_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_information_fill_24.xml new file mode 100644 index 0000000000..ec7aea93c8 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_information_fill_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lightbulb_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lightbulb_24.xml new file mode 100644 index 0000000000..0b3ddafe53 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lightbulb_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_link_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_link_24.xml new file mode 100644 index 0000000000..d08122a767 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_link_24.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_location_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_location_24.xml new file mode 100644 index 0000000000..2ea1d78e61 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_location_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_location_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_location_slash_24.xml new file mode 100644 index 0000000000..db74edb5a5 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_location_slash_24.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_20.xml new file mode 100644 index 0000000000..17978486c7 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_20.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_24.xml new file mode 100644 index 0000000000..fd8eeb959f --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_slash_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_slash_20.xml new file mode 100644 index 0000000000..48d0d4a80c --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_slash_20.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_slash_24.xml new file mode 100644 index 0000000000..b31e23b456 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_slash_24.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_warning_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_warning_20.xml new file mode 100644 index 0000000000..2e854faba6 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_warning_20.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_warning_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_warning_24.xml new file mode 100644 index 0000000000..92a72604ac --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_lock_warning_24.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_login_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_login_24.xml new file mode 100644 index 0000000000..5b1261437e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_login_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_chrome_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_chrome_24.xml new file mode 100644 index 0000000000..99a9ace127 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_chrome_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_firefox_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_firefox_24.xml new file mode 100644 index 0000000000..cb2d61d9c1 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_firefox_24.xml @@ -0,0 +1,14 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_safari_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_safari_24.xml new file mode 100644 index 0000000000..0a5384c40a --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_logo_safari_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_microphone_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_microphone_24.xml new file mode 100644 index 0000000000..e74647aa45 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_microphone_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_microphone_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_microphone_slash_24.xml new file mode 100644 index 0000000000..4fe5205213 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_microphone_slash_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_more_grid_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_more_grid_24.xml new file mode 100644 index 0000000000..a3ff0f9ee2 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_more_grid_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_mozilla.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_mozilla.xml new file mode 100644 index 0000000000..e06824d921 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_mozilla.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_night_mode_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_night_mode_24.xml new file mode 100644 index 0000000000..33b152e9cd --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_night_mode_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_24.xml new file mode 100644 index 0000000000..c657fa63bd --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_dot_badge_fill_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_dot_badge_fill_20.xml new file mode 100644 index 0000000000..ccc3d2277a --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_dot_badge_fill_20.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_slash_24.xml new file mode 100644 index 0000000000..2fac0b2e16 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_notification_slash_24.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_open_in.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_open_in.xml new file mode 100644 index 0000000000..22ed8d0e8e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_open_in.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_packaging_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_packaging_24.xml new file mode 100644 index 0000000000..cb11947a79 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_packaging_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_page_zoom_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_page_zoom_24.xml new file mode 100644 index 0000000000..4656a7edfc --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_page_zoom_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_page_zoom_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_page_zoom_fill_24.xml new file mode 100644 index 0000000000..7618b38421 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_page_zoom_fill_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_passkey_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_passkey_24.xml new file mode 100644 index 0000000000..1c21042887 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_passkey_24.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pause_badge_fill_16.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pause_badge_fill_16.xml new file mode 100644 index 0000000000..0ce77d4936 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pause_badge_fill_16.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_permissions_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_permissions_24.xml new file mode 100644 index 0000000000..0320b89369 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_permissions_24.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pet.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pet.xml new file mode 100644 index 0000000000..2294410c09 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pet.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_24.xml new file mode 100644 index 0000000000..48c5ab7837 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_badge_fill_16.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_badge_fill_16.xml new file mode 100644 index 0000000000..6838f756f5 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_badge_fill_16.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_fill_24.xml new file mode 100644 index 0000000000..98e17c09f4 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_fill_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_filled.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_filled.xml new file mode 100644 index 0000000000..80394cc3be --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_filled.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_slash_24.xml new file mode 100644 index 0000000000..a7a930a25e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_slash_24.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_slash_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_slash_fill_24.xml new file mode 100644 index 0000000000..468269cd9f --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_pin_slash_fill_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_play_badge_fill_16.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_play_badge_fill_16.xml new file mode 100644 index 0000000000..4381c247f8 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_play_badge_fill_16.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_plugin_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_plugin_24.xml new file mode 100644 index 0000000000..762c701245 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_plugin_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_plus_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_plus_24.xml new file mode 100644 index 0000000000..57d12452ff --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_plus_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_preferences.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_preferences.xml new file mode 100644 index 0000000000..9085f636b0 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_preferences.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_price_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_price_24.xml new file mode 100644 index 0000000000..dbcc92a58f --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_price_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_print_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_print_24.xml new file mode 100644 index 0000000000..681fffdff0 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_print_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_24.xml new file mode 100644 index 0000000000..5d3237c2ce --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_24.xml @@ -0,0 +1,14 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_20.xml new file mode 100644 index 0000000000..b6ce9a33e8 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_20.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_24.xml new file mode 100644 index 0000000000..bcb350e8fd --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_24.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_48.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_48.xml new file mode 100644 index 0000000000..dfb4091077 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_48.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_stroke_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_stroke_20.xml new file mode 100644 index 0000000000..2ee67055dd --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_private_mode_circle_fill_stroke_20.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_qr_code_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_qr_code_24.xml new file mode 100644 index 0000000000..d3765da043 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_qr_code_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_quality_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_quality_24.xml new file mode 100644 index 0000000000..e087551b52 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_quality_24.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reader_view_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reader_view_24.xml new file mode 100644 index 0000000000..6ee608ecfd --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reader_view_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reader_view_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reader_view_fill_24.xml new file mode 100644 index 0000000000..f170cc57e7 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reader_view_fill_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reading_list_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reading_list_24.xml new file mode 100644 index 0000000000..632926fcbf --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reading_list_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reading_list_add_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reading_list_add_24.xml new file mode 100644 index 0000000000..13e979a615 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reading_list_add_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reorder.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reorder.xml new file mode 100644 index 0000000000..0be284c3ff --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_reorder.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_rocket.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_rocket.xml new file mode 100644 index 0000000000..248a5018d5 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_rocket.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_rocket_filled.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_rocket_filled.xml new file mode 100644 index 0000000000..f10710664d --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_rocket_filled.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_save_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_save_24.xml new file mode 100644 index 0000000000..9c821755e0 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_save_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_save_file_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_save_file_24.xml new file mode 100644 index 0000000000..e9c274bfc8 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_save_file_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_search_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_search_24.xml new file mode 100644 index 0000000000..370db05187 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_search_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_select_all.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_select_all.xml new file mode 100644 index 0000000000..1445bed499 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_select_all.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_settings_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_settings_24.xml new file mode 100644 index 0000000000..ce46e1f161 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_settings_24.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_share_android_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_share_android_24.xml new file mode 100644 index 0000000000..426216f346 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_share_android_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_share_apple_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_share_apple_24.xml new file mode 100644 index 0000000000..993051d08a --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_share_apple_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shield_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shield_24.xml new file mode 100644 index 0000000000..47b961d4d1 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shield_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shield_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shield_slash_24.xml new file mode 100644 index 0000000000..9d2077dcad --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shield_slash_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shipping_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shipping_24.xml new file mode 100644 index 0000000000..67ac9492d1 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shipping_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shopping_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shopping_24.xml new file mode 100644 index 0000000000..662fb38991 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_shopping_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_social_tracker_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_social_tracker_24.xml new file mode 100644 index 0000000000..8ed2638b8a --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_social_tracker_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sparkle_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sparkle_24.xml new file mode 100644 index 0000000000..71ae38b99e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sparkle_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_fill_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_fill_20.xml new file mode 100644 index 0000000000..a8c7f438ce --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_fill_20.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_half_fill_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_half_fill_20.xml new file mode 100644 index 0000000000..fa35cd5990 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_half_fill_20.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_one_half_fill_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_one_half_fill_20.xml new file mode 100644 index 0000000000..c3871dcb8f --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_star_one_half_fill_20.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_stop.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_stop.xml new file mode 100644 index 0000000000..cbc5269212 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_stop.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_storage_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_storage_24.xml new file mode 100644 index 0000000000..0b0408778e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_storage_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_storage_slash_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_storage_slash_24.xml new file mode 100644 index 0000000000..6efcb91aef --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_storage_slash_24.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sync_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sync_24.xml new file mode 100644 index 0000000000..8b4e07514e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sync_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sync_tabs_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sync_tabs_24.xml new file mode 100644 index 0000000000..5e776b729e --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_sync_tabs_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab.xml new file mode 100644 index 0000000000..8be248636b --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_badge_fill_20.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_badge_fill_20.xml new file mode 100644 index 0000000000..f99ada90d9 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_badge_fill_20.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_new.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_new.xml new file mode 100644 index 0000000000..8822eb5979 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_new.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_number_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_number_24.xml new file mode 100644 index 0000000000..30b357c61c --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_number_24.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_tray_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_tray_24.xml new file mode 100644 index 0000000000..43e6db826f --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tab_tray_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_themes_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_themes_24.xml new file mode 100644 index 0000000000..180523e4c2 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_themes_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tool_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tool_24.xml new file mode 100644 index 0000000000..78cea3d700 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tool_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_translate_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_translate_24.xml new file mode 100644 index 0000000000..7fd0e1d3a1 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_translate_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tree.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tree.xml new file mode 100644 index 0000000000..305131d365 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_tree.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_update_circle_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_update_circle_24.xml new file mode 100644 index 0000000000..872902f4c3 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_update_circle_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_vacation.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_vacation.xml new file mode 100644 index 0000000000..07a6924ee1 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_vacation.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_warning_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_warning_24.xml new file mode 100644 index 0000000000..fcb74e05dc --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_warning_24.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_warning_fill_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_warning_fill_24.xml new file mode 100644 index 0000000000..b49799f930 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_warning_fill_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_web_extension_default_icon.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_web_extension_default_icon.xml new file mode 100644 index 0000000000..8aa89c3b51 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_web_extension_default_icon.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_whats_new_24.xml b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_whats_new_24.xml new file mode 100644 index 0000000000..e2156ea6d4 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/drawable/mozac_ic_whats_new_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/values/attrs.xml b/mobile/android/android-components/components/ui/icons/src/main/res/values/attrs.xml new file mode 100644 index 0000000000..c41244c9c1 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/values/attrs.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/values/colors.xml b/mobile/android/android-components/components/ui/icons/src/main/res/values/colors.xml new file mode 100644 index 0000000000..b785281bc7 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/values/colors.xml @@ -0,0 +1,15 @@ + + + + + #FFFFFF + + + #000000 + #FFFFFF + + + #000000 + diff --git a/mobile/android/android-components/components/ui/icons/src/main/res/values/mozac_ui_icons_strings.xml b/mobile/android/android-components/components/ui/icons/src/main/res/values/mozac_ui_icons_strings.xml new file mode 100644 index 0000000000..9dbb12a529 --- /dev/null +++ b/mobile/android/android-components/components/ui/icons/src/main/res/values/mozac_ui_icons_strings.xml @@ -0,0 +1,17 @@ + + + + mozac_error_shred_file + mozac_error_question_file + mozac_error_surprised + mozac_error_hourglass + mozac_error_eye_roll + mozac_error_unplugged + mozac_error_lock + mozac_error_confused + mozac_error_no_internet + mozac_error_asleep + mozac_error_inspect + \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/tabcounter/.gitignore b/mobile/android/android-components/components/ui/tabcounter/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/.gitignore @@ -0,0 +1 @@ +/build diff --git a/mobile/android/android-components/components/ui/tabcounter/README.md b/mobile/android/android-components/components/ui/tabcounter/README.md new file mode 100644 index 0000000000..38500615e0 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/README.md @@ -0,0 +1,37 @@ +# [Android Components](../../../README.md) > UI > Tabcounter + +A button that shows the current tab count and can animate state changes. + +## Usage + +Create a tab counter in XML: + +```xml + +``` + +Styleable attributes can be set on your theme as well: + +```xml + +``` + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:ui-tabcounter:{latest-version}" +``` + +## License + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/ diff --git a/mobile/android/android-components/components/ui/tabcounter/build.gradle b/mobile/android/android-components/components/ui/tabcounter/build.gradle new file mode 100644 index 0000000000..04e3ccc2e1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/build.gradle @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'com.google.devtools.ksp' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + buildFeatures { + viewBinding true + } + + namespace 'mozilla.components.ui.tabcounter' +} + +dependencies { + implementation platform(ComponentsDependencies.androidx_compose_bom) + implementation project(':support-ktx') + implementation project(':support-utils') + + implementation ComponentsDependencies.androidx_core_ktx + implementation project(':concept-menu') + implementation project(':browser-menu2') + implementation project(':support-base') + implementation project(':ui-colors') + implementation project(':ui-icons') + + testImplementation project(":support-test") + + testImplementation ComponentsDependencies.androidx_test_junit + testImplementation ComponentsDependencies.testing_robolectric +} + +apply from: '../../../android-lint.gradle' +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) diff --git a/mobile/android/android-components/components/ui/tabcounter/proguard-rules.pro b/mobile/android/android-components/components/ui/tabcounter/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/AndroidManifest.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..928c7b2243 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/java/mozilla/components/ui/tabcounter/TabCounter.kt b/mobile/android/android-components/components/ui/tabcounter/src/main/java/mozilla/components/ui/tabcounter/TabCounter.kt new file mode 100644 index 0000000000..2ba8aa8540 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/java/mozilla/components/ui/tabcounter/TabCounter.kt @@ -0,0 +1,348 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.tabcounter + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Typeface +import android.util.AttributeSet +import android.util.TypedValue +import android.view.LayoutInflater +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.RelativeLayout +import android.widget.TextView +import androidx.annotation.VisibleForTesting +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.core.view.updatePadding +import mozilla.components.support.utils.DrawableUtils +import mozilla.components.ui.tabcounter.databinding.MozacUiTabcounterLayoutBinding +import java.text.NumberFormat + +class TabCounter @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0, +) : RelativeLayout(context, attrs, defStyle) { + + private val animationSet: AnimatorSet + private var binding: MozacUiTabcounterLayoutBinding + private var counterBox: ImageView + private var counterText: TextView + private var counterRoot: FrameLayout + private var counterMask: ImageView + + init { + binding = MozacUiTabcounterLayoutBinding.inflate(LayoutInflater.from(context), this) + counterBox = binding.counterBox + counterText = binding.counterText + counterRoot = binding.counterRoot + counterMask = binding.counterMask + + setCount(INTERNAL_COUNT) + + context.obtainStyledAttributes(attrs, R.styleable.TabCounter, defStyle, 0).apply { + val counterColor = getColorStateList( + R.styleable.TabCounter_tabCounterTintColor, + ) ?: ContextCompat.getColorStateList(context, R.color.mozac_ui_tabcounter_default_tint) + + counterColor?.let { + setColor(it) + } + + clipChildren = false + + recycle() + } + + animationSet = createAnimatorSet() + } + + /** + * Sets the colors of the tab counter box and text. + */ + @VisibleForTesting + internal fun setColor(colorStateList: ColorStateList) { + val tabCounterBox = + DrawableUtils.loadAndTintDrawable(context, R.drawable.mozac_ui_tabcounter_box, colorStateList) + counterBox.setImageDrawable(tabCounterBox) + counterText.setTextColor(colorStateList) + } + + private fun updateContentDescription(count: Int) { + counterRoot.contentDescription = if (count == 1) { + context?.getString(R.string.mozac_tab_counter_open_tab_tray_single) + } else { + String.format( + context.getString(R.string.mozac_tab_counter_open_tab_tray_plural), + count.toString(), + ) + } + } + + fun setCountWithAnimation(count: Int) { + setCount(count) + + // No need to animate on these cases. + when { + INTERNAL_COUNT == 0 -> return // Initial state. + INTERNAL_COUNT == count -> return // There isn't any tab added or removed. + INTERNAL_COUNT > MAX_VISIBLE_TABS -> return // There are still over MAX_VISIBLE_TABS tabs open. + } + + // Cancel previous animations if necessary. + if (animationSet.isRunning) { + animationSet.cancel() + } + // Trigger animations. + animationSet.start() + } + + /** + * Toggles the visibility of the mask overlay on the counter + * + * @param showMask [Boolean] used to determine whether to show or hide the mask. + */ + fun toggleCounterMask(showMask: Boolean) { + counterMask.isVisible = showMask + } + + fun setCount(count: Int) { + updateContentDescription(count) + adjustTextSize(count) + counterText.text = formatCountForDisplay(count) + INTERNAL_COUNT = count + } + + private fun createAnimatorSet(): AnimatorSet { + val animatorSet = AnimatorSet() + createBoxAnimatorSet(animatorSet) + createTextAnimatorSet(animatorSet) + return animatorSet + } + + private fun createBoxAnimatorSet(animatorSet: AnimatorSet) { + // The first animator, fadeout in 33 ms (49~51, 2 frames). + val fadeOut = ObjectAnimator.ofFloat( + counterBox, + "alpha", + ANIM_BOX_FADEOUT_FROM, + ANIM_BOX_FADEOUT_TO, + ).setDuration(ANIM_BOX_FADEOUT_DURATION) + + // Move up on y-axis, from 0.0 to -5.3 in 50ms, with fadeOut (49~52, 3 frames). + val moveUp1 = ObjectAnimator.ofFloat( + counterBox, + "translationY", + ANIM_BOX_MOVEUP1_TO, + ANIM_BOX_MOVEUP1_FROM, + ).setDuration(ANIM_BOX_MOVEUP1_DURATION) + + // Move down on y-axis, from -5.3 to -1.0 in 116ms, after moveUp1 (52~59, 7 frames). + val moveDown2 = ObjectAnimator.ofFloat( + counterBox, + "translationY", + ANIM_BOX_MOVEDOWN2_FROM, + ANIM_BOX_MOVEDOWN2_TO, + ).setDuration(ANIM_BOX_MOVEDOWN2_DURATION) + + // FadeIn in 66ms, with moveDown2 (52~56, 4 frames). + val fadeIn = ObjectAnimator.ofFloat( + counterBox, + "alpha", + ANIM_BOX_FADEIN_FROM, + ANIM_BOX_FADEIN_TO, + ).setDuration(ANIM_BOX_FADEIN_DURATION) + + // Move down on y-axis, from -1.0 to 2.7 in 116ms, after moveDown2 (59~66, 7 frames). + val moveDown3 = ObjectAnimator.ofFloat( + counterBox, + "translationY", + ANIM_BOX_MOVEDOWN3_FROM, + ANIM_BOX_MOVEDOWN3_TO, + ).setDuration(ANIM_BOX_MOVEDOWN3_DURATION) + + // Move up on y-axis, from 2.7 to 0 in 133ms, after moveDown3 (66~74, 8 frames). + val moveUp4 = ObjectAnimator.ofFloat( + counterBox, + "translationY", + ANIM_BOX_MOVEDOWN4_FROM, + ANIM_BOX_MOVEDOWN4_TO, + ).setDuration(ANIM_BOX_MOVEDOWN4_DURATION) + + // Scale up height from 2% to 105% in 100ms, after moveUp1 and delay 16ms (53~59, 6 frames). + val scaleUp1 = ObjectAnimator.ofFloat( + counterBox, + "scaleY", + ANIM_BOX_SCALEUP1_FROM, + ANIM_BOX_SCALEUP1_TO, + ).setDuration(ANIM_BOX_SCALEUP1_DURATION) + scaleUp1.startDelay = ANIM_BOX_SCALEUP1_DELAY // delay 1 frame after moveUp1 + + // Scale down height from 105% to 99% in 116ms, after scaleUp1 (59~66, 7 frames). + val scaleDown2 = ObjectAnimator.ofFloat( + counterBox, + "scaleY", + ANIM_BOX_SCALEDOWN2_FROM, + ANIM_BOX_SCALEDOWN2_TO, + ).setDuration(ANIM_BOX_SCALEDOWN2_DURATION) + + // Scale up height from 99% to 100% in 133ms, after scaleDown2 (66~74, 8 frames). + val scaleUp3 = ObjectAnimator.ofFloat( + counterBox, + "scaleY", + ANIM_BOX_SCALEUP3_FROM, + ANIM_BOX_SCALEUP3_TO, + ).setDuration(ANIM_BOX_SCALEUP3_DURATION) + + animatorSet.play(fadeOut).with(moveUp1) + animatorSet.play(moveUp1).before(moveDown2) + animatorSet.play(moveDown2).with(fadeIn) + animatorSet.play(moveDown2).before(moveDown3) + animatorSet.play(moveDown3).before(moveUp4) + + animatorSet.play(moveUp1).before(scaleUp1) + animatorSet.play(scaleUp1).before(scaleDown2) + animatorSet.play(scaleDown2).before(scaleUp3) + } + + private fun createTextAnimatorSet(animatorSet: AnimatorSet) { + val firstAnimator = animatorSet.childAnimations[0] + + // Fadeout in 100ms, with firstAnimator (49~51, 2 frames). + val fadeOut = ObjectAnimator.ofFloat( + counterText, + "alpha", + ANIM_TEXT_FADEOUT_FROM, + ANIM_TEXT_FADEOUT_TO, + ).setDuration(ANIM_TEXT_FADEOUT_DURATION) + + // FadeIn in 66 ms, after fadeOut with delay 96ms (57~61, 4 frames). + val fadeIn = ObjectAnimator.ofFloat( + counterText, + "alpha", + ANIM_TEXT_FADEIN_FROM, + ANIM_TEXT_FADEIN_TO, + ).setDuration(ANIM_TEXT_FADEIN_DURATION) + fadeIn.startDelay = (ANIM_TEXT_FADEIN_DELAY) // delay 6 frames after fadeOut + + // Move down on y-axis, from 0 to 4.4 in 66ms, with fadeIn (57~61, 4 frames). + val moveDown = ObjectAnimator.ofFloat( + counterText, + "translationY", + ANIM_TEXT_MOVEDOWN_FROM, + ANIM_TEXT_MOVEDOWN_TO, + ).setDuration(ANIM_TEXT_MOVEDOWN_DURATION) + moveDown.startDelay = (ANIM_TEXT_MOVEDOWN_DELAY) // delay 6 frames after fadeOut + + // Move up on y-axis, from 0 to 4.4 in 66ms, after moveDown (61~69, 8 frames). + val moveUp = ObjectAnimator.ofFloat( + counterText, + "translationY", + ANIM_TEXT_MOVEUP_FROM, + ANIM_TEXT_MOVEUP_TO, + ).setDuration(ANIM_TEXT_MOVEUP_DURATION) + + animatorSet.play(firstAnimator).with(fadeOut) + animatorSet.play(fadeOut).before(fadeIn) + animatorSet.play(fadeIn).with(moveDown) + animatorSet.play(moveDown).before(moveUp) + } + + private fun formatCountForDisplay(count: Int): String { + return if (count > MAX_VISIBLE_TABS) { + counterText.updatePadding(bottom = INFINITE_CHAR_PADDING_BOTTOM) + SO_MANY_TABS_OPEN + } else { + NumberFormat.getInstance().format(count.toLong()) + } + } + + private fun adjustTextSize(newCount: Int) { + val newRatio = if (newCount in TWO_DIGITS_TAB_COUNT_THRESHOLD..MAX_VISIBLE_TABS) { + TWO_DIGITS_SIZE_RATIO + } else { + ONE_DIGIT_SIZE_RATIO + } + + val counterBoxWidth = + context.resources.getDimensionPixelSize(R.dimen.mozac_tab_counter_box_width_height) + val textSize = newRatio * counterBoxWidth + counterText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) + counterText.setTypeface(null, Typeface.BOLD) + counterText.setPadding(0, 0, 0, 0) + } + + companion object { + var INTERNAL_COUNT = 0 + + const val MAX_VISIBLE_TABS = 99 + const val SO_MANY_TABS_OPEN = "∞" + const val INFINITE_CHAR_PADDING_BOTTOM = 6 + + const val ONE_DIGIT_SIZE_RATIO = 0.5f + const val TWO_DIGITS_SIZE_RATIO = 0.4f + const val TWO_DIGITS_TAB_COUNT_THRESHOLD = 10 + + // createBoxAnimatorSet + private const val ANIM_BOX_FADEOUT_FROM = 1.0f + private const val ANIM_BOX_FADEOUT_TO = 0.0f + private const val ANIM_BOX_FADEOUT_DURATION = 33L + + private const val ANIM_BOX_MOVEUP1_FROM = 0.0f + private const val ANIM_BOX_MOVEUP1_TO = -5.3f + private const val ANIM_BOX_MOVEUP1_DURATION = 50L + + private const val ANIM_BOX_MOVEDOWN2_FROM = -5.3f + private const val ANIM_BOX_MOVEDOWN2_TO = -1.0f + private const val ANIM_BOX_MOVEDOWN2_DURATION = 167L + + private const val ANIM_BOX_FADEIN_FROM = 0.01f + private const val ANIM_BOX_FADEIN_TO = 1.0f + private const val ANIM_BOX_FADEIN_DURATION = 66L + private const val ANIM_BOX_MOVEDOWN3_FROM = -1.0f + private const val ANIM_BOX_MOVEDOWN3_TO = 2.7f + private const val ANIM_BOX_MOVEDOWN3_DURATION = 116L + + private const val ANIM_BOX_MOVEDOWN4_FROM = 2.7f + private const val ANIM_BOX_MOVEDOWN4_TO = 0.0f + private const val ANIM_BOX_MOVEDOWN4_DURATION = 133L + + private const val ANIM_BOX_SCALEUP1_FROM = 0.02f + private const val ANIM_BOX_SCALEUP1_TO = 1.05f + private const val ANIM_BOX_SCALEUP1_DURATION = 100L + private const val ANIM_BOX_SCALEUP1_DELAY = 16L + + private const val ANIM_BOX_SCALEDOWN2_FROM = 1.05f + private const val ANIM_BOX_SCALEDOWN2_TO = 0.99f + private const val ANIM_BOX_SCALEDOWN2_DURATION = 116L + + private const val ANIM_BOX_SCALEUP3_FROM = 0.99f + private const val ANIM_BOX_SCALEUP3_TO = 1.00f + private const val ANIM_BOX_SCALEUP3_DURATION = 133L + + // createTextAnimatorSet + private const val ANIM_TEXT_FADEOUT_FROM = 1.0f + private const val ANIM_TEXT_FADEOUT_TO = 0.0f + private const val ANIM_TEXT_FADEOUT_DURATION = 33L + + private const val ANIM_TEXT_FADEIN_FROM = 0.01f + private const val ANIM_TEXT_FADEIN_TO = 1.0f + private const val ANIM_TEXT_FADEIN_DURATION = 66L + private const val ANIM_TEXT_FADEIN_DELAY = 16L * 6 + + private const val ANIM_TEXT_MOVEDOWN_FROM = 0.0f + private const val ANIM_TEXT_MOVEDOWN_TO = 4.4f + private const val ANIM_TEXT_MOVEDOWN_DURATION = 66L + private const val ANIM_TEXT_MOVEDOWN_DELAY = 16L * 6 + + private const val ANIM_TEXT_MOVEUP_FROM = 4.4f + private const val ANIM_TEXT_MOVEUP_TO = 0.0f + private const val ANIM_TEXT_MOVEUP_DURATION = 66L + } +} diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/java/mozilla/components/ui/tabcounter/TabCounterMenu.kt b/mobile/android/android-components/components/ui/tabcounter/src/main/java/mozilla/components/ui/tabcounter/TabCounterMenu.kt new file mode 100644 index 0000000000..4f0329a85b --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/java/mozilla/components/ui/tabcounter/TabCounterMenu.kt @@ -0,0 +1,101 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.tabcounter + +import android.content.Context +import androidx.core.content.ContextCompat.getColor +import mozilla.components.browser.menu2.BrowserMenuController +import mozilla.components.concept.menu.MenuController +import mozilla.components.concept.menu.candidate.DrawableMenuIcon +import mozilla.components.concept.menu.candidate.TextMenuCandidate +import mozilla.components.concept.menu.candidate.TextStyle +import mozilla.components.ui.icons.R as iconsR + +/** + * The menu that is shown when clicking on the [TabCounter] + * + * @param context the context. + * @param onItemTapped behavior for when an item in the menu is tapped. + * @param iconColor optional color to specify tint of menu icons + */ +open class TabCounterMenu( + context: Context, + onItemTapped: (Item) -> Unit, + iconColor: Int? = null, +) { + + /** + * Represents the menu items. + * + * [CloseTab] menu item for closing a tab. + * [NewTab] menu item for opening a new tab. + * [NewPrivateTab] menu item for opening a new private tab. + * [DuplicateTab] menu item for duplicating the current tab. + */ + @Suppress("UndocumentedPublicClass") + open class Item { + object CloseTab : Item() + object NewTab : Item() + object NewPrivateTab : Item() + object DuplicateTab : Item() + } + + var newTabItem: TextMenuCandidate + var newPrivateTabItem: TextMenuCandidate + var closeTabItem: TextMenuCandidate + var duplicateTabItem: TextMenuCandidate + + val menuController: MenuController by lazy { BrowserMenuController() } + + init { + newTabItem = TextMenuCandidate( + text = context.getString(R.string.mozac_browser_menu_new_tab), + start = DrawableMenuIcon( + context, + iconsR.drawable.mozac_ic_plus_24, + tint = iconColor ?: getColor(context, R.color.mozac_ui_tabcounter_default_text), + ), + textStyle = TextStyle(), + ) { + onItemTapped(Item.NewTab) + } + + newPrivateTabItem = TextMenuCandidate( + text = context.getString(R.string.mozac_browser_menu_new_private_tab), + start = DrawableMenuIcon( + context, + iconsR.drawable.mozac_ic_private_mode_24, + tint = iconColor ?: getColor(context, R.color.mozac_ui_tabcounter_default_text), + ), + textStyle = TextStyle(), + ) { + onItemTapped(Item.NewPrivateTab) + } + + closeTabItem = TextMenuCandidate( + text = context.getString(R.string.mozac_close_tab), + start = DrawableMenuIcon( + context, + iconsR.drawable.mozac_ic_cross_24, + tint = iconColor ?: getColor(context, R.color.mozac_ui_tabcounter_default_text), + ), + textStyle = TextStyle(), + ) { + onItemTapped(Item.CloseTab) + } + + duplicateTabItem = TextMenuCandidate( + text = context.getString(R.string.mozac_ui_tabcounter_duplicate_tab), + start = DrawableMenuIcon( + context, + iconsR.drawable.mozac_ic_tab, + tint = iconColor ?: getColor(context, R.color.mozac_ui_tabcounter_default_text), + ), + textStyle = TextStyle(), + ) { + onItemTapped(Item.DuplicateTab) + } + } +} diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_bar.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_bar.xml new file mode 100644 index 0000000000..75aef5e019 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_bar.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_box.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_box.xml new file mode 100644 index 0000000000..489efcb568 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_box.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_round_rectangle_ripple.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_round_rectangle_ripple.xml new file mode 100644 index 0000000000..e79ba0457f --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/drawable/mozac_ui_tabcounter_round_rectangle_ripple.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/layout/mozac_ui_tabcounter_layout.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/layout/mozac_ui_tabcounter_layout.xml new file mode 100644 index 0000000000..ce03eed04b --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/layout/mozac_ui_tabcounter_layout.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-am/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-am/strings.xml new file mode 100644 index 0000000000..0aeb48b020 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-am/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ትር ክፈት። ትሮችን ለመቀየር መታ ያድርጉ። + + %1$s ትር ክፈት። ትሮችን ለመቀየር መታ ያድርጉ። + + አዲስ ትር + + አዲስ የግል ትር + + ትርን ዝጋ + + ትርን አባዛ + + የትር ቆጣሪ ሰሪ-አሞሌ አዝራር። + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ar/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000000..4f66de8ec0 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ar/strings.xml @@ -0,0 +1,17 @@ + + + + لسان واحد مفتوح. انقر لتبديل الألسنة. + + %1$s من الألسنة مفتوح. انقر لتبديل الألسنة. + + لسان جديد + + لسان خاص جديد + + أغلِق اللسان + + كرّر اللسان + + زر ”عدد الألسنة“ في شريط الأدوات. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ast/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ast/strings.xml new file mode 100644 index 0000000000..ed05585c87 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ast/strings.xml @@ -0,0 +1,17 @@ + + + + 1 llingüeta abierta. Toca pa cambiar a otra. + + %1$s llingüetes abiertes. Toca pa cambiar a otra. + + Llingüeta nueva + + Llingüeta privada nueva + + Zarrar la llingüeta + + Duplicar la llingüeta + + El botón del contador de llingüetes de la barra de ferramientes. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-azb/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-azb/strings.xml new file mode 100644 index 0000000000..9f856c949a --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-azb/strings.xml @@ -0,0 +1,17 @@ + + + + آچیق تاغ. تاغ‌لاری دگیشدیرمک اوچون توخونون. + + %1$s آچیق تاغ. تاغلاری دگیشدیرمک اوچون توخونون. + + یئنی تاغ + + یئنی گیزلی تاغ + + تاغی باغلا + + تاغی ایکی‌له + + تاغ سایی‌جی‌نین تولبار دویمه‌سی. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-be/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-be/strings.xml new file mode 100644 index 0000000000..395e798a81 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-be/strings.xml @@ -0,0 +1,17 @@ + + + + 1 адкрытая картка. Націсніце, каб пераключыць карткі. + + Адкрытых картак: %1$s. Націсніце, каб пераключыць карткі. + + Новая картка + + Новая прыватная картка + + Закрыць картку + + Дубляваць картку + + Кнопка лічыльніка картак на панэлі інструментаў. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bg/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bg/strings.xml new file mode 100644 index 0000000000..0714493b0d --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bg/strings.xml @@ -0,0 +1,17 @@ + + + + 1 отворен раздел. Докоснете за превключване на раздели. + + %1$s отворени раздела. Докоснете, за превключване на раздели. + + Нов раздел + + Нов поверителен раздел + + Затваряне на раздел + + Дублиране на раздел + + Бутон към инструментите от брояча на раздели. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bn/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bn/strings.xml new file mode 100644 index 0000000000..1136e9a18c --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bn/strings.xml @@ -0,0 +1,17 @@ + + + + খোলা ট্যাব ১টি। ট্যাব পাল্টাও। + + %1$sটি খোলা ট্যাব। ট্যাব পাল্টাও। + + নতুন ট্যাব + + নতুন ব্যক্তিগত ট্যাব + + ট্যাব বন্ধ করুন + + অনুরূপ ট্যাব + + ট্যাব গণনাকারী টুলবার বোতাম। + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-br/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-br/strings.xml new file mode 100644 index 0000000000..813c4a9382 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-br/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ivinell digor. Stokit evit mont dʼun ivinell all. + + %1$s ivinell digor. Stokit evit mont dʼun ivinell all. + + Ivinell nevez + + Ivinell prevez nevez + + Serriñ an ivinell + + Eilañ an ivinell + + An afell kontañ ivinelloù er varrenn-ostilhoù. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bs/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bs/strings.xml new file mode 100644 index 0000000000..8b89e9972f --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-bs/strings.xml @@ -0,0 +1,17 @@ + + + + 1 otvoren tab. Dodirnite za promjenu tabova. + + %1$s otvorenih tabova. Dodirnite za promjenu tabova. + + Novi tab + + Novi privatni tab + + Zatvori tab + + Dupliciraj tab + + Tipka brojača tabova na alatnoj traci. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ca/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ca/strings.xml new file mode 100644 index 0000000000..141fb164b0 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ca/strings.xml @@ -0,0 +1,17 @@ + + + + 1 pestanya oberta. Toqueu per canviar de pestanya. + + %1$s pestanyes obertes. Toqueu per canviar de pestanya. + + Pestanya nova + + Pestanya privada nova + + Tanca la pestanya + + Duplica la pestanya + + El botó del comptador de pestanyes de la barra d’eines. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cak/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cak/strings.xml new file mode 100644 index 0000000000..0ac9556c3d --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cak/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ruwi\' jaqon. Tachapa\' richin nak\'ëx ruwi\'. + + %1$s ruwi\' ejaqon. Tachapa\' richin nak\'ëx ruwi\'. + + K\'ak\'a\' ruwi\' + + K\'ak\'a\' ichinan ruwi\' + + Titz\'apïx ruwi\' + + Tikamulüx ruwi\' + + Ri rupitz\'b\'al ajilanel ruwi\' pa rukajtz\'ik samajib\'äl. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ceb/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ceb/strings.xml new file mode 100644 index 0000000000..c629119fc1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ceb/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ang abli nga tab. i-Tap para mobalhin ug mga tab. + + %1$s ang abli nga mga tab. i-Tap para mobalhin ug mga tab. + + Bag-o nga tab + + Bag-o nga pribadong tab + + i-Close ang tab + + Duplicate nga tab + + Ang tab counter toolbar button. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ckb/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ckb/strings.xml new file mode 100644 index 0000000000..46e0ffeadb --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ckb/strings.xml @@ -0,0 +1,17 @@ + + + + ١ بازدەر کراوەیە. پەنجەدابگرە بۆ گۆڕینی بازدەرەکان. + + %1$s بازدەر کراوەیە. پەنجەدابگرە بۆ گۆڕینی بازدەرەکان. + + بازدەری نوێ + + بازدەری تایبەتی نوێ + + بازدەر دابخە + + بازدەری دووبارە + + دوگمەی ژمارەی بازدەرەکان لە توڵامراز. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-co/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-co/strings.xml new file mode 100644 index 0000000000..d72dfc8f8f --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-co/strings.xml @@ -0,0 +1,17 @@ + + + + 1 unghjetta aperta. Picchichjà per cambià d’unghjetta. + + %1$s unghjette aperte. Picchichjà per cambià d’unghjetta. + + Nova unghjetta + + Nova unghjetta privata + + Chjode l’unghjetta + + Duppià l’unghjetta + + U buttone di cuntatore d’unghjetta in a barra d’attrezzi. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cs/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cs/strings.xml new file mode 100644 index 0000000000..de8db31078 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cs/strings.xml @@ -0,0 +1,17 @@ + + + + Jeden otevřený panel. Klepnutím panely přepnete. + + %1$s otevřených panelů. Klepnutím přepnete panely. + + Nový panel + + Nový anonymní panel + + Zavřít panel + + Duplikovat panel + + Tlačítko s počtem panelů na liště. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cy/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cy/strings.xml new file mode 100644 index 0000000000..78e83d1611 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-cy/strings.xml @@ -0,0 +1,17 @@ + + + + 1 tab ar agor. Tapio i newid tabiau. + + %1$s tab ar agor. Tapio i newid tabiau. + + Tab newydd + + Tab preifat newydd + + Cau tab + + Dyblygu tab + + Botwm y bar offer cyfrif tabiau. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-da/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-da/strings.xml new file mode 100644 index 0000000000..15549d060c --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-da/strings.xml @@ -0,0 +1,17 @@ + + + + 1 åbent faneblad. Tryk for at skifte faneblade. + + %1$s åbne faneblade. Tryk for at skifte faneblade. + + Nyt faneblad + + Nyt privat faneblad + + Luk faneblad + + Kopier faneblad + + Værktøjslinjeknappen til fanebladstæller. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-de/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-de/strings.xml new file mode 100644 index 0000000000..69ff81ea3b --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-de/strings.xml @@ -0,0 +1,17 @@ + + + + 1 offener Tab. Antippen, um Tabs zu wechseln. + + %1$s offene Tabs. Antippen, um Tabs zu wechseln. + + Neuer Tab + + Neuer privater Tab + + Tab schließen + + Tab klonen + + Die Schaltfläche der Tab-Zähler-Symbolleiste. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-dsb/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-dsb/strings.xml new file mode 100644 index 0000000000..6eb7e31beb --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-dsb/strings.xml @@ -0,0 +1,17 @@ + + + + Wócynjone rejtariki: 1. Pótusniśo, aby rejtariki pśešaltował. + + Wócynjone rejtariki: %1$s. Pótusniśo, aby rejtariki pśešaltował. + + Nowy rejtarik + + Nowy priwatny rejtarik + + Rejtarik zacyniś + + Rejtarik pódwójś + + Symbol rejtarikowego licaka na symbolowej rědce. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-el/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-el/strings.xml new file mode 100644 index 0000000000..9a3eb98254 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-el/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ανοικτή καρτέλα. Πατήστε για εναλλαγή καρτελών. + + %1$s ανοικτές καρτέλες. Πατήστε για εναλλαγή καρτελών. + + Νέα καρτέλα + + Νέα ιδιωτική καρτέλα + + Κλείσιμο καρτέλας + + Αντιγραφή καρτέλας + + Το κουμπί μέτρησης καρτελών της γραμμής εργαλείων. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-en-rCA/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-en-rCA/strings.xml new file mode 100644 index 0000000000..04c14f2b61 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-en-rCA/strings.xml @@ -0,0 +1,17 @@ + + + + 1 open tab. Tap to switch tabs. + + %1$s open tabs. Tap to switch tabs. + + New tab + + New private tab + + Close tab + + Duplicate tab + + The tab counter toolbar button. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-en-rGB/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000..04c14f2b61 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,17 @@ + + + + 1 open tab. Tap to switch tabs. + + %1$s open tabs. Tap to switch tabs. + + New tab + + New private tab + + Close tab + + Duplicate tab + + The tab counter toolbar button. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-eo/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-eo/strings.xml new file mode 100644 index 0000000000..8936839830 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-eo/strings.xml @@ -0,0 +1,17 @@ + + + + 1 malfermita langeto. Tuŝetu por ŝanĝi langeton. + + %1$s malfermitaj langetoj. Tuŝetu por ŝanĝi langetojn. + + Nova langeto + + Nova privata langeto + + Fermi langeton + + Duobligi langeton + + La ilara butono kun nombro de langetoj. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rAR/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rAR/strings.xml new file mode 100644 index 0000000000..dccbab6fc8 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rAR/strings.xml @@ -0,0 +1,17 @@ + + + + 1 pestaña abierta. Tocá para cambiar de pestaña. + + %1$s pestañas abiertas. Tocá para cambiar de pestaña. + + Nueva pestaña + + Nueva pestaña privada + + Cerrar pestaña + + Pestaña duplicada + + El botón contador de pestañas de la barra de herramientas. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rCL/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rCL/strings.xml new file mode 100644 index 0000000000..fec84a0de1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rCL/strings.xml @@ -0,0 +1,17 @@ + + + + 1 pestaña abierta. Toca para cambiar de pestaña. + + %1$s pestañas abiertas. Toca para cambiar de pestaña. + + Nueva pestaña + + Nueva pestaña privada + + Cerrar pestaña + + Duplicar pestaña + + El botón contador de pestañas de la barra de herramientas. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rES/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rES/strings.xml new file mode 100644 index 0000000000..fec84a0de1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rES/strings.xml @@ -0,0 +1,17 @@ + + + + 1 pestaña abierta. Toca para cambiar de pestaña. + + %1$s pestañas abiertas. Toca para cambiar de pestaña. + + Nueva pestaña + + Nueva pestaña privada + + Cerrar pestaña + + Duplicar pestaña + + El botón contador de pestañas de la barra de herramientas. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rMX/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rMX/strings.xml new file mode 100644 index 0000000000..fec84a0de1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es-rMX/strings.xml @@ -0,0 +1,17 @@ + + + + 1 pestaña abierta. Toca para cambiar de pestaña. + + %1$s pestañas abiertas. Toca para cambiar de pestaña. + + Nueva pestaña + + Nueva pestaña privada + + Cerrar pestaña + + Duplicar pestaña + + El botón contador de pestañas de la barra de herramientas. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es/strings.xml new file mode 100644 index 0000000000..fec84a0de1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-es/strings.xml @@ -0,0 +1,17 @@ + + + + 1 pestaña abierta. Toca para cambiar de pestaña. + + %1$s pestañas abiertas. Toca para cambiar de pestaña. + + Nueva pestaña + + Nueva pestaña privada + + Cerrar pestaña + + Duplicar pestaña + + El botón contador de pestañas de la barra de herramientas. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-et/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-et/strings.xml new file mode 100644 index 0000000000..a1dca163dd --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-et/strings.xml @@ -0,0 +1,17 @@ + + + + 1 avatud kaart. Kaartide vahetamiseks puuduta. + + %1$s avatud kaarti. Kaartide vahetamiseks puuduta. + + Uus kaart + + Uus privaatne kaart + + Sulge kaart + + Klooni kaart + + Kaartide loenduri tööriistariba nupp. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-eu/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-eu/strings.xml new file mode 100644 index 0000000000..f2d08959b2 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-eu/strings.xml @@ -0,0 +1,17 @@ + + + + Irekitako fitxa bat. Sakatu fitxaz aldatzeko. + + Irekitako %1$s fitxa. Sakatu fitxaz aldatzeko. + + Fitxa berria + + Fitxa pribatu berria + + Itxi fitxa + + Bikoiztu fitxa + + Fitxen kontagailuaren tresna-barrako botoia. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fa/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fa/strings.xml new file mode 100644 index 0000000000..75cc015960 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fa/strings.xml @@ -0,0 +1,17 @@ + + + + ۱ زبانهٔ باز. برای تعویض زبانه‌ها ضربه بزنید. + + %1$s زبانهٔ باز. برای تغییر زبانه‌ها ضربه بزنید. + + زبانهٔ جدید + + زبانهٔ خصوصی جدید + + بستن زبانه + + تکثیر زبانه + + دکمهٔ نوار ابزار شمارندهٔ زبانه‌ها. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ff/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ff/strings.xml new file mode 100644 index 0000000000..c9c6f48e08 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ff/strings.xml @@ -0,0 +1,7 @@ + + + + Tabbere hesere + + Tabbere suuriinde hesere + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fi/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000000..cd5743f8f5 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fi/strings.xml @@ -0,0 +1,17 @@ + + + + 1 avoin välilehti. Napauta vaihtaaksesi. + + %1$s avointa välilehteä. Napauta vaihtaaksesi. + + Uusi välilehti + + Uusi yksityinen välilehti + + Sulje välilehti + + Monista välilehti + + Välilehtien laskurin työkalupalkin painike. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fr/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000000..de7950588c --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fr/strings.xml @@ -0,0 +1,17 @@ + + + + 1 onglet ouvert. Appuyez pour changer d’onglet. + + %1$s onglets ouverts. Appuyez pour changer d’onglet. + + Nouvel onglet + + Nouvel onglet privé + + Fermer l’onglet + + Dupliquer l’onglet + + Le bouton compteur d’onglets de la barre d’outils. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fur/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fur/strings.xml new file mode 100644 index 0000000000..365119d527 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fur/strings.xml @@ -0,0 +1,17 @@ + + + + 1 schede vierte. Tocje par cambiâ schede. + + %1$s schedis viertis. Tocje par cambiâ schede. + + Gnove schede + + Gnove schede privade + + Siere schede + + Dupliche schede + + Il boton cul contadôr des schedis te sbare dai struments. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fy-rNL/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fy-rNL/strings.xml new file mode 100644 index 0000000000..7c6443528e --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-fy-rNL/strings.xml @@ -0,0 +1,17 @@ + + + + 1 iepen ljepblêd. Tik om tusken ljepblêden te wikseljen. + + %1$s iepen ljepblêden. Tik om tusken ljepblêden te wikseljen. + + Nij ljepblêd + + Nij priveeljepblêd + + Ljepblêd slute + + Ljepblêd duplisearje + + De ljepblêdteller-arkbalkeknop. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gd/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gd/strings.xml new file mode 100644 index 0000000000..b9bbced3ea --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gd/strings.xml @@ -0,0 +1,17 @@ + + + + Tha taba fosgailte. Thoir gnogag airson leum a ghearradh gu taba eile. + + Tha tabaichean (%1$s) fosgailte. Thoir gnogag airson leum a ghearradh gu taba eile. + + Taba ùr + + Taba prìobhaideach ùr + + Dùin an taba + + Dùblaich an taba + + Putan bàr-inneal cunntair nan taba. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gl/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gl/strings.xml new file mode 100644 index 0000000000..c4112f16fd --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gl/strings.xml @@ -0,0 +1,17 @@ + + + + 1 lapela aberta. Toque para cambiar de lapela. + + %1$s lapelas abertas. Toque para cambiar de lapela. + + Nova lapela + + Nova lapela privada + + Pechar lapela + + Duplicar o separador + + O botón da barra de ferramentas do contador de lapelas. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gn/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gn/strings.xml new file mode 100644 index 0000000000..b1fa6062c3 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-gn/strings.xml @@ -0,0 +1,17 @@ + + + + 1 tendayke ijurujáva. Eikutu emoambue hag̃ua tendayke. + + %1$s tendayke ijurujáva. Eikutu emoambue hag̃ua tendayke. + + Tendayke pyahu + + Tendayke pyahu ñemigua + + Tendayke mboty + + Tendayke ikõiva + + Pe votõ tendayke papaha tembiporu renda pegua. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hi-rIN/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hi-rIN/strings.xml new file mode 100644 index 0000000000..519a98c230 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hi-rIN/strings.xml @@ -0,0 +1,15 @@ + + + + 1 खुले टैब। टैब स्विच करने के लिए टैप करें। + + %1$s खुले टैब। टैब स्विच करने के लिए टैप करें। + + नया टैब + + नई निजी टैब + + टैब बंद करें + + डुप्लीकेट टैब + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hr/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hr/strings.xml new file mode 100644 index 0000000000..0233a91fa4 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hr/strings.xml @@ -0,0 +1,17 @@ + + + + 1 otvorena kartica. Dodirni za prebacivanje kartica. + + %1$s otvorene kartice. Dodirni za prebacivanje kartica. + + Nova kartica + + Nova privatna kartica + + Zatvori karticu + + Dupliciraj karticu + + Tipka brojača kartica na alatnoj traci. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hsb/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hsb/strings.xml new file mode 100644 index 0000000000..1affc525e1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hsb/strings.xml @@ -0,0 +1,17 @@ + + + + Wočinjene rajtarki: 1. Podótkńće so, zo byšće rajtarki přepinał. + + Wočinjene rajtarki: %1$s. Podótkńće so, zo byšće rajtarki přepinał. + + Nowy rajtark + + Nowy priwatny rajtark + + Rajtark začinić + + Rajtark podwojić + + Symbol rajtarkoweho ličaka na symbolowej lajsće. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hu/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hu/strings.xml new file mode 100644 index 0000000000..abc31799fc --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hu/strings.xml @@ -0,0 +1,17 @@ + + + + 1 nyitott lap. Koppintson a lapváltáshoz. + + %1$s nyitott lap. Koppintson a lapváltáshoz. + + Új lap + + Új privát lap + + Lap bezárása + + Lap duplikálása + + A lapszámláló eszköztárgomb. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hy-rAM/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hy-rAM/strings.xml new file mode 100644 index 0000000000..201c4bff6f --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-hy-rAM/strings.xml @@ -0,0 +1,17 @@ + + + + 1 բաց ներդիր: Հպեք՝ ներդիրին անցնելու համար: + + %1$s բաց ներդիրներ: Հպեք՝ ներդիրին անցնելու համար: + + Նոր ներդիր + + Նոր մասնավոր ներդիր + + Փակել ներդիրը + + Կրկնօրինակել ներդիրը + + Ներդիրի հաշվիչի գործիքագոտու կոճակը: + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ia/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ia/strings.xml new file mode 100644 index 0000000000..2e80e79466 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ia/strings.xml @@ -0,0 +1,18 @@ + + + + 1 scheda aperte. Tocca pro cambiar le scheda. + + + %1$s schedas aperte. Tocca pro cambiar le scheda. + + Nove scheda + + Nove scheda private + + Clauder le scheda + + Duplicar le scheda + + Le button contator de schedas del barra de instrumentos. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-in/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-in/strings.xml new file mode 100644 index 0000000000..63a84de095 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-in/strings.xml @@ -0,0 +1,17 @@ + + + + 1 tab terbuka. Ketuk untuk beralih tab. + + %1$s tab terbuka. Ketuk untuk beralih tab. + + Tab baru + + Tab pribadi baru + + Tutup tab + + Gandakan tab + + Tombol bilah alat penghitung tab. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-is/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-is/strings.xml new file mode 100644 index 0000000000..85bf97412a --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-is/strings.xml @@ -0,0 +1,17 @@ + + + + 1 opinn flipi. Ýttu til að skipta um flipa. + + %1$s opnir flipar. Ýttu til að skipta um flipa. + + Nýr flipi + + Nýr huliðsflipi + + Loka flipa + + Tvítaka flipa + + Flipateljara-hnappurinn á verkfæraslánni. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-it/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-it/strings.xml new file mode 100644 index 0000000000..bb6ba5c1c4 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-it/strings.xml @@ -0,0 +1,17 @@ + + + + Aperta 1 scheda. Tocca per cambiare scheda. + + Aperte %1$s schede. Tocca per cambiare scheda. + + Nuova scheda + + Nuova scheda anonima + + Chiudi scheda + + Duplica scheda + + Il pulsante nella barra degli strumenti con il numero di schede + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-iw/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-iw/strings.xml new file mode 100644 index 0000000000..57d4a70de1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-iw/strings.xml @@ -0,0 +1,17 @@ + + + + לשונית אחת פתוחה. יש להקיש כדי להחליף לשוניות. + + %1$s לשוניות פתוחות. יש להקיש כדי להחליף לשוניות. + + לשונית חדשה + + לשונית פרטית חדשה + + סגירת לשונית + + שכפול לשונית + + כפתור סרגל הכלים של מונה הלשוניות. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ja/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000000..9780a8380f --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ja/strings.xml @@ -0,0 +1,17 @@ + + + + 開いているタブ 1 個。タップしてタブを切り替えます。 + + 開いているタブ %1$s 個。タップしてタブを切り替えます。 + + 新しいタブ + + 新しいプライベートタブ + + タブを閉じる + + タブを複製 + + タブカウンターのツールバーボタンです。 + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ka/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ka/strings.xml new file mode 100644 index 0000000000..00d535442e --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ka/strings.xml @@ -0,0 +1,17 @@ + + + + 1 გახსნილი ჩანართი. შეეხეთ ჩანართების გადასართველად. + + %1$s გახსნილი ჩანართი. შეეხეთ ჩანართების გადასართველად. + + ახალი ჩანართი + + ახალი პირადი ჩანართი + + ჩანართის დახურვა + + ჩანართის გაორმაგება + + ჩანართის მრიცხველის ღილაკი სამართავ ზოლზე. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kaa/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kaa/strings.xml new file mode 100644 index 0000000000..5854750cf2 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kaa/strings.xml @@ -0,0 +1,17 @@ + + + + 1 bet ashıq. Basqa betlerge ótiw ushın basıń. + + %1$s bet ashıq. Basqa betlerge ótiw ushın basıń. + + Jańa bet + + Jańa jeke bet + + Betti jabıw + + Betti nusqalaw + + Ásbaplar panelinde betler sanaw túymesi. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kab/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kab/strings.xml new file mode 100644 index 0000000000..e3c1f477b6 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kab/strings.xml @@ -0,0 +1,17 @@ + + + + 1 yiccer i yeldin. Sit akken ad tbeddleḍ iccer. + + %1$s waccaren yeldin. Sit akken ad tettbeddileḍ gar waccaren. + + Iccer amaynut + + Iccer uslig amaynut + + Mdel iccer + + Sleg iccer + + Taqeffalt n ugalis n yifecka n umesmiḍan n waccaren. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kk/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kk/strings.xml new file mode 100644 index 0000000000..0149ee5e5c --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kk/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ашық бет. Беттерді ауыстыру үшін шертіңіз. + + %1$s ашық бет. Беттерді ауыстыру үшін шертіңіз. + + Жаңа бет + + Жаңа жекелік беті + + Бетті жабу + + Бетті қосарлау + + Беттер санағышы болатын панель батырмасы. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kmr/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kmr/strings.xml new file mode 100644 index 0000000000..d05d912cb7 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-kmr/strings.xml @@ -0,0 +1,17 @@ + + + + 1 hilpekîna vekirî. Ji bo hilpekînê biguherînî, bitikîne. + + %1$s hilpekînên vekirî. Ji bo hilpekînê biguherînî, bitikîne. + + Hilpekîna nû + + Hilpekîna veşartî ya nû + + Hilpekînê bigire + + Hilpekînê zêde bike + + Bişkoka darikê amûran a jimarkera hilpekînan. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ko/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..4811b27a84 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ko/strings.xml @@ -0,0 +1,17 @@ + + + + 열린 탭 1개. 탭을 전환하려면 누르세요. + + 열린 탭 %1$s개. 탭을 전환하려면 누르세요. + + 새 탭 + + 새 사생활 보호 탭 + + 탭 닫기 + + 탭 복제 + + 탭 카운터 도구 모음 버튼입니다. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-lo/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-lo/strings.xml new file mode 100644 index 0000000000..a82e9d6e4e --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-lo/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ເປີດແທັບ. ແຕະເພື່ອປ່ຽນແທັບ. + + %1$s ເປີດແທັບ. ແຕະເພື່ອປ່ຽນແທັບ. + + ແທັບໃຫມ່ + + ແທັບສ່ວນໂຕໃຫມ່ + + ປິດແທັບ + + ແທັບທີ່ຊໍ້າກັນ + + ປຸ່ມແຖບເຄື່ອງມື ໂຕນັບແຖບ. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-lt/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-lt/strings.xml new file mode 100644 index 0000000000..9b592c0023 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-lt/strings.xml @@ -0,0 +1,17 @@ + + + + 1 atverta kortelė. Bakstelėkite, norėdami pereiti tarp kortelių. + + %1$s atvertos kortelės. Bakstelėkite, norėdami pereiti tarp kortelių. + + Nauja kortelė + + Nauja privačioji kortelė + + Užverti kortelę + + Dubliuoti kortelę + + Kortelių skaičiaus mygtukas priemonių juostoje. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-my/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-my/strings.xml new file mode 100644 index 0000000000..13d029ac49 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-my/strings.xml @@ -0,0 +1,17 @@ + + + + ဖွင့်ထားသော တပ်ဗ် 1 ။ တပ်ဗ်များ ပြောင်းရန် နှိပ်ပါ။ + + ဖွင့်ထားသော တပ်ဗ်များ %1$s ။ တက်ဗ်များ ပြောင်းရန် နှိပ်ပါ။ + + တပ်ဗ် အသစ် + + ကိုယ်ပိုင် သီးသန့် တက်ဗ် အသစ် + + တပ်ဗ်ကို ပိတ်ပါ + + တပ်ဗ်ကို ပွားပါ + + တပ်ဗ် ကောင်တာ တူးဘား ခလုတ် + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nb-rNO/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nb-rNO/strings.xml new file mode 100644 index 0000000000..941a1a5647 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nb-rNO/strings.xml @@ -0,0 +1,17 @@ + + + + 1 åpen fane. Trykk for å bytte fane. + + %1$s åpne faner. Trykk for å bytte fane. + + Ny fane + + Ny privat fane + + Lukk fane + + Dupliser fane + + Fane-teller verktøylinjeknapp. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ne-rNP/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ne-rNP/strings.xml new file mode 100644 index 0000000000..b0458adc54 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ne-rNP/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ट्याब खोल्नुहोस् । ट्याबहरु बिचमा स्वीच गर्नको लागि ट्याप गर्नुहोस् । + + %1$s ट्याबहरु खोल्नुहोस् । ट्याबहरु बिचमा स्वीच गर्नको लागि ट्याप गर्नुहोस् । + + नयाँ ट्याब + + नयाँ निजी ट्याब + + ट्याब बन्द गर्नुहोस् + + नक्कल ट्याब + + ट्याब काउन्टर उपकरणपट्टी बटन् । + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nl/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000000..e34f69d0bc --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nl/strings.xml @@ -0,0 +1,17 @@ + + + + 1 open tabblad. Tik om tussen tabbladen te wisselen. + + %1$s open tabbladen. Tik om tussen tabbladen te wisselen. + + Nieuw tabblad + + Nieuw privétabblad + + Tabblad sluiten + + Tabblad dupliceren + + De tabbladteller-werkbalkknop. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nn-rNO/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nn-rNO/strings.xml new file mode 100644 index 0000000000..71cd5c74de --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-nn-rNO/strings.xml @@ -0,0 +1,17 @@ + + + + 1 open fane. Trykk for å byte fane. + + %1$s opne faner. Trykk for å byte fane. + + Ny fane + + Ny privat fane + + Lat att fane + + Dupliser fane + + Fane-teljar verktøylinjeknapp. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-oc/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-oc/strings.xml new file mode 100644 index 0000000000..78c7d4501e --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-oc/strings.xml @@ -0,0 +1,17 @@ + + + + 1 onglet dubèrt. Tocatz per bascular. + + %1$s onglets dubèrts. Tocatz per bascular. + + Onglet novèl + + Onglet de nav. privada + + Tampar l’onglet + + Duplicar l’onglet + + Lo boton comptador d’onglets de la barra d’aisinas. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-or/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-or/strings.xml new file mode 100644 index 0000000000..5405b5f6e0 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-or/strings.xml @@ -0,0 +1,11 @@ + + + + ନୂଆ ଟ୍ୟାବ୍ + + ନୂଆ ବ୍ୟକ୍ତିଗତ ଟ୍ୟାବ୍ + + ଟ୍ୟାବ୍ ବନ୍ଦ କରନ୍ତୁ + + ଟ୍ୟାବ୍ ନକଲ କରନ୍ତୁ + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pa-rIN/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pa-rIN/strings.xml new file mode 100644 index 0000000000..68a1578f17 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pa-rIN/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ਟੈਬ ਖੁੱਲ੍ਹੀ ਹੈ। ਟੈਬਾਂ ਵਿੱਚ ਬਦਲਣ ਲਈ ਛੂਹੋ। + + %1$s ਟੈਬਾਂ ਖੁੱਲ੍ਹੀਆਂ। ਟੈਬਾਂ ਵਿੱਚ ਸਵਿੱਚ ਕਰਨ ਵਾਸਤੇ ਟੈਪ ਕਰੋ। + + ਨਵੀਂ ਟੈਬ + + ਨਵੀਂ ਨਿੱਜੀ ਟੈਬ + + ਟੈਬ ਬੰਦ ਕਰੋ + + ਡੁਪਲੀਕੇਟ ਟੈਬ + + ਟੈਬ ਗਿਣਤੀ ਟੂਲ-ਪੱਟੀ ਬਟਨ ਹੈ। + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pa-rPK/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pa-rPK/strings.xml new file mode 100644 index 0000000000..a91863a90b --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pa-rPK/strings.xml @@ -0,0 +1,17 @@ + + + + اک ٹیب کھُلھی اے۔ ہورناں ٹیب جاوݨ لئی اِتھے چھوہو۔ + + %1$s ٹیباں کھُلھیاں ہن۔ ہورناں ٹیب جاوݨ لئی اِتھے چھوہو۔ + + نویں ٹیب کھولھو + + نجی ٹیب کھولھو + + ٹیب بند کرو + + ٹیب کاپی کرو + + ٹیب دی گݨتی والا بٹن اے۔ + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pl/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000000..6179d81a5a --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pl/strings.xml @@ -0,0 +1,17 @@ + + + + Otwarte karty: 1. Stuknij, aby przełączyć karty. + + Otwarte karty: %1$s. Stuknij, aby przełączyć karty. + + Nowa karta + + Nowa karta prywatna + + Zamknij kartę + + Duplikuj kartę + + Przycisk paska narzędzi z liczbą kart. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pt-rBR/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..0f4f87ffab --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,17 @@ + + + + 1 aba aberta. Toque para alternar abas. + + %1$s abas abertas. Toque para alternar abas. + + Nova aba + + Nova aba privativa + + Fechar aba + + Duplicar aba + + O botão contador de abas da barra de ferramentas. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pt-rPT/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..9abddce5aa --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,17 @@ + + + + 1 separador aberto. Toque para mudar de separador. + + %1$s separadores abertos. Toque para mudar de separadores. + + Novo separador + + Novo separador privado + + Fechar separador + + Duplicar separador + + O botão da barra de ferramentas com o número de separadores. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-rm/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-rm/strings.xml new file mode 100644 index 0000000000..4a162573f2 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-rm/strings.xml @@ -0,0 +1,17 @@ + + + + 1 tab avert. Tutgar per midar tab. + + %1$s tabs averts. Tutgar per midar tab. + + Nov tab + + Nov tab privat + + Serrar il tab + + Duplitgar il tab + + Il buttun en la trav d\'utensils cun il dumber da tabs. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ro/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ro/strings.xml new file mode 100644 index 0000000000..e2726988b1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ro/strings.xml @@ -0,0 +1,11 @@ + + + + 1 filă deschisă. Atinge pentru a comuta între file. + + %1$s file deschise. Atinge pentru a comuta între file. + + Filă privată nouă + + Închide fila + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ru/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000000..dcde495148 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ru/strings.xml @@ -0,0 +1,17 @@ + + + + 1 открытая вкладка. Нажмите, чтобы переключить вкладки. + + Открытых вкладок: %1$s. Нажмите, чтобы переключить вкладки. + + Новая вкладка + + Новая приватная вкладка + + Закрыть вкладку + + Дублировать вкладку + + Кнопка счётчика вкладок на панели инструментов. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sat/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sat/strings.xml new file mode 100644 index 0000000000..a6878d4562 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sat/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ᱠᱷᱩᱞᱟᱹ ᱴᱮᱵᱽ ᱾ ᱴᱮᱵᱽ ᱠᱚ ᱥᱣᱤᱪ ᱞᱟᱹᱜᱤᱫ ᱚᱛᱟᱭ ᱢᱮ ᱾ + + %1$s ᱠᱷᱩᱞᱟᱹ ᱴᱮᱵᱽᱠᱚ ᱾ ᱴᱮᱵᱽᱠᱚ ᱥᱣᱤᱪ ᱞᱟᱹᱜᱤᱫ ᱚᱛᱟᱭ ᱢᱮ ᱾ + + ᱱᱟᱶᱟ ᱴᱮᱵᱽ + + ᱱᱟᱶᱟ ᱱᱤᱡᱮᱨᱟᱠ ᱴᱮᱵᱽ + + ᱴᱮᱵᱽ ᱵᱚᱸᱫᱽᱚᱭ ᱢᱮ + + ᱰᱩᱯᱞᱤᱠᱮᱴ ᱴᱮᱵᱽ + + ᱴᱮᱵᱽ ᱠᱟᱣᱱᱴᱟᱹᱨ ᱴᱩᱞᱵᱟᱨ ᱵᱩᱛᱟᱹᱢ ᱾ + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sc/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sc/strings.xml new file mode 100644 index 0000000000..d19f2c5d25 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sc/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ischeda aberta. Toca pro cuncambiare ischedas. + + %1$s ischedas abertas. Toca pro cuncambiare ischedas. + + Ischeda noa + + Ischeda privada noa + + Serra s’ischeda + + Dùplica s’ischeda + + Su butone de su contadore de ischedas de sa barra de ainas. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-si/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-si/strings.xml new file mode 100644 index 0000000000..783718eb2c --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-si/strings.xml @@ -0,0 +1,17 @@ + + + + විවෘත පටිති 1. මාරු වීමට ඔබන්න. + + විවෘත පටිති %1$s. මාරු වීමට ඔබන්න. + + නව පටිත්ත + + නව පෞද්. පටිත්ත + + පටිත්ත වසන්න + + පටිත්තෙහි අනුපිටපතක් + + පටිති ගණනය මෙවලම් තීරු බොත්තම. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sk/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sk/strings.xml new file mode 100644 index 0000000000..5f8dc12703 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sk/strings.xml @@ -0,0 +1,17 @@ + + + + 1 otvorená karta. Ťuknutím prepnete karty. + + %1$s otvorených kariet. Ťuknutím prepnete karty. + + Nová karta + + Nová súkromná karta + + Zavrieť kartu + + Duplikovať kartu + + Tlačidlo počítadla kariet na paneli nástrojov. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-skr/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-skr/strings.xml new file mode 100644 index 0000000000..b4315df355 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-skr/strings.xml @@ -0,0 +1,17 @@ + + + + ١ ٹیب کھولو۔ ٹیبز بدلݨ کیتے دباؤ۔ + + %1$s ٹیبز کھولو۔ ٹیبز بدلݨ کیتے دباؤ۔ + + نواں ٹیب + + نویں نجی ٹیب + + ٹیب بند کرو + + واڳی ٹیب + + ٹیباں ڳݨݨ آلا ٹولبار بٹݨ۔ + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sl/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sl/strings.xml new file mode 100644 index 0000000000..fe24fbb221 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sl/strings.xml @@ -0,0 +1,17 @@ + + + + 1 odprt zavihek. Tapnite za preklop zavihkov. + + Odprtih zavihkov: %1$s. Tapnite za preklop zavihkov. + + Nov zavihek + + Nov zasebni zavihek + + Zapri zavihek + + Podvoji zavihek + + Gumb števca zavihkov v orodni vrstici. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sq/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sq/strings.xml new file mode 100644 index 0000000000..a17e1ece9d --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sq/strings.xml @@ -0,0 +1,17 @@ + + + + 1 skedë e hapur. Prekeni që të ndërroni skeda. + + %1$s skeda të hapura. Prekeni që të ndërroni skeda. + + Skedë e re + + Skedë e re private + + Mbylle skedën + + Përdytëso skedën + + Butoni i numrit të skedave te paneli. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sr/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sr/strings.xml new file mode 100644 index 0000000000..7963c5b8f8 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sr/strings.xml @@ -0,0 +1,17 @@ + + + + 1 отворени језичак. Додирни за пребацивање језичака. + + %1$s отворених језичака. Додирни за пребацивање језичака. + + Нови језичак + + Нови приватни језичак + + Затвори језичак + + Удвостручи језичак + + Дугме за бројач језичака. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-su/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-su/strings.xml new file mode 100644 index 0000000000..fdc4401b78 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-su/strings.xml @@ -0,0 +1,17 @@ + + + + 1 tab muka. Toél pikeun pindah tab. + + %1$s tab muka. Toél pikeun pindah tab. + + Tab anyar + + Tab nyamuni anyar + + Tutup tab + + Duplikat tab + + Tombol tulbar pangitung tab. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sv-rSE/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sv-rSE/strings.xml new file mode 100644 index 0000000000..bed22b6674 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-sv-rSE/strings.xml @@ -0,0 +1,17 @@ + + + + 1 öppen flik. Tryck för att växla mellan flikar. + + %1$s öppna flikar. Tryck för att växla mellan flikar. + + Ny flik + + Ny privat flik + + Stäng flik + + Duplicera flik + + Verktygsfältknapp för flikräknare. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-szl/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-szl/strings.xml new file mode 100644 index 0000000000..abdc3c96f5 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-szl/strings.xml @@ -0,0 +1,17 @@ + + + + Jedna ôtwarto karta. Tyknij, coby przełōnczyć karty. + + Ôtwarte karty: %1$s. Tyknij, coby je zmiynić. + + Nowo karta + + Nowo prywatno karta + + Zawrzij karta + + Tupluj karta + + Knefel z poskym z noczyniami do rachowanio kart. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-te/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-te/strings.xml new file mode 100644 index 0000000000..056d3aebcd --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-te/strings.xml @@ -0,0 +1,13 @@ + + + + 1 తెరిచివున్న ట్యాబు. ట్యాబుల మధ్య మారడానికి తాకండి. + + %1$s తెరిచివున్న ట్యాబులు. ట్యాబుల మధ్య మారడానికి తాకండి. + + కొత్త ట్యాబు + + కొత్త అంతరంగిక ట్యాబు + + ట్యాబును మూసివేయి + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tg/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tg/strings.xml new file mode 100644 index 0000000000..950305d610 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tg/strings.xml @@ -0,0 +1,17 @@ + + + + 1 варақаи кушода. Барои гузариш байни варақаҳо, зарба занед. + + %1$s варақаи кушода. Барои гузариш байни варақаҳо, зарба занед. + + Варақаи нав + + Варақаи хусусии нав + + Пӯшидани варақа + + Такроран кушодани варақа + + Тугмаи ҳисобкунаки варақаҳо дар навори абзорҳо. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-th/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-th/strings.xml new file mode 100644 index 0000000000..cee1d74cd6 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-th/strings.xml @@ -0,0 +1,17 @@ + + + + 1 แท็บที่เปิด แตะเพื่อสลับไปยังแท็บ + + %1$s แท็บที่เปิด แตะเพื่อสลับไปยังแท็บ + + แท็บใหม่ + + แท็บส่วนตัวใหม่ + + ปิดแท็บ + + ทำสำเนาแท็บ + + ปุ่มแถบเครื่องมือตัวนับแท็บ + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tl/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tl/strings.xml new file mode 100644 index 0000000000..ca7ef24882 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tl/strings.xml @@ -0,0 +1,17 @@ + + + + 1 nakabukas na tab. I-tap para lumipat ng tab. + + %1$s nakabukas na tab. I-tap para lumipat ng tab. + + Bagong tab + + Bagong pribadong tab + + Isara ang tab + + Kaparehong tab + + Ang toolbar button para sa bilang ng tab. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tr/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000000..896b18dbf7 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tr/strings.xml @@ -0,0 +1,17 @@ + + + + 1 açık sekme. Sekme değiştirmek için dokunun. + + %1$s açık sekme. Sekme değiştirmek için dokunun. + + Yeni sekme + + Yeni gizli sekme + + Sekmeyi kapat + + Sekmeyi çoğalt + + Sekme sayacı araç çubuğu düğmesi. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-trs/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-trs/strings.xml new file mode 100644 index 0000000000..732c0ebab6 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-trs/strings.xml @@ -0,0 +1,17 @@ + + + + 1 nā\'nïn rakïj ñanj. Gūru\'man ra\'a da\' nādūnāt rakïj ñanj. + + %1$s nā\'nïn nej rakïj ñanj. Gūru\'man ra\'a da\' nādūnāt nej rakïj ñanj. + + Rakïj ñanj nākàa + + Rakïj ñaj nākà gārasun \'ngō rïn\' + + Narán rakïj ñanj + + Rakïj ñanj nata’a + + Butûn riña ‘na’ nej dukuán ahia nej si rāsun nej rakïj ñanj. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tt/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tt/strings.xml new file mode 100644 index 0000000000..d89b5bbbaa --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tt/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ачык таб. Табларны күчерү өчен басыгыз. + + %1$s ачык таб. Табларны күчерү өчен басыгыз. + + Яңа таб + + Яңа хосусый таб + + Табны ябу + + Табны кабатлау + + Кораллар панелендәге таблар cанагычы төймәсе. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tzm/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tzm/strings.xml new file mode 100644 index 0000000000..3ca40446d7 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-tzm/strings.xml @@ -0,0 +1,9 @@ + + + + Aseksel amaynu + + Aseksel uslig amaynu + + Mdel aseksel + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ug/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ug/strings.xml new file mode 100644 index 0000000000..a2a92a2c73 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ug/strings.xml @@ -0,0 +1,17 @@ + + + + 1 بەتكۈچ ئوچۇق. بەتكۈچنى ئالماشتۇرۇش ئۈچۈن چېكىڭ. + + %1$s بەتكۈچ ئوچۇق. بەتكۈچنى ئالماشتۇرۇش ئۈچۈن چېكىڭ. + + يېڭى بەتكۈچ + + يېڭى شەخسىي بەتكۈچ + + بەتكۈچنى تاقاش + + تەكرار بەتكۈچ + + بەتكۈچ سانىغۇچ قورال بالداق توپچىسى. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-uk/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-uk/strings.xml new file mode 100644 index 0000000000..1f46eb3d93 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-uk/strings.xml @@ -0,0 +1,17 @@ + + + + 1 відкрита вкладка. Торкніться, щоб перемкнути вкладки. + + %1$s відкритих вкладок. Торкніться, щоб перемкнути вкладки. + + Нова вкладка + + Нова приватна вкладка + + Закрити вкладку + + Дублювати вкладку + + Кнопка панелі інструментів лічильника вкладок. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ur/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ur/strings.xml new file mode 100644 index 0000000000..da3b2630ae --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-ur/strings.xml @@ -0,0 +1,17 @@ + + + + 1 کھلا ٹیب۔ ٹیبز بدلنے کے لئے دبائیں۔ + + %1$s کھلے ٹیب۔ ٹیب بدلنے کے لئے دبائیں۔ + + نیا ٹیب + + نیا نجی ٹیب + + ٹیب بند کریں + + دوهرا ٹیب + + ٹیب کاؤنٹر ٹول بار کا بٹن۔ + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-uz/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-uz/strings.xml new file mode 100644 index 0000000000..a9b46408c3 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-uz/strings.xml @@ -0,0 +1,17 @@ + + + + 1 ta ochiq varaq. Boshqa varaqqa oʻtish uchun bosing. + + %1$s ta ochiq varaq. Boshqa varaqqa oʻtish uchun bosing. + + Yangi varaq + + Yangi maxfiy varaq + + Varaqni yopish + + Varaqni nusxalash + + Asboblar panelidagi varaq taymer tugmasi. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-vi/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-vi/strings.xml new file mode 100644 index 0000000000..af59036db1 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-vi/strings.xml @@ -0,0 +1,17 @@ + + + + 1 thẻ đang mở. Chạm để chuyển thẻ. + + %1$s thẻ đang mở. Chạm để chuyển thẻ. + + Thẻ mới + + Thẻ riêng tư mới + + Đóng thẻ + + Nhân đôi thẻ + + Nút thanh công cụ bộ đếm thẻ. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-yo/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-yo/strings.xml new file mode 100644 index 0000000000..13d72778f6 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-yo/strings.xml @@ -0,0 +1,17 @@ + + + + 1 sí táàbù. Tẹ̀ ẹ́ láti bọ́ sí àwọn táàbù. + + %1$s sí táàbù. Tẹ̀ ẹ́ láti bọ́ sí àwọn táàbù. + + Táàbù tuntun + + Táàbù ìkọ̀kọ̀ tuntun + + Pa táàbù dé + + Ẹ̀dà táàbù + + Táàbù náà rí bọ́tìnì irinṣé. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-zh-rCN/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..c2a979f9d5 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,17 @@ + + + + 打开了 1 个标签页,点击即可切换。 + + 打开了 %1$s 个标签页,点击即可切换。 + + 新建标签页 + + 新建隐私标签页 + + 关闭标签页 + + 克隆标签页 + + 标签页计数器工具栏按钮。 + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-zh-rTW/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..ef18d2a974 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,17 @@ + + + + 開啟了 1 個分頁,點擊即可切換分頁。 + + 開啟了 %1$s 個分頁,點擊即可切換分頁。 + + 開新分頁 + + 開新隱私分頁 + + 關閉分頁 + + 複製分頁 + + 分頁計數器工具列按鈕。 + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/attrs.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/attrs.xml new file mode 100644 index 0000000000..70195f2198 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/attrs.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/colors.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/colors.xml new file mode 100644 index 0000000000..f01e0ca318 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/colors.xml @@ -0,0 +1,9 @@ + + + + #FF272727 + #FFFFFF + #20123A + \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/dimens.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..e247e31552 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/dimens.xml @@ -0,0 +1,8 @@ + + + + + 24dp + \ No newline at end of file diff --git a/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/strings.xml b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/strings.xml new file mode 100644 index 0000000000..b63520c1a0 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/main/res/values/strings.xml @@ -0,0 +1,20 @@ + + + + + 1 open tab. Tap to switch tabs. + + %1$s open tabs. Tap to switch tabs. + + New tab + + New private tab + + Close tab + + Duplicate tab + + The tab counter toolbar button. + diff --git a/mobile/android/android-components/components/ui/tabcounter/src/test/java/mozilla/components/ui/tabcounter/TabCounterMenuTest.kt b/mobile/android/android-components/components/ui/tabcounter/src/test/java/mozilla/components/ui/tabcounter/TabCounterMenuTest.kt new file mode 100644 index 0000000000..40ec54066b --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/test/java/mozilla/components/ui/tabcounter/TabCounterMenuTest.kt @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.tabcounter + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class TabCounterMenuTest { + + @Test + fun `return only the new tab item`() { + val onItemTapped: (TabCounterMenu.Item) -> Unit = spy { Unit } + val menu = TabCounterMenu(testContext, onItemTapped) + + val item = menu.newTabItem + assertEquals("New tab", item.text) + item.onClick() + + verify(onItemTapped).invoke(TabCounterMenu.Item.NewTab) + } + + @Test + fun `return only the new private tab item`() { + val onItemTapped: (TabCounterMenu.Item) -> Unit = spy { Unit } + val menu = TabCounterMenu(testContext, onItemTapped) + + val item = menu.newPrivateTabItem + assertEquals("New private tab", item.text) + item.onClick() + + verify(onItemTapped).invoke(TabCounterMenu.Item.NewPrivateTab) + } + + @Test + fun `return a close button`() { + val onItemTapped: (TabCounterMenu.Item) -> Unit = spy { Unit } + val menu = TabCounterMenu(testContext, onItemTapped) + + val item = menu.closeTabItem + assertEquals("Close tab", item.text) + item.onClick() + + verify(onItemTapped).invoke(TabCounterMenu.Item.CloseTab) + } +} diff --git a/mobile/android/android-components/components/ui/tabcounter/src/test/java/mozilla/components/ui/tabcounter/TabCounterTest.kt b/mobile/android/android-components/components/ui/tabcounter/src/test/java/mozilla/components/ui/tabcounter/TabCounterTest.kt new file mode 100644 index 0000000000..881a19e47a --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/test/java/mozilla/components/ui/tabcounter/TabCounterTest.kt @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.tabcounter + +import android.content.res.ColorStateList +import android.view.LayoutInflater +import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.ui.tabcounter.TabCounter.Companion.SO_MANY_TABS_OPEN +import mozilla.components.ui.tabcounter.databinding.MozacUiTabcounterLayoutBinding +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class TabCounterTest { + + private lateinit var tabCounter: TabCounter + private lateinit var binding: MozacUiTabcounterLayoutBinding + + @Before + fun setUp() { + tabCounter = TabCounter(testContext) + binding = + MozacUiTabcounterLayoutBinding.inflate(LayoutInflater.from(testContext), tabCounter) + } + + @Test + fun `Default tab count is set to zero`() { + assertEquals("0", binding.counterText.text) + } + + @Test + fun `Set tab count as single digit value shows count`() { + tabCounter.setCount(1) + assertEquals("1", binding.counterText.text) + } + + @Test + fun `Set tab count as two digit number shows count`() { + tabCounter.setCount(99) + assertEquals("99", binding.counterText.text) + } + + @Test + fun `Setting tab count as three digit value shows correct icon`() { + tabCounter.setCount(100) + assertEquals(SO_MANY_TABS_OPEN, binding.counterText.text) + } + + @Test + fun `Setting tab color shows correct icon`() { + val colorStateList: ColorStateList = mock() + + tabCounter.setColor(colorStateList) + assertEquals(binding.counterText.textColors, colorStateList) + } + + @Test + fun `Toggling the counterMask will set the mask to visible`() { + assertEquals(binding.counterMask.visibility, View.GONE) + tabCounter.toggleCounterMask(true) + assertEquals(binding.counterMask.visibility, View.VISIBLE) + } +} diff --git a/mobile/android/android-components/components/ui/tabcounter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/mobile/android/android-components/components/ui/tabcounter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..cf1c399ea8 --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,2 @@ +mock-maker-inline +// This allows mocking final classes (classes are final by default in Kotlin) diff --git a/mobile/android/android-components/components/ui/tabcounter/src/test/resources/robolectric.properties b/mobile/android/android-components/components/ui/tabcounter/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..932b01b9eb --- /dev/null +++ b/mobile/android/android-components/components/ui/tabcounter/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 diff --git a/mobile/android/android-components/components/ui/widgets/README.md b/mobile/android/android-components/components/ui/widgets/README.md new file mode 100644 index 0000000000..524ef9d794 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/README.md @@ -0,0 +1,19 @@ +# [Android Components](../../../README.md) > UI > Widgets + +The standard set of Mozilla widgets. + +## Usage + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:ui-widgets:{latest-version}" +``` + +## License + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/ diff --git a/mobile/android/android-components/components/ui/widgets/build.gradle b/mobile/android/android-components/components/ui/widgets/build.gradle new file mode 100644 index 0000000000..e1ccc51159 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/build.gradle @@ -0,0 +1,51 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + namespace 'mozilla.components.ui.widgets' +} + +dependencies { + implementation platform(ComponentsDependencies.androidx_compose_bom) + implementation project(':concept-base') + implementation project(':concept-engine') + implementation project(':ui-colors') + implementation project(':ui-icons') + implementation project(':support-ktx') + implementation project(':concept-toolbar') + + implementation ComponentsDependencies.androidx_appcompat + implementation ComponentsDependencies.androidx_constraintlayout + implementation ComponentsDependencies.androidx_core_ktx + implementation ComponentsDependencies.google_material + implementation ComponentsDependencies.androidx_swiperefreshlayout + + testImplementation project(":support-test") + testImplementation project(':support-test-fakes') + + testImplementation ComponentsDependencies.androidx_test_junit + testImplementation ComponentsDependencies.testing_robolectric +} + +apply from: '../../../android-lint.gradle' +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) diff --git a/mobile/android/android-components/components/ui/widgets/proguard-rules.pro b/mobile/android/android-components/components/ui/widgets/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/ui/widgets/src/main/AndroidManifest.xml b/mobile/android/android-components/components/ui/widgets/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e16cda1d34 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/Extentions.kt b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/Extentions.kt new file mode 100644 index 0000000000..fb17b7eb5a --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/Extentions.kt @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets + +import android.widget.TextView +import android.view.View.TEXT_ALIGNMENT_CENTER as CENTER + +/** + * A shortcut to align buttons' text to center inside AlertDialog. + */ +fun androidx.appcompat.app.AlertDialog.withCenterAlignedButtons(): androidx.appcompat.app.AlertDialog { + findViewById(android.R.id.button1)?.let { it.textAlignment = CENTER } + findViewById(android.R.id.button2)?.let { it.textAlignment = CENTER } + findViewById(android.R.id.button3)?.let { it.textAlignment = CENTER } + return this +} + +/** + * A shortcut to align buttons' text to center inside AlertDialog. + * + * Important: On Android API levels lower than 24, this method must be called only AFTER the dialog + * has been shown. Calling this method prior to displaying the dialog on those API levels will cause + * partial initialization of the view, leading to a crash. + * + * Usage example: + * dialog.setOnShowListener { + * dialog.withCenterAlignedButtons() + * } + */ +fun android.app.AlertDialog.withCenterAlignedButtons(): android.app.AlertDialog { + findViewById(android.R.id.button1)?.let { it.textAlignment = CENTER } + findViewById(android.R.id.button2)?.let { it.textAlignment = CENTER } + findViewById(android.R.id.button3)?.let { it.textAlignment = CENTER } + return this +} diff --git a/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/SnackbarDelegate.kt b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/SnackbarDelegate.kt new file mode 100644 index 0000000000..8e2dd2fad7 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/SnackbarDelegate.kt @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets + +import android.view.View +import com.google.android.material.snackbar.Snackbar + +/** + * Delegate to display a snackbar. + */ +interface SnackbarDelegate { + /** + * Displays a snackbar. + * + * @param snackBarParentView The view to find a parent from for displaying the Snackbar. + * @param text The text to show. Can be formatted text. + * @param duration How long to display the message. + * @param action String resource to display for the action. + * @param listener callback to be invoked when the action is clicked. + */ + fun show( + snackBarParentView: View, + text: Int, + duration: Int, + action: Int = 0, + listener: ((v: View) -> Unit)? = null, + ) +} + +/** + * Default implementation for [SnackbarDelegate]. Will display a standard default Snackbar. + */ +class DefaultSnackbarDelegate : SnackbarDelegate { + override fun show( + snackBarParentView: View, + text: Int, + duration: Int, + action: Int, + listener: ((v: View) -> Unit)?, + ) { + val snackbar = Snackbar.make( + snackBarParentView, + text, + duration, + ) + + if (action != 0 && listener != null) { + snackbar.setAction(action, listener) + } + + snackbar.show() + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/VerticalSwipeRefreshLayout.kt b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/VerticalSwipeRefreshLayout.kt new file mode 100644 index 0000000000..aa8bc10b67 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/VerticalSwipeRefreshLayout.kt @@ -0,0 +1,216 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import androidx.annotation.VisibleForTesting +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import kotlin.math.abs + +/** + * [SwipeRefreshLayout] that filters only vertical scrolls for triggering pull to refresh. + * + * Following situations will not trigger pull to refresh: + * - a scroll happening more on the horizontal axis + * - a scale in/out gesture + * - a quick scale gesture + * + * To control responding to scrolls and showing the pull to refresh throbber or not + * use the [View.isEnabled] property. + */ +class VerticalSwipeRefreshLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, +) : SwipeRefreshLayout(context, attrs) { + @VisibleForTesting + internal var isQuickScaleInProgress = false + + @VisibleForTesting + internal var quickScaleEvents = QuickScaleEvents() + private var previousX = 0f + private var previousY = 0f + private val doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout() + private val doubleTapSlop = ViewConfiguration.get(context).scaledDoubleTapSlop + private val doubleTapSlopSquare = doubleTapSlop * doubleTapSlop + + @VisibleForTesting + internal var hadMultiTouch: Boolean = false + + @VisibleForTesting + internal var disallowInterceptTouchEvent = false + + @Suppress("ComplexMethod", "ReturnCount") + override fun onInterceptTouchEvent(event: MotionEvent): Boolean { + // Setting "isEnabled = false" is recommended for users of this ViewGroup + // who who are not interested in the pull to refresh functionality + // Setting this easily avoids executing code unneededsly before the check for "canChildScrollUp". + if (!isEnabled || disallowInterceptTouchEvent) { + return false + } + + if (MotionEvent.ACTION_DOWN == event.action) { + hadMultiTouch = false + } + + // Layman's scale gesture (with two fingers) detector. + // Allows for quick, serial inference as opposed to using ScaleGestureDetector + // which uses callbacks and would be hard to synchronize in the little time we have. + if (event.pointerCount > 1 || hadMultiTouch) { + hadMultiTouch = true + return false + } + + val eventAction = event.action + + // Cleanup if the gesture has been aborted or quick scale just ended/ + if (MotionEvent.ACTION_CANCEL == eventAction || + (MotionEvent.ACTION_UP == eventAction && isQuickScaleInProgress) + ) { + forgetQuickScaleEvents() + return callSuperOnInterceptTouchEvent(event) + } + + // Disable pull to refresh if quick scale is in progress. + maybeAddDoubleTapEvent(event) + if (isQuickScaleInProgress(quickScaleEvents)) { + isQuickScaleInProgress = true + return false + } + + // Disable pull to refresh if the move was more on the X axis. + if (MotionEvent.ACTION_DOWN == eventAction) { + previousX = event.x + previousY = event.y + } else if (MotionEvent.ACTION_MOVE == eventAction) { + val xDistance = abs(event.x - previousX) + val yDistance = abs(event.y - previousY) + previousX = event.x + previousY = event.y + if (xDistance > yDistance) { + return false + } + } + + return callSuperOnInterceptTouchEvent(event) + } + + override fun onStartNestedScroll(child: View, target: View, nestedScrollAxes: Int): Boolean { + // Ignoring nested scrolls from descendants. + // Allowing descendants to trigger nested scrolls would defeat the purpose of this class + // and result in pull to refresh to happen for all movements on the Y axis + // (even as part of scale/quick scale gestures) while also doubling the throbber with the overscroll shadow. + return if (isEnabled) { + return false + } else { + callSuperOnStartNestedScroll(child, target, nestedScrollAxes) + } + } + + @SuppressLint("Recycle") // we do recycle the events in forgetQuickScaleEvents() + @VisibleForTesting + internal fun maybeAddDoubleTapEvent(event: MotionEvent) { + val currentEventAction = event.action + + // A double tap event must follow the order: + // ACTION_DOWN - ACTION_UP - ACTION_DOWN + // all these events happening in an interval defined by a system constant - DOUBLE_TAP_TIMEOUT + + if (MotionEvent.ACTION_DOWN == currentEventAction) { + if (quickScaleEvents.upEvent != null) { + if (event.eventTime - quickScaleEvents.upEvent!!.eventTime > doubleTapTimeout) { + // Too much time passed for the MotionEvents sequence to be considered + // a quick scale gesture. Restart counting. + forgetQuickScaleEvents() + quickScaleEvents.firstDownEvent = MotionEvent.obtain(event) + } else { + quickScaleEvents.secondDownEvent = MotionEvent.obtain(event) + } + } else { + // This may be the first time the user touches the screen or + // the gesture was not finished with ACTION_UP. + forgetQuickScaleEvents() + quickScaleEvents.firstDownEvent = MotionEvent.obtain(event) + } + } + // For the double tap events series we need ACTION_DOWN first + // and then ACTION_UP second. + else if (MotionEvent.ACTION_UP == currentEventAction && quickScaleEvents.firstDownEvent != null) { + quickScaleEvents.upEvent = MotionEvent.obtain(event) + } + } + + override fun requestDisallowInterceptTouchEvent(b: Boolean) { + // We need to disable Pull to Refresh on this layout be we don't want to propagate the + // request to the parent, because they may use the gesture for other purpose, like + // propagating it to ToolbarBehavior + this.disallowInterceptTouchEvent = b + } + + @VisibleForTesting + internal fun forgetQuickScaleEvents() { + quickScaleEvents.firstDownEvent?.recycle() + quickScaleEvents.upEvent?.recycle() + quickScaleEvents.secondDownEvent?.recycle() + quickScaleEvents.firstDownEvent = null + quickScaleEvents.upEvent = null + quickScaleEvents.secondDownEvent = null + + isQuickScaleInProgress = false + } + + @VisibleForTesting + internal fun isQuickScaleInProgress(events: QuickScaleEvents): Boolean { + return if (events.isNotNull()) { + isQuickScaleInProgress(events.firstDownEvent!!, events.upEvent!!, events.secondDownEvent!!) + } else { + false + } + } + + // Method closely following GestureDetectorCompat#isConsideredDoubleTap. + // Allows for serial inference of double taps as opposed to using callbacks. + @VisibleForTesting + internal fun isQuickScaleInProgress( + firstDown: MotionEvent, + firstUp: MotionEvent, + secondDown: MotionEvent, + ): Boolean { + if (secondDown.eventTime - firstUp.eventTime > doubleTapTimeout) { + return false + } + + val deltaX = firstDown.x.toInt() - secondDown.x.toInt() + val deltaY = firstDown.y.toInt() - secondDown.y.toInt() + + return deltaX * deltaX + deltaY * deltaY < doubleTapSlopSquare + } + + @VisibleForTesting + internal fun callSuperOnInterceptTouchEvent(event: MotionEvent) = + super.onInterceptTouchEvent(event) + + @VisibleForTesting + internal fun callSuperOnStartNestedScroll(child: View, target: View, nestedScrollAxes: Int) = + super.onStartNestedScroll(child, target, nestedScrollAxes) + + private fun QuickScaleEvents.isNotNull(): Boolean { + return firstDownEvent != null && upEvent != null && secondDownEvent != null + } + + /** + * Wrapper over the MotionEvents that compose a quickScale gesture. + */ + @VisibleForTesting + internal data class QuickScaleEvents( + var firstDownEvent: MotionEvent? = null, + var upEvent: MotionEvent? = null, + var secondDownEvent: MotionEvent? = null, + ) +} diff --git a/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt new file mode 100644 index 0000000000..0eca198957 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt @@ -0,0 +1,106 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets + +import android.content.Context +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.appcompat.content.res.AppCompatResources.getDrawable +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isVisible + +/** + * Shared UI widget for showing a website in a list of websites, + * such as in bookmarks, history, site exceptions, or collections. + */ +class WidgetSiteItemView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : ConstraintLayout(context, attrs, defStyleAttr) { + + private val labelView: TextView by lazy { findViewById(R.id.label) } + private val captionView: TextView by lazy { findViewById(R.id.caption) } + private val iconWrapper: FrameLayout by lazy { findViewById(R.id.favicon_wrapper) } + private val secondaryButton: ImageButton by lazy { findViewById(R.id.secondary_button) } + + /** + * ImageView that should display favicons. + */ + val iconView: ImageView by lazy { findViewById(R.id.favicon) } + + init { + LayoutInflater.from(context).inflate(R.layout.mozac_widget_site_item, this, true) + } + + /** + * Sets the text displayed inside of the site item view. + * + * @param label Main label text, such as a site title. + * @param caption Sub caption text, such as a URL. If null, the caption is hidden. + */ + fun setText(label: CharSequence, caption: CharSequence?) { + labelView.text = label + captionView.text = caption + captionView.isVisible = caption != null + } + + /** + * Add a view that will overlay the favicon, such as a checkmark. + */ + fun addIconOverlay(overlay: View) { + iconWrapper.addView(overlay) + } + + /** + * Add a secondary button, such as an overflow menu. + * + * @param icon Drawable to display in the button. + * @param contentDescription Accessible description of the button's purpose. + * @param onClickListener Listener called when the button is clicked. + */ + fun setSecondaryButton( + icon: Drawable?, + contentDescription: CharSequence, + onClickListener: (View) -> Unit, + ) { + secondaryButton.isVisible = true + secondaryButton.setImageDrawable(icon) + secondaryButton.contentDescription = contentDescription + secondaryButton.setOnClickListener(onClickListener) + } + + /** + * Add a secondary button, such as an overflow menu. + * + * @param icon Drawable to display in the button. + * @param contentDescription Accessible description of the button's purpose. + * @param onClickListener Listener called when the button is clicked. + */ + fun setSecondaryButton( + @DrawableRes icon: Int, + @StringRes contentDescription: Int, + onClickListener: (View) -> Unit, + ) = setSecondaryButton( + icon = getDrawable(context, icon), + contentDescription = context.getString(contentDescription), + onClickListener = onClickListener, + ) + + /** + * Removes the secondary button if it was previously set in [setSecondaryButton]. + */ + fun removeSecondaryButton() { + secondaryButton.isVisible = false + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/BrowserGestureDetector.kt b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/BrowserGestureDetector.kt new file mode 100644 index 0000000000..b15df65078 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/BrowserGestureDetector.kt @@ -0,0 +1,192 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.content.Context +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.ScaleGestureDetector +import androidx.annotation.VisibleForTesting +import mozilla.components.concept.base.crash.CrashReporting +import kotlin.math.abs + +/** + * Wraps exceptions that are caught by [BrowserGestureDetector]. + * Instances of this class are submitted via [CrashReporting]. This wrapping helps easily identify + * exceptions related to [BrowserGestureDetector]. + */ +internal class BrowserGestureDetectorException(e: Throwable) : Throwable(e) + +/** + * Custom [MotionEvent] gestures detector with scroll / zoom callbacks. + * + * Favors zoom gestures in detriment of the scroll gestures with: + * - higher sensitivity for multi-finger zoom gestures + * - ignoring scrolls if zoom is in progress + * + * @param applicationContext context used for registering internal gesture listeners. + * @param listener client interested in zoom / scroll events. + */ +internal class BrowserGestureDetector( + applicationContext: Context, + listener: GesturesListener, + private val crashReporting: CrashReporting? = null, +) { + @VisibleForTesting + internal var gestureDetector = GestureDetector( + applicationContext, + CustomScrollDetectorListener { previousEvent: MotionEvent?, currentEvent: MotionEvent, distanceX, distanceY -> + run { + listener.onScroll?.invoke(distanceX, distanceY) + + // We got many crashes because of the initial event - ACTION_DOWN being null. + // Investigations to be continued in android-components/issues/8552. + // In the meantime we'll protect against this with a simple null check. + if (previousEvent != null) { + if (abs(currentEvent.y - previousEvent.y) >= abs(currentEvent.x - previousEvent.x)) { + listener.onVerticalScroll?.invoke(distanceY) + } else { + listener.onHorizontalScroll?.invoke(distanceX) + } + } + } + }, + ) + + @VisibleForTesting + internal var scaleGestureDetector = ScaleGestureDetector( + applicationContext, + CustomScaleDetectorListener( + listener.onScaleBegin ?: {}, + listener.onScale ?: {}, + listener.onScaleEnd ?: {}, + ), + ) + + /** + * Accepts MotionEvents and dispatches zoom / scroll events to the registered listener when appropriate. + * + * Applications should pass a complete and consistent event stream to this method. + * A complete and consistent event stream involves all MotionEvents from the initial ACTION_DOWN + * to the final ACTION_UP or ACTION_CANCEL. + * + * @return if the event was handled by any of the registered detectors + */ + @Suppress("ComplexCondition") + internal fun handleTouchEvent(event: MotionEvent): Boolean { + val eventAction = event.actionMasked + + // A double tap for a quick scale gesture (quick double tap followed by a drag) + // would trigger a ACTION_CANCEL event before the MOVE_EVENT. + // This would prevent the scale detector from properly inferring the movement. + // We'll want to ignore ACTION_CANCEL but process the next stream of events. + if (eventAction != MotionEvent.ACTION_CANCEL) { + scaleGestureDetector.onTouchEvent(event) + } + + // Ignore scrolling if zooming is already in progress. + // Always pass motion begin / end events just to have the detector ready + // to infer scrolls when the scale gesture ended. + return if (!scaleGestureDetector.isInProgress || + eventAction == MotionEvent.ACTION_DOWN || + eventAction == MotionEvent.ACTION_UP || + eventAction == MotionEvent.ACTION_CANCEL + ) { + @Suppress("TooGenericExceptionCaught") + try { + gestureDetector.onTouchEvent(event) + } catch (e: Exception) { + crashReporting?.submitCaughtException(BrowserGestureDetectorException(e)) + false + } + } else { + false + } + } + + /** + * A convenience containing listeners for zoom / scroll events + * + * Provide implementation for the events you are interested in. + * The others will be no-op. + */ + internal class GesturesListener( + /** + * Responds to scroll events for a gesture in progress. + * The distance in x and y is also supplied for convenience. + */ + val onScroll: ((distanceX: Float, distanceY: Float) -> Unit)? = { _, _ -> run {} }, + + /** + * Responds to an in progress scroll occuring more on the vertical axis. + * The scroll distance is also supplied for convenience. + */ + val onVerticalScroll: ((distance: Float) -> Unit)? = {}, + + /** + * Responds to an in progress scroll occurring more on the horizontal axis. + * The scroll distance is also supplied for convenience. + */ + val onHorizontalScroll: ((distance: Float) -> Unit)? = {}, + + /** + * Responds to the the beginning of a new scale gesture. + * Reported by new pointers going down. + */ + val onScaleBegin: ((scaleFactor: Float) -> Unit)? = {}, + + /** + * Responds to scaling events for a gesture in progress. + * The scaling factor is also supplied for convenience. + * This value is represents the difference from the previous scale event to the current event. + */ + val onScale: ((scaleFactor: Float) -> Unit)? = {}, + + /** + * Responds to the end of a scale gesture. + * Reported by existing pointers going up. + */ + val onScaleEnd: ((scaleFactor: Float) -> Unit)? = {}, + ) + + private class CustomScrollDetectorListener( + val onScrolling: ( + previousEvent: MotionEvent?, + currentEvent: MotionEvent, + distanceX: Float, + distanceY: Float, + ) -> Unit, + ) : GestureDetector.SimpleOnGestureListener() { + override fun onScroll( + e1: MotionEvent?, + e2: MotionEvent, + distanceX: Float, + distanceY: Float, + ): Boolean { + onScrolling(e1, e2, distanceX, distanceY) + return true + } + } + + private class CustomScaleDetectorListener( + val onScaleBegin: (scaleFactor: Float) -> Unit = {}, + val onScale: (scaleFactor: Float) -> Unit = {}, + val onScaleEnd: (scaleFactor: Float) -> Unit = {}, + ) : ScaleGestureDetector.SimpleOnScaleGestureListener() { + override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { + onScaleBegin(detector.scaleFactor) + return true + } + + override fun onScale(detector: ScaleGestureDetector): Boolean { + onScale(detector.scaleFactor) + return true + } + + override fun onScaleEnd(detector: ScaleGestureDetector) { + onScaleEnd(detector.scaleFactor) + } + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/EngineViewClippingBehavior.kt b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/EngineViewClippingBehavior.kt new file mode 100644 index 0000000000..d0f532bfbc --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/EngineViewClippingBehavior.kt @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import androidx.annotation.VisibleForTesting +import androidx.coordinatorlayout.widget.CoordinatorLayout +import mozilla.components.concept.engine.EngineView +import mozilla.components.concept.toolbar.ScrollableToolbar +import mozilla.components.support.ktx.android.view.findViewInHierarchy +import kotlin.math.roundToInt + +/** + * A [CoordinatorLayout.Behavior] implementation that allows the [EngineView] to automatically + * size itself in relation to the Y translation of the [ScrollableToolbar]. + * + * This is useful for dynamic [ScrollableToolbar]s ensuring the web content is displayed immediately + * below / above the toolbar even when that is animated. + * + * @param context [Context] used for various Android interactions + * @param attrs XML set attributes configuring this + * @param engineViewParent NestedScrollingChild parent of the [EngineView] + * @param toolbarHeight size of [ScrollableToolbar] when it is placed above the [EngineView] + * @param toolbarPosition whether the [ScrollableToolbar] is placed above or below the [EngineView] + */ +class EngineViewClippingBehavior( + context: Context?, + attrs: AttributeSet?, + engineViewParent: View, + toolbarHeight: Int, + toolbarPosition: ToolbarPosition, +) : CoordinatorLayout.Behavior(context, attrs) { + + @VisibleForTesting + internal val engineView = engineViewParent.findViewInHierarchy { it is EngineView } as EngineView? + + @VisibleForTesting + internal var toolbarChangedAction: (Float) -> Unit? + private val bottomToolbarChangedAction = { newToolbarTranslationY: Float -> + if (!newToolbarTranslationY.isNaN()) { + engineView?.setVerticalClipping(-newToolbarTranslationY.roundToInt()) + } + } + private val topToolbarChangedAction = { newToolbarTranslationY: Float -> + // the top toolbar is translated upwards when collapsing-> all values received are 0 or negative + engineView?.let { + it.setVerticalClipping(newToolbarTranslationY.roundToInt()) + // Need to add the toolbarHeight to effectively place the engineView below the toolbar. + engineViewParent.translationY = newToolbarTranslationY + toolbarHeight + } + } + + init { + toolbarChangedAction = if (toolbarPosition == ToolbarPosition.TOP) { + topToolbarChangedAction + } else { + bottomToolbarChangedAction + } + } + + override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean { + if (dependency is ScrollableToolbar) { + return true + } + + return super.layoutDependsOn(parent, child, dependency) + } + + /** + * Apply vertical clipping to [EngineView]. This requires [EngineViewClippingBehavior] to be set + * in/on the [EngineView] or its parent. Must be a direct descending child of [CoordinatorLayout]. + */ + override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean { + toolbarChangedAction.invoke(dependency.translationY) + + return true + } +} + +/** + * Where the toolbar is placed on the screen. + */ +enum class ToolbarPosition { + TOP, + BOTTOM, +} diff --git a/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/EngineViewScrollingBehavior.kt b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/EngineViewScrollingBehavior.kt new file mode 100644 index 0000000000..08da7e5064 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/EngineViewScrollingBehavior.kt @@ -0,0 +1,237 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import androidx.annotation.VisibleForTesting +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.view.ViewCompat +import mozilla.components.concept.base.crash.CrashReporting +import mozilla.components.concept.engine.EngineView +import mozilla.components.support.ktx.android.view.findViewInHierarchy + +/** + * Where the view is placed on the screen. + */ +enum class ViewPosition { + TOP, + BOTTOM, +} + +/** + * A [CoordinatorLayout.Behavior] implementation to be used when placing [View] at the bottom of the screen. + * + * This is safe to use even if the [View] may be added / removed from a parent layout later + * or if it could have Visibility.GONE set. + * + * This implementation will: + * - Show/Hide the [View] automatically when scrolling vertically. + * - Snap the [View] to be hidden or visible when the user stops scrolling. + */ +class EngineViewScrollingBehavior( + val context: Context?, + attrs: AttributeSet?, + private val viewPosition: ViewPosition, + private val crashReporting: CrashReporting? = null, +) : CoordinatorLayout.Behavior(context, attrs) { + // This implementation is heavily based on this blog article: + // https://android.jlelse.eu/scroll-your-bottom-navigation-view-away-with-10-lines-of-code-346f1ed40e9e + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var shouldSnapAfterScroll: Boolean = false + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var startedScroll = false + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var isScrollEnabled = false + + /** + * Reference to [EngineView] used to check user's [android.view.MotionEvent]s. + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var engineView: EngineView? = null + + /** + * Reference to the actual [View] that we'll animate. + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var dynamicScrollView: View? = null + + /** + * Depending on how user's touch was consumed by EngineView / current website, + * + * we will animate the dynamic navigation bar if: + * - touches were used for zooming / panning operations in the website. + * + * We will do nothing if: + * - the website is not scrollable + * - the website handles the touch events itself through it's own touch event listeners. + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal val shouldScroll: Boolean + get() = engineView?.getInputResultDetail()?.let { + (it.canScrollToBottom() || it.canScrollToTop()) && isScrollEnabled + } ?: false + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var gesturesDetector: BrowserGestureDetector = createGestureDetector() + + @VisibleForTesting + internal var yTranslator: ViewYTranslator = createYTranslationStrategy() + + private fun createYTranslationStrategy() = ViewYTranslator(viewPosition) + + override fun onStartNestedScroll( + coordinatorLayout: CoordinatorLayout, + child: View, + directTargetChild: View, + target: View, + axes: Int, + type: Int, + ): Boolean { + return if (dynamicScrollView != null) { + startNestedScroll(axes, type, child) + } else { + return false // not interested in subsequent scroll events + } + } + + override fun onStopNestedScroll( + coordinatorLayout: CoordinatorLayout, + child: View, + target: View, + type: Int, + ) { + if (dynamicScrollView != null) { + stopNestedScroll(type, child) + } + } + + override fun onInterceptTouchEvent( + parent: CoordinatorLayout, + child: View, + ev: MotionEvent, + ): Boolean { + if (dynamicScrollView != null) { + gesturesDetector.handleTouchEvent(ev) + } + return false // allow events to be passed to below listeners + } + + override fun onLayoutChild( + parent: CoordinatorLayout, + child: View, + layoutDirection: Int, + ): Boolean { + dynamicScrollView = child + engineView = parent.findViewInHierarchy { it is EngineView } as? EngineView + + return super.onLayoutChild(parent, child, layoutDirection) + } + + /** + * Used to expand the [View] + */ + fun forceExpand(view: View) { + yTranslator.expandWithAnimation(view) + } + + /** + * Used to collapse the [View] + */ + fun forceCollapse(view: View) { + yTranslator.collapseWithAnimation(view) + } + + /** + * Allow this view to be animated. + * + * @see disableScrolling + */ + fun enableScrolling() { + isScrollEnabled = true + } + + /** + * Disable scrolling of the view irrespective of the intrinsic checks. + * + * @see enableScrolling + */ + fun disableScrolling() { + isScrollEnabled = false + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun tryToScrollVertically(distance: Float) { + dynamicScrollView?.let { view -> + if (shouldScroll && startedScroll) { + yTranslator.translate(view, distance) + } else if (engineView?.getInputResultDetail()?.isTouchHandlingUnknown() == false) { + // Force expand the view if the user scrolled up, it is not already expanded and + // an animation to expand it is not already in progress, + // otherwise the user could get stuck in a state where they cannot show the view + // See https://github.com/mozilla-mobile/android-components/issues/7101 + yTranslator.forceExpandIfNotAlready(view, distance) + } + } + } + + /** + * Helper function to ease testing. + * (Re)Initializes the [BrowserGestureDetector] in a new context. + * + * Useful in spied behaviors, to ensure callbacks are of the spy and not of the initially created object + * if the passed in argument is the result of [createGestureDetector]. + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun initGesturesDetector(detector: BrowserGestureDetector) { + gesturesDetector = detector + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun createGestureDetector() = + BrowserGestureDetector( + context!!, + BrowserGestureDetector.GesturesListener( + onVerticalScroll = ::tryToScrollVertically, + onScaleBegin = { + // Scale shouldn't animate the view but a small y translation is still possible + // because of a previous scroll. Try to be swift about such an in progress animation. + yTranslator.snapImmediately(dynamicScrollView) + }, + ), + crashReporting = crashReporting, + ) + + @VisibleForTesting + internal fun startNestedScroll(axes: Int, type: Int, view: View): Boolean { + return if (shouldScroll && axes == ViewCompat.SCROLL_AXIS_VERTICAL) { + startedScroll = true + shouldSnapAfterScroll = type == ViewCompat.TYPE_TOUCH + yTranslator.cancelInProgressTranslation() + true + } else if (engineView?.getInputResultDetail()?.isTouchUnhandled() == true) { + // Force expand the view if event is unhandled, otherwise user could get stuck in a + // state where they cannot show the view + yTranslator.cancelInProgressTranslation() + yTranslator.expandWithAnimation(view) + false + } else { + false + } + } + + @VisibleForTesting + internal fun stopNestedScroll(type: Int, view: View) { + startedScroll = false + if (shouldSnapAfterScroll || type == ViewCompat.TYPE_NON_TOUCH) { + yTranslator.snapWithAnimation(view) + } + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/ViewYTranslationStrategy.kt b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/ViewYTranslationStrategy.kt new file mode 100644 index 0000000000..8311f2d21a --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/ViewYTranslationStrategy.kt @@ -0,0 +1,189 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.animation.ValueAnimator +import android.view.View +import android.view.animation.DecelerateInterpolator +import androidx.annotation.VisibleForTesting +import kotlin.math.max +import kotlin.math.min + +@VisibleForTesting +internal const val SNAP_ANIMATION_DURATION = 150L + +/** + * Helper class with methods for different behaviors for when translating a [View] on the Y axis. + */ +internal abstract class ViewYTranslationStrategy { + @VisibleForTesting + var animator = ValueAnimator().apply { + interpolator = DecelerateInterpolator() + duration = SNAP_ANIMATION_DURATION + } + + /** + * Snap the [View] to be collapsed or expanded, depending on whatever state is closer + * over a short amount of time. + */ + abstract fun snapWithAnimation(view: View) + + /** + * Snap the [View] to be collapsed or expanded, depending on whatever state is closer immediately. + */ + abstract fun snapImmediately(view: View?) + + /** + * Translate the [View] to it's full visible height. + */ + abstract fun expandWithAnimation(view: View) + + /** + * Force expanding the [View] depending on the [distance] value that should be translated + * cancelling any other translation already in progress. + */ + abstract fun forceExpandWithAnimation(view: View, distance: Float) + + /** + * Translate the [View] to it's full 0 visible height. + */ + abstract fun collapseWithAnimation(view: View) + + /** + * Translate [view] immediately to the specified [distance] amount (positive or negative). + */ + abstract fun translate(view: View, distance: Float) + + /** + * Translate [view] to the indicated [targetTranslationY] vaue over a short amount of time. + */ + open fun animateToTranslationY(view: View, targetTranslationY: Float) = with(animator) { + addUpdateListener { view.translationY = it.animatedValue as Float } + setFloatValues(view.translationY, targetTranslationY) + start() + } + + /** + * Cancel any translation animations currently in progress. + */ + fun cancelInProgressTranslation() = animator.cancel() +} + +/** + * Helper class containing methods for translating a [View] on the Y axis + * between 0 and [View.getHeight] + */ +internal class BottomViewBehaviorStrategy : ViewYTranslationStrategy() { + @VisibleForTesting + internal var wasLastExpanding = false + + override fun snapWithAnimation(view: View) { + if (view.translationY >= (view.height / 2f)) { + collapseWithAnimation(view) + } else { + expandWithAnimation(view) + } + } + + override fun snapImmediately(view: View?) { + if (animator.isStarted) { + animator.end() + } else { + view?.apply { + translationY = if (translationY >= height / 2) { + height.toFloat() + } else { + 0f + } + } + } + } + + override fun expandWithAnimation(view: View) { + animateToTranslationY(view, 0f) + } + + override fun forceExpandWithAnimation(view: View, distance: Float) { + val shouldExpandToolbar = distance < 0 + val isToolbarExpanded = view.translationY == 0f + if (shouldExpandToolbar && !isToolbarExpanded && !wasLastExpanding) { + animator.cancel() + expandWithAnimation(view) + } + } + + override fun collapseWithAnimation(view: View) { + animateToTranslationY(view, view.height.toFloat()) + } + + override fun translate(view: View, distance: Float) { + view.translationY = + max(0f, min(view.height.toFloat(), view.translationY + distance)) + } + + override fun animateToTranslationY(view: View, targetTranslationY: Float) { + wasLastExpanding = targetTranslationY <= view.translationY + super.animateToTranslationY(view, targetTranslationY) + } +} + +/** + * Helper class containing methods for translating a [View] on the Y axis + * between -[View.getHeight] and 0. + */ +internal class TopViewBehaviorStrategy : ViewYTranslationStrategy() { + @VisibleForTesting + internal var wasLastExpanding = false + + override fun snapWithAnimation(view: View) { + if (view.translationY >= -(view.height / 2f)) { + expandWithAnimation(view) + } else { + collapseWithAnimation(view) + } + } + + override fun snapImmediately(view: View?) { + if (animator.isStarted) { + animator.end() + } else { + view?.apply { + translationY = if (translationY >= -height / 2) { + 0f + } else { + -height.toFloat() + } + } + } + } + + override fun expandWithAnimation(view: View) { + animateToTranslationY(view, 0f) + } + + override fun forceExpandWithAnimation(view: View, distance: Float) { + val isExpandingInProgress = animator.isStarted && wasLastExpanding + val shouldExpandToolbar = distance < 0 + val isToolbarExpanded = view.translationY == 0f + if (shouldExpandToolbar && !isToolbarExpanded && !isExpandingInProgress) { + animator.cancel() + expandWithAnimation(view) + } + } + + override fun collapseWithAnimation(view: View) { + animateToTranslationY(view, -view.height.toFloat()) + } + + override fun translate(view: View, distance: Float) { + view.translationY = + min(0f, max(-view.height.toFloat(), view.translationY - distance)) + } + + override fun animateToTranslationY(view: View, targetTranslationY: Float) { + wasLastExpanding = targetTranslationY >= view.translationY + super.animateToTranslationY(view, targetTranslationY) + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/ViewYTranslator.kt b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/ViewYTranslator.kt new file mode 100644 index 0000000000..042b810b40 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/behavior/ViewYTranslator.kt @@ -0,0 +1,81 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.view.View +import androidx.annotation.VisibleForTesting + +/** + * Helper class with methods for translating on the Y axis a top / bottom [View]. + * + * @param viewPosition whether the view is displayed immediately at the top of the screen or + * immediately at the bottom. This affects how it will be translated: + * - if place at the bottom it will be Y translated between 0 and [View.getHeight] + * - if place at the top it will be Y translated between -[View.getHeight] and 0 + */ +class ViewYTranslator(viewPosition: ViewPosition) { + @VisibleForTesting + internal var strategy = getTranslationStrategy(viewPosition) + + /** + * Snap the [View] to be collapsed or expanded, depending on whatever state is closer + * over a short amount of time. + */ + internal fun snapWithAnimation(view: View) { + strategy.snapWithAnimation(view) + } + + /** + * Snap the [View] to be collapsed or expanded, depending on whatever state is closer immediately. + */ + fun snapImmediately(view: View?) { + strategy.snapImmediately(view) + } + + /** + * Translate the [View] to it's full visible height over a short amount of time. + */ + internal fun expandWithAnimation(view: View) { + strategy.expandWithAnimation(view) + } + + /** + * Translate the [View] to be hidden from view over a short amount of time. + */ + internal fun collapseWithAnimation(view: View) { + strategy.collapseWithAnimation(view) + } + + /** + * Force expanding the [View] depending on the [distance] value that should be translated + * cancelling any other translation already in progress. + */ + fun forceExpandIfNotAlready(view: View, distance: Float) { + strategy.forceExpandWithAnimation(view, distance) + } + + /** + * Translate [view] immediately to the specified [distance] amount (positive or negative). + */ + fun translate(view: View, distance: Float) { + strategy.translate(view, distance) + } + + /** + * Cancel any translation animations currently in progress. + */ + fun cancelInProgressTranslation() { + strategy.cancelInProgressTranslation() + } + + @VisibleForTesting + internal fun getTranslationStrategy(viewPosition: ViewPosition): ViewYTranslationStrategy { + return if (viewPosition == ViewPosition.TOP) { + TopViewBehaviorStrategy() + } else { + BottomViewBehaviorStrategy() + } + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/drawable/mozac_widget_favicon_background.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/drawable/mozac_widget_favicon_background.xml new file mode 100644 index 0000000000..93e6f01141 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/drawable/mozac_widget_favicon_background.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/drawable/rounded_button_background.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/drawable/rounded_button_background.xml new file mode 100644 index 0000000000..260a02530d --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/drawable/rounded_button_background.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml new file mode 100644 index 0000000000..3ddfd7a2c3 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-am/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-am/strings.xml new file mode 100644 index 0000000000..cdde258cef --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-am/strings.xml @@ -0,0 +1,5 @@ + + + + ምስል ወደ ቅንጥብ ሰሌዳ ተቀድቷል + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ar/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000000..eb2045ed67 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ar/strings.xml @@ -0,0 +1,5 @@ + + + + نُسخت الصورة إلى الحافظة + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ast/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ast/strings.xml new file mode 100644 index 0000000000..013fbec0b8 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ast/strings.xml @@ -0,0 +1,5 @@ + + + + La imaxe copióse al cartafueyu + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-azb/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-azb/strings.xml new file mode 100644 index 0000000000..706d647459 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-azb/strings.xml @@ -0,0 +1,5 @@ + + + + عکس کلیپ‌بوردا کوپی اولدو + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-be/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-be/strings.xml new file mode 100644 index 0000000000..7096e05e05 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-be/strings.xml @@ -0,0 +1,5 @@ + + + + Відарыс скапіяваны ў буфер абмену + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-bg/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-bg/strings.xml new file mode 100644 index 0000000000..9c2defef43 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-bg/strings.xml @@ -0,0 +1,5 @@ + + + + Изображението е копирано + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-br/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-br/strings.xml new file mode 100644 index 0000000000..3ae6d8340a --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-br/strings.xml @@ -0,0 +1,5 @@ + + + + Skeudenn eilet er golver + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-bs/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-bs/strings.xml new file mode 100644 index 0000000000..b0e64d0437 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-bs/strings.xml @@ -0,0 +1,5 @@ + + + + Slika je kopirana u privremenu memoriju + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ca/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ca/strings.xml new file mode 100644 index 0000000000..75a4596b48 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ca/strings.xml @@ -0,0 +1,5 @@ + + + + S’ha copiat la imatge al porta-retalls + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-cak/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-cak/strings.xml new file mode 100644 index 0000000000..dee19cb2a1 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-cak/strings.xml @@ -0,0 +1,5 @@ + + + + Xwachib\'ëx ri wachib\'äl pa molwuj + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-co/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-co/strings.xml new file mode 100644 index 0000000000..dc5ae642c1 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-co/strings.xml @@ -0,0 +1,5 @@ + + + + Fiura cupiata in u preme’papei + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-cs/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-cs/strings.xml new file mode 100644 index 0000000000..039f1848dc --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-cs/strings.xml @@ -0,0 +1,5 @@ + + + + Obrázek zkopírován do schránky + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-cy/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-cy/strings.xml new file mode 100644 index 0000000000..274e4c8a92 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-cy/strings.xml @@ -0,0 +1,5 @@ + + + + Copïwyd delwedd i’r clipfwrdd + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-da/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-da/strings.xml new file mode 100644 index 0000000000..1e8162dcf1 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-da/strings.xml @@ -0,0 +1,5 @@ + + + + Billede kopieret til udklipsholder + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-de/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-de/strings.xml new file mode 100644 index 0000000000..a6f23ccb4a --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-de/strings.xml @@ -0,0 +1,5 @@ + + + + Grafik in Zwischenablage kopiert + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-dsb/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-dsb/strings.xml new file mode 100644 index 0000000000..5119090d13 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-dsb/strings.xml @@ -0,0 +1,5 @@ + + + + Wobraz jo se kopěrował do mjazywótkłada + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-el/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-el/strings.xml new file mode 100644 index 0000000000..57da3ec96d --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-el/strings.xml @@ -0,0 +1,5 @@ + + + + Η εικόνα αντιγράφτηκε στο πρόχειρο + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-en-rCA/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-en-rCA/strings.xml new file mode 100644 index 0000000000..638ee8543e --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-en-rCA/strings.xml @@ -0,0 +1,5 @@ + + + + Image copied to clipboard + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-en-rGB/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000..638ee8543e --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,5 @@ + + + + Image copied to clipboard + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-eo/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-eo/strings.xml new file mode 100644 index 0000000000..9cc052f510 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-eo/strings.xml @@ -0,0 +1,5 @@ + + + + Bildo kopiita al la tondujo + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rAR/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rAR/strings.xml new file mode 100644 index 0000000000..06f644e903 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rAR/strings.xml @@ -0,0 +1,5 @@ + + + + Imagen copiada al portapapeles + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rCL/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rCL/strings.xml new file mode 100644 index 0000000000..06f644e903 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rCL/strings.xml @@ -0,0 +1,5 @@ + + + + Imagen copiada al portapapeles + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rES/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rES/strings.xml new file mode 100644 index 0000000000..06f644e903 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rES/strings.xml @@ -0,0 +1,5 @@ + + + + Imagen copiada al portapapeles + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rMX/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rMX/strings.xml new file mode 100644 index 0000000000..06f644e903 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es-rMX/strings.xml @@ -0,0 +1,5 @@ + + + + Imagen copiada al portapapeles + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-es/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es/strings.xml new file mode 100644 index 0000000000..06f644e903 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-es/strings.xml @@ -0,0 +1,5 @@ + + + + Imagen copiada al portapapeles + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-et/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-et/strings.xml new file mode 100644 index 0000000000..b77f8fe2fc --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-et/strings.xml @@ -0,0 +1,5 @@ + + + + Pilt kopeeriti vahemällu + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-eu/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-eu/strings.xml new file mode 100644 index 0000000000..18039c3b03 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-eu/strings.xml @@ -0,0 +1,5 @@ + + + + Irudia arbelean kopiatu da + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-fa/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fa/strings.xml new file mode 100644 index 0000000000..4d9c243f3c --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fa/strings.xml @@ -0,0 +1,5 @@ + + + + تصویر به تخته‌گیره رونوشت شد + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-fi/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000000..426de77fe1 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fi/strings.xml @@ -0,0 +1,5 @@ + + + + Kuva kopioitu leikepöydälle + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-fr/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000000..c41cec7a23 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fr/strings.xml @@ -0,0 +1,5 @@ + + + + Image copiée dans le presse-papiers + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-fur/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fur/strings.xml new file mode 100644 index 0000000000..5fb71c5b51 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fur/strings.xml @@ -0,0 +1,5 @@ + + + + Imagjin copiade intes notis + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-fy-rNL/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fy-rNL/strings.xml new file mode 100644 index 0000000000..db7c6fa54d --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-fy-rNL/strings.xml @@ -0,0 +1,5 @@ + + + + Ofbylding nei klamboerd kopiearre + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-gd/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-gd/strings.xml new file mode 100644 index 0000000000..a2c04bd312 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-gd/strings.xml @@ -0,0 +1,5 @@ + + + + Chaidh lethbhreac dhen dealbh a chur air an stòr-bhòrd + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-gl/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-gl/strings.xml new file mode 100644 index 0000000000..3e7252778d --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-gl/strings.xml @@ -0,0 +1,5 @@ + + + + Copiouse a imaxe ao portapapeis + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-gn/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-gn/strings.xml new file mode 100644 index 0000000000..af5e3ba7cb --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-gn/strings.xml @@ -0,0 +1,5 @@ + + + + Embohasa ta’ãnga kuatiajokohápe + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-hr/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-hr/strings.xml new file mode 100644 index 0000000000..dbfcb2d809 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-hr/strings.xml @@ -0,0 +1,5 @@ + + + + Slika je kopirana u međuspremnik + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-hsb/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-hsb/strings.xml new file mode 100644 index 0000000000..473527a6cd --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-hsb/strings.xml @@ -0,0 +1,5 @@ + + + + Wobraz je so do mjezyskłada kopěrował + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-hu/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-hu/strings.xml new file mode 100644 index 0000000000..0ca081817d --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-hu/strings.xml @@ -0,0 +1,5 @@ + + + + Kép vágólapra másolva + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-hy-rAM/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-hy-rAM/strings.xml new file mode 100644 index 0000000000..4567efaed4 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-hy-rAM/strings.xml @@ -0,0 +1,5 @@ + + + + Պատկերը պատճենվել է սեղմատախտակին + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ia/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ia/strings.xml new file mode 100644 index 0000000000..6b8f02500b --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ia/strings.xml @@ -0,0 +1,5 @@ + + + + Imagine copiate al area de transferentia + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-in/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-in/strings.xml new file mode 100644 index 0000000000..4b6807e51e --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-in/strings.xml @@ -0,0 +1,5 @@ + + + + Gambar disalin ke papan klip + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-is/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-is/strings.xml new file mode 100644 index 0000000000..c2e2bb5714 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-is/strings.xml @@ -0,0 +1,5 @@ + + + + Mynd afrituð á klippispjald + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-it/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-it/strings.xml new file mode 100644 index 0000000000..79c58ec52e --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-it/strings.xml @@ -0,0 +1,5 @@ + + + + Immagine copiata negli appunti + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-iw/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-iw/strings.xml new file mode 100644 index 0000000000..85bf7a942a --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-iw/strings.xml @@ -0,0 +1,5 @@ + + + + התמונה הועתקה ללוח + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ja/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000000..805eacfc87 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ja/strings.xml @@ -0,0 +1,5 @@ + + + + 画像をクリップボードにコピーしました + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ka/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ka/strings.xml new file mode 100644 index 0000000000..8de04f6263 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ka/strings.xml @@ -0,0 +1,5 @@ + + + + სურათის ასლი აღებულია + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-kaa/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-kaa/strings.xml new file mode 100644 index 0000000000..cb2c90ba5b --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-kaa/strings.xml @@ -0,0 +1,5 @@ + + + + Súwret almasıw buferine kóshirip alındı + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-kab/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-kab/strings.xml new file mode 100644 index 0000000000..093f1d2616 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-kab/strings.xml @@ -0,0 +1,5 @@ + + + + Tugna tettwanɣel ɣef wafus + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-kk/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-kk/strings.xml new file mode 100644 index 0000000000..80b99ef483 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-kk/strings.xml @@ -0,0 +1,5 @@ + + + + Сурет алмасу буферіне көшірілді + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-kmr/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-kmr/strings.xml new file mode 100644 index 0000000000..b299c8f6b7 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-kmr/strings.xml @@ -0,0 +1,5 @@ + + + + Wêne li panoyê hate kopîkirin + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ko/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..5d6ebf83e7 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ko/strings.xml @@ -0,0 +1,5 @@ + + + + 클립보드에 이미지 복사됨 + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-lo/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-lo/strings.xml new file mode 100644 index 0000000000..ab066dba6f --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-lo/strings.xml @@ -0,0 +1,5 @@ + + + + ສຳເນົາຮູບໃສ່ຄລິບບອດແລ້ວ + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-nb-rNO/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-nb-rNO/strings.xml new file mode 100644 index 0000000000..bef36125f1 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-nb-rNO/strings.xml @@ -0,0 +1,5 @@ + + + + Bilde kopiert til utklippstavlen + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-nl/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000000..06070807bb --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-nl/strings.xml @@ -0,0 +1,5 @@ + + + + Afbeelding naar klembord gekopieerd + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-nn-rNO/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-nn-rNO/strings.xml new file mode 100644 index 0000000000..16c41f8d38 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-nn-rNO/strings.xml @@ -0,0 +1,5 @@ + + + + Bilde kopiert til utklippstavla + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-oc/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-oc/strings.xml new file mode 100644 index 0000000000..131c779121 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-oc/strings.xml @@ -0,0 +1,5 @@ + + + + Imatge copiat al quichapapièrs + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-pa-rIN/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pa-rIN/strings.xml new file mode 100644 index 0000000000..03300db934 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pa-rIN/strings.xml @@ -0,0 +1,5 @@ + + + + ਚਿੱਤਰ ਨੂੰ ਕਲਿੱਪਬੋਰਡ ਲਈ ਕਾਪੀ ਕੀਤਾ + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-pa-rPK/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pa-rPK/strings.xml new file mode 100644 index 0000000000..cb4e8c879c --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pa-rPK/strings.xml @@ -0,0 +1,5 @@ + + + + کاپی کیتی گئی + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-pl/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000000..8a79fb5c5e --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pl/strings.xml @@ -0,0 +1,5 @@ + + + + Skopiowano obraz do schowka + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-pt-rBR/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..223e60bc6a --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,5 @@ + + + + Imagem copiada para área de transferência + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-pt-rPT/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..48dc017c65 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,5 @@ + + + + Imagem copiada para a área de transferência + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-rm/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-rm/strings.xml new file mode 100644 index 0000000000..8b428f02b1 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-rm/strings.xml @@ -0,0 +1,5 @@ + + + + Copià il maletg en l\'archiv provisoric + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ro/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ro/strings.xml new file mode 100644 index 0000000000..be7fc12e65 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ro/strings.xml @@ -0,0 +1,5 @@ + + + + Imaginea a fost copiată în clipboard + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ru/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000000..35ac3be80a --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ru/strings.xml @@ -0,0 +1,5 @@ + + + + Изображение скопировано в буфер обмена + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-sat/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sat/strings.xml new file mode 100644 index 0000000000..d945ea02c5 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sat/strings.xml @@ -0,0 +1,5 @@ + + + + ᱨᱮᱴᱚᱯᱵᱚᱰ ᱨᱮ ᱪᱤᱛᱟᱹᱨ ᱱᱚᱠᱚᱞ ᱮᱱᱟ + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-sc/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sc/strings.xml new file mode 100644 index 0000000000..6d122f7e8b --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sc/strings.xml @@ -0,0 +1,5 @@ + + + + Immàgine copiada in punta de billete + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-si/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-si/strings.xml new file mode 100644 index 0000000000..95d46d5ddd --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-si/strings.xml @@ -0,0 +1,5 @@ + + + + රූපය පසුරුපුවරුවට පිටපත් විය + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-sk/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sk/strings.xml new file mode 100644 index 0000000000..02dc6628cb --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sk/strings.xml @@ -0,0 +1,5 @@ + + + + Obrázok bol skopírovaný do schránky + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-skr/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-skr/strings.xml new file mode 100644 index 0000000000..da370ffcb7 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-skr/strings.xml @@ -0,0 +1,5 @@ + + + + تصویر کلپ بورڈ تے نقل تھی ڳئی + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-sl/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sl/strings.xml new file mode 100644 index 0000000000..b3a3418289 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sl/strings.xml @@ -0,0 +1,5 @@ + + + + Slika kopirana v odložišče + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-sq/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sq/strings.xml new file mode 100644 index 0000000000..b76b67a352 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sq/strings.xml @@ -0,0 +1,5 @@ + + + + Figura u kopjua në të papastër + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-sr/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sr/strings.xml new file mode 100644 index 0000000000..f0fbe5c485 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sr/strings.xml @@ -0,0 +1,5 @@ + + + + Слика је копирана у привремену меморију + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-su/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-su/strings.xml new file mode 100644 index 0000000000..91a37e77b6 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-su/strings.xml @@ -0,0 +1,5 @@ + + + + Gambar ditiron kana papan klip + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-sv-rSE/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sv-rSE/strings.xml new file mode 100644 index 0000000000..51210de628 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-sv-rSE/strings.xml @@ -0,0 +1,5 @@ + + + + Bilden har kopierats till urklipp + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-tg/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-tg/strings.xml new file mode 100644 index 0000000000..b304da28c1 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-tg/strings.xml @@ -0,0 +1,5 @@ + + + + Тасвир ба ҳофизаи муваққатӣ нусха бардошта шуд + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-th/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-th/strings.xml new file mode 100644 index 0000000000..52a77b64dc --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-th/strings.xml @@ -0,0 +1,5 @@ + + + + คัดลอกภาพไปยังคลิปบอร์ดแล้ว + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-tr/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000000..b0ef721f12 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-tr/strings.xml @@ -0,0 +1,5 @@ + + + + Resim panoya kopyalandı + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-trs/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-trs/strings.xml new file mode 100644 index 0000000000..d905bed3df --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-trs/strings.xml @@ -0,0 +1,5 @@ + + + + Ñadū’hua ngà nanun riña portapapel + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-tt/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-tt/strings.xml new file mode 100644 index 0000000000..ad4402d83c --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-tt/strings.xml @@ -0,0 +1,5 @@ + + + + Рәсем алмашу буферына копияләнде + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-ug/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ug/strings.xml new file mode 100644 index 0000000000..2719590251 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-ug/strings.xml @@ -0,0 +1,5 @@ + + + + سۈرەت چاپلاش تاختىسىغا كۆچۈرۈلدى + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-uk/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-uk/strings.xml new file mode 100644 index 0000000000..49cffe6ba2 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-uk/strings.xml @@ -0,0 +1,5 @@ + + + + Зображення скопійовано в буфер обміну + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-vi/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-vi/strings.xml new file mode 100644 index 0000000000..8127c7855b --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-vi/strings.xml @@ -0,0 +1,5 @@ + + + + Đã sao chép ảnh vào khay nhớ tạm + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-zh-rCN/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..98d08c5c6a --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,5 @@ + + + + 图像已复制到剪贴板 + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values-zh-rTW/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..39326e4ac0 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,5 @@ + + + + 已將圖片複製至剪貼簿 + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values/attrs.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values/attrs.xml new file mode 100644 index 0000000000..60a3b3a35f --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values/attrs.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values/colors.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values/colors.xml new file mode 100644 index 0000000000..105264e605 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + + + #E0E0E6 + #C50042 + #312A65 + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values/dimens.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..9f6d4587fd --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values/dimens.xml @@ -0,0 +1,14 @@ + + + + 40dp + 8dp + + 56dp + 16sp + 12sp + 32dp + 4dp + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values/strings.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values/strings.xml new file mode 100644 index 0000000000..6a82d7eaef --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + + Image copied to clipboard + diff --git a/mobile/android/android-components/components/ui/widgets/src/main/res/values/styles.xml b/mobile/android/android-components/components/ui/widgets/src/main/res/values/styles.xml new file mode 100644 index 0000000000..515b98e12e --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/main/res/values/styles.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/TestUtils.kt b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/TestUtils.kt new file mode 100644 index 0000000000..cda854782e --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/TestUtils.kt @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets + +import android.view.MotionEvent + +object TestUtils { + fun getMotionEvent( + action: Int, + x: Float = 0f, + y: Float = 0f, + eventTime: Long = System.currentTimeMillis(), + previousEvent: MotionEvent? = null, + ): MotionEvent { + val downTime = previousEvent?.downTime ?: System.currentTimeMillis() + + var pointerCount = previousEvent?.pointerCount ?: 0 + if (action == MotionEvent.ACTION_POINTER_DOWN) { + pointerCount++ + } else if (action == MotionEvent.ACTION_DOWN) { + pointerCount = 1 + } else if (previousEvent?.action == MotionEvent.ACTION_POINTER_UP) { + pointerCount-- + } + + val properties = Array( + pointerCount, + TestUtils::getPointerProperties, + ) + val pointerCoords = + getPointerCoords( + x, + y, + pointerCount, + previousEvent, + ) + + return MotionEvent.obtain( + downTime, eventTime, + action, pointerCount, properties, + pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0, + ) + } + + private fun getPointerCoords( + x: Float, + y: Float, + pointerCount: Int, + previousEvent: MotionEvent? = null, + ): Array { + val currentEventCoords = MotionEvent.PointerCoords().apply { + this.x = x; this.y = y; pressure = 1f; size = 1f + } + + return if (pointerCount > 1 && previousEvent != null) { + arrayOf( + MotionEvent.PointerCoords().apply { + this.x = previousEvent.x; this.y = previousEvent.y; pressure = 1f; size = 1f + }, + currentEventCoords, + ) + } else { + arrayOf(currentEventCoords) + } + } + + private fun getPointerProperties(id: Int): MotionEvent.PointerProperties = + MotionEvent.PointerProperties().apply { + this.id = id; this.toolType = MotionEvent.TOOL_TYPE_FINGER + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/VerticalSwipeRefreshLayoutTest.kt b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/VerticalSwipeRefreshLayoutTest.kt new file mode 100644 index 0000000000..b06e67be3f --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/VerticalSwipeRefreshLayoutTest.kt @@ -0,0 +1,430 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets + +import android.view.MotionEvent +import android.view.MotionEvent.ACTION_CANCEL +import android.view.MotionEvent.ACTION_DOWN +import android.view.MotionEvent.ACTION_MOVE +import android.view.MotionEvent.ACTION_POINTER_DOWN +import android.view.MotionEvent.ACTION_POINTER_UP +import android.view.MotionEvent.ACTION_UP +import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.ui.widgets.VerticalSwipeRefreshLayout.QuickScaleEvents +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class VerticalSwipeRefreshLayoutTest { + private lateinit var swipeLayout: VerticalSwipeRefreshLayout + + @Before + fun setup() { + swipeLayout = VerticalSwipeRefreshLayout(testContext) + } + + @Test + fun `onInterceptTouchEvent should abort pull to refresh and return false if the View is disabled`() { + swipeLayout = spy(swipeLayout) + val secondFingerEvent = TestUtils.getMotionEvent(ACTION_POINTER_DOWN) + + swipeLayout.isEnabled = false + assertFalse(swipeLayout.onInterceptTouchEvent(secondFingerEvent)) + verify(swipeLayout, times(0)).callSuperOnInterceptTouchEvent(secondFingerEvent) + } + + @Test + fun `onInterceptTouchEvent should abort pull to refresh and return false if the motion was multitouch`() { + swipeLayout.isEnabled = true + swipeLayout.setOnChildScrollUpCallback { _, _ -> false } + + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, x = 0f, y = 5f, previousEvent = downEvent) + val secondFingerEvent = TestUtils.getMotionEvent(ACTION_POINTER_DOWN, previousEvent = moveEvent) + val secondFingerNextEvent = + TestUtils.getMotionEvent(ACTION_MOVE, x = 0f, y = 5f, previousEvent = secondFingerEvent) + val secondFingerUp = TestUtils.getMotionEvent(ACTION_POINTER_UP, previousEvent = secondFingerNextEvent) + val upEvent = TestUtils.getMotionEvent(ACTION_UP, previousEvent = secondFingerUp) + val newDownEvent = TestUtils.getMotionEvent(ACTION_DOWN) + + swipeLayout.onInterceptTouchEvent(downEvent) + assertFalse(swipeLayout.onInterceptTouchEvent(moveEvent)) + assertFalse(swipeLayout.hadMultiTouch) + assertFalse(swipeLayout.onInterceptTouchEvent(secondFingerEvent)) + assertTrue(swipeLayout.hadMultiTouch) + assertFalse(swipeLayout.onInterceptTouchEvent(secondFingerNextEvent)) + assertTrue(swipeLayout.hadMultiTouch) + assertFalse(swipeLayout.onInterceptTouchEvent(secondFingerUp)) + assertTrue(swipeLayout.hadMultiTouch) + assertFalse(swipeLayout.onInterceptTouchEvent(upEvent)) + assertTrue(swipeLayout.hadMultiTouch) + assertFalse(swipeLayout.onInterceptTouchEvent(newDownEvent)) + assertFalse(swipeLayout.hadMultiTouch) + } + + @Test + fun `onInterceptTouchEvent should abort pull to refresh and return false if zoom is in progress`() { + swipeLayout = spy(swipeLayout) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN, 0f, 0f) + val pointerDownEvent = + TestUtils.getMotionEvent(ACTION_POINTER_DOWN, 200f, 200f, previousEvent = downEvent) + swipeLayout.isEnabled = true + swipeLayout.setOnChildScrollUpCallback { _, _ -> true } + + swipeLayout.onInterceptTouchEvent(downEvent) + verify(swipeLayout, times(1)).callSuperOnInterceptTouchEvent(downEvent) + + swipeLayout.onInterceptTouchEvent(pointerDownEvent) + assertFalse(swipeLayout.onInterceptTouchEvent(pointerDownEvent)) + verify(swipeLayout, times(0)).callSuperOnInterceptTouchEvent(pointerDownEvent) + } + + @Test + fun `onInterceptTouchEvent should cleanup if ACTION_CANCEL`() { + swipeLayout = spy(swipeLayout) + val cancelEvent = TestUtils.getMotionEvent( + ACTION_CANCEL, + previousEvent = TestUtils.getMotionEvent(ACTION_DOWN), + ) + swipeLayout.isEnabled = true + swipeLayout.setOnChildScrollUpCallback { _, _ -> true } + + swipeLayout.onInterceptTouchEvent(cancelEvent) + + verify(swipeLayout).forgetQuickScaleEvents() + verify(swipeLayout).callSuperOnInterceptTouchEvent(cancelEvent) + } + + @Test + fun `onInterceptTouchEvent should cleanup if quick scale ended`() { + swipeLayout = spy(swipeLayout) + val upEvent = TestUtils.getMotionEvent( + ACTION_CANCEL, + previousEvent = TestUtils.getMotionEvent(ACTION_DOWN), + ) + swipeLayout.isEnabled = true + swipeLayout.isQuickScaleInProgress = true + swipeLayout.setOnChildScrollUpCallback { _, _ -> true } + + swipeLayout.onInterceptTouchEvent(upEvent) + + verify(swipeLayout).forgetQuickScaleEvents() + verify(swipeLayout).callSuperOnInterceptTouchEvent(upEvent) + } + + @Test + fun `onInterceptTouchEvent should disable pull to refresh if quick scale is in progress`() { + // default DOUBLE_TAP_TIMEOUT is 300ms + + swipeLayout = spy(swipeLayout) + val firstDownEvent = TestUtils.getMotionEvent(ACTION_DOWN, eventTime = 100) + val upEvent = + TestUtils.getMotionEvent(ACTION_UP, eventTime = 200, previousEvent = firstDownEvent) + val newDownEvent = + TestUtils.getMotionEvent(ACTION_DOWN, eventTime = 500, previousEvent = upEvent) + val previousEvents = QuickScaleEvents(firstDownEvent, upEvent, null) + swipeLayout.quickScaleEvents = previousEvents + swipeLayout.isQuickScaleInProgress = false + + assertFalse(swipeLayout.onInterceptTouchEvent(newDownEvent)) + assertTrue(swipeLayout.isQuickScaleInProgress) + verify(swipeLayout).maybeAddDoubleTapEvent(newDownEvent) + verify(swipeLayout, times(0)).callSuperOnInterceptTouchEvent(newDownEvent) + } + + @Test + fun `onInterceptTouchEvent should disable pull to refresh if move was more on the x axys`() { + // default DOUBLE_TAP_TIMEOUT is 300ms + + swipeLayout = spy(swipeLayout) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN, x = 0f, y = 0f, eventTime = 0) + val moveEvent = TestUtils.getMotionEvent( + ACTION_MOVE, + x = 1f, + y = 0f, + eventTime = 100, + previousEvent = downEvent, + ) + swipeLayout.isEnabled = true + swipeLayout.isQuickScaleInProgress = false + swipeLayout.setOnChildScrollUpCallback { _, _ -> false } + + swipeLayout.onInterceptTouchEvent(downEvent) + verify(swipeLayout).callSuperOnInterceptTouchEvent(downEvent) + + assertFalse(swipeLayout.onInterceptTouchEvent(moveEvent)) + verify(swipeLayout, times(0)).callSuperOnInterceptTouchEvent(moveEvent) + } + + @Test + fun `onInterceptTouchEvent should allow pull to refresh if move was more on the y axys`() { + // default DOUBLE_TAP_TIMEOUT is 300ms + + swipeLayout = spy(swipeLayout) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN, x = 0f, y = 0f, eventTime = 0) + val moveEvent = TestUtils.getMotionEvent( + ACTION_MOVE, + x = 0f, + y = 1f, + eventTime = 100, + previousEvent = downEvent, + ) + swipeLayout.isEnabled = true + swipeLayout.isQuickScaleInProgress = false + swipeLayout.setOnChildScrollUpCallback { _, _ -> false } + + swipeLayout.onInterceptTouchEvent(downEvent) + verify(swipeLayout).callSuperOnInterceptTouchEvent(downEvent) + + swipeLayout.onInterceptTouchEvent(moveEvent) + verify(swipeLayout).callSuperOnInterceptTouchEvent(moveEvent) + } + + @Test + fun `Should not respond descendants initiated scrolls if this View is enabled`() { + swipeLayout = spy(swipeLayout) + val childView: View = mock() + val targetView: View = mock() + val scrollAxis = 0 + + swipeLayout.isEnabled = true + + assertFalse(swipeLayout.onStartNestedScroll(childView, targetView, scrollAxis)) + verify(swipeLayout, times(0)).callSuperOnStartNestedScroll( + childView, + targetView, + scrollAxis, + ) + } + + @Test + fun `Should delegate super#onStartNestedScroll if this View is not enabled`() { + swipeLayout = spy(swipeLayout) + val childView: View = mock() + val targetView: View = mock() + val scrollAxis = 0 + + swipeLayout.isEnabled = false + swipeLayout.onStartNestedScroll(childView, targetView, scrollAxis) + + verify(swipeLayout).callSuperOnStartNestedScroll(childView, targetView, scrollAxis) + } + + @Test + fun `maybeAddDoubleTapEvent should not modify quickScaleEvents if not for ACTION_DOWN or ACTION_UP`() { + val emptyListOfEvents = QuickScaleEvents() + swipeLayout.quickScaleEvents = emptyListOfEvents + + swipeLayout.maybeAddDoubleTapEvent(TestUtils.getMotionEvent(ACTION_POINTER_DOWN)) + + assertEquals(emptyListOfEvents, swipeLayout.quickScaleEvents) + } + + @Test + fun `maybeAddDoubleTapEvent will add ACTION_UP as second event if there is already one event in sequence`() { + val firstEvent = spy(TestUtils.getMotionEvent(ACTION_DOWN)) + val secondEvent = + spy(TestUtils.getMotionEvent(ACTION_UP, eventTime = 133, previousEvent = firstEvent)) + val expectedResult = Triple( + firstEvent, + secondEvent, + null, + ) + swipeLayout.quickScaleEvents = QuickScaleEvents(firstEvent, null, null) + + swipeLayout.maybeAddDoubleTapEvent(secondEvent) + + // A Triple assert or MotionEvent assert fails probably because of the copies made + // Verifying the expected actions and eventTime should be good enough. + assertEquals(expectedResult.first, swipeLayout.quickScaleEvents.firstDownEvent) + assertEquals( + expectedResult.second!!.actionMasked, + swipeLayout.quickScaleEvents.upEvent!!.actionMasked, + ) + assertEquals( + expectedResult.second!!.eventTime, + swipeLayout.quickScaleEvents.upEvent!!.eventTime, + ) + assertEquals(null, swipeLayout.quickScaleEvents.secondDownEvent) + } + + @Test + fun `maybeAddDoubleTapEvent will not add ACTION_UP if there is not a first event already in sequence`() { + val firstEvent = spy(TestUtils.getMotionEvent(ACTION_DOWN)) + val secondEvent = + spy(TestUtils.getMotionEvent(ACTION_UP, eventTime = 133, previousEvent = firstEvent)) + val expectedResult = QuickScaleEvents() + swipeLayout.quickScaleEvents = expectedResult + + swipeLayout.maybeAddDoubleTapEvent(secondEvent) + + assertEquals(null, swipeLayout.quickScaleEvents.firstDownEvent) + assertEquals(null, swipeLayout.quickScaleEvents.upEvent) + assertEquals(null, swipeLayout.quickScaleEvents.secondDownEvent) + } + + @Test + fun `maybeAddDoubleTapEvent will add the first ACTION_DOWN if the events list is otherwise empty`() { + swipeLayout = spy(swipeLayout) + val emptyListOfEvents = QuickScaleEvents() + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN, eventTime = 133) + swipeLayout.quickScaleEvents = emptyListOfEvents + + swipeLayout.maybeAddDoubleTapEvent(downEvent) + + verify(swipeLayout).forgetQuickScaleEvents() + assertEquals(downEvent.actionMasked, swipeLayout.quickScaleEvents.firstDownEvent!!.actionMasked) + assertEquals(downEvent.eventTime, swipeLayout.quickScaleEvents.firstDownEvent!!.eventTime) + assertEquals(null, swipeLayout.quickScaleEvents.upEvent) + assertEquals(null, swipeLayout.quickScaleEvents.secondDownEvent) + } + + @Test + fun `maybeAddDoubleTapEvent will reset the first ACTION_DOWN if the events list does not contain other events`() { + swipeLayout = spy(swipeLayout) + val previousDownEvent = TestUtils.getMotionEvent(ACTION_DOWN, eventTime = 111) + val previousEvents = QuickScaleEvents(previousDownEvent, null, null) + val newDownEvent = TestUtils.getMotionEvent(ACTION_DOWN, eventTime = 222) + swipeLayout.quickScaleEvents = previousEvents + + swipeLayout.maybeAddDoubleTapEvent(newDownEvent) + + verify(swipeLayout).forgetQuickScaleEvents() + assertEquals(newDownEvent.actionMasked, swipeLayout.quickScaleEvents.firstDownEvent!!.actionMasked) + assertEquals(newDownEvent.eventTime, swipeLayout.quickScaleEvents.firstDownEvent!!.eventTime) + assertEquals(null, swipeLayout.quickScaleEvents.upEvent) + assertEquals(null, swipeLayout.quickScaleEvents.secondDownEvent) + } + + @Test + fun `maybeAddDoubleTapEvent will reset ACTION_DOWN if timeout was reached`() { + // default DOUBLE_TAP_TIMEOUT is 300ms + + swipeLayout = spy(swipeLayout) + val firstDownEvent = TestUtils.getMotionEvent(ACTION_DOWN, eventTime = 100) + val upEvent = + TestUtils.getMotionEvent(ACTION_UP, eventTime = 200, previousEvent = firstDownEvent) + val newDownEvent = + TestUtils.getMotionEvent(ACTION_DOWN, eventTime = 501, previousEvent = upEvent) + val previousEvents = QuickScaleEvents(firstDownEvent, upEvent, null) + swipeLayout.quickScaleEvents = previousEvents + + swipeLayout.maybeAddDoubleTapEvent(newDownEvent) + + verify(swipeLayout).forgetQuickScaleEvents() + assertEquals(newDownEvent.actionMasked, swipeLayout.quickScaleEvents.firstDownEvent!!.actionMasked) + assertEquals(newDownEvent.eventTime, swipeLayout.quickScaleEvents.firstDownEvent!!.eventTime) + assertEquals(null, swipeLayout.quickScaleEvents.upEvent) + assertEquals(null, swipeLayout.quickScaleEvents.secondDownEvent) + } + + @Test + fun `maybeAddDoubleTapEvent will add a second ACTION_DOWN already have two events and timeout is not reached`() { + // default DOUBLE_TAP_TIMEOUT is 300ms + + swipeLayout = spy(swipeLayout) + val firstDownEvent = TestUtils.getMotionEvent(ACTION_DOWN, eventTime = 100) + val upEvent = + TestUtils.getMotionEvent(ACTION_UP, eventTime = 200, previousEvent = firstDownEvent) + val newDownEvent = + TestUtils.getMotionEvent(ACTION_DOWN, eventTime = 500, previousEvent = upEvent) + val previousEvents = QuickScaleEvents(firstDownEvent, upEvent, null) + swipeLayout.quickScaleEvents = previousEvents + + swipeLayout.maybeAddDoubleTapEvent(newDownEvent) + + verify(swipeLayout, times(0)).forgetQuickScaleEvents() + assertEquals(firstDownEvent.actionMasked, swipeLayout.quickScaleEvents.firstDownEvent!!.actionMasked) + assertEquals(firstDownEvent.eventTime, swipeLayout.quickScaleEvents.firstDownEvent!!.eventTime) + assertEquals(upEvent.actionMasked, swipeLayout.quickScaleEvents.upEvent!!.actionMasked) + assertEquals(upEvent.eventTime, swipeLayout.quickScaleEvents.upEvent!!.eventTime) + assertEquals(newDownEvent.actionMasked, swipeLayout.quickScaleEvents.secondDownEvent!!.actionMasked) + assertEquals(newDownEvent.eventTime, swipeLayout.quickScaleEvents.secondDownEvent!!.eventTime) + } + + @Test + fun `forgetQuickScaleEvents should recycle all events and reset the quickScaleStatus`() { + val firstEvent = spy(TestUtils.getMotionEvent(ACTION_DOWN)) + val secondEvent = spy(TestUtils.getMotionEvent(ACTION_UP, previousEvent = firstEvent)) + val thirdEvent = spy(TestUtils.getMotionEvent(ACTION_DOWN)) + swipeLayout.quickScaleEvents = QuickScaleEvents(firstEvent, secondEvent, thirdEvent) + swipeLayout.isQuickScaleInProgress = true + + swipeLayout.forgetQuickScaleEvents() + + verify(firstEvent).recycle() + verify(secondEvent).recycle() + verify(thirdEvent).recycle() + assertEquals(QuickScaleEvents(), swipeLayout.quickScaleEvents) + assertFalse(swipeLayout.isQuickScaleInProgress) + } + + @Test + fun `isQuickScaleInProgress should return false if timeout was reached`() { + // default DOUBLE_TAP_TIMEOUT is 300ms + + val firstEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val secondEvent = TestUtils.getMotionEvent(ACTION_UP, 0f, 0f, 0, firstEvent) + val thirdEvent = TestUtils.getMotionEvent(ACTION_DOWN, 0f, 0f, 301L, secondEvent) + + assertFalse(swipeLayout.isQuickScaleInProgress(firstEvent, secondEvent, thirdEvent)) + } + + @Test + fun `isQuickScaleInProgress should return false if taps were too apart`() { + val firstEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val secondEvent = TestUtils.getMotionEvent(ACTION_UP, 0f, 0f, 0, firstEvent) + val thirdEvent = TestUtils.getMotionEvent(ACTION_DOWN, 200f, 20f, 200L, secondEvent) + + assertFalse(swipeLayout.isQuickScaleInProgress(firstEvent, secondEvent, thirdEvent)) + } + + @Test + fun `isQuickScaleInProgress should return true if taps were close`() { + val firstEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val secondEvent = TestUtils.getMotionEvent(ACTION_UP, 0f, 0f, 0, firstEvent) + val thirdEvent = TestUtils.getMotionEvent(ACTION_DOWN, 20f, 20f, 200L, secondEvent) + + assertTrue(swipeLayout.isQuickScaleInProgress(firstEvent, secondEvent, thirdEvent)) + } + + @Test + fun `isQuickScaleInProgress should return false if any event is null`() { + // Using the same values as in the above test that asserts true + val firstEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val secondEvent = TestUtils.getMotionEvent(ACTION_UP, 0f, 0f, 0, firstEvent) + val thirdEvent = TestUtils.getMotionEvent(ACTION_DOWN, 20f, 20f, 200L, secondEvent) + val oneNullEvent = QuickScaleEvents(firstEvent, secondEvent, null) + val twoNullEvents = QuickScaleEvents(null, null, thirdEvent) + val allNullEvents = QuickScaleEvents(null, null, null) + + assertFalse(swipeLayout.isQuickScaleInProgress(oneNullEvent)) + assertFalse(swipeLayout.isQuickScaleInProgress(twoNullEvents)) + assertFalse(swipeLayout.isQuickScaleInProgress(allNullEvents)) + } + + @Test + fun `isQuickScaleInProgress should return true for valid sequence of non null events`() { + // Using the same values as in the above test that asserts true + swipeLayout = spy(swipeLayout) + val firstEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val secondEvent = TestUtils.getMotionEvent(ACTION_UP, 0f, 0f, 0, firstEvent) + val thirdEvent = TestUtils.getMotionEvent(ACTION_DOWN, 20f, 20f, 200L, secondEvent) + val quickScaleEvents = QuickScaleEvents(firstEvent, secondEvent, thirdEvent) + + assertTrue(swipeLayout.isQuickScaleInProgress(quickScaleEvents)) + verify(swipeLayout).isQuickScaleInProgress(firstEvent, secondEvent, thirdEvent) + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/WidgetSiteItemViewTest.kt b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/WidgetSiteItemViewTest.kt new file mode 100644 index 0000000000..babc038dc0 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/WidgetSiteItemViewTest.kt @@ -0,0 +1,93 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets + +import android.graphics.drawable.Drawable +import android.widget.ImageButton +import android.widget.TextView +import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import mozilla.components.ui.icons.R as iconsR + +@RunWith(AndroidJUnit4::class) +class WidgetSiteItemViewTest { + + private lateinit var view: WidgetSiteItemView + + @Before + fun setup() { + val context = ContextThemeWrapper(testContext, R.style.Mozac_Widgets_TestTheme) + view = WidgetSiteItemView(context) + } + + @Test + fun `setText hides the caption`() { + val labelView = view.findViewById(R.id.label) + val captionView = view.findViewById(R.id.caption) + + view.setText(label = "label", caption = null) + assertEquals("label", labelView.text) + assertTrue(captionView.isGone) + + view.setText(label = "Label", caption = "") + assertEquals("Label", labelView.text) + assertEquals("", captionView.text) + assertTrue(captionView.isVisible) + } + + @Test + fun `setSecondaryButton shows the button`() { + val secondaryButton = view.findViewById(R.id.secondary_button) + val drawable = mock() + var clicked = false + view.setSecondaryButton( + icon = drawable, + contentDescription = "Menu", + onClickListener = { clicked = true }, + ) + assertTrue(secondaryButton.isVisible) + assertEquals(drawable, secondaryButton.drawable) + assertEquals("Menu", secondaryButton.contentDescription) + + secondaryButton.performClick() + assertTrue(clicked) + } + + @Test + fun `setSecondaryButton with resource IDs shows the button`() { + val secondaryButton = view.findViewById(R.id.secondary_button) + var clicked = false + view.setSecondaryButton( + icon = iconsR.drawable.mozac_ic_lock_24, + contentDescription = iconsR.string.mozac_error_lock, + onClickListener = { clicked = true }, + ) + assertTrue(secondaryButton.isVisible) + assertNotNull(secondaryButton.drawable) + assertEquals("mozac_error_lock", secondaryButton.contentDescription) + + secondaryButton.performClick() + assertTrue(clicked) + } + + @Test + fun `removeSecondaryButton does nothing if set was not called`() { + val secondaryButton = view.findViewById(R.id.secondary_button) + assertTrue(secondaryButton.isGone) + + view.removeSecondaryButton() + assertTrue(secondaryButton.isGone) + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/BrowserGestureDetectorTest.kt b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/BrowserGestureDetectorTest.kt new file mode 100644 index 0000000000..66c4b826ff --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/BrowserGestureDetectorTest.kt @@ -0,0 +1,231 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.MotionEvent.ACTION_CANCEL +import android.view.MotionEvent.ACTION_DOWN +import android.view.MotionEvent.ACTION_MOVE +import android.view.MotionEvent.ACTION_POINTER_DOWN +import android.view.MotionEvent.ACTION_UP +import android.view.ScaleGestureDetector +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.base.crash.CrashReporting +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyFloat +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` + +@RunWith(AndroidJUnit4::class) +class BrowserGestureDetectorTest { + // Robolectric currently (April 17th 2020) only offer a stub in it's `ShadowScaleGestureDetector` + // so unit tests based on the actual implementation of `ScaleGestureListener` are not possible. + + // Used spies and not mocks as it was observed that verifying more of the below as mocks + // will fail the tests because of "UnfinishedVerificationException" + private val scrollListener = spy { _: Float, _: Float -> run {} } + private val verticalScrollListener = spy { _: Float -> run {} } + private val horizontalScrollListener = spy { _: Float -> run {} } + private val scaleBeginListener = spy { _: Float -> run {} } + private val scaleInProgressListener = spy { _: Float -> run {} } + private val scaleEndListener = spy { _: Float -> run {} } + private val gesturesListener = BrowserGestureDetector.GesturesListener( + onScroll = scrollListener, + onVerticalScroll = verticalScrollListener, + onHorizontalScroll = horizontalScrollListener, + onScaleBegin = scaleBeginListener, + onScale = scaleInProgressListener, + onScaleEnd = scaleEndListener, + ) + + @Test + fun `Detector should not attempt to detect zoom if MotionEvent's action is ACTION_CANCEL`() { + val detector = spy(BrowserGestureDetector(testContext, mock())) + val scaleDetector: ScaleGestureDetector = mock() + detector.scaleGestureDetector = scaleDetector + + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val cancelEvent = TestUtils.getMotionEvent(ACTION_CANCEL, previousEvent = downEvent) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, previousEvent = downEvent) + detector.handleTouchEvent(downEvent) + detector.handleTouchEvent(cancelEvent) + detector.handleTouchEvent(downEvent) + detector.handleTouchEvent(moveEvent) + + verify(scaleDetector, times(3)).onTouchEvent(any()) + } + + @Test + fun `Detector should not attempt to detect scrolls if a zoom gesture is in progress`() { + val detector = spy(BrowserGestureDetector(testContext, mock())) + val scrollDetector: GestureDetector = mock() + val scaleDetector: ScaleGestureDetector = mock() + detector.gestureDetector = scrollDetector + detector.scaleGestureDetector = scaleDetector + `when`(scaleDetector.isInProgress).thenReturn(true) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, previousEvent = downEvent) + + detector.handleTouchEvent(downEvent) + detector.handleTouchEvent(moveEvent) + + // In this case we let ACTION_DOWN, ACTION_UP, ACTION_CANCEL be handled but not others. + verify(scrollDetector, times(1)).onTouchEvent(downEvent) + verify(scrollDetector, never()).onTouchEvent(moveEvent) + } + + @Test + fun `Detector's handleTouchEvent returns false if the event was not handled`() { + val detector = spy(BrowserGestureDetector(testContext, mock())) + val unhandledEvent = TestUtils.getMotionEvent(ACTION_DOWN) + + // Neither the scale detector, nor the scroll detector should be interested + // in a one of a time ACTION_CANCEL MotionEvent + val wasEventHandled = detector.handleTouchEvent( + TestUtils.getMotionEvent(ACTION_CANCEL, previousEvent = unhandledEvent), + ) + + assertFalse(wasEventHandled) + } + + @Test + fun `Detector's handleTouchEvent returns true if the event was handled`() { + val detector = spy(BrowserGestureDetector(testContext, mock())) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, 0f, 0f, previousEvent = downEvent) + val moveEvent2 = TestUtils.getMotionEvent(ACTION_MOVE, 100f, 100f, previousEvent = moveEvent) + + detector.handleTouchEvent(downEvent) + detector.handleTouchEvent(moveEvent) + val wasEventHandled = detector.handleTouchEvent(moveEvent2) + + assertTrue(wasEventHandled) + } + + @Test + fun `Detector should inform about scroll and vertical scrolls events`() { + val detector = spy(BrowserGestureDetector(testContext, gesturesListener)) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, 0f, 0f, previousEvent = downEvent) + val moveEvent2 = TestUtils.getMotionEvent(ACTION_MOVE, 100f, 200f, previousEvent = moveEvent) + + detector.handleTouchEvent(downEvent) + detector.handleTouchEvent(moveEvent) + detector.handleTouchEvent(moveEvent2) + + // If the movement was more on the Y axis both "onScroll" and "onVerticalScroll" callbacks + // should be called but no others. + verify(scrollListener).invoke(-100f, -200f) + verify(verticalScrollListener).invoke(-200f) + verify(horizontalScrollListener, never()).invoke(anyFloat()) + verify(scaleBeginListener, never()).invoke(anyFloat()) + verify(scaleInProgressListener, never()).invoke(anyFloat()) + verify(scaleEndListener, never()).invoke(anyFloat()) + } + + @Test + fun `Detector should prioritize vertical scrolls over horizontal scrolls`() { + val detector = spy(BrowserGestureDetector(testContext, gesturesListener)) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, 0f, 0f, previousEvent = downEvent) + val moveEvent2 = TestUtils.getMotionEvent(ACTION_MOVE, 100f, 100f, previousEvent = moveEvent) + + detector.handleTouchEvent(downEvent) + detector.handleTouchEvent(moveEvent) + detector.handleTouchEvent(moveEvent2) + + // If the movement was for the same amount on both the Y axis and the X axis + // both "onScroll" and "onVerticalScroll" callbacks should be called but no others. + verify(scrollListener).invoke(-100f, -100f) + verify(verticalScrollListener).invoke(-100f) + verify(horizontalScrollListener, never()).invoke(anyFloat()) + verify(scaleBeginListener, never()).invoke(anyFloat()) + verify(scaleInProgressListener, never()).invoke(anyFloat()) + verify(scaleEndListener, never()).invoke(anyFloat()) + } + + @Test + fun `Detector should inform about scroll and horizontal scrolls events`() { + val detector = spy(BrowserGestureDetector(testContext, gesturesListener)) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, 0f, 0f, previousEvent = downEvent) + val moveEvent2 = TestUtils.getMotionEvent(ACTION_MOVE, 101f, 100f, previousEvent = moveEvent) + + detector.handleTouchEvent(downEvent) + detector.handleTouchEvent(moveEvent) + detector.handleTouchEvent(moveEvent2) + + // If the movement was for the same amount on both the Y axis and the X axis + // both "onScroll" and "onVerticalScroll" callbacks should be called but no others. + verify(scrollListener).invoke(-101f, -100f) + verify(horizontalScrollListener).invoke(-101f) + verify(verticalScrollListener, never()).invoke(anyFloat()) + verify(scaleBeginListener, never()).invoke(anyFloat()) + verify(scaleInProgressListener, never()).invoke(anyFloat()) + verify(scaleEndListener, never()).invoke(anyFloat()) + } + + @Test + fun `Detector should always pass the ACTION_DOWN, ACTION_UP, ACTION_CANCEL events to the scroll detector`() { + val detector = spy(BrowserGestureDetector(testContext, mock())) + val scrollDetector: GestureDetector = mock() + val scaleDetector: ScaleGestureDetector = mock() + detector.gestureDetector = scrollDetector + detector.scaleGestureDetector = scaleDetector + // The aforementioned events should always be passed to the scroll detector, + // even if scaling is in progress. + `when`(scaleDetector.isInProgress).thenReturn(true) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, previousEvent = downEvent) + val upEvent = TestUtils.getMotionEvent(ACTION_UP, previousEvent = moveEvent) + val cancelEvent = TestUtils.getMotionEvent(ACTION_CANCEL, previousEvent = upEvent) + + listOf(downEvent, moveEvent, upEvent, cancelEvent).forEach { + detector.handleTouchEvent(it) + } + + // With scaling in progress we let ACTION_DOWN, ACTION_UP, ACTION_CANCEL be handled but not others. + verify(scrollDetector, times(1)).onTouchEvent(downEvent) + verify(scrollDetector, times(1)).onTouchEvent(upEvent) + verify(scrollDetector, times(1)).onTouchEvent(cancelEvent) + verify(scrollDetector, never()).onTouchEvent(moveEvent) + } + + @Test + fun `Detector should not crash when the scroll detector receives a null first MotionEvent`() { + val crashReporting: CrashReporting = mock() + val detector = BrowserGestureDetector(testContext, gesturesListener, crashReporting) + // We need a previous event for ACTION_MOVE. + // Don't use ACTION_DOWN (first pointer on the screen) but ACTION_POINTER_DOWN (other later pointer) + // just to artificially be able to recreate the bug from 8552. This should not happen IRL. + val downEvent = TestUtils.getMotionEvent(ACTION_POINTER_DOWN) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, 0f, 0f, previousEvent = downEvent) + val moveEvent2 = TestUtils.getMotionEvent(ACTION_MOVE, 100f, 200f, previousEvent = moveEvent) + + detector.handleTouchEvent(downEvent) + detector.handleTouchEvent(moveEvent) + detector.handleTouchEvent(moveEvent2) + + verify(scrollListener).invoke(-100f, -200f) + + // We don't crash but neither can identify vertical / horizontal scrolls. + + verify(verticalScrollListener, never()).invoke(anyFloat()) + verify(horizontalScrollListener, never()).invoke(anyFloat()) + verify(scaleBeginListener, never()).invoke(anyFloat()) + verify(scaleInProgressListener, never()).invoke(anyFloat()) + verify(scaleEndListener, never()).invoke(anyFloat()) + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/EngineViewClippingBehaviorTest.kt b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/EngineViewClippingBehaviorTest.kt new file mode 100644 index 0000000000..13538bc591 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/EngineViewClippingBehaviorTest.kt @@ -0,0 +1,221 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.content.Context +import android.view.View +import android.widget.EditText +import android.widget.ImageView +import android.widget.TextView +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.engine.EngineView +import mozilla.components.concept.toolbar.ScrollableToolbar +import mozilla.components.support.test.fakes.engine.FakeEngineView +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class EngineViewClippingBehaviorTest { + + @Test + fun `EngineView clipping and bottom toolbar offset are kept in sync`() { + val engineView: EngineView = spy(FakeEngineView(testContext)) + val toolbar: View = mock() + doReturn(100).`when`(toolbar).height + doReturn(42f).`when`(toolbar).translationY + + val behavior = EngineViewClippingBehavior( + mock(), + null, + engineView.asView(), + toolbar.height, + ToolbarPosition.BOTTOM, + ) + + behavior.onDependentViewChanged(mock(), mock(), toolbar) + + verify(engineView).setVerticalClipping(-42) + assertEquals(0f, engineView.asView().translationY) + } + + @Test + fun `EngineView clipping and top toolbar offset are kept in sync`() { + val engineView: EngineView = spy(FakeEngineView(testContext)) + val toolbar: View = mock() + doReturn(100).`when`(toolbar).height + doReturn(42f).`when`(toolbar).translationY + + val behavior = EngineViewClippingBehavior( + mock(), + null, + engineView.asView(), + toolbar.height, + ToolbarPosition.TOP, + ) + + behavior.onDependentViewChanged(mock(), mock(), toolbar) + + verify(engineView).setVerticalClipping(42) + assertEquals(142f, engineView.asView().translationY) + } + + @Test + fun `Behavior does not depend on normal views`() { + val behavior = EngineViewClippingBehavior( + mock(), + null, + mock(), + 0, + ToolbarPosition.BOTTOM, + ) + + assertFalse(behavior.layoutDependsOn(mock(), mock(), TextView(testContext))) + assertFalse(behavior.layoutDependsOn(mock(), mock(), EditText(testContext))) + assertFalse(behavior.layoutDependsOn(mock(), mock(), ImageView(testContext))) + } + + @Test + fun `Behavior depends on BrowserToolbar`() { + val behavior = EngineViewClippingBehavior( + mock(), + null, + mock(), + 0, + ToolbarPosition.BOTTOM, + ) + + assertTrue(behavior.layoutDependsOn(mock(), mock(), BrowserToolbar(testContext))) + } + + @Test + fun `GIVEN a bottom toolbar WHEN translation has below a half decimal THEN set vertical clipping with the floor value`() { + val engineView: FakeEngineView = mock() + val behavior = EngineViewClippingBehavior( + mock(), + null, + engineView, + 100, + ToolbarPosition.BOTTOM, + ) + + behavior.toolbarChangedAction(40.4f) + + verify(engineView).setVerticalClipping(-40) + } + + @Test + fun `GIVEN a bottom toolbar WHEN translation has exactly half of a decimal THEN set vertical clipping with the ceiling value`() { + val engineView: FakeEngineView = mock() + val behavior = EngineViewClippingBehavior( + mock(), + null, + engineView, + 100, + ToolbarPosition.BOTTOM, + ) + + behavior.toolbarChangedAction(40.5f) + + verify(engineView).setVerticalClipping(-41) + } + + @Test + fun `GIVEN a bottom toolbar WHEN translation has more than a half decimal THEN set vertical clipping with the ceiling value`() { + val engineView: FakeEngineView = mock() + val behavior = EngineViewClippingBehavior( + mock(), + null, + engineView, + 100, + ToolbarPosition.BOTTOM, + ) + + behavior.toolbarChangedAction(40.6f) + + verify(engineView).setVerticalClipping(-41) + } + + @Test + fun `GIVEN a top toolbar WHEN translation has below a half decimal THEN set vertical clipping with the floor value`() { + val engineView: FakeEngineView = mock() + val behavior = EngineViewClippingBehavior( + mock(), + null, + engineView, + 100, + ToolbarPosition.TOP, + ) + + behavior.toolbarChangedAction(40.4f) + + verify(engineView).setVerticalClipping(40) + } + + @Test + fun `GIVEN a top toolbar WHEN translation has exactly half of a decimal THEN set vertical clipping with the ceiling value`() { + val engineView: FakeEngineView = mock() + val behavior = EngineViewClippingBehavior( + mock(), + null, + engineView, + 100, + ToolbarPosition.TOP, + ) + + behavior.toolbarChangedAction(40.5f) + + verify(engineView).setVerticalClipping(41) + } + + @Test + fun `GIVEN a top toolbar WHEN translation has more than a half decimal THEN set vertical clipping with the ceiling value`() { + val engineView: FakeEngineView = mock() + val behavior = EngineViewClippingBehavior( + mock(), + null, + engineView, + 100, + ToolbarPosition.TOP, + ) + + behavior.toolbarChangedAction(40.6f) + + verify(engineView).setVerticalClipping(41) + } + + @Test + fun `GIVEN a bottom toolbar WHEN translation returns NaN THEN no exception thrown`() { + val engineView: EngineView = spy(FakeEngineView(testContext)) + val toolbar: View = mock() + doReturn(100).`when`(toolbar).height + doReturn(Float.NaN).`when`(toolbar).translationY + + val behavior = EngineViewClippingBehavior( + mock(), + null, + engineView.asView(), + toolbar.height, + ToolbarPosition.BOTTOM, + ) + + behavior.onDependentViewChanged(mock(), mock(), toolbar) + assertEquals(0f, engineView.asView().translationY) + } +} + +private class BrowserToolbar(context: Context) : TextView(context), ScrollableToolbar { + override fun enableScrolling() {} + override fun disableScrolling() {} + override fun expand() {} + override fun collapse() {} +} diff --git a/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/EngineViewScrollingBehaviorTest.kt b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/EngineViewScrollingBehaviorTest.kt new file mode 100644 index 0000000000..0f0c10b71a --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/EngineViewScrollingBehaviorTest.kt @@ -0,0 +1,575 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.content.Context +import android.graphics.Bitmap +import android.view.MotionEvent.ACTION_DOWN +import android.view.MotionEvent.ACTION_MOVE +import android.view.View +import android.widget.FrameLayout +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.view.ViewCompat +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.EngineView +import mozilla.components.concept.engine.INPUT_UNHANDLED +import mozilla.components.concept.engine.InputResultDetail +import mozilla.components.concept.engine.selection.SelectionActionDelegate +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyFloat +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` + +@RunWith(AndroidJUnit4::class) +class EngineViewScrollingBehaviorTest { + @Test + fun `onStartNestedScroll should attempt scrolling only if browserToolbar is valid`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + doReturn(true).`when`(behavior).shouldScroll + + behavior.dynamicScrollView = null + var acceptsNestedScroll = behavior.onStartNestedScroll( + coordinatorLayout = mock(), + child = mock(), + directTargetChild = mock(), + target = mock(), + axes = ViewCompat.SCROLL_AXIS_VERTICAL, + type = ViewCompat.TYPE_TOUCH, + ) + assertFalse(acceptsNestedScroll) + verify(behavior, never()).startNestedScroll(anyInt(), anyInt(), any()) + + behavior.dynamicScrollView = mock() + acceptsNestedScroll = behavior.onStartNestedScroll( + coordinatorLayout = mock(), + child = mock(), + directTargetChild = mock(), + target = mock(), + axes = ViewCompat.SCROLL_AXIS_VERTICAL, + type = ViewCompat.TYPE_TOUCH, + ) + assertTrue(acceptsNestedScroll) + verify(behavior).startNestedScroll(anyInt(), anyInt(), any()) + } + + @Test + fun `startNestedScroll should cancel an ongoing snap animation`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + doReturn(true).`when`(behavior).shouldScroll + + val acceptsNestedScroll = behavior.startNestedScroll( + axes = ViewCompat.SCROLL_AXIS_VERTICAL, + type = ViewCompat.TYPE_TOUCH, + view = mock(), + ) + + assertTrue(acceptsNestedScroll) + verify(yTranslator).cancelInProgressTranslation() + } + + @Test + fun `startNestedScroll should not accept nested scrolls on the horizontal axis`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + doReturn(true).`when`(behavior).shouldScroll + + var acceptsNestedScroll = behavior.startNestedScroll( + axes = ViewCompat.SCROLL_AXIS_VERTICAL, + type = ViewCompat.TYPE_TOUCH, + view = mock(), + ) + assertTrue(acceptsNestedScroll) + + acceptsNestedScroll = behavior.startNestedScroll( + axes = ViewCompat.SCROLL_AXIS_HORIZONTAL, + type = ViewCompat.TYPE_TOUCH, + view = mock(), + ) + assertFalse(acceptsNestedScroll) + } + + @Test + fun `GIVEN a gesture that doesn't scroll the toolbar WHEN startNestedScroll THEN toolbar is expanded and nested scroll not accepted`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val engineView: EngineView = mock() + val inputResultDetail: InputResultDetail = mock() + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + doReturn(false).`when`(behavior).shouldScroll + doReturn(true).`when`(inputResultDetail).isTouchUnhandled() + behavior.engineView = engineView + doReturn(inputResultDetail).`when`(engineView).getInputResultDetail() + + val acceptsNestedScroll = behavior.startNestedScroll( + axes = ViewCompat.SCROLL_AXIS_VERTICAL, + type = ViewCompat.TYPE_TOUCH, + view = mock(), + ) + + verify(yTranslator).cancelInProgressTranslation() + verify(yTranslator).expandWithAnimation(any()) + assertFalse(acceptsNestedScroll) + } + + @Test + fun `Behavior should not accept nested scrolls on the horizontal axis`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + behavior.dynamicScrollView = mock() + doReturn(true).`when`(behavior).shouldScroll + + var acceptsNestedScroll = behavior.onStartNestedScroll( + coordinatorLayout = mock(), + child = mock(), + directTargetChild = mock(), + target = mock(), + axes = ViewCompat.SCROLL_AXIS_VERTICAL, + type = ViewCompat.TYPE_TOUCH, + ) + assertTrue(acceptsNestedScroll) + + acceptsNestedScroll = behavior.onStartNestedScroll( + coordinatorLayout = mock(), + child = mock(), + directTargetChild = mock(), + target = mock(), + axes = ViewCompat.SCROLL_AXIS_HORIZONTAL, + type = ViewCompat.TYPE_TOUCH, + ) + assertFalse(acceptsNestedScroll) + } + + @Test + fun `Behavior should delegate the onStartNestedScroll logic`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val view: View = mock() + behavior.dynamicScrollView = view + val inputType = ViewCompat.TYPE_TOUCH + val axes = ViewCompat.SCROLL_AXIS_VERTICAL + + behavior.onStartNestedScroll( + coordinatorLayout = mock(), + child = view, + directTargetChild = mock(), + target = mock(), + axes = axes, + type = inputType, + ) + + verify(behavior).startNestedScroll(axes, inputType, view) + } + + @Test + fun `onStopNestedScroll should attempt stopping nested scrolling only if browserToolbar is valid`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + + behavior.dynamicScrollView = null + behavior.onStopNestedScroll( + coordinatorLayout = mock(), + child = mock(), + target = mock(), + type = 0, + ) + verify(behavior, never()).stopNestedScroll(anyInt(), any()) + + behavior.dynamicScrollView = mock() + behavior.onStopNestedScroll( + coordinatorLayout = mock(), + child = mock(), + target = mock(), + type = 0, + ) + verify(behavior).stopNestedScroll(anyInt(), any()) + } + + @Test + fun `Behavior should delegate the onStopNestedScroll logic`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val inputType = ViewCompat.TYPE_TOUCH + val view: View = mock() + + behavior.dynamicScrollView = null + behavior.onStopNestedScroll( + coordinatorLayout = mock(), + child = view, + target = mock(), + type = inputType, + ) + verify(behavior, never()).stopNestedScroll(inputType, view) + } + + @Test + fun `stopNestedScroll will snap toolbar up if toolbar is more than 50 percent visible`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + behavior.dynamicScrollView = mock() + doReturn(true).`when`(behavior).shouldScroll + + val child = mock() + doReturn(100).`when`(child).height + doReturn(10f).`when`(child).translationY + + behavior.onStartNestedScroll( + coordinatorLayout = mock(), + child = child, + directTargetChild = mock(), + target = mock(), + axes = ViewCompat.SCROLL_AXIS_VERTICAL, + type = ViewCompat.TYPE_TOUCH, + ) + + assertTrue(behavior.shouldSnapAfterScroll) + verify(yTranslator).cancelInProgressTranslation() + verify(yTranslator, never()).expandWithAnimation(any()) + verify(yTranslator, never()).collapseWithAnimation(any()) + + behavior.stopNestedScroll(0, child) + + verify(yTranslator).snapWithAnimation(child) + } + + @Test + fun `stopNestedScroll will snap toolbar down if toolbar is less than 50 percent visible`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + doReturn(true).`when`(behavior).shouldScroll + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + + val child = mock() + behavior.dynamicScrollView = child + doReturn(100).`when`(child).height + doReturn(90f).`when`(child).translationY + + behavior.onStartNestedScroll( + coordinatorLayout = mock(), + child = child, + directTargetChild = mock(), + target = mock(), + axes = ViewCompat.SCROLL_AXIS_VERTICAL, + type = ViewCompat.TYPE_TOUCH, + ) + + assertTrue(behavior.shouldSnapAfterScroll) + verify(yTranslator).cancelInProgressTranslation() + verify(yTranslator, never()).expandWithAnimation(any()) + verify(yTranslator, never()).collapseWithAnimation(any()) + + behavior.stopNestedScroll(0, child) + + verify(yTranslator).snapWithAnimation(child) + } + + @Test + fun `onStopNestedScroll should snap the toolbar only if browserToolbar is valid`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + behavior.dynamicScrollView = null + + behavior.onStopNestedScroll( + coordinatorLayout = mock(), + child = mock(), + target = mock(), + type = ViewCompat.TYPE_TOUCH, + ) + + verify(behavior, never()).stopNestedScroll(anyInt(), any()) + } + + @Test + fun `Behavior will intercept MotionEvents and pass them to the custom gesture detector`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val gestureDetector: BrowserGestureDetector = mock() + behavior.initGesturesDetector(gestureDetector) + behavior.dynamicScrollView = mock() + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + + behavior.onInterceptTouchEvent(mock(), mock(), downEvent) + + verify(gestureDetector).handleTouchEvent(downEvent) + } + + @Test + fun `Behavior should only dispatch MotionEvents to the gesture detector only if browserToolbar is valid`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val gestureDetector: BrowserGestureDetector = mock() + behavior.initGesturesDetector(gestureDetector) + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN) + + behavior.onInterceptTouchEvent(mock(), mock(), downEvent) + + verify(gestureDetector, never()).handleTouchEvent(downEvent) + } + + @Test + fun `Behavior will apply translation to toolbar only for vertical scrolls`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + behavior.initGesturesDetector(behavior.createGestureDetector()) + val child = spy(View(testContext, null, 0)) + behavior.dynamicScrollView = child + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN, 0f, 0f) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, 0f, 100f, downEvent) + + behavior.onInterceptTouchEvent(mock(), mock(), downEvent) + behavior.onInterceptTouchEvent(mock(), mock(), moveEvent) + + verify(behavior).tryToScrollVertically(-100f) + } + + @Test + fun `GIVEN a null InputResultDetail from the EngineView WHEN shouldScroll is called THEN it returns false`() { + val behavior = EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM) + behavior.engineView = null + assertFalse(behavior.shouldScroll) + behavior.engineView = mock() + `when`(behavior.engineView!!.getInputResultDetail()).thenReturn(null) + + assertFalse(behavior.shouldScroll) + } + + @Test + fun `GIVEN an InputResultDetail with the right values and scroll enabled WHEN shouldScroll is called THEN it returns true`() { + val behavior = EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM) + val engineView: EngineView = mock() + behavior.engineView = engineView + behavior.isScrollEnabled = true + val validInputResultDetail: InputResultDetail = mock() + doReturn(validInputResultDetail).`when`(engineView).getInputResultDetail() + + doReturn(true).`when`(validInputResultDetail).canScrollToBottom() + doReturn(false).`when`(validInputResultDetail).canScrollToTop() + assertTrue(behavior.shouldScroll) + + doReturn(false).`when`(validInputResultDetail).canScrollToBottom() + doReturn(true).`when`(validInputResultDetail).canScrollToTop() + assertTrue(behavior.shouldScroll) + + doReturn(true).`when`(validInputResultDetail).canScrollToBottom() + doReturn(true).`when`(validInputResultDetail).canScrollToTop() + assertTrue(behavior.shouldScroll) + } + + @Test + fun `GIVEN an InputResultDetail with the right values but with scroll disabled WHEN shouldScroll is called THEN it returns false`() { + val behavior = EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM) + behavior.engineView = mock() + behavior.isScrollEnabled = false + val validInputResultDetail: InputResultDetail = mock() + doReturn(true).`when`(validInputResultDetail).canScrollToBottom() + doReturn(true).`when`(validInputResultDetail).canScrollToTop() + + assertFalse(behavior.shouldScroll) + } + + @Test + fun `GIVEN scroll enabled but EngineView cannot scroll to bottom WHEN shouldScroll is called THEN it returns false`() { + val behavior = EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM) + behavior.engineView = mock() + behavior.isScrollEnabled = true + val validInputResultDetail: InputResultDetail = mock() + doReturn(false).`when`(validInputResultDetail).canScrollToBottom() + doReturn(true).`when`(validInputResultDetail).canScrollToTop() + + assertFalse(behavior.shouldScroll) + } + + @Test + fun `GIVEN scroll enabled but EngineView cannot scroll to top WHEN shouldScroll is called THEN it returns false`() { + val behavior = EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM) + behavior.engineView = mock() + behavior.isScrollEnabled = true + val validInputResultDetail: InputResultDetail = mock() + doReturn(true).`when`(validInputResultDetail).canScrollToBottom() + doReturn(false).`when`(validInputResultDetail).canScrollToTop() + + assertFalse(behavior.shouldScroll) + } + + @Test + fun `Behavior will vertically scroll nested scroll started and EngineView handled the event`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + doReturn(true).`when`(behavior).shouldScroll + val child = spy(View(testContext, null, 0)) + behavior.dynamicScrollView = child + doReturn(100).`when`(child).height + doReturn(0f).`when`(child).translationY + behavior.startedScroll = true + + behavior.tryToScrollVertically(25f) + + verify(yTranslator).translate(child, 25f) + } + + @Test + fun `Behavior will not scroll vertically if startedScroll is false`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + doReturn(true).`when`(behavior).shouldScroll + val child = spy(View(testContext, null, 0)) + behavior.dynamicScrollView = child + doReturn(100).`when`(child).height + doReturn(0f).`when`(child).translationY + behavior.startedScroll = false + + behavior.tryToScrollVertically(25f) + + verify(yTranslator, never()).translate(any(), anyFloat()) + } + + @Test + fun `Behavior will not scroll vertically if EngineView did not handled the event`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + doReturn(false).`when`(behavior).shouldScroll + val child = spy(View(testContext, null, 0)) + behavior.dynamicScrollView = child + doReturn(100).`when`(child).height + doReturn(0f).`when`(child).translationY + + behavior.tryToScrollVertically(25f) + + verify(yTranslator, never()).translate(any(), anyFloat()) + } + + @Test + fun `forceExpand should delegate the translator`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + val view: View = mock() + + behavior.forceExpand(view) + + verify(yTranslator).expandWithAnimation(view) + } + + @Test + fun `forceCollapse should delegate the translator`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + val view: View = mock() + + behavior.forceCollapse(view) + + verify(yTranslator).collapseWithAnimation(view) + } + + @Test + fun `Behavior will forceExpand when scrolling up and !shouldScroll if the touch was handled in the browser`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + behavior.initGesturesDetector(behavior.createGestureDetector()) + val view: View = spy(View(testContext, null, 0)) + behavior.dynamicScrollView = view + val engineView: EngineView = mock() + behavior.engineView = engineView + val handledTouchInput = InputResultDetail.newInstance().copy(INPUT_UNHANDLED) + doReturn(handledTouchInput).`when`(engineView).getInputResultDetail() + + doReturn(100).`when`(view).height + doReturn(100f).`when`(view).translationY + + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN, 0f, 0f) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, 0f, 30f, downEvent) + + behavior.onInterceptTouchEvent(mock(), mock(), downEvent) + behavior.onInterceptTouchEvent(mock(), mock(), moveEvent) + + verify(behavior).tryToScrollVertically(-30f) + verify(yTranslator).forceExpandIfNotAlready(view, -30f) + } + + @Test + fun `Behavior will not forceExpand when scrolling up and !shouldScroll if the touch was not yet handled in the browser`() { + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + val yTranslator: ViewYTranslator = mock() + behavior.yTranslator = yTranslator + behavior.initGesturesDetector(behavior.createGestureDetector()) + val view: View = spy(View(testContext, null, 0)) + behavior.dynamicScrollView = view + val engineView: EngineView = mock() + behavior.engineView = engineView + val handledTouchInput = InputResultDetail.newInstance() + doReturn(handledTouchInput).`when`(engineView).getInputResultDetail() + + doReturn(100).`when`(view).height + doReturn(100f).`when`(view).translationY + + val downEvent = TestUtils.getMotionEvent(ACTION_DOWN, 0f, 0f) + val moveEvent = TestUtils.getMotionEvent(ACTION_MOVE, 0f, 30f, downEvent) + + behavior.onInterceptTouchEvent(mock(), mock(), downEvent) + behavior.onInterceptTouchEvent(mock(), mock(), moveEvent) + + verify(behavior).tryToScrollVertically(-30f) + verify(yTranslator, never()).forceExpandIfNotAlready(view, -30f) + } + + @Test + fun `onLayoutChild initializes browserToolbar and engineView`() { + val view = View(testContext) + val engineView = createDummyEngineView(testContext).asView() + val container = CoordinatorLayout(testContext).apply { + addView(View(testContext)) + addView(engineView) + } + val behavior = spy(EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM)) + + behavior.onLayoutChild(container, view, ViewCompat.LAYOUT_DIRECTION_LTR) + + assertEquals(view, behavior.dynamicScrollView) + assertEquals(engineView, behavior.engineView) + } + + @Test + fun `enableScrolling sets isScrollEnabled to true`() { + val behavior = EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM) + + assertFalse(behavior.isScrollEnabled) + behavior.enableScrolling() + + assertTrue(behavior.isScrollEnabled) + } + + @Test + fun `disableScrolling sets isScrollEnabled to false`() { + val behavior = EngineViewScrollingBehavior(testContext, null, ViewPosition.BOTTOM) + behavior.isScrollEnabled = true + + assertTrue(behavior.isScrollEnabled) + behavior.disableScrolling() + + assertFalse(behavior.isScrollEnabled) + } + + private fun createDummyEngineView(context: Context): EngineView = DummyEngineView(context) + + open class DummyEngineView(context: Context) : FrameLayout(context), EngineView { + override fun setVerticalClipping(clippingHeight: Int) {} + override fun setDynamicToolbarMaxHeight(height: Int) {} + override fun setActivityContext(context: Context?) {} + override fun captureThumbnail(onFinish: (Bitmap?) -> Unit) = Unit + override fun render(session: EngineSession) {} + override fun release() {} + override var selectionActionDelegate: SelectionActionDelegate? = null + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/TestUtils.kt b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/TestUtils.kt new file mode 100644 index 0000000000..2e5bbaa9df --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/TestUtils.kt @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.view.MotionEvent + +object TestUtils { + fun getMotionEvent( + action: Int, + x: Float = 0f, + y: Float = 0f, + previousEvent: MotionEvent? = null, + ): MotionEvent { + val currentTime = System.currentTimeMillis() + val downTime = previousEvent?.downTime ?: System.currentTimeMillis() + + var pointerCount = previousEvent?.pointerCount ?: 0 + if (action == MotionEvent.ACTION_POINTER_DOWN) { + pointerCount++ + } else if (action == MotionEvent.ACTION_DOWN) { + pointerCount = 1 + } + + val properties = Array(pointerCount, TestUtils::getPointerProperties) + val pointerCoords = getPointerCoords(x, y, pointerCount) + + return MotionEvent.obtain( + downTime, currentTime, + action, pointerCount, properties, + pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0, + ) + } + + private fun getPointerCoords( + x: Float, + y: Float, + pointerCount: Int, + previousEvent: MotionEvent? = null, + ): Array { + val currentEventCoords = MotionEvent.PointerCoords().apply { + this.x = x; this.y = y; pressure = 1f; size = 1f + } + + return if (pointerCount > 1 && previousEvent != null) { + arrayOf( + MotionEvent.PointerCoords().apply { + this.x = previousEvent.x; this.y = previousEvent.y; pressure = 1f; size = 1f + }, + currentEventCoords, + ) + } else { + arrayOf(currentEventCoords) + } + } + + private fun getPointerProperties(id: Int): MotionEvent.PointerProperties = + MotionEvent.PointerProperties().apply { + this.id = id; this.toolType = MotionEvent.TOOL_TYPE_FINGER + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/ViewYTranslationStrategyTest.kt b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/ViewYTranslationStrategyTest.kt new file mode 100644 index 0000000000..62d152f7f6 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/ViewYTranslationStrategyTest.kt @@ -0,0 +1,712 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.animation.ValueAnimator +import android.view.View +import android.view.animation.DecelerateInterpolator +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class ViewYTranslationStrategyTest { + @Test + fun `snapAnimator should use a DecelerateInterpolator with SNAP_ANIMATION_DURATION for bottom toolbar translations`() { + val strategy = BottomViewBehaviorStrategy() + + assertTrue(strategy.animator.interpolator is DecelerateInterpolator) + assertEquals(SNAP_ANIMATION_DURATION, strategy.animator.duration) + } + + @Test + fun `snapAnimator should use a DecelerateInterpolator with SNAP_ANIMATION_DURATION for top toolbar translations`() { + val strategy = TopViewBehaviorStrategy() + + assertTrue(strategy.animator.interpolator is DecelerateInterpolator) + assertEquals(SNAP_ANIMATION_DURATION, strategy.animator.duration) + } + + @Test + fun `BottomToolbarBehaviorStrategy should start with isToolbarExpanding = false`() { + val strategy = BottomViewBehaviorStrategy() + + assertFalse(strategy.wasLastExpanding) + } + + @Test + fun `TopToolbarBehaviorStrategy should start with isToolbarExpanding = false`() { + val strategy = TopViewBehaviorStrategy() + + assertFalse(strategy.wasLastExpanding) + } + + @Test + fun `BottomToolbarBehaviorStrategy - snapWithAnimation should collapse toolbar if more than half hidden`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(50f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(51f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(100f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(333f).`when`(view).translationY + strategy.snapWithAnimation(view) + + verify(strategy, times(4)).collapseWithAnimation(view) + verify(strategy, never()).expandWithAnimation(view) + } + + @Test + fun `BottomToolbarBehaviorStrategy - snapWithAnimation should expand toolbar if more than half visible`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(49f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(0f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(-50f).`when`(view).translationY + strategy.snapWithAnimation(view) + + verify(strategy, times(3)).expandWithAnimation(view) + verify(strategy, never()).collapseWithAnimation(view) + } + + @Test + fun `TopToolbarBehaviorStrategy - snapWithAnimation should collapse toolbar if more than half hidden`() { + val strategy = spy(TopViewBehaviorStrategy()) + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(-51f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(-100f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(-333f).`when`(view).translationY + strategy.snapWithAnimation(view) + + verify(strategy, times(3)).collapseWithAnimation(view) + verify(strategy, never()).expandWithAnimation(view) + } + + @Test + fun `TopToolbarBehaviorStrategy - snapWithAnimation should expand toolbar if more than half visible`() { + val strategy = spy(TopViewBehaviorStrategy()) + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(-50f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(-49f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(0f).`when`(view).translationY + strategy.snapWithAnimation(view) + + doReturn(50f).`when`(view).translationY + strategy.snapWithAnimation(view) + + verify(strategy, times(4)).expandWithAnimation(view) + verify(strategy, never()).collapseWithAnimation(view) + } + + @Test + fun `BottomToolbarBehaviorStrategy - snapImmediately should end translations animations if in progress`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(true).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + + strategy.snapImmediately(view) + + verify(animator).end() + verify(view, never()).translationY + } + + @Test + fun `BottomToolbarBehaviorStrategy - snapImmediately should translate away the toolbar if half translated`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(50f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 100f + } + + @Test + fun `BottomToolbarBehaviorStrategy - snapImmediately should translate away the toolbar if more than half`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(55f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 100f + } + + @Test + fun `BottomToolbarBehaviorStrategy - snapImmediately should translate away the toolbar if translated off screen`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(555f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 100f + } + + @Test + fun `BottomToolbarBehaviorStrategy - snapImmediately should translate to 0 the toolbar if translated less than half`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(49f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 0f + } + + @Test + fun `BottomToolbarBehaviorStrategy - snapImmediately should translate to 0 the toolbar if translated 0`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(0f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 0f + } + + @Test + fun `BottomToolbarBehaviorStrategy - snapImmediately should translate to 0 the toolbar if translated inside the screen`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(-1f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 0f + } + + @Test + fun `TopToolbarBehaviorStrategy - snapImmediately should end translations animations if in progress`() { + val strategy = spy(TopViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(true).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + + strategy.snapImmediately(view) + + verify(animator).end() + verify(view, never()).translationY + } + + @Test + fun `TopToolbarBehaviorStrategy - snapImmediately should translate translate to 0 the toolbar if translated less than half`() { + val strategy = spy(TopViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(-49f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 0f + } + + @Test + fun `TopToolbarBehaviorStrategy - snapImmediately should translate to 0 the toolbar if translated 0`() { + val strategy = spy(TopViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(0f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 0f + } + + @Test + fun `TopToolbarBehaviorStrategy - snapImmediately should translate to 0 the toolbar if translated inside the screen`() { + val strategy = spy(TopViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(1f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 0f + } + + @Test + fun `TopToolbarBehaviorStrategy - snapImmediately should translate to 0 the toolbar if half translated`() { + val strategy = spy(TopViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(-50f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = 0f + } + + @Test + fun `TopToolbarBehaviorStrategy - snapImmediately should translate away the toolbar if more than half translated`() { + val strategy = spy(TopViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(-55f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = -100f + } + + @Test + fun `TopToolbarBehaviorStrategy - snapImmediately should translate to 0 the toolbar if translated offscreen`() { + val strategy = spy(TopViewBehaviorStrategy()) + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100).`when`(view).height + + doReturn(-111f).`when`(view).translationY + strategy.snapImmediately(view) + verify(view).translationY = -100f + } + + @Test + fun `BottomToolbarBehaviorStrategy - expandWithAnimation should translate the toolbar to to y 0`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val view: View = mock() + + strategy.expandWithAnimation(view) + + verify(strategy).animateToTranslationY(view, 0f) + } + + @Test + fun `TopToolbarBehaviorStrategy - expandWithAnimation should translate the toolbar to to y 0`() { + val strategy = spy(TopViewBehaviorStrategy()) + val view: View = mock() + + strategy.expandWithAnimation(view) + + verify(strategy).animateToTranslationY(view, 0f) + } + + @Test + fun `BottomToolbarBehaviorStrategy - forceExpandWithAnimation should expand toolbar`() { + // Setting the scenario in which forceExpandWithAnimation will actually do what the name says. + // Below this test we can change each variable one at a time to test them in isolation. + + val strategy = spy(BottomViewBehaviorStrategy()) + strategy.wasLastExpanding = false + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100f).`when`(view).translationY + + strategy.forceExpandWithAnimation(view, -100f) + + verify(strategy.animator).cancel() + verify(strategy).expandWithAnimation(any()) + } + + @Test + fun `BottomToolbarBehaviorStrategy - forceExpandWithAnimation should not force expand the toolbar if not currently collapsing`() { + val strategy = spy(BottomViewBehaviorStrategy()) + strategy.wasLastExpanding = true + val animator: ValueAnimator = mock() + doReturn(true).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100f).`when`(view).translationY + + strategy.forceExpandWithAnimation(view, -100f) + + verify(strategy.animator, never()).cancel() + verify(strategy, never()).expandWithAnimation(any()) + } + + @Test + fun `BottomToolbarBehaviorStrategy - forceExpandWithAnimation should not expand if user swipes down`() { + val strategy = spy(BottomViewBehaviorStrategy()) + strategy.wasLastExpanding = false + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(100f).`when`(view).translationY + + strategy.forceExpandWithAnimation(view, 100f) + + verify(strategy.animator, never()).cancel() + verify(strategy, never()).expandWithAnimation(any()) + } + + @Test + fun `BottomToolbarBehaviorStrategy - forceExpandWithAnimation should not expand the toolbar if it is already expanded`() { + val strategy = spy(BottomViewBehaviorStrategy()) + strategy.wasLastExpanding = false + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(0f).`when`(view).translationY + + strategy.forceExpandWithAnimation(view, -100f) + + verify(strategy.animator, never()).cancel() + verify(strategy, never()).expandWithAnimation(any()) + } + + @Test + fun `TopToolbarBehaviorStrategy - forceExpandWithAnimation should expand toolbar`() { + // Setting the scenario in which forceExpandWithAnimation will actually do what the name says. + // Below this test we can change each variable one at a time to test them in isolation. + + val strategy = spy(TopViewBehaviorStrategy()) + strategy.wasLastExpanding = false + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(-100f).`when`(view).translationY + + strategy.forceExpandWithAnimation(view, -100f) + + verify(strategy.animator).cancel() + verify(strategy).expandWithAnimation(any()) + } + + @Test + fun `TopToolbarBehaviorStrategy - forceExpandWithAnimation should not force expand the toolbar if not currently collapsing`() { + val strategy = spy(TopViewBehaviorStrategy()) + strategy.wasLastExpanding = true + val animator: ValueAnimator = mock() + doReturn(true).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(-100f).`when`(view).translationY + + strategy.forceExpandWithAnimation(view, -100f) + + verify(strategy.animator, never()).cancel() + verify(strategy, never()).expandWithAnimation(any()) + } + + @Test + fun `TopToolbarBehaviorStrategy - forceExpandWithAnimation should not expand if user swipes up`() { + val strategy = spy(TopViewBehaviorStrategy()) + strategy.wasLastExpanding = false + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(-100f).`when`(view).translationY + + strategy.forceExpandWithAnimation(view, 10f) + + verify(strategy.animator, never()).cancel() + verify(strategy, never()).expandWithAnimation(any()) + } + + @Test + fun `TopToolbarBehaviorStrategy - forceExpandWithAnimation should not expand the toolbar if it is already expanded`() { + val strategy = spy(TopViewBehaviorStrategy()) + strategy.wasLastExpanding = false + val animator: ValueAnimator = mock() + doReturn(false).`when`(animator).isStarted + strategy.animator = animator + val view: View = mock() + doReturn(0f).`when`(view).translationY + + strategy.forceExpandWithAnimation(view, -100f) + + verify(strategy.animator, never()).cancel() + verify(strategy, never()).expandWithAnimation(any()) + } + + @Test + fun `BottomToolbarBehaviorStrategy - collapseWithAnimation should animate translating the toolbar down, off-screen`() { + val strategy = spy(BottomViewBehaviorStrategy()) + val view: View = mock() + doReturn(100).`when`(view).height + + strategy.collapseWithAnimation(view) + + verify(strategy).animateToTranslationY(view, 100f) + } + + @Test + fun `TopToolbarBehaviorStrategy - collapseWithAnimation should animate translating the toolbar up, off-screen`() { + val strategy = spy(TopViewBehaviorStrategy()) + val view: View = mock() + doReturn(100).`when`(view).height + + strategy.collapseWithAnimation(view) + + verify(strategy).animateToTranslationY(view, -100f) + } + + @Test + fun `BottomToolbarBehaviorStrategy - translate should translate up the toolbar with the distance parameter`() { + val strategy = BottomViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(50f).`when`(view).translationY + + strategy.translate(view, -25f) + + verify(view).translationY = 25f + } + + @Test + fun `BottomToolbarBehaviorStrategy - translate should translate down the toolbar with the distance parameter`() { + val strategy = BottomViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(50f).`when`(view).translationY + + strategy.translate(view, 25f) + + verify(view).translationY = 75f + } + + @Test + fun `BottomToolbarBehaviorStrategy - translate should not translate up the toolbar if already expanded`() { + val strategy = BottomViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(0f).`when`(view).translationY + + strategy.translate(view, -1f) + + verify(view).translationY = 0f + } + + @Test + fun `BottomToolbarBehaviorStrategy - translate should not translate up the toolbar more than to 0`() { + val strategy = BottomViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(50f).`when`(view).translationY + + strategy.translate(view, -51f) + + verify(view).translationY = 0f + } + + @Test + fun `BottomToolbarBehaviorStrategy - translate should not translate down the toolbar if already collapsed`() { + val strategy = BottomViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(100f).`when`(view).translationY + + strategy.translate(view, 1f) + + verify(view).translationY = 100f + } + + @Test + fun `BottomToolbarBehaviorStrategy - translate should not translate down the toolbar more than it's height`() { + val strategy = BottomViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(50f).`when`(view).translationY + + strategy.translate(view, 51f) + + verify(view).translationY = 100f + } + + @Test + fun `TopToolbarBehaviorStrategy - translate should translate down the toolbar with the distance parameter`() { + val strategy = TopViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(-50f).`when`(view).translationY + + strategy.translate(view, 25f) + + verify(view).translationY = -75f + } + + @Test + fun `TopToolbarBehaviorStrategy - translate should translate up the toolbar with the distance parameter`() { + val strategy = TopViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(-50f).`when`(view).translationY + + strategy.translate(view, 25f) + + verify(view).translationY = -75f + } + + @Test + fun `TopToolbarBehaviorStrategy - translate should not translate down the toolbar if already expanded`() { + val strategy = TopViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(0f).`when`(view).translationY + + strategy.translate(view, -1f) + + verify(view).translationY = 0f + } + + @Test + fun `TopToolbarBehaviorStrategy - translate should not translate down the toolbar more than to 0`() { + val strategy = TopViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(-50f).`when`(view).translationY + + strategy.translate(view, -51f) + + verify(view).translationY = 0f + } + + @Test + fun `TopToolbarBehaviorStrategy - translate should not translate up the toolbar if already collapsed`() { + val strategy = TopViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(-100f).`when`(view).translationY + + strategy.translate(view, 1f) + + verify(view).translationY = -100f + } + + @Test + fun `TopToolbarBehaviorStrategy - translate should not translate up the toolbar more than it's height`() { + val strategy = TopViewBehaviorStrategy() + val view: View = mock() + doReturn(100).`when`(view).height + doReturn(-50f).`when`(view).translationY + + strategy.translate(view, 51f) + + verify(view).translationY = -100f + } + + @Test + fun `BottomToolbarBehaviorStrategy - animateToTranslationY should set wasLastExpanding if expanding`() { + val strategy = BottomViewBehaviorStrategy() + strategy.wasLastExpanding = false + val view: View = mock() + doReturn(50f).`when`(view).translationY + + strategy.animateToTranslationY(view, 10f) + assertTrue(strategy.wasLastExpanding) + + strategy.animateToTranslationY(view, 60f) + assertFalse(strategy.wasLastExpanding) + } + + @Test + fun `BottomToolbarBehaviorStrategy - animateToTranslationY should animate to the indicated y translation`() { + val strategy = spy(BottomViewBehaviorStrategy()) + strategy.wasLastExpanding = false + val view = View(testContext) + val animator: ValueAnimator = spy(strategy.animator) + strategy.animator = animator + + strategy.animateToTranslationY(view, 10f) + + verify(animator).start() + animator.end() + assertEquals(10f, view.translationY) + } + + @Test + fun `TopToolbarBehaviorStrategy - animateToTranslationY should set wasLastExpanding if expanding`() { + val strategy = TopViewBehaviorStrategy() + strategy.wasLastExpanding = false + val view: View = mock() + doReturn(-50f).`when`(view).translationY + + strategy.animateToTranslationY(view, -10f) + assertTrue(strategy.wasLastExpanding) + + strategy.animateToTranslationY(view, -60f) + assertFalse(strategy.wasLastExpanding) + } + + @Test + fun `TopToolbarBehaviorStrategy - animateToTranslationY should animate to the indicated y translation`() { + val strategy = spy(TopViewBehaviorStrategy()) + strategy.wasLastExpanding = false + val view = View(testContext) + val animator: ValueAnimator = spy(strategy.animator) + strategy.animator = animator + + strategy.animateToTranslationY(view, -10f) + + verify(animator).start() + animator.end() + assertEquals(-10f, view.translationY) + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/ViewYTranslatorTest.kt b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/ViewYTranslatorTest.kt new file mode 100644 index 0000000000..6a3a908adc --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/java/mozilla/components/ui/widgets/behavior/ViewYTranslatorTest.kt @@ -0,0 +1,113 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.ui.widgets.behavior + +import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.mock +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class ViewYTranslatorTest { + @Test + fun `yTranslator should use BottomToolbarBehaviorStrategy for bottom placed toolbars`() { + val yTranslator = ViewYTranslator(ViewPosition.BOTTOM) + + assertTrue(yTranslator.strategy is BottomViewBehaviorStrategy) + } + + @Test + fun `yTranslator should use TopToolbarBehaviorStrategy for top placed toolbars`() { + val yTranslator = ViewYTranslator(ViewPosition.TOP) + + assertTrue(yTranslator.strategy is TopViewBehaviorStrategy) + } + + @Test + fun `yTranslator should delegate it's strategy for snapWithAnimation`() { + val yTranslator = ViewYTranslator(ViewPosition.BOTTOM) + val strategy: ViewYTranslationStrategy = mock() + yTranslator.strategy = strategy + val view: View = mock() + + yTranslator.snapWithAnimation(view) + + verify(strategy).snapWithAnimation(view) + } + + @Test + fun `yTranslator should delegate it's strategy for expandWithAnimation`() { + val yTranslator = ViewYTranslator(ViewPosition.BOTTOM) + val strategy: ViewYTranslationStrategy = mock() + yTranslator.strategy = strategy + val view: View = mock() + + yTranslator.expandWithAnimation(view) + + verify(strategy).expandWithAnimation(view) + } + + @Test + fun `yTranslator should delegate it's strategy for collapseWithAnimation`() { + val yTranslator = ViewYTranslator(ViewPosition.BOTTOM) + val strategy: ViewYTranslationStrategy = mock() + yTranslator.strategy = strategy + val view: View = mock() + + yTranslator.collapseWithAnimation(view) + + verify(strategy).collapseWithAnimation(view) + } + + @Test + fun `yTranslator should delegate it's strategy for forceExpandIfNotAlready`() { + val yTranslator = ViewYTranslator(ViewPosition.BOTTOM) + val strategy: ViewYTranslationStrategy = mock() + yTranslator.strategy = strategy + val view: View = mock() + + yTranslator.forceExpandIfNotAlready(view, 14f) + + verify(strategy).forceExpandWithAnimation(view, 14f) + } + + @Test + fun `yTranslator should delegate it's strategy for translate`() { + val yTranslator = ViewYTranslator(ViewPosition.BOTTOM) + val strategy: ViewYTranslationStrategy = mock() + yTranslator.strategy = strategy + val view: View = mock() + + yTranslator.translate(view, 23f) + + verify(strategy).translate(view, 23f) + } + + @Test + fun `yTranslator should delegate it's strategy for cancelInProgressTranslation`() { + val yTranslator = ViewYTranslator(ViewPosition.BOTTOM) + val strategy: ViewYTranslationStrategy = mock() + yTranslator.strategy = strategy + + yTranslator.cancelInProgressTranslation() + + verify(strategy).cancelInProgressTranslation() + } + + @Test + fun `yTranslator should delegate it's strategy for snapImmediately`() { + val yTranslator = ViewYTranslator(ViewPosition.BOTTOM) + val strategy: ViewYTranslationStrategy = mock() + yTranslator.strategy = strategy + val view: View = mock() + + yTranslator.snapImmediately(view) + + verify(strategy).snapImmediately(view) + } +} diff --git a/mobile/android/android-components/components/ui/widgets/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/mobile/android/android-components/components/ui/widgets/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..cf1c399ea8 --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,2 @@ +mock-maker-inline +// This allows mocking final classes (classes are final by default in Kotlin) diff --git a/mobile/android/android-components/components/ui/widgets/src/test/resources/robolectric.properties b/mobile/android/android-components/components/ui/widgets/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..932b01b9eb --- /dev/null +++ b/mobile/android/android-components/components/ui/widgets/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 -- cgit v1.2.3