From c30a6232df03e1efbd9f3b226777b07e087a1122 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 12 Oct 2020 14:27:29 +0200 Subject: BASELINE: Update Chromium to 85.0.4183.140 Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen --- .../browser_ui/android/bottomsheet/BUILD.gn | 42 +- .../components/browser_ui/android/bottomsheet/DEPS | 7 + .../android/bottomsheet/internal/BUILD.gn | 44 + .../browser_ui/bottomsheet/BottomSheet.java | 1244 ++++++++++++++++++++ .../bottomsheet/BottomSheetControllerFactory.java | 33 + .../bottomsheet/BottomSheetControllerImpl.java | 505 ++++++++ .../bottomsheet/BottomSheetSwipeDetector.java | 269 +++++ .../bottomsheet/BottomSheetSwipeDetectorTest.java | 319 +++++ .../bottomsheet/TouchRestrictingFrameLayout.java | 50 + .../bottomsheet/java/res/layout/bottom_sheet.xml | 44 + .../android/bottomsheet/java/res/values/dimens.xml | 12 + .../bottomsheet/BottomSheetController.java | 158 +++ .../bottomsheet/BottomSheetObserver.java | 57 + .../bottomsheet/EmptyBottomSheetObserver.java | 30 + .../bottomsheet/ManagedBottomSheetController.java | 54 + .../browser_ui/android/bottomsheet/test/BUILD.gn | 16 + .../browser_ui/client_certificate/OWNERS | 7 + .../browser_ui/client_certificate/android/BUILD.gn | 58 + .../browser_ui/client_certificate/android/DEPS | 6 + .../SSLClientCertificateRequest.java | 320 +++++ .../SSLClientCertificateRequestTest.java | 63 + .../android/ssl_client_certificate_request.cc | 394 +++++++ .../android/ssl_client_certificate_request.h | 32 + .../browser_ui/http_auth/android/BUILD.gn | 33 + .../components/browser_ui/http_auth/android/DEPS | 3 + .../components/browser_ui/http_auth/android/OWNERS | 1 + .../browser_ui/http_auth/android/java/res/OWNERS | 9 + .../android/java/res/layout/http_auth_dialog.xml | 52 + .../browser_ui/http_auth/LoginPrompt.java | 142 +++ chromium/components/browser_ui/media/OWNERS | 4 + .../components/browser_ui/media/android/BUILD.gn | 95 ++ chromium/components/browser_ui/media/android/DEPS | 6 + .../java/res/drawable-hdpi/audio_playing.png | Bin 0 -> 292 bytes .../res/drawable-hdpi/audio_playing_square.png | Bin 0 -> 770 bytes .../drawable-hdpi/ic_fast_forward_white_36dp.png | Bin 0 -> 324 bytes .../drawable-hdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 338 bytes .../res/drawable-hdpi/ic_skip_next_white_36dp.png | Bin 0 -> 248 bytes .../drawable-hdpi/ic_skip_previous_white_36dp.png | Bin 0 -> 271 bytes .../java/res/drawable-mdpi/audio_playing.png | Bin 0 -> 201 bytes .../res/drawable-mdpi/audio_playing_square.png | Bin 0 -> 521 bytes .../drawable-mdpi/ic_fast_forward_white_36dp.png | Bin 0 -> 248 bytes .../drawable-mdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 272 bytes .../res/drawable-mdpi/ic_skip_next_white_36dp.png | Bin 0 -> 183 bytes .../drawable-mdpi/ic_skip_previous_white_36dp.png | Bin 0 -> 201 bytes .../java/res/drawable-xhdpi/audio_playing.png | Bin 0 -> 352 bytes .../res/drawable-xhdpi/audio_playing_square.png | Bin 0 -> 1056 bytes .../drawable-xhdpi/ic_fast_forward_white_36dp.png | Bin 0 -> 326 bytes .../drawable-xhdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 375 bytes .../res/drawable-xhdpi/ic_skip_next_white_36dp.png | Bin 0 -> 281 bytes .../drawable-xhdpi/ic_skip_previous_white_36dp.png | Bin 0 -> 309 bytes .../java/res/drawable-xxhdpi/audio_playing.png | Bin 0 -> 517 bytes .../res/drawable-xxhdpi/audio_playing_square.png | Bin 0 -> 1851 bytes .../drawable-xxhdpi/ic_fast_forward_white_36dp.png | Bin 0 -> 545 bytes .../drawable-xxhdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 546 bytes .../drawable-xxhdpi/ic_skip_next_white_36dp.png | Bin 0 -> 384 bytes .../ic_skip_previous_white_36dp.png | Bin 0 -> 402 bytes .../java/res/drawable-xxxhdpi/audio_playing.png | Bin 0 -> 675 bytes .../res/drawable-xxxhdpi/audio_playing_square.png | Bin 0 -> 2695 bytes .../ic_fast_forward_white_36dp.png | Bin 0 -> 625 bytes .../drawable-xxxhdpi/ic_fast_rewind_white_36dp.png | Bin 0 -> 644 bytes .../drawable-xxxhdpi/ic_skip_next_white_36dp.png | Bin 0 -> 434 bytes .../ic_skip_previous_white_36dp.png | Bin 0 -> 442 bytes .../browser_ui/media/MediaImageCallback.java | 22 + .../browser_ui/media/MediaImageManager.java | 235 ++++ .../browser_ui/media/MediaImageManagerTest.java | 298 +++++ .../MediaNotificationButtonComputationTest.java | 96 ++ .../media/MediaNotificationController.java | 905 ++++++++++++++ .../media/MediaNotificationImageUtils.java | 74 ++ .../browser_ui/media/MediaNotificationInfo.java | 354 ++++++ .../media/MediaNotificationListener.java | 57 + .../browser_ui/media/MediaNotificationUma.java | 47 + .../browser_ui/media/MediaSessionHelper.java | 573 +++++++++ .../browser_ui/media/MediaSessionUma.java | 42 + .../browser_ui/modaldialog/android/BUILD.gn | 2 +- .../modaldialog/AppModalPresenterTest.java | 14 +- .../modaldialog/ModalDialogViewTest.java | 20 +- .../browser_ui/notifications/android/BUILD.gn | 1 + .../notifications/ForegroundServiceUtils.java | 91 ++ .../browser_ui/settings/android/BUILD.gn | 4 + .../java/res/layout/settings_action_bar_shadow.xml | 19 + .../settings/android/java/res/values/styles.xml | 43 +- .../browser_ui/settings/ChromeBasePreference.java | 1 - .../browser_ui/settings/IconPreference.java | 38 + chromium/components/browser_ui/share/DEPS | 7 + chromium/components/browser_ui/share/OWNERS | 5 + .../components/browser_ui/share/android/BUILD.gn | 53 + .../android/java/res/layout/share_dialog_item.xml | 29 + .../browser_ui/share/ShareDialogAdapter.java | 52 + .../components/browser_ui/share/ShareHelper.java | 394 +++++++ .../browser_ui/share/ShareImageFileUtils.java | 449 +++++++ .../browser_ui/share/ShareImageFileUtilsTest.java | 348 ++++++ .../components/browser_ui/share/ShareParams.java | 246 ++++ chromium/components/browser_ui/site_settings/DEPS | 1 + .../browser_ui/site_settings/android/BUILD.gn | 8 +- .../java/res/drawable/settings_bluetooth.xml | 5 + .../java/res/xml/site_settings_preferences.xml | 46 +- .../site_settings/ContentSettingsResources.java | 10 + .../browser_ui/site_settings/PermissionInfo.java | 45 +- .../site_settings/SingleCategorySettings.java | 35 +- .../site_settings/SingleWebsiteSettings.java | 61 +- .../browser_ui/site_settings/SiteSettings.java | 7 +- .../site_settings/SiteSettingsCategory.java | 21 +- .../site_settings/SiteSettingsPrefClient.java | 7 - .../site_settings/SiteSettingsPreference.java | 36 - .../site_settings/WebsitePermissionsFetcher.java | 43 +- .../site_settings/WebsitePreference.java | 14 +- .../site_settings/WebsitePreferenceBridge.java | 55 +- .../site_settings/WebsiteAddressTest.java | 2 +- .../android/site_settings_feature_list.cc | 1 + .../site_settings/android/storage_info_fetcher.cc | 17 +- .../android/website_preference_bridge.cc | 18 +- .../strings/android/browser_ui_strings.grd | 135 ++- ...INFO_LITE_MODE_HTTPS_IMAGE_COMPRESSION.png.sha1 | 1 + .../scheduled_for_later.png.sha1 | 1 + .../browser_ui/strings/android/site_settings.grdp | 12 + .../IDS_WEBSITE_SETTINGS_BLUETOOTH.png.sha1 | 1 + ...EBSITE_SETTINGS_CATEGORY_BLUETOOTH_ASK.png.sha1 | 1 + ...TE_SETTINGS_CATEGORY_BLUETOOTH_BLOCKED.png.sha1 | 1 + .../android/translations/browser_ui_strings_af.xtb | 15 + .../android/translations/browser_ui_strings_am.xtb | 15 + .../android/translations/browser_ui_strings_ar.xtb | 15 + .../android/translations/browser_ui_strings_as.xtb | 15 + .../android/translations/browser_ui_strings_az.xtb | 15 + .../android/translations/browser_ui_strings_be.xtb | 15 + .../android/translations/browser_ui_strings_bg.xtb | 15 + .../android/translations/browser_ui_strings_bn.xtb | 15 + .../android/translations/browser_ui_strings_bs.xtb | 15 + .../android/translations/browser_ui_strings_ca.xtb | 15 + .../android/translations/browser_ui_strings_cs.xtb | 15 + .../android/translations/browser_ui_strings_da.xtb | 15 + .../android/translations/browser_ui_strings_de.xtb | 15 + .../android/translations/browser_ui_strings_el.xtb | 15 + .../translations/browser_ui_strings_en-GB.xtb | 15 + .../translations/browser_ui_strings_es-419.xtb | 15 + .../android/translations/browser_ui_strings_es.xtb | 15 + .../android/translations/browser_ui_strings_et.xtb | 15 + .../android/translations/browser_ui_strings_eu.xtb | 15 + .../android/translations/browser_ui_strings_fa.xtb | 15 + .../android/translations/browser_ui_strings_fi.xtb | 15 + .../translations/browser_ui_strings_fil.xtb | 15 + .../translations/browser_ui_strings_fr-CA.xtb | 15 + .../android/translations/browser_ui_strings_fr.xtb | 15 + .../android/translations/browser_ui_strings_gl.xtb | 15 + .../android/translations/browser_ui_strings_gu.xtb | 15 + .../android/translations/browser_ui_strings_hi.xtb | 15 + .../android/translations/browser_ui_strings_hr.xtb | 15 + .../android/translations/browser_ui_strings_hu.xtb | 15 + .../android/translations/browser_ui_strings_hy.xtb | 15 + .../android/translations/browser_ui_strings_id.xtb | 15 + .../android/translations/browser_ui_strings_is.xtb | 15 + .../android/translations/browser_ui_strings_it.xtb | 15 + .../android/translations/browser_ui_strings_iw.xtb | 15 + .../android/translations/browser_ui_strings_ja.xtb | 15 + .../android/translations/browser_ui_strings_ka.xtb | 15 + .../android/translations/browser_ui_strings_kk.xtb | 15 + .../android/translations/browser_ui_strings_km.xtb | 15 + .../android/translations/browser_ui_strings_kn.xtb | 15 + .../android/translations/browser_ui_strings_ko.xtb | 15 + .../android/translations/browser_ui_strings_ky.xtb | 15 + .../android/translations/browser_ui_strings_lo.xtb | 15 + .../android/translations/browser_ui_strings_lt.xtb | 15 + .../android/translations/browser_ui_strings_lv.xtb | 15 + .../android/translations/browser_ui_strings_mk.xtb | 15 + .../android/translations/browser_ui_strings_ml.xtb | 15 + .../android/translations/browser_ui_strings_mn.xtb | 15 + .../android/translations/browser_ui_strings_mr.xtb | 15 + .../android/translations/browser_ui_strings_ms.xtb | 15 + .../android/translations/browser_ui_strings_my.xtb | 15 + .../android/translations/browser_ui_strings_ne.xtb | 15 + .../android/translations/browser_ui_strings_nl.xtb | 15 + .../android/translations/browser_ui_strings_no.xtb | 15 + .../android/translations/browser_ui_strings_or.xtb | 15 + .../android/translations/browser_ui_strings_pa.xtb | 15 + .../android/translations/browser_ui_strings_pl.xtb | 15 + .../translations/browser_ui_strings_pt-BR.xtb | 15 + .../translations/browser_ui_strings_pt-PT.xtb | 15 + .../android/translations/browser_ui_strings_ro.xtb | 15 + .../android/translations/browser_ui_strings_ru.xtb | 15 + .../android/translations/browser_ui_strings_si.xtb | 15 + .../android/translations/browser_ui_strings_sk.xtb | 15 + .../android/translations/browser_ui_strings_sl.xtb | 15 + .../android/translations/browser_ui_strings_sq.xtb | 15 + .../translations/browser_ui_strings_sr-Latn.xtb | 15 + .../android/translations/browser_ui_strings_sr.xtb | 15 + .../android/translations/browser_ui_strings_sv.xtb | 15 + .../android/translations/browser_ui_strings_sw.xtb | 15 + .../android/translations/browser_ui_strings_ta.xtb | 15 + .../android/translations/browser_ui_strings_te.xtb | 15 + .../android/translations/browser_ui_strings_th.xtb | 15 + .../android/translations/browser_ui_strings_tr.xtb | 15 + .../android/translations/browser_ui_strings_uk.xtb | 15 + .../android/translations/browser_ui_strings_ur.xtb | 15 + .../android/translations/browser_ui_strings_uz.xtb | 15 + .../android/translations/browser_ui_strings_vi.xtb | 15 + .../translations/browser_ui_strings_zh-CN.xtb | 15 + .../translations/browser_ui_strings_zh-HK.xtb | 15 + .../translations/browser_ui_strings_zh-TW.xtb | 15 + .../android/translations/browser_ui_strings_zu.xtb | 15 + .../components/browser_ui/styles/android/BUILD.gn | 34 + .../color/default_icon_color_light_tint_list.xml | 12 +- .../java/res/drawable-hdpi/ic_pause_white_24dp.png | Bin 0 -> 103 bytes .../java/res/drawable-hdpi/ic_pause_white_36dp.png | Bin 0 -> 123 bytes .../res/drawable-hdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 194 bytes .../res/drawable-hdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 242 bytes .../android/java/res/drawable-hdpi/top_round.9.png | Bin 0 -> 558 bytes .../java/res/drawable-mdpi/ic_pause_white_24dp.png | Bin 0 -> 82 bytes .../java/res/drawable-mdpi/ic_pause_white_36dp.png | Bin 0 -> 103 bytes .../res/drawable-mdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 154 bytes .../res/drawable-mdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 194 bytes .../android/java/res/drawable-mdpi/top_round.9.png | Bin 0 -> 357 bytes .../java/res/drawable-night-hdpi/top_round.9.png | Bin 0 -> 832 bytes .../java/res/drawable-night-mdpi/top_round.9.png | Bin 0 -> 557 bytes .../java/res/drawable-night-xhdpi/top_round.9.png | Bin 0 -> 1058 bytes .../java/res/drawable-night-xxhdpi/top_round.9.png | Bin 0 -> 1616 bytes .../res/drawable-night-xxxhdpi/top_round.9.png | Bin 0 -> 2135 bytes .../res/drawable-xhdpi/ic_pause_white_24dp.png | Bin 0 -> 90 bytes .../res/drawable-xhdpi/ic_pause_white_36dp.png | Bin 0 -> 92 bytes .../drawable-xhdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 220 bytes .../drawable-xhdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 283 bytes .../java/res/drawable-xhdpi/top_round.9.png | Bin 0 -> 729 bytes .../res/drawable-xxhdpi/ic_pause_white_24dp.png | Bin 0 -> 92 bytes .../res/drawable-xxhdpi/ic_pause_white_36dp.png | Bin 0 -> 158 bytes .../drawable-xxhdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 283 bytes .../drawable-xxhdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 390 bytes .../java/res/drawable-xxhdpi/top_round.9.png | Bin 0 -> 964 bytes .../res/drawable-xxxhdpi/ic_pause_white_24dp.png | Bin 0 -> 94 bytes .../res/drawable-xxxhdpi/ic_pause_white_36dp.png | Bin 0 -> 110 bytes .../drawable-xxxhdpi/ic_play_arrow_white_24dp.png | Bin 0 -> 343 bytes .../drawable-xxxhdpi/ic_play_arrow_white_36dp.png | Bin 0 -> 461 bytes .../java/res/drawable-xxxhdpi/top_round.9.png | Bin 0 -> 1335 bytes .../android/java/res/drawable/ic_security_grey.xml | 11 + .../android/java/res/drawable/ic_update_grey.xml | 11 + .../android/java/res/drawable/ic_vpn_key_grey.xml | 5 + .../components/browser_ui/util/android/BUILD.gn | 8 + .../browser_ui/util/date/CalendarFactory.java | 44 + .../browser_ui/util/date/CalendarUtils.java | 57 + .../browser_ui/util/date/StringUtils.java | 52 + chromium/components/browser_ui/webshare/OWNERS | 5 + .../browser_ui/webshare/android/BUILD.gn | 42 + .../components/browser_ui/webshare/android/DEPS | 5 + .../browser_ui/webshare/BlobReceiver.java | 177 +++ .../browser_ui/webshare/ShareServiceImpl.java | 290 +++++ .../browser_ui/webshare/ShareServiceImplTest.java | 84 ++ .../browser_ui/webshare/SharedFileCollator.java | 74 ++ .../webshare/SharedFileCollatorTest.java | 108 ++ .../components/browser_ui/widget/android/BUILD.gn | 6 + .../java/res/layout/promo_dialog_layout.xml | 2 + .../widget/android/java/res/values/dimens.xml | 2 +- .../browser_ui/widget/DualControlLayoutTest.java | 3 +- .../browser_ui/widget/InsetObserverViewTest.java | 3 +- .../browser_ui/widget/MoreProgressButtonTest.java | 5 +- .../components/browser_ui/widget/PromoDialog.java | 17 +- .../browser_ui/widget/PromoDialogLayout.java | 14 +- .../browser_ui/widget/PromoDialogTest.java | 2 +- .../browser_ui/widget/RadioButtonLayoutTest.java | 3 +- .../browser_ui/widget/RadioButtonRenderTest.java | 6 +- .../RadioButtonWithDescriptionLayoutTest.java | 3 +- .../widget/RadioButtonWithEditTextTest.java | 19 +- .../widget/RoundedIconGeneratorTest.java | 3 +- .../browser_ui/widget/WrappingLayoutTest.java | 6 +- .../widget/highlight/ViewHighlighterTest.java | 3 +- .../widget/image_tiles/TileCoordinatorImpl.java | 2 +- .../widget/image_tiles/TileListView.java | 24 +- .../widget/listmenu/ListMenuRenderTest.java | 3 +- .../widget/promo/PromoCardCoordinatorTest.java | 2 +- .../widget/promo/PromoCardImpressionTest.java | 3 +- .../widget/promo/PromoCardViewRenderTest.java | 18 +- .../browser_ui/widget/scrim/ScrimTest.java | 20 +- .../widget/text/AlertDialogEditText.java | 62 +- 269 files changed, 11320 insertions(+), 342 deletions(-) create mode 100644 chromium/components/browser_ui/android/bottomsheet/DEPS create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/BUILD.gn create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetector.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetectorTest.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/res/layout/bottom_sheet.xml create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/res/values/dimens.xml create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/EmptyBottomSheetObserver.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java create mode 100644 chromium/components/browser_ui/android/bottomsheet/test/BUILD.gn create mode 100644 chromium/components/browser_ui/client_certificate/OWNERS create mode 100644 chromium/components/browser_ui/client_certificate/android/BUILD.gn create mode 100644 chromium/components/browser_ui/client_certificate/android/DEPS create mode 100644 chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java create mode 100644 chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequestTest.java create mode 100644 chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.cc create mode 100644 chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.h create mode 100644 chromium/components/browser_ui/http_auth/android/BUILD.gn create mode 100644 chromium/components/browser_ui/http_auth/android/DEPS create mode 100644 chromium/components/browser_ui/http_auth/android/OWNERS create mode 100644 chromium/components/browser_ui/http_auth/android/java/res/OWNERS create mode 100644 chromium/components/browser_ui/http_auth/android/java/res/layout/http_auth_dialog.xml create mode 100644 chromium/components/browser_ui/http_auth/android/java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java create mode 100644 chromium/components/browser_ui/media/OWNERS create mode 100644 chromium/components/browser_ui/media/android/BUILD.gn create mode 100644 chromium/components/browser_ui/media/android/DEPS create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing_square.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageCallback.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManager.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationButtonComputationTest.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationImageUtils.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfo.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationListener.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java create mode 100644 chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionUma.java create mode 100644 chromium/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java create mode 100644 chromium/components/browser_ui/settings/android/java/res/layout/settings_action_bar_shadow.xml create mode 100644 chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/IconPreference.java create mode 100644 chromium/components/browser_ui/share/DEPS create mode 100644 chromium/components/browser_ui/share/OWNERS create mode 100644 chromium/components/browser_ui/share/android/BUILD.gn create mode 100644 chromium/components/browser_ui/share/android/java/res/layout/share_dialog_item.xml create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareDialogAdapter.java create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java create mode 100644 chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareParams.java create mode 100644 chromium/components/browser_ui/site_settings/android/java/res/drawable/settings_bluetooth.xml delete mode 100644 chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreference.java create mode 100644 chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_PAGE_INFO_LITE_MODE_HTTPS_IMAGE_COMPRESSION.png.sha1 create mode 100644 chromium/components/browser_ui/strings/android/browser_ui_strings_grd/scheduled_for_later.png.sha1 create mode 100644 chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_BLUETOOTH.png.sha1 create mode 100644 chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_ASK.png.sha1 create mode 100644 chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_BLOCKED.png.sha1 create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-hdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-mdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-xhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-xxhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-night-xxxhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/top_round.9.png create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable/ic_security_grey.xml create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable/ic_update_grey.xml create mode 100644 chromium/components/browser_ui/styles/android/java/res/drawable/ic_vpn_key_grey.xml create mode 100644 chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarFactory.java create mode 100644 chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarUtils.java create mode 100644 chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/StringUtils.java create mode 100644 chromium/components/browser_ui/webshare/OWNERS create mode 100644 chromium/components/browser_ui/webshare/android/BUILD.gn create mode 100644 chromium/components/browser_ui/webshare/android/DEPS create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/BlobReceiver.java create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollator.java create mode 100644 chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollatorTest.java (limited to 'chromium/components/browser_ui') diff --git a/chromium/components/browser_ui/android/bottomsheet/BUILD.gn b/chromium/components/browser_ui/android/bottomsheet/BUILD.gn index a9965b2f22d..f43f2943228 100644 --- a/chromium/components/browser_ui/android/bottomsheet/BUILD.gn +++ b/chromium/components/browser_ui/android/bottomsheet/BUILD.gn @@ -2,11 +2,47 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//build/config/android/config.gni") import("//build/config/android/rules.gni") +import("//chrome/android/features/android_library_factory_tmpl.gni") android_library("java") { - sources = [ "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java" ] + sources = [ + "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java", + "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java", + "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java", + "java/src/org/chromium/components/browser_ui/bottomsheet/EmptyBottomSheetObserver.java", + ] - deps = [ "//third_party/android_deps:androidx_annotation_annotation_java" ] + deps = [ + ":java_resources", + "//components/browser_ui/widget/android:java", + "//third_party/android_deps:androidx_annotation_annotation_java", + "//ui/android:ui_java", + ] +} + +android_resources("java_resources") { + custom_package = "org.chromium.components.browser_ui.bottomsheet" + sources = [ + "java/res/layout/bottom_sheet.xml", + "java/res/values/dimens.xml", + ] + + deps = [ "//components/browser_ui/strings/android:browser_ui_strings_grd" ] +} + +# The only dependent on this code outside of this component should be glue. +android_library("manager_java") { + sources = [ "java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java" ] + + deps = [ ":java" ] +} + +android_library_factory("factory_java") { + sources = [ "internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java" ] + + deps = [ + ":java", + ":manager_java", + ] } diff --git a/chromium/components/browser_ui/android/bottomsheet/DEPS b/chromium/components/browser_ui/android/bottomsheet/DEPS new file mode 100644 index 00000000000..1ff4639a034 --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/DEPS @@ -0,0 +1,7 @@ +noparent = True +include_rules = [ + "+base/android", + "+base/test/android", + "+components/browser_ui/widget/android", + "+ui/android", +] diff --git a/chromium/components/browser_ui/android/bottomsheet/internal/BUILD.gn b/chromium/components/browser_ui/android/bottomsheet/internal/BUILD.gn new file mode 100644 index 00000000000..a81af4a478e --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/internal/BUILD.gn @@ -0,0 +1,44 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_library("java") { + visibility = [ + ":*", + "../test:*", + "//chrome/android:chrome_all_java", + ] + + sources = [ + "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java", + "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java", + "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java", + "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetector.java", + "java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java", + ] + + deps = [ + "..:java", + "..:java_resources", + "..:manager_java", + "//base:base_java", + "//components/browser_ui/widget/android:java", + "//third_party/android_deps:androidx_annotation_annotation_java", + "//ui/android:ui_java", + ] +} + +android_library("junit_tests") { + bypass_platform_checks = true + testonly = true + sources = [ "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetectorTest.java" ] + deps = [ + ":java", + "//base:base_java", + "//base:base_junit_test_support", + "//third_party/junit", + "//third_party/mockito:mockito_java", + ] +} diff --git a/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java new file mode 100644 index 00000000000..1ced11721f2 --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java @@ -0,0 +1,1244 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Color; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.widget.FrameLayout; + +import androidx.annotation.DimenRes; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.MathUtils; +import org.chromium.base.ObserverList; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent.HeightMode; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason; +import org.chromium.ui.KeyboardVisibilityDelegate; +import org.chromium.ui.util.AccessibilityUtil; + +/** + * This class defines the bottom sheet that has multiple states and a persistently showing toolbar. + * Namely, the states are: + * - PEEK: Only the toolbar is visible at the bottom of the screen. + * - HALF: The sheet is expanded to consume around half of the screen. + * - FULL: The sheet is expanded to its full height. + * + * All the computation in this file is based off of the bottom of the screen instead of the top + * for simplicity. This means that the bottom of the screen is 0 on the Y axis. + */ +class BottomSheet extends FrameLayout + implements BottomSheetSwipeDetector.SwipeableBottomSheet, View.OnLayoutChangeListener { + /** + * The base duration of the settling animation of the sheet. 218 ms is a spec for material + * design (this is the minimum time a user is guaranteed to pay attention to something). + */ + private static final int BASE_ANIMATION_DURATION_MS = 218; + + /** + * The fraction of the way to the next state the sheet must be swiped to animate there when + * released. This is the value used when there are 3 active states. A smaller value here means + * a smaller swipe is needed to move the sheet around. + */ + private static final float THRESHOLD_TO_NEXT_STATE_3 = 0.4f; + + /** This is similar to {@link #THRESHOLD_TO_NEXT_STATE_3} but for 2 states instead of 3. */ + private static final float THRESHOLD_TO_NEXT_STATE_2 = 0.3f; + + /** The height ratio for the sheet in the SheetState.HALF state. */ + private static final float HALF_HEIGHT_RATIO = 0.75f; + + /** The desired height of a content that has just been shown or whose height was invalidated. */ + private static final float HEIGHT_UNSPECIFIED = -1.0f; + + /** A flag to force the small screen state of the bottom sheet. */ + private static Boolean sIsSmallScreenForTesting; + + /** The interpolator that the height animator uses. */ + private final Interpolator mInterpolator = new DecelerateInterpolator(1.0f); + + /** The list of observers of this sheet. */ + private final ObserverList mObservers = new ObserverList<>(); + + /** The visible rect for the screen taking the keyboard into account. */ + private final Rect mVisibleViewportRect = new Rect(); + + /** An out-array for use with getLocationInWindow to prevent constant allocations. */ + private final int[] mCachedLocation = new int[2]; + + /** The minimum distance between half and full states to allow the half state. */ + private final float mMinHalfFullDistance; + + /** The height of the shadow that sits above the toolbar. */ + private final int mToolbarShadowHeight; + + /** The view that contains the sheet. */ + private ViewGroup mSheetContainer; + + /** For detecting scroll and fling events on the bottom sheet. */ + private BottomSheetSwipeDetector mGestureDetector; + + /** The animator used to move the sheet to a fixed state when released by the user. */ + private ValueAnimator mSettleAnimator; + + /** The width of the view that contains the bottom sheet. */ + private int mContainerWidth; + + /** The height of the view that contains the bottom sheet. */ + private int mContainerHeight; + + /** The desired height of the current content view. */ + private float mContentDesiredHeight = HEIGHT_UNSPECIFIED; + + /** + * The current offset of the sheet from the bottom of the screen in px. This does not include + * added offset from the scrolling of the browser controls which allows the sheet's toolbar to + * show and hide in-sync with the top toolbar. + */ + private float mCurrentOffsetPx; + + /** The current state that the sheet is in. */ + @SheetState + private int mCurrentState = SheetState.HIDDEN; + + /** The target sheet state. This is the state that the sheet is currently moving to. */ + @SheetState + private int mTargetState = SheetState.NONE; + + /** While scrolling, this holds the state the scrolling started in. Otherwise, it's NONE. */ + @SheetState + int mScrollingStartState = SheetState.NONE; + + /** A handle to the content being shown by the sheet. */ + @Nullable + protected BottomSheetContent mSheetContent; + + /** A handle to the FrameLayout that holds the content of the bottom sheet. */ + private TouchRestrictingFrameLayout mBottomSheetContentContainer; + + /** + * The last offset ratio sent to observers of onSheetOffsetChanged(). This is used to ensure the + * min and max values are provided at least once (0 and 1). + */ + private float mLastOffsetRatioSent; + + /** The FrameLayout used to hold the bottom sheet toolbar. */ + private TouchRestrictingFrameLayout mToolbarHolder; + + /** + * The default toolbar view. This is shown when the current bottom sheet content doesn't have + * its own toolbar and when the bottom sheet is closed. + */ + protected View mDefaultToolbarView; + + /** Whether the {@link BottomSheet} and its children should react to touch events. */ + private boolean mIsTouchEnabled; + + /** Whether the sheet is currently open. */ + private boolean mIsSheetOpen; + + /** Whether {@link #destroy()} has been called. **/ + private boolean mIsDestroyed; + + /** The ratio in the range [0, 1] that the browser controls are hidden. */ + private float mBrowserControlsHiddenRatio; + + /** A means of checking whether accessibility is currently enabled. */ + private AccessibilityUtil mAccessibilityUtil; + + @Override + public boolean shouldGestureMoveSheet(MotionEvent initialEvent, MotionEvent currentEvent) { + // If the sheet is scrolling off-screen or in the process of hiding, gestures should not + // affect it. + if (getCurrentOffsetPx() < getSheetHeightForState(SheetState.PEEK) + || getOffsetFromBrowserControls() > 0) { + return false; + } + + // If the sheet is already open, the experiment is not enabled, or accessibility is enabled + // there is no need to restrict the swipe area. + if (isSheetOpen() || mAccessibilityUtil.isAccessibilityEnabled()) { + return true; + } + + float startX = mVisibleViewportRect.left; + float endX = getToolbarView().getWidth() + mVisibleViewportRect.left; + return currentEvent.getRawX() > startX && currentEvent.getRawX() < endX; + } + + /** + * Constructor for inflation from XML. + * @param context An Android context. + * @param atts The XML attributes. + */ + public BottomSheet(Context context, AttributeSet atts) { + super(context, atts); + + mMinHalfFullDistance = + getResources().getDimensionPixelSize(R.dimen.bottom_sheet_min_full_half_distance); + mToolbarShadowHeight = getResources().getDimensionPixelOffset(getTopShadowResourceId()); + + mGestureDetector = new BottomSheetSwipeDetector(context, this); + mIsTouchEnabled = true; + } + + /** @return The dimen describing the height of the shadow above the bottom sheet. */ + static @DimenRes int getTopShadowResourceId() { + return R.dimen.bottom_sheet_toolbar_shadow_height; + } + + static @DimenRes int getShadowTopOffsetResourceId() { + return R.dimen.bottom_sheet_shadow_top_offset; + } + + /** + * Called when the activity containing the {@link BottomSheet} is destroyed. + */ + void destroy() { + mIsDestroyed = true; + mIsTouchEnabled = false; + mObservers.clear(); + endAnimations(); + } + + /** @param accessibilityUtil A mechanism for testing whether accessibility is enabled. */ + void setAccssibilityUtil(AccessibilityUtil accessibilityUtil) { + mAccessibilityUtil = accessibilityUtil; + } + + /** Immediately end all animations and null the animators. */ + void endAnimations() { + if (mSettleAnimator != null) mSettleAnimator.end(); + mSettleAnimator = null; + } + + /** @return Whether the sheet is in the process of hiding. */ + boolean isHiding() { + return mSettleAnimator != null && mTargetState == SheetState.HIDDEN; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + if (!isTouchEventInUsableArea(e) && e.getActionMasked() == MotionEvent.ACTION_DOWN) { + return false; + } + + // If touch is disabled, act like a black hole and consume touch events without doing + // anything with them. + if (!mIsTouchEnabled) return true; + + if (isHiding()) return false; + + return mGestureDetector.onInterceptTouchEvent(e); + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + if (!isTouchEventInUsableArea(e) && e.getActionMasked() == MotionEvent.ACTION_DOWN) { + return false; + } + + // If touch is disabled, act like a black hole and consume touch events without doing + // anything with them. + if (!mIsTouchEnabled) return true; + + mGestureDetector.onTouchEvent(e); + + return true; + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + assert heightSize != 0; + int height = heightSize + mToolbarShadowHeight; + int mode = isFullHeightWrapContent() ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY; + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, mode)); + } + + /** + * Adds layout change listeners to the views that the bottom sheet depends on. Namely the + * heights of the root view and control container are important as they are used in many of the + * calculations in this class. + * @param window Android window for getting insets. + * @param keyboardDelegate Delegate for hiding the keyboard. + */ + public void init(Window window, KeyboardVisibilityDelegate keyboardDelegate) { + View root = (View) getParent(); + + mToolbarHolder = + (TouchRestrictingFrameLayout) findViewById(R.id.bottom_sheet_toolbar_container); + mToolbarHolder.setBackgroundResource( + org.chromium.components.browser_ui.styles.R.drawable.top_round); + + mDefaultToolbarView = mToolbarHolder.findViewById(R.id.bottom_sheet_toolbar); + + getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; + + mBottomSheetContentContainer = + (TouchRestrictingFrameLayout) findViewById(R.id.bottom_sheet_content); + mBottomSheetContentContainer.setBottomSheet(this); + mBottomSheetContentContainer.setBackgroundResource( + org.chromium.components.browser_ui.styles.R.drawable.top_round); + + mContainerWidth = root.getWidth(); + mContainerHeight = root.getHeight(); + + // Listen to height changes on the root. + root.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + private int mPreviousKeyboardHeight; + + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + // Compute the new height taking the keyboard into account. + // TODO(mdjones): Share this logic with LocationBarLayout: crbug.com/725725. + int previousWidth = mContainerWidth; + int previousHeight = mContainerHeight; + mContainerWidth = right - left; + mContainerHeight = bottom - top; + + if (previousWidth != mContainerWidth || previousHeight != mContainerHeight) { + if (mCurrentState == SheetState.HALF && !isHalfStateEnabled()) { + setSheetState(SheetState.FULL, false); + } + invalidateContentDesiredHeight(); + } + + int heightMinusKeyboard = (int) mContainerHeight; + int keyboardHeight = 0; + + // Reset mVisibleViewportRect regardless of sheet open state as it is used outside + // of calculating the keyboard height. + window.getDecorView().getWindowVisibleDisplayFrame(mVisibleViewportRect); + if (isSheetOpen()) { + int decorHeight = window.getDecorView().getHeight(); + heightMinusKeyboard = Math.min(decorHeight, mVisibleViewportRect.height()); + keyboardHeight = (int) (mContainerHeight - heightMinusKeyboard); + } + + if (keyboardHeight != mPreviousKeyboardHeight) { + // If the keyboard height changed, recompute the padding for the content area. + // This shrinks the content size while retaining the default background color + // where the keyboard is appearing. If the sheet is not showing, resize the + // sheet to its default state. + mBottomSheetContentContainer.setPadding( + mBottomSheetContentContainer.getPaddingLeft(), + mBottomSheetContentContainer.getPaddingTop(), + mBottomSheetContentContainer.getPaddingRight(), keyboardHeight); + } + + if (previousHeight != mContainerHeight + || mPreviousKeyboardHeight != keyboardHeight) { + // If we are in the middle of a touch event stream (i.e. scrolling while + // keyboard is up) don't set the sheet state. Instead allow the gesture detector + // to position the sheet and make sure the keyboard hides. + if (mGestureDetector.isScrolling() && keyboardDelegate != null) { + keyboardDelegate.hideKeyboard(BottomSheet.this); + } else { + cancelAnimation(); + setSheetState(mCurrentState, false); + } + } + + mPreviousKeyboardHeight = keyboardHeight; + } + }); + + // Listen to height changes on the toolbar. + mToolbarHolder.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + // Make sure the size of the layout actually changed. + if (bottom - top == oldBottom - oldTop && right - left == oldRight - oldLeft) { + return; + } + + if (!mGestureDetector.isScrolling() && isRunningSettleAnimation()) return; + + setSheetState(mCurrentState, false); + } + }); + + mSheetContainer = (ViewGroup) this.getParent(); + mSheetContainer.removeView(this); + } + + /** @param ratio The current browser controls hidden ratio. */ + void setBrowserControlsHiddenRatio(float ratio) { + mBrowserControlsHiddenRatio = ratio; + + if (getSheetState() == SheetState.HIDDEN) return; + if (getCurrentOffsetPx() > getSheetHeightForState(SheetState.PEEK)) return; + + // Updating the offset will automatically account for the browser controls. + setSheetOffsetFromBottom(getCurrentOffsetPx(), StateChangeReason.SWIPE); + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + + // Trigger a relayout on window focus to correct any positioning issues when leaving Chrome + // previously. This is required as a layout is not triggered when coming back to Chrome + // with the keyboard previously shown. + if (hasWindowFocus) requestLayout(); + } + + @Override + public boolean isContentScrolledToTop() { + return mSheetContent == null || mSheetContent.getVerticalScrollOffset() <= 0; + } + + @Override + public float getCurrentOffsetPx() { + return mCurrentOffsetPx; + } + + @Override + public float getMinOffsetPx() { + return (swipeToDismissEnabled() ? getHiddenRatio() : getPeekRatio()) * mContainerHeight; + } + + /** + * Test whether a motion event is in the area of the sheet considered to be usable (i.e. not + * on the shadow shown above the sheet or some other decorative part of the view). + * @param e The motion event relative to the bottom sheet view. + * @return Whether the event is considered to be in the usable area of the sheet. + */ + public boolean isTouchEventInUsableArea(MotionEvent event) { + return event.getY() > getToolbarShadowHeight(); + } + + @Override + public boolean isTouchEventInToolbar(MotionEvent event) { + mToolbarHolder.getLocationInWindow(mCachedLocation); + // This check only tests for collision for the Y component since the sheet is the full width + // of the screen. We only care if the touch event is above the bottom of the toolbar since + // we won't receive an event if the touch is outside the sheet. + return mCachedLocation[1] + mToolbarHolder.getHeight() > event.getRawY(); + } + + /** + * @return Whether flinging down hard enough will close the sheet. + */ + private boolean swipeToDismissEnabled() { + return mSheetContent != null ? mSheetContent.swipeToDismissEnabled() : true; + } + + /** + * @return Whether the half state should be skipped when moving the sheet down. + */ + private boolean shouldSkipHalfStateOnScrollingDown() { + return mSheetContent == null || mSheetContent.skipHalfStateOnScrollingDown(); + } + + /** + * @return The minimum sheet state that the user can swipe to. i.e. flinging down will either + * close the sheet or peek it. + */ + @SheetState + int getMinSwipableSheetState() { + return swipeToDismissEnabled() || !isPeekStateEnabled() ? SheetState.HIDDEN + : SheetState.PEEK; + } + + /** + * Get the state that the bottom sheet should open to with the provided content. + * @return The minimum opened state for the current content. + */ + @SheetState + int getOpeningState() { + if (mSheetContent == null) { + return SheetState.HIDDEN; + } else if (isPeekStateEnabled()) { + return SheetState.PEEK; + } else if (isHalfStateEnabled()) { + return SheetState.HALF; + } + return SheetState.FULL; + } + + @Override + public float getMaxOffsetPx() { + return getFullRatio() * mContainerHeight; + } + + /** + * Show content in the bottom sheet's content area. + * @param content The {@link BottomSheetContent} to show, or null if no content should be shown. + */ + @VisibleForTesting + void showContent(@Nullable final BottomSheetContent content) { + // If the desired content is already showing, do nothing. + if (mSheetContent == content) return; + + // Remove this as listener from previous content layout and size changes. + if (mSheetContent != null) { + mSheetContent.setContentSizeListener(null); + mSheetContent.getContentView().removeOnLayoutChangeListener(this); + } + + swapViews(content != null ? content.getContentView() : null, + mSheetContent != null ? mSheetContent.getContentView() : null, + mBottomSheetContentContainer); + + View newToolbar = content != null ? content.getToolbarView() : null; + swapViews(newToolbar, mSheetContent != null ? mSheetContent.getToolbarView() : null, + mToolbarHolder); + + // We hide the default toolbar if the new content has its own. + mDefaultToolbarView.setVisibility(newToolbar != null ? GONE : VISIBLE); + + onSheetContentChanged(content); + } + + /** + * Removes the oldView (or sets it to invisible) and adds the new view to the specified parent. + * @param newView The new view to transition to. + * @param oldView The old view to transition from. + * @param parent The parent for newView and oldView. + */ + private void swapViews(final View newView, final View oldView, final ViewGroup parent) { + if (oldView != null && oldView.getParent() != null) parent.removeView(oldView); + if (newView != null && parent != newView.getParent()) parent.addView(newView); + } + + /** + * A notification that the sheet is exiting the peek state into one that shows content. + * @param reason The reason the sheet was opened, if any. + */ + private void onSheetOpened(@StateChangeReason int reason) { + if (mIsSheetOpen) return; + + mIsSheetOpen = true; + + for (BottomSheetObserver o : mObservers) o.onSheetOpened(reason); + } + + /** + * A notification that the sheet has returned to the peeking state. + * @param reason The {@link StateChangeReason} that the sheet was closed, + * if any. + */ + private void onSheetClosed(@StateChangeReason int reason) { + if (!mIsSheetOpen) return; + mIsSheetOpen = false; + + for (BottomSheetObserver o : mObservers) o.onSheetClosed(reason); + // If the sheet contents are cleared out before #onSheetClosed is called, do not try to + // retrieve the accessibility string. + if (getCurrentSheetContent() != null) { + announceForAccessibility(getResources().getString( + getCurrentSheetContent().getSheetClosedAccessibilityStringId())); + } + clearFocus(); + + setFocusable(false); + setFocusableInTouchMode(false); + setContentDescription(null); + } + + /** + * Cancels and nulls the height animation if it exists. + */ + private void cancelAnimation() { + if (mSettleAnimator == null) return; + mSettleAnimator.cancel(); + mSettleAnimator = null; + } + + /** + * Creates the sheet's animation to a target state. + * @param targetState The target state. + * @param reason The reason the sheet started animation. + */ + private void createSettleAnimation( + @SheetState final int targetState, @StateChangeReason final int reason) { + mTargetState = targetState; + mSettleAnimator = + ValueAnimator.ofFloat(getCurrentOffsetPx(), getSheetHeightForState(targetState)); + mSettleAnimator.setDuration(BASE_ANIMATION_DURATION_MS); + mSettleAnimator.setInterpolator(mInterpolator); + + // When the animation is canceled or ends, reset the handle to null. + mSettleAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + if (mIsDestroyed) return; + + mSettleAnimator = null; + setInternalCurrentState(targetState, reason); + mTargetState = SheetState.NONE; + } + }); + + mSettleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + setSheetOffsetFromBottom((Float) animator.getAnimatedValue(), reason); + } + }); + + setInternalCurrentState(SheetState.SCROLLING, reason); + mSettleAnimator.start(); + } + + /** + * @return Get the height in px that the peeking bar is offset due to the browser controls. + */ + private float getOffsetFromBrowserControls() { + if (mSheetContent == null || !mSheetContent.hideOnScroll() || !isPeekStateEnabled()) { + return 0; + } + + return getPeekRatio() * mContainerHeight * mBrowserControlsHiddenRatio; + } + + /** + * Sets the sheet's offset relative to the bottom of the screen. + * @param offset The offset that the sheet should be. + */ + void setSheetOffsetFromBottom(float offset, @StateChangeReason int reason) { + mCurrentOffsetPx = offset; + + // The browser controls offset is added here so that the sheet's toolbar behaves like the + // browser controls do. + float translationY = (mContainerHeight - mCurrentOffsetPx) + getOffsetFromBrowserControls(); + + if (isSheetOpen() && MathUtils.areFloatsEqual(translationY, getTranslationY())) return; + + setTranslationY(translationY); + + float hiddenHeight = getHiddenRatio() * mContainerHeight; + if (mCurrentOffsetPx <= hiddenHeight && this.getParent() != null) { + mSheetContainer.removeView(this); + } else if (mCurrentOffsetPx > hiddenHeight && this.getParent() == null) { + mSheetContainer.addView(this); + } + + // Do open/close computation based on the minimum allowed state by the sheet's content. + // Note that when transitioning from hidden to peek, even dismissable sheets may want + // to have a peek state. + @SheetState + int minSwipableState = getMinSwipableSheetState(); + if (isPeekStateEnabled() && (!isSheetOpen() || mTargetState == SheetState.PEEK)) { + minSwipableState = SheetState.PEEK; + } + + float minScrollableHeight = getSheetHeightForState(minSwipableState); + boolean isAtMinHeight = MathUtils.areFloatsEqual(getCurrentOffsetPx(), minScrollableHeight); + boolean heightLessThanPeek = getCurrentOffsetPx() < minScrollableHeight; + // Trigger the onSheetClosed event when the sheet is moving toward the hidden state if peek + // is disabled. This should be fine since touch is disabled when the sheet's target is + // hidden. + boolean triggerCloseWithHidden = !isPeekStateEnabled() && mTargetState == SheetState.HIDDEN; + + if (isSheetOpen() && (heightLessThanPeek || isAtMinHeight || triggerCloseWithHidden)) { + onSheetClosed(reason); + } else if (!isSheetOpen() && mTargetState != SheetState.HIDDEN + && getCurrentOffsetPx() > minScrollableHeight) { + onSheetOpened(reason); + } + + sendOffsetChangeEvents(); + } + + @Override + public void setSheetOffset(float offset, boolean shouldAnimate) { + cancelAnimation(); + if (mSheetContent == null) return; + + if (shouldAnimate) { + float velocityY = getCurrentOffsetPx() - offset; + + @SheetState + int targetState = getTargetSheetState(offset, -velocityY); + + setSheetState(targetState, true, StateChangeReason.SWIPE); + } else { + setInternalCurrentState(SheetState.SCROLLING, StateChangeReason.SWIPE); + setSheetOffsetFromBottom(offset, StateChangeReason.SWIPE); + } + } + + /** + * @return The ratio of the height of the screen that the hidden state is. + */ + @VisibleForTesting + float getHiddenRatio() { + return 0; + } + + /** @return Whether the peeking state for the sheet's content is enabled. */ + boolean isPeekStateEnabled() { + return mSheetContent != null && mSheetContent.getPeekHeight() != HeightMode.DISABLED; + } + + /** @return Whether the half-height of the sheet is enabled. */ + private boolean isHalfStateEnabled() { + if (mSheetContent == null) return false; + + // Half state is invalid on small screens, when wrapping content at full height, and when + // explicitly disabled. + return !isSmallScreen() && mSheetContent.getHalfHeightRatio() != HeightMode.DISABLED + && mSheetContent.getFullHeightRatio() != HeightMode.WRAP_CONTENT; + } + + /** @return Whether the height mode for the full state is WRAP_CONTENT. */ + private boolean isFullHeightWrapContent() { + return mSheetContent != null + && mSheetContent.getFullHeightRatio() == HeightMode.WRAP_CONTENT; + } + + /** + * @return The ratio of the height of the screen that the peeking state is. + */ + public float getPeekRatio() { + if (mContainerHeight <= 0 || !isPeekStateEnabled()) return 0; + + // If the content has a custom peek ratio set, use that instead of computing one. + if (mSheetContent != null && mSheetContent.getPeekHeight() != HeightMode.DEFAULT) { + assert mSheetContent.getPeekHeight() + != HeightMode.WRAP_CONTENT : "The peek mode can't wrap content."; + float ratio = mSheetContent.getPeekHeight() / (float) mContainerHeight; + assert ratio > 0 && ratio <= 1 : "Custom peek ratios must be in the range of (0, 1]."; + return ratio; + } + assert getToolbarView() != null : "Using default peek height requires a non-null toolbar"; + + View toolbarView = getToolbarView(); + int toolbarHeight = toolbarView.getHeight(); + if (toolbarHeight == 0) { + // If the toolbar is not laid out yet and has a fixed height layout parameter, we assume + // that the toolbar will have this height in the future. + ViewGroup.LayoutParams layoutParams = toolbarView.getLayoutParams(); + if (layoutParams != null) { + if (layoutParams.height > 0) { + toolbarHeight = layoutParams.height; + } else { + toolbarView.measure( + MeasureSpec.makeMeasureSpec(mContainerWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec( + (int) mContainerHeight, MeasureSpec.AT_MOST)); + toolbarHeight = toolbarView.getMeasuredHeight(); + } + } + } + return (toolbarHeight + mToolbarShadowHeight) / (float) mContainerHeight; + } + + private View getToolbarView() { + return mSheetContent != null && mSheetContent.getToolbarView() != null + ? mSheetContent.getToolbarView() + : mDefaultToolbarView; + } + + /** + * @return The ratio of the height of the screen that the half expanded state is. + */ + @VisibleForTesting + float getHalfRatio() { + if (mContainerHeight <= 0 || !isHalfStateEnabled()) return 0; + + float customHalfRatio = mSheetContent.getHalfHeightRatio(); + assert customHalfRatio + != HeightMode.WRAP_CONTENT + : "Half-height cannot be WRAP_CONTENT. This is only supported for full-height."; + + return customHalfRatio == HeightMode.DEFAULT ? HALF_HEIGHT_RATIO : customHalfRatio; + } + + /** + * @return The ratio of the height of the screen that the fully expanded state is. + */ + @VisibleForTesting + float getFullRatio() { + if (mContainerHeight <= 0 || mSheetContent == null) return 0; + + float customFullRatio = mSheetContent.getFullHeightRatio(); + assert customFullRatio != HeightMode.DISABLED : "The full height cannot be DISABLED."; + + if (isFullHeightWrapContent()) { + ensureContentDesiredHeightIsComputed(); + float heightPx = + Math.min(mContainerHeight, mContentDesiredHeight + mToolbarShadowHeight); + return heightPx / mContainerHeight; + } + + return customFullRatio == HeightMode.DEFAULT + ? (mContainerHeight + mToolbarShadowHeight) / (float) mContainerHeight + : customFullRatio; + } + + /** + * @return The height of the container that the bottom sheet exists in. + */ + public float getSheetContainerHeight() { + return mContainerHeight; + } + + /** + * Sends notifications if the sheet is transitioning from the peeking to half expanded state and + * from the peeking to fully expanded state. The peek to half events are only sent when the + * sheet is between the peeking and half states. + */ + private void sendOffsetChangeEvents() { + float offsetWithBrowserControls = getCurrentOffsetPx() - getOffsetFromBrowserControls(); + + // Do not send events for states less than the hidden state unless 0 has not been sent. + if (offsetWithBrowserControls <= getSheetHeightForState(SheetState.HIDDEN) + && mLastOffsetRatioSent <= 0) { + return; + } + + float screenRatio = + mContainerHeight > 0 ? offsetWithBrowserControls / (float) mContainerHeight : 0; + + // This ratio is relative to the peek and full positions of the sheet. + float maxHiddenFullRatio = getFullRatio() - getHiddenRatio(); + float hiddenFullRatio = maxHiddenFullRatio == 0 + ? 0 + : MathUtils.clamp((screenRatio - getHiddenRatio()) / maxHiddenFullRatio, 0, 1); + + if (offsetWithBrowserControls < getSheetHeightForState(SheetState.HIDDEN)) { + mLastOffsetRatioSent = 0; + } else { + mLastOffsetRatioSent = + MathUtils.areFloatsEqual(hiddenFullRatio, 0) ? 0 : hiddenFullRatio; + } + + for (BottomSheetObserver o : mObservers) { + o.onSheetOffsetChanged(mLastOffsetRatioSent, getCurrentOffsetPx()); + } + + if (isPeekStateEnabled() + && MathUtils.areFloatsEqual( + offsetWithBrowserControls, getSheetHeightForState(SheetState.PEEK))) { + for (BottomSheetObserver o : mObservers) o.onSheetFullyPeeked(); + } + } + + /** @see #setSheetState(int, boolean, int) */ + void setSheetState(@SheetState int state, boolean animate) { + setSheetState(state, animate, StateChangeReason.NONE); + } + + /** + * Moves the sheet to the provided state. + * @param state The state to move the panel to. This cannot be SheetState.SCROLLING or + * SheetState.NONE. + * @param animate If true, the sheet will animate to the provided state, otherwise it will + * move there instantly. + * @param reason The reason the sheet state is changing. This can be specified to indicate to + * observers that a more specific event has occurred, otherwise + * STATE_CHANGE_REASON_NONE can be used. + */ + void setSheetState(@SheetState int state, boolean animate, @StateChangeReason int reason) { + assert state != SheetState.NONE; + + // Setting state to SCROLLING is not a valid operation. This can happen only when + // we're already in the scrolling state. Make it no-op. + if (state == SheetState.SCROLLING) { + assert mCurrentState == SheetState.SCROLLING && isRunningSettleAnimation(); + return; + } + + if (state == SheetState.HALF && !isHalfStateEnabled()) state = SheetState.FULL; + + mTargetState = state; + + cancelAnimation(); + + if (animate && state != mCurrentState) { + createSettleAnimation(state, reason); + } else { + setSheetOffsetFromBottom(getSheetHeightForState(state), reason); + setInternalCurrentState(mTargetState, reason); + mTargetState = SheetState.NONE; + } + } + + /** + * @return The target state that the sheet is moving to during animation. If the sheet is + * stationary or a target state has not been determined, SheetState.NONE will be + * returned. + */ + int getTargetSheetState() { + return mTargetState; + } + + /** + * @return The current state of the bottom sheet. If the sheet is animating, this will be the + * state the sheet is animating to. + */ + @SheetState + int getSheetState() { + return mCurrentState; + } + + /** @return Whether the sheet is currently open. */ + boolean isSheetOpen() { + return mIsSheetOpen; + } + + /** + * Set the current state of the bottom sheet. This is for internal use to notify observers of + * state change events. + * @param state The current state of the sheet. + * @param reason The reason the state is changing if any. + */ + private void setInternalCurrentState(@SheetState int state, @StateChangeReason int reason) { + if (state == mCurrentState) return; + + // TODO(mdjones): This shouldn't be able to happen, but does occasionally during layout. + // Fix the race condition that is making this happen. + if (state == SheetState.NONE) { + setSheetState(getTargetSheetState(getCurrentOffsetPx(), 0), false); + return; + } + + // Remember which state precedes the scrolling. + mScrollingStartState = state == SheetState.SCROLLING + ? mCurrentState != SheetState.SCROLLING ? mCurrentState : SheetState.NONE + : SheetState.NONE; // Not scrolling anymore. + mCurrentState = state; + + if (mCurrentState == SheetState.HALF || mCurrentState == SheetState.FULL) { + int resId = mCurrentState == SheetState.FULL + ? getCurrentSheetContent().getSheetFullHeightAccessibilityStringId() + : getCurrentSheetContent().getSheetHalfHeightAccessibilityStringId(); + announceForAccessibility(getResources().getString(resId)); + + // TalkBack will announce the content description if it has changed, so wait to set the + // content description until after announcing full/half height. + setFocusable(true); + setFocusableInTouchMode(true); + String contentDescription = getResources().getString( + getCurrentSheetContent().getSheetContentDescriptionStringId()); + + if (getCurrentSheetContent().swipeToDismissEnabled()) { + contentDescription += ". " + + getResources().getString( + org.chromium.components.browser_ui.widget.R.string + .bottom_sheet_accessibility_description); + } + + setContentDescription(contentDescription); + if (getFocusedChild() == null) requestFocus(); + } + + for (BottomSheetObserver o : mObservers) { + o.onSheetStateChanged(mCurrentState); + } + } + + /** + * If the animation to settle the sheet in one of its states is running. + * @return True if the animation is running. + */ + private boolean isRunningSettleAnimation() { + return mSettleAnimator != null; + } + + /** @return The current sheet content, or null if there is no content. */ + @Nullable + BottomSheetContent getCurrentSheetContent() { + return mSheetContent; + } + + /** + * Gets the height of the bottom sheet based on a provided state. + * @param state The state to get the height from. + * @return The height of the sheet at the provided state. + */ + private float getSheetHeightForState(@SheetState int state) { + if (isFullHeightWrapContent() && state == SheetState.FULL) { + ensureContentDesiredHeightIsComputed(); + return mContentDesiredHeight + mToolbarShadowHeight; + } + + return getRatioForState(state) * mContainerHeight; + } + + private void ensureContentDesiredHeightIsComputed() { + if (mContentDesiredHeight != HEIGHT_UNSPECIFIED) { + return; + } + + mSheetContent.getContentView().measure( + MeasureSpec.makeMeasureSpec(mContainerWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(mContainerHeight, MeasureSpec.AT_MOST)); + mContentDesiredHeight = mSheetContent.getContentView().getMeasuredHeight(); + } + + private float getRatioForState(int state) { + switch (state) { + case SheetState.HIDDEN: + return getHiddenRatio(); + case SheetState.PEEK: + return getPeekRatio(); + case SheetState.HALF: + return getHalfRatio(); + case SheetState.FULL: + return getFullRatio(); + } + + throw new IllegalArgumentException("Invalid state: " + state); + } + + /** + * Adds an observer to the bottom sheet. + * @param observer The observer to add. + */ + void addObserver(BottomSheetObserver observer) { + mObservers.addObserver(observer); + } + + /** + * Removes an observer to the bottom sheet. + * @param observer The observer to remove. + */ + void removeObserver(BottomSheetObserver observer) { + mObservers.removeObserver(observer); + } + + /** + * Gets the target state of the sheet based on the sheet's height and velocity. + * @param sheetHeight The current height of the sheet. + * @param yVelocity The current Y velocity of the sheet. If this value is positive, the movement + * is from bottom to top. + * @return The target state of the bottom sheet. + */ + @SheetState + private int getTargetSheetState(float sheetHeight, float yVelocity) { + if (sheetHeight <= getMinOffsetPx()) return getMinSwipableSheetState(); + if (sheetHeight >= getMaxOffsetPx()) return SheetState.FULL; + + boolean isMovingDownward = yVelocity < 0; + + // If velocity shouldn't affect dismissing the sheet, reverse effect on the sheet height. + if (isMovingDownward && !swipeToDismissEnabled()) sheetHeight -= yVelocity; + + // Find the two states that the sheet height is between. + @SheetState + int prevState = mScrollingStartState; + @SheetState + int nextState = isMovingDownward ? getLargestCollapsingState(isMovingDownward, sheetHeight) + : getSmallestExpandingState(isMovingDownward, sheetHeight); + + // Go into the next state only if the threshold for minimal change has been cleared. + return hasCrossedThresholdToNextState(prevState, nextState, sheetHeight, isMovingDownward) + ? nextState + : prevState; + } + + /** + * Returns whether the sheet was scrolled far enough to transition into the next state. + * @param prev The state before the scrolling transition happened. + * @param next The state before the scrolling transitions into. + * @param sheetMovesDown True if the sheet moves down. + * @param sheetHeight The current sheet height in flux. + * @return True, iff the sheet was scrolled far enough to transition from |prev| to |next|. + */ + private boolean hasCrossedThresholdToNextState( + @SheetState int prev, @SheetState int next, float sheetHeight, boolean sheetMovesDown) { + if (next == prev) return false; + // Moving from an internal/temporary state always works: + if (prev == SheetState.NONE || prev == SheetState.SCROLLING) return true; + float lowerBound = getSheetHeightForState(prev); + float distance = getSheetHeightForState(next) - lowerBound; + return Math.abs((sheetHeight - lowerBound) / distance) + > getThresholdToNextState(prev, next, sheetMovesDown); + } + + /** + * The threshold to enter a state depends on whether a transition skips the half state. The more + * states to cross, the smaller the (percentual) threshold. A small threshold is used iff: + * * It doesn't move into the HALF state, + * * Skipping the HALF state is allowed, and + * * The is large enough to skip the HALF state + * @param prev The state before the scrolling transition happened. + * @param next The state before the scrolling transitions into. + * @param sheetMovesDown True if the sheet is being moved down. + * @return a threshold (as percentage of the scroll distance covered). + */ + private float getThresholdToNextState( + @SheetState int prev, @SheetState int next, boolean sheetMovesDown) { + if (next == SheetState.HALF) return THRESHOLD_TO_NEXT_STATE_3; + boolean crossesHalf = sheetMovesDown && prev > SheetState.HALF && next < SheetState.HALF + || !sheetMovesDown && prev < SheetState.HALF && next > SheetState.HALF; + if (!crossesHalf) return THRESHOLD_TO_NEXT_STATE_3; + if (!shouldSkipHalfStateOnScrollingDown()) return THRESHOLD_TO_NEXT_STATE_3; + return THRESHOLD_TO_NEXT_STATE_2; + } + + /** + * Returns the largest, acceptable state whose height is smaller than the given sheet height. + * E.g. if a sheet is between FULL and HALF, collapsing states are PEEK and HALF. Although HALF + * is closer to the sheet's height, it might have to be skipped. Then, PEEK is returned instead. + * @param sheetMovesDown If the sheet moves down, some smaller states might be skipped. + * @param sheetHeight The current sheet height in flux. + * @return The largest, acceptable, collapsing state. + */ + private @SheetState int getLargestCollapsingState(boolean sheetMovesDown, float sheetHeight) { + @SheetState + int largestCollapsingState = getMinSwipableSheetState(); + boolean skipHalfState = !isHalfStateEnabled() || shouldSkipHalfStateOnScrollingDown(); + for (@SheetState int i = largestCollapsingState + 1; i < SheetState.FULL; i++) { + if (i == SheetState.PEEK && !isPeekStateEnabled()) continue; + if (i == SheetState.HALF && skipHalfState) continue; + + if (sheetHeight > getSheetHeightForState(i) + || sheetHeight == getSheetHeightForState(i) && !sheetMovesDown) { + largestCollapsingState = i; + } + } + return largestCollapsingState; + } + + /** + * Returns the smallest, acceptable state whose height is larger than the given sheet height. + * E.g. if the sheet is between PEEK and HALF, expanding states are HALF and FULL. Although HALF + * is closer to the sheet's height, it might not be enabled. Then, FULL is returned instead. + * @param sheetMovesDown If the sheet moves down, some collapsing states might be skipped. This + * affects the smallest possible expanding state as well. + * @param sheetHeight The current sheet height in flux. + * @return The smallest, acceptable, expanding state. + */ + private @SheetState int getSmallestExpandingState(boolean sheetMovesDown, float sheetHeight) { + @SheetState + int largestCollapsingState = getLargestCollapsingState(sheetMovesDown, sheetHeight); + @SheetState + int smallestExpandingState = SheetState.FULL; + for (@SheetState int i = smallestExpandingState - 1; i > largestCollapsingState + 1; i--) { + if (i == SheetState.HALF && !isHalfStateEnabled()) continue; + if (i == SheetState.PEEK && !isPeekStateEnabled()) continue; + + if (sheetHeight <= getSheetHeightForState(i)) { + smallestExpandingState = i; + } + } + + return smallestExpandingState; + } + + @VisibleForTesting + public static void setSmallScreenForTesting(boolean isSmallScreen) { + sIsSmallScreenForTesting = isSmallScreen; + } + + public boolean isSmallScreen() { + if (sIsSmallScreenForTesting != null) return sIsSmallScreenForTesting; + + // A small screen is defined by there being less than 160dp between half and full states. + float fullHeightRatio = + (mContainerHeight + mToolbarShadowHeight) / (float) mContainerHeight; + float fullToHalfDiff = (fullHeightRatio - HALF_HEIGHT_RATIO) * mContainerHeight; + return fullToHalfDiff < mMinHalfFullDistance; + } + + /** + * @return The height of the toolbar shadow. + */ + public int getToolbarShadowHeight() { + return mToolbarShadowHeight; + } + + /** + * Called when the sheet content has changed, to update dependent state and notify observers. + * @param content The new sheet content, or null if the sheet has no content. + */ + protected void onSheetContentChanged(@Nullable final BottomSheetContent content) { + mSheetContent = content; + + if (isFullHeightWrapContent()) { + // Listen for layout/size changes. + if (!content.setContentSizeListener(this::onContentSizeChanged)) { + content.getContentView().addOnLayoutChangeListener(this); + } + + invalidateContentDesiredHeight(); + ensureContentIsWrapped(/* animate= */ true); + + // HALF state is forbidden when wrapping the content. + if (mCurrentState == SheetState.HALF) { + setSheetState(SheetState.FULL, /* animate= */ true); + } + } + + for (BottomSheetObserver o : mObservers) { + o.onSheetContentChanged(content); + } + mToolbarHolder.setBackgroundColor(Color.TRANSPARENT); + } + + /** + * Called when the sheet content layout changed. + */ + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, + int oldTop, int oldRight, int oldBottom) { + invalidateContentDesiredHeight(); + ensureContentIsWrapped(/* animate= */ true); + } + + /** + * Called when the sheet content size changed. + */ + private void onContentSizeChanged(int width, int height, int oldWidth, int oldHeight) { + boolean heightChanged = mContentDesiredHeight != height; + mContentDesiredHeight = height; + + if (heightChanged && mCurrentState == SheetState.SCROLLING) { + endAnimations(); + return; + } + + ensureContentIsWrapped(/* animate= */ false); + } + + private void ensureContentIsWrapped(boolean animate) { + if (mCurrentState == SheetState.HIDDEN || mCurrentState == SheetState.PEEK) return; + + // The SCROLLING state is used when animating the sheet height or when the user is swiping + // the sheet. If it is the latter, we should not change the sheet height. + if (!isRunningSettleAnimation() && mCurrentState == SheetState.SCROLLING) return; + setSheetState(mCurrentState, animate); + } + + private void invalidateContentDesiredHeight() { + mContentDesiredHeight = HEIGHT_UNSPECIFIED; + } + + /** + * WARNING: This destroys the state of the BottomSheet. Only use in tests and only use once. + * Puts the sheet into a scrolling state that can't be reached in tests otherwise. + * + * @param sheetHeightInPx The height in px that the sheet should be "scrolled" to. + * @param yUpwardsVelocity The sheet's upwards y velocity when reaching the scrolled height. + * @return The state the bottom sheet would target when the scrolling ends. + */ + @VisibleForTesting + @SheetState + int forceScrollingStateForTesting(float sheetHeightInPx, float yUpwardsVelocity) { + mScrollingStartState = mCurrentState; + mCurrentState = SheetState.SCROLLING; + return getTargetSheetState(sheetHeightInPx, yUpwardsVelocity); + } +} diff --git a/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java new file mode 100644 index 00000000000..732588d5234 --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java @@ -0,0 +1,33 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; + +import org.chromium.base.Callback; +import org.chromium.base.supplier.Supplier; +import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator; +import org.chromium.ui.KeyboardVisibilityDelegate; + +/** A factory for producing a {@link BottomSheetController}. */ +public class BottomSheetControllerFactory { + /** + * @param scrim A supplier of scrim to be shown behind the sheet. + * @param initializedCallback A callback for the sheet having been created. + * @param window The activity's window. + * @param keyboardDelegate A means of hiding the keyboard. + * @param root The view that should contain the sheet. + * @param inflater A mechanism for building views from XML. + * @return A new instance of the {@link BottomSheetController}. + */ + public static ManagedBottomSheetController createBottomSheetController( + final Supplier scrim, Callback initializedCallback, + Window window, KeyboardVisibilityDelegate keyboardDelegate, Supplier root) { + return new BottomSheetControllerImpl( + scrim, initializedCallback, window, keyboardDelegate, root); + } +} diff --git a/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java new file mode 100644 index 00000000000..88dd095af89 --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java @@ -0,0 +1,505 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.Callback; +import org.chromium.base.supplier.Supplier; +import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator; +import org.chromium.components.browser_ui.widget.scrim.ScrimProperties; +import org.chromium.ui.KeyboardVisibilityDelegate; +import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.util.AccessibilityUtil; +import org.chromium.ui.util.TokenHolder; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.PriorityQueue; + +/** + * This class is responsible for managing the content shown by the {@link BottomSheet}. Features + * wishing to show content in the {@link BottomSheet} UI must implement {@link BottomSheetContent} + * and call {@link #requestShowContent(BottomSheetContent, boolean)} which will return true if the + * content was actually shown (see full doc on method). + */ +class BottomSheetControllerImpl implements ManagedBottomSheetController { + /** The initial capacity for the priority queue handling pending content show requests. */ + private static final int INITIAL_QUEUE_CAPACITY = 1; + + /** The height of the shadow that sits above the toolbar. */ + private int mToolbarShadowHeight; + + /** The offset of the toolbar shadow from the top that remains empty. */ + private int mShadowTopOffset; + + /** A handle to the {@link BottomSheet} that this class controls. */ + private BottomSheet mBottomSheet; + + /** A queue for content that is waiting to be shown in the {@link BottomSheet}. */ + private PriorityQueue mContentQueue; + + /** Whether the controller is already processing a hide request for the tab. */ + private boolean mIsProcessingHideRequest; + + /** A runnable that initializes the bottom sheet when necessary. */ + private Runnable mSheetInitializer; + + /** + * A list of observers maintained by this controller until the bottom sheet is created, at which + * point they will be added to the bottom sheet. + */ + private List mPendingSheetObservers; + + /** The state of the sheet so it can be returned to what it was prior to suppression. */ + @SheetState + private int mSheetStateBeforeSuppress; + + /** The content being shown prior to the sheet being suppressed. */ + private BottomSheetContent mContentWhenSuppressed; + + /** A means of accessing the ScrimCoordinator. */ + private Supplier mScrimCoordinatorSupplier; + + /** + * A set of tokens for features suppressing the bottom sheet. If this holder has tokens, the + * sheet is suppressed. + */ + private final TokenHolder mSuppressionTokens; + + /** A means of checking whether accessibility is currently enabled. */ + private AccessibilityUtil mAccessibilityUtil; + + /** + * Build a new controller of the bottom sheet. + * @param scrim A supplier of the scrim that shows when the bottom sheet is opened. + * @param initializedCallback A callback for the sheet being created (as the sheet is not + * initialized until first use. + * @param window A means of accessing the screen size. + * @param keyboardDelegate A means of hiding the keyboard. + * @param root The view that should contain the sheet. + */ + public BottomSheetControllerImpl(final Supplier scrim, + Callback initializedCallback, Window window, + KeyboardVisibilityDelegate keyboardDelegate, Supplier root) { + mScrimCoordinatorSupplier = scrim; + mPendingSheetObservers = new ArrayList<>(); + mSuppressionTokens = new TokenHolder(() -> onSuppressionTokensChanged()); + + mSheetInitializer = () -> { + initializeSheet(initializedCallback, window, keyboardDelegate, root); + }; + } + + /** + * Do the actual initialization of the bottom sheet. + * @param initializedCallback A callback for the creation of the sheet. + * @param window A means of accessing the screen size. + * @param keyboardDelegate A means of hiding the keyboard. + * @param root The view that should contain the sheet. + */ + private void initializeSheet(Callback initializedCallback, Window window, + KeyboardVisibilityDelegate keyboardDelegate, Supplier root) { + LayoutInflater.from(root.get().getContext()).inflate(R.layout.bottom_sheet, root.get()); + mBottomSheet = (BottomSheet) root.get().findViewById(R.id.bottom_sheet); + initializedCallback.onResult(mBottomSheet); + + mBottomSheet.init(window, keyboardDelegate); + mBottomSheet.setAccssibilityUtil(mAccessibilityUtil); + mToolbarShadowHeight = mBottomSheet.getResources().getDimensionPixelOffset( + BottomSheet.getTopShadowResourceId()); + mShadowTopOffset = mBottomSheet.getResources().getDimensionPixelOffset( + BottomSheet.getShadowTopOffsetResourceId()); + + // Initialize the queue with a comparator that checks content priority. + mContentQueue = new PriorityQueue<>(INITIAL_QUEUE_CAPACITY, + (content1, content2) -> content1.getPriority() - content2.getPriority()); + + PropertyModel scrimProperties = + new PropertyModel.Builder(ScrimProperties.REQUIRED_KEYS) + .with(ScrimProperties.TOP_MARGIN, 0) + .with(ScrimProperties.AFFECTS_STATUS_BAR, true) + .with(ScrimProperties.ANCHOR_VIEW, mBottomSheet) + .with(ScrimProperties.SHOW_IN_FRONT_OF_ANCHOR_VIEW, false) + .with(ScrimProperties.CLICK_DELEGATE, + () -> { + if (!mBottomSheet.isSheetOpen()) return; + mBottomSheet.setSheetState( + mBottomSheet.getMinSwipableSheetState(), true, + StateChangeReason.TAP_SCRIM); + }) + .build(); + + mBottomSheet.addObserver(new EmptyBottomSheetObserver() { + /** + * Whether the scrim was shown for the last content. + * TODO(mdjones): We should try to make sure the content in the sheet is not nulled + * prior to the close event occurring; sheets that don't have a peek + * state make this difficult since the sheet needs to be hidden before it + * is closed. + */ + private boolean mScrimShown; + + @Override + public void onSheetOpened(@StateChangeReason int reason) { + if (mBottomSheet.getCurrentSheetContent() != null + && mBottomSheet.getCurrentSheetContent().hasCustomScrimLifecycle()) { + return; + } + + mScrimCoordinatorSupplier.get().showScrim(scrimProperties); + mScrimShown = true; + } + + @Override + public void onSheetClosed(@StateChangeReason int reason) { + // Hide the scrim if the current content doesn't have a custom scrim lifecycle. + if (mScrimShown) { + mScrimCoordinatorSupplier.get().hideScrim(true); + mScrimShown = false; + } + + // Try to swap contents unless the sheet's content has a custom lifecycle. + if (mBottomSheet.getCurrentSheetContent() != null + && !mBottomSheet.getCurrentSheetContent().hasCustomLifecycle()) { + // If the sheet is closed, it is an opportunity for another content to try to + // take its place if it is a higher priority. + BottomSheetContent content = mBottomSheet.getCurrentSheetContent(); + BottomSheetContent nextContent = mContentQueue.peek(); + if (content != null && nextContent != null + && nextContent.getPriority() < content.getPriority()) { + mContentQueue.add(content); + mBottomSheet.setSheetState(SheetState.HIDDEN, true); + } + } + } + + @Override + public void onSheetStateChanged(@SheetState int state) { + // If hiding request is in progress, destroy the current sheet content being hidden + // even when it is in suppressed state. See https://crbug.com/1057966. + if (state != SheetState.HIDDEN + || (!mIsProcessingHideRequest && mSuppressionTokens.hasTokens())) { + return; + } + if (mBottomSheet.getCurrentSheetContent() != null) { + mBottomSheet.getCurrentSheetContent().destroy(); + } + mIsProcessingHideRequest = false; + showNextContent(true); + } + }); + + // Add any of the pending observers that were added prior to the sheet being created. + for (int i = 0; i < mPendingSheetObservers.size(); i++) { + mBottomSheet.addObserver(mPendingSheetObservers.get(i)); + } + mPendingSheetObservers.clear(); + + mSheetInitializer = null; + } + + @Override + public void setBrowserControlsHiddenRatio(float ratio) { + if (mBottomSheet != null) mBottomSheet.setBrowserControlsHiddenRatio(ratio); + } + + @Override + public ScrimCoordinator getScrimCoordinator() { + return mScrimCoordinatorSupplier.get(); + } + + @Override + public PropertyModel createScrimParams() { + return new PropertyModel.Builder(ScrimProperties.REQUIRED_KEYS) + .with(ScrimProperties.TOP_MARGIN, 0) + .with(ScrimProperties.AFFECTS_STATUS_BAR, true) + .with(ScrimProperties.ANCHOR_VIEW, mBottomSheet) + .with(ScrimProperties.SHOW_IN_FRONT_OF_ANCHOR_VIEW, false) + .with(ScrimProperties.CLICK_DELEGATE, + () -> { + if (!mBottomSheet.isSheetOpen()) return; + mBottomSheet.setSheetState(mBottomSheet.getMinSwipableSheetState(), + true, StateChangeReason.TAP_SCRIM); + }) + .build(); + } + + // Destroyable implementation. + @Override + public void destroy() { + if (mBottomSheet != null) mBottomSheet.destroy(); + } + + @Override + public boolean handleBackPress() { + // If suppressed (therefore invisible), users are likely to expect for Chrome + // browser, not the bottom sheet, to react. Do not consume the event. + if (mBottomSheet == null || mSuppressionTokens.hasTokens()) return false; + + // Give the sheet the opportunity to handle the back press itself before falling to the + // default "close" behavior. + if (getCurrentSheetContent() != null && getCurrentSheetContent().handleBackPress()) { + return true; + } + + if (!mBottomSheet.isSheetOpen()) return false; + int sheetState = mBottomSheet.getMinSwipableSheetState(); + mBottomSheet.setSheetState(sheetState, true, StateChangeReason.BACK_PRESS); + return true; + } + + @Override + public BottomSheetContent getCurrentSheetContent() { + return mBottomSheet == null ? null : mBottomSheet.getCurrentSheetContent(); + } + + @Override + @SheetState + public int getSheetState() { + return mBottomSheet == null ? SheetState.HIDDEN : mBottomSheet.getSheetState(); + } + + @Override + @SheetState + public int getTargetSheetState() { + return mBottomSheet == null ? SheetState.NONE : mBottomSheet.getTargetSheetState(); + } + + @Override + public boolean isSheetOpen() { + return mBottomSheet != null && mBottomSheet.isSheetOpen(); + } + + @Override + public boolean isSheetHiding() { + return mBottomSheet == null ? false : mBottomSheet.isHiding(); + } + + @Override + public int getCurrentOffset() { + return mBottomSheet == null ? 0 : (int) mBottomSheet.getCurrentOffsetPx(); + } + + @Override + public int getContainerHeight() { + return mBottomSheet != null ? (int) mBottomSheet.getSheetContainerHeight() : 0; + } + + @Override + public int getTopShadowHeight() { + return mToolbarShadowHeight + mShadowTopOffset; + } + + @Override + public void addObserver(BottomSheetObserver observer) { + if (mBottomSheet == null) { + mPendingSheetObservers.add(observer); + return; + } + mBottomSheet.addObserver(observer); + } + + @Override + public void removeObserver(BottomSheetObserver observer) { + if (mBottomSheet != null) { + mBottomSheet.removeObserver(observer); + } else { + mPendingSheetObservers.remove(observer); + } + } + + /** Handle a change in the state of the token holder responsible for the suppression tokens. */ + private void onSuppressionTokensChanged() { + if (!mSuppressionTokens.hasTokens()) doUnsuppression(); + } + + @Override + public int suppressSheet(@StateChangeReason int reason) { + boolean hadTokens = mSuppressionTokens.hasTokens(); + int token = mSuppressionTokens.acquireToken(); + if (!hadTokens && mBottomSheet != null) { + mSheetStateBeforeSuppress = getSheetState(); + mContentWhenSuppressed = getCurrentSheetContent(); + mBottomSheet.setSheetState(SheetState.HIDDEN, false, reason); + } + + return token; + } + + @Override + public void unsuppressSheet(int token) { + mSuppressionTokens.releaseToken(token); + } + + private void doUnsuppression() { + if (mBottomSheet == null) return; + + if (mBottomSheet.getCurrentSheetContent() != null) { + @SheetState + int openState = mContentWhenSuppressed == getCurrentSheetContent() + ? mSheetStateBeforeSuppress + : mBottomSheet.getOpeningState(); + mBottomSheet.setSheetState(openState, true); + } else { + // In the event the previous content was hidden, try to show the next one. + showNextContent(true); + } + mContentWhenSuppressed = null; + mSheetStateBeforeSuppress = SheetState.NONE; + } + + @Override + public void setAccssibilityUtil(AccessibilityUtil enabledSupplier) { + mAccessibilityUtil = enabledSupplier; + } + + @VisibleForTesting + void setSheetStateForTesting(@SheetState int state, boolean animate) { + mBottomSheet.setSheetState(state, animate); + } + + @VisibleForTesting + View getBottomSheetViewForTesting() { + return mBottomSheet; + } + + @VisibleForTesting + public void endAnimationsForTesting() { + mBottomSheet.endAnimations(); + } + + @Override + public boolean requestShowContent(BottomSheetContent content, boolean animate) { + if (mBottomSheet == null) mSheetInitializer.run(); + + // If already showing the requested content, do nothing. + if (content == mBottomSheet.getCurrentSheetContent()) return true; + + boolean shouldSuppressExistingContent = mBottomSheet.getCurrentSheetContent() != null + && content.getPriority() < mBottomSheet.getCurrentSheetContent().getPriority() + && canBottomSheetSwitchContent(); + + // Always add the content to the queue, it will be handled after the sheet closes if + // necessary. If already hidden, |showNextContent| will handle the request. + mContentQueue.add(content); + + if (mBottomSheet.getCurrentSheetContent() == null) { + showNextContent(animate); + return true; + } else if (shouldSuppressExistingContent) { + mContentQueue.add(mBottomSheet.getCurrentSheetContent()); + mBottomSheet.setSheetState(SheetState.HIDDEN, animate); + return true; + } + return false; + } + + @Override + public void hideContent( + BottomSheetContent content, boolean animate, @StateChangeReason int hideReason) { + if (mBottomSheet == null) return; + + if (content != mBottomSheet.getCurrentSheetContent()) { + mContentQueue.remove(content); + return; + } + + if (mIsProcessingHideRequest) return; + + // Handle showing the next content if it exists. + if (mBottomSheet.getSheetState() == SheetState.HIDDEN) { + // If the sheet is already hidden, destroy it and simply show the next content. + // TODO(mdjones): Add tests to make sure the content is being destroyed as expected. + if (mBottomSheet.getCurrentSheetContent() != null) { + mBottomSheet.getCurrentSheetContent().destroy(); + } + showNextContent(animate); + } else { + mIsProcessingHideRequest = true; + mBottomSheet.setSheetState(SheetState.HIDDEN, animate, hideReason); + } + } + + @Override + public void hideContent(BottomSheetContent content, boolean animate) { + hideContent(content, animate, StateChangeReason.NONE); + } + + @Override + public void expandSheet() { + if (mBottomSheet == null || mSuppressionTokens.hasTokens()) return; + + if (mBottomSheet.getCurrentSheetContent() == null) return; + mBottomSheet.setSheetState(SheetState.HALF, true); + } + + @Override + public boolean collapseSheet(boolean animate) { + if (mBottomSheet == null || mSuppressionTokens.hasTokens()) return false; + if (mBottomSheet.isSheetOpen() && mBottomSheet.isPeekStateEnabled()) { + mBottomSheet.setSheetState(SheetState.PEEK, animate); + return true; + } + return false; + } + + /** + * Show the next {@link BottomSheetContent} if it is available and peek the sheet. If no content + * is available the sheet's content is set to null. + * @param animate Whether the sheet should animate opened. + */ + private void showNextContent(boolean animate) { + if (mContentQueue.isEmpty()) { + mBottomSheet.showContent(null); + return; + } + + BottomSheetContent nextContent = mContentQueue.poll(); + mBottomSheet.showContent(nextContent); + mBottomSheet.setSheetState(mBottomSheet.getOpeningState(), animate); + } + + @Override + public void clearRequestsAndHide() { + if (mBottomSheet == null) return; + + clearRequests(mContentQueue.iterator()); + + BottomSheetContent currentContent = mBottomSheet.getCurrentSheetContent(); + if (currentContent == null || !currentContent.hasCustomLifecycle()) { + hideContent(currentContent, /* animate= */ true); + } + mContentWhenSuppressed = null; + mSheetStateBeforeSuppress = SheetState.NONE; + } + + /** + * Remove all contents from {@code iterator} that don't have a custom lifecycle. + * @param iterator The iterator whose items must be removed. + */ + private void clearRequests(Iterator iterator) { + while (iterator.hasNext()) { + if (!iterator.next().hasCustomLifecycle()) { + iterator.remove(); + } + } + } + + /** + * The bottom sheet cannot change content while it is open. If the user has the bottom sheet + * open, they are currently engaged in a task and shouldn't be interrupted. + * @return Whether the sheet currently supports switching its content. + */ + private boolean canBottomSheetSwitchContent() { + return !mBottomSheet.isSheetOpen(); + } +} diff --git a/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetector.java b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetector.java new file mode 100644 index 00000000000..ae9d4e7e04d --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetector.java @@ -0,0 +1,269 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import android.content.Context; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.VelocityTracker; + +import org.chromium.base.MathUtils; +import org.chromium.base.ThreadUtils; + +/** + * A class that determines whether a sequence of motion events is a valid swipe in the context of a + * bottom sheet. The {@link SwipeableBottomSheet} that this class is built with provides information + * useful to determining if a swipe is valid. This class does not move the sheet itself, it only + * provides information on if/where it should move and whether it should animate. The + * {@link SwipeableBottomSheet} is responsible for applying the changes to the relevant views. Each + * swipe or fling is converted into a sequence of calls to + * {@link SwipeableBottomSheet#setSheetOffset(float, boolean)}. + */ +class BottomSheetSwipeDetector extends GestureDetector.SimpleOnGestureListener { + /** The minimum y/x ratio that a scroll must have to be considered vertical. */ + private static final float MIN_VERTICAL_SCROLL_SLOPE = 2.0f; + + /** + * The base duration of the settling animation of the sheet. 218 ms is a spec for material + * design (this is the minimum time a user is guaranteed to pay attention to something). + */ + public static final long BASE_ANIMATION_DURATION_MS = 218; + + /** For detecting scroll and fling events on the bottom sheet. */ + private final GestureDetector mGestureDetector; + + /** An interface for retrieving information from a bottom sheet. */ + private final SwipeableBottomSheet mSheetDelegate; + + /** Track the velocity of the user's scrolls to determine up or down direction. */ + private VelocityTracker mVelocityTracker; + + /** Whether or not the user is scrolling the bottom sheet. */ + private boolean mIsScrolling; + + /** + * An interface for views that are swipable from the bottom of the screen. This interface + * assumes that any part of the bottom sheet visible at the peeking state is the toolbar. + */ + public interface SwipeableBottomSheet { + /** + * @return Whether the content being shown in the sheet is scrolled to the top. + */ + boolean isContentScrolledToTop(); + + /** + * Gets the sheet's offset from the bottom of the screen. + * @return The sheet's distance from the bottom of the screen. + */ + float getCurrentOffsetPx(); + + /** + * Gets the minimum offset of the bottom sheet. + * @return The min offset. + */ + float getMinOffsetPx(); + + /** + * Gets the maximum offset of the bottom sheet. + * @return The max offset. + */ + float getMaxOffsetPx(); + + /** + * @param event The motion event to test. + * @return Whether the provided motion event is inside the toolbar. + */ + boolean isTouchEventInToolbar(MotionEvent event); + + /** + * Check if a particular gesture or touch event should move the bottom sheet when in peeking + * mode. If the "chrome-home-swipe-logic" flag is not set this function returns true. + * @param initialDownEvent The event that started the scroll. + * @param currentEvent The current motion event. + * @return True if the bottom sheet should move. + */ + boolean shouldGestureMoveSheet(MotionEvent initialDownEvent, MotionEvent currentEvent); + + /** + * Set the sheet's offset. + * @param offset The target offset. + * @param shouldAnimate Whether the sheet should animate to that position. + */ + void setSheetOffset(float offset, boolean shouldAnimate); + } + + /** + * This class is responsible for detecting swipe and scroll events on the bottom sheet or + * ignoring them when appropriate. + */ + private class SwipeGestureListener extends GestureDetector.SimpleOnGestureListener { + @Override + public boolean onDown(MotionEvent e) { + if (e == null) return false; + return mSheetDelegate.shouldGestureMoveSheet(e, e); + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + if (e1 == null || !mSheetDelegate.shouldGestureMoveSheet(e1, e2)) return false; + + // Only start scrolling if the scroll is up or down. If the user is already scrolling, + // continue moving the sheet. + float slope = Math.abs(distanceX) > 0f ? Math.abs(distanceY) / Math.abs(distanceX) + : MIN_VERTICAL_SCROLL_SLOPE; + if (!mIsScrolling && slope < MIN_VERTICAL_SCROLL_SLOPE) { + mVelocityTracker.clear(); + return false; + } + + mVelocityTracker.addMovement(e2); + + boolean isSheetInMaxPosition = MathUtils.areFloatsEqual( + mSheetDelegate.getCurrentOffsetPx(), mSheetDelegate.getMaxOffsetPx()); + + // Allow the bottom sheet's content to be scrolled up without dragging the sheet down. + if (!mSheetDelegate.isTouchEventInToolbar(e2) && isSheetInMaxPosition + && !mSheetDelegate.isContentScrolledToTop()) { + return false; + } + + // If the sheet is in the max position, don't move the sheet if the scroll is upward. + // Instead, allow the sheet's content to handle it if it needs to. + if (isSheetInMaxPosition && distanceY > 0) return false; + + boolean isSheetInMinPosition = MathUtils.areFloatsEqual( + mSheetDelegate.getCurrentOffsetPx(), mSheetDelegate.getMinOffsetPx()); + + // Similarly, if the sheet is in the min position, don't move if the scroll is downward. + if (isSheetInMinPosition && distanceY < 0) return false; + + float newOffset = mSheetDelegate.getCurrentOffsetPx() + distanceY; + + mIsScrolling = true; + + mSheetDelegate.setSheetOffset( + MathUtils.clamp(newOffset, mSheetDelegate.getMinOffsetPx(), + mSheetDelegate.getMaxOffsetPx()), + false); + + return true; + } + + @Override + public void onLongPress(MotionEvent e) { + mIsScrolling = false; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (e1 == null || !mSheetDelegate.shouldGestureMoveSheet(e1, e2) || !mIsScrolling) { + return false; + } + + mIsScrolling = false; + + float newOffset = mSheetDelegate.getCurrentOffsetPx() + getFlingDistance(-velocityY); + + mSheetDelegate.setSheetOffset( + MathUtils.clamp(newOffset, mSheetDelegate.getMinOffsetPx(), + mSheetDelegate.getMaxOffsetPx()), + true); + + return true; + } + } + + /** + * Default constructor. + * @param context A context for the GestureDetector this class uses. + * @param delegate A SwipeableBottomSheet that processes swipes. + */ + public BottomSheetSwipeDetector(Context context, SwipeableBottomSheet delegate) { + mGestureDetector = new GestureDetector( + context, new SwipeGestureListener(), ThreadUtils.getUiThreadHandler()); + mGestureDetector.setIsLongpressEnabled(true); + + mSheetDelegate = delegate; + mVelocityTracker = VelocityTracker.obtain(); + } + + /** + * Test whether or not a motion event should be intercepted by this class. + * @param e The motion event to test. + * @return Whether or not the event was intercepted. + */ + public boolean onInterceptTouchEvent(MotionEvent e) { + // The incoming motion event may have been adjusted by the view sending it down. Create a + // motion event with the raw (x, y) coordinates of the original so the gesture detector + // functions properly. + mGestureDetector.onTouchEvent(createRawMotionEvent(e)); + + return mIsScrolling; + } + + /** + * Process a motion event. + * @param e The motion event to process. + * @return Whether or not the motion event was used. + */ + public boolean onTouchEvent(MotionEvent e) { + // The down event is interpreted above in onInterceptTouchEvent, it does not need to be + // interpreted a second time. + if (e.getActionMasked() != MotionEvent.ACTION_DOWN) { + mGestureDetector.onTouchEvent(createRawMotionEvent(e)); + } + + // If the user is scrolling and the event is a cancel or up action, update scroll state and + // return. Fling should have already cleared the mIsScrolling flag, the following is for the + // non-fling release. + if (mIsScrolling + && (e.getActionMasked() == MotionEvent.ACTION_UP + || e.getActionMasked() == MotionEvent.ACTION_CANCEL)) { + mIsScrolling = false; + + mVelocityTracker.computeCurrentVelocity(1000); + + float newOffset = mSheetDelegate.getCurrentOffsetPx() + + getFlingDistance(-mVelocityTracker.getYVelocity()); + + mSheetDelegate.setSheetOffset( + MathUtils.clamp(newOffset, mSheetDelegate.getMinOffsetPx(), + mSheetDelegate.getMaxOffsetPx()), + true); + } + + return true; + } + + /** + * @return Whether or not a gesture is currently being detected as a scroll. + */ + public boolean isScrolling() { + return mIsScrolling; + } + + /** + * Creates an unadjusted version of a MotionEvent. + * @param e The original event. + * @return The unadjusted version of the event. + */ + private MotionEvent createRawMotionEvent(MotionEvent e) { + MotionEvent rawEvent = MotionEvent.obtain(e); + rawEvent.setLocation(e.getRawX(), e.getRawY()); + return rawEvent; + } + + /** + * Gets the distance of a fling based on the velocity and the base animation time. This formula + * assumes the deceleration curve is quadratic (t^2), hence the displacement formula should be: + * displacement = initialVelocity * duration / 2. + * @param velocity The velocity of the fling. + * @return The distance the fling would cover. + */ + private float getFlingDistance(float velocity) { + // This includes conversion from seconds to ms. + return velocity * BASE_ANIMATION_DURATION_MS / 2000f; + } +} diff --git a/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetectorTest.java b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetectorTest.java new file mode 100644 index 00000000000..a3a557a9cfa --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetSwipeDetectorTest.java @@ -0,0 +1,319 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.view.MotionEvent; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import org.chromium.base.MathUtils; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetSwipeDetector.SwipeableBottomSheet; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for the {@link BottomSheetSwipeDetector} class. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public final class BottomSheetSwipeDetectorTest { + /** The minimum height of the bottom sheet. */ + private static final float MIN_SHEET_OFFSET = 100; + + /** An arbitrary screen height. */ + private static final float SCREEN_HEIGHT = 1000; + + /** An instance of the mock swipable sheet. */ + private MockSwipeableBottomSheet mSwipeableBottomSheet; + + /** The swipe detector to process motion events. */ + private BottomSheetSwipeDetector mSwipeDetector; + + /** A mock implementation of a swipeable bottom sheet. */ + private static class MockSwipeableBottomSheet implements SwipeableBottomSheet { + /** The minimum offset of the sheet. */ + private final float mMinOffset; + + /** The maximum offset of the sheet. */ + private final float mMaxOffset; + + /** Whether the content in the sheet is currently scrolled to the top. */ + public boolean isContentScrolledToTop; + + /** Whether the sheet should currently be animating. */ + public boolean shouldBeAnimating; + + /** The current offset of the bottom sheet. */ + private float mCurrentSheetOffset; + + public MockSwipeableBottomSheet(float minOffset, float maxOffset) { + mMinOffset = minOffset; + mMaxOffset = maxOffset; + + // The sheet should be initialized at the minimum state. + mCurrentSheetOffset = mMinOffset; + + isContentScrolledToTop = true; + } + + @Override + public boolean isContentScrolledToTop() { + return isContentScrolledToTop; + } + + @Override + public float getCurrentOffsetPx() { + return mCurrentSheetOffset; + } + + @Override + public float getMinOffsetPx() { + return mMinOffset; + } + + @Override + public float getMaxOffsetPx() { + return mMaxOffset; + } + + @Override + public boolean isTouchEventInToolbar(MotionEvent event) { + // This will be implementation specific in practice. This checks that the motion event + // occured above the bottom of the toolbar. + return event.getRawY() < (mMaxOffset - mCurrentSheetOffset) + mMinOffset; + } + + @Override + public boolean shouldGestureMoveSheet( + MotionEvent initialDownEvent, MotionEvent currentEvent) { + return true; + } + + @Override + public void setSheetOffset(float offset, boolean shouldAnimate) { + mCurrentSheetOffset = offset; + shouldBeAnimating = shouldAnimate; + } + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mSwipeableBottomSheet = new MockSwipeableBottomSheet(MIN_SHEET_OFFSET, SCREEN_HEIGHT); + mSwipeDetector = new BottomSheetSwipeDetector(null, mSwipeableBottomSheet); + } + + /** + * Create a list of motion events simulating a scroll event stream from (x1, y1) to (x2, y2) + * and apply it to the provided swipe detector. + * @param x1 The start x. + * @param y1 The start y. + * @param x2 The end x. + * @param y2 The end y. + * @param detector The detector to apply the swipe to. + * @param endScroll Whether or not to include the up event at the end of the stream. + * @return A list of motion events. + */ + private static void performScroll(float x1, float y1, float x2, float y2, + BottomSheetSwipeDetector detector, boolean endScroll) { + int moveEventCount = 10; + + ArrayList eventStream = new ArrayList<>(); + float xInterval = (x2 - x1) / moveEventCount; + float yInterval = (y2 - y1) / moveEventCount; + eventStream.add(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, x1, y1, 0)); + for (int i = 0; i < moveEventCount; i++) { + eventStream.add(MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + x1 + ((i + 1) * xInterval), y1 + ((i + 1) * yInterval), 0)); + } + if (endScroll) eventStream.add(MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, x2, y2, 0)); + + applyGestureStream(eventStream, detector); + } + + /** + * Apply a list of events to a swipe detector. + * @param stream The list of motion events to apply to the detector. + * @param detector The detector to apply the swipe to. + */ + private static void applyGestureStream( + List stream, BottomSheetSwipeDetector detector) { + for (MotionEvent e : stream) { + if (!detector.isScrolling()) { + detector.onInterceptTouchEvent(e); + } else { + detector.onTouchEvent(e); + } + } + } + + /** Test that the sheet moves when scrolled up from min height. */ + @Test + public void testScrollToolbarUp_minHeight() { + assertEquals("The sheet should be at the minimum state.", MIN_SHEET_OFFSET, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + final float halfScreenHeight = SCREEN_HEIGHT / 2f; + + // Scrolling up half the screen should put the sheet at half + the min offset. + performScroll(0, SCREEN_HEIGHT, 0, halfScreenHeight, mSwipeDetector, true); + + assertEquals("The sheet is not at the correct height.", halfScreenHeight + MIN_SHEET_OFFSET, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + assertTrue("The sheet should be set to animate.", mSwipeableBottomSheet.shouldBeAnimating); + } + + /** Test that the sheet is not told to animate mid-stream. */ + @Test + public void testScrollToolbarUp_minHeight_noUpEvent() { + final float halfScreenHeight = SCREEN_HEIGHT / 2f; + + performScroll(0, SCREEN_HEIGHT, 0, halfScreenHeight, mSwipeDetector, false); + + assertEquals("The sheet is not at the correct height.", halfScreenHeight + MIN_SHEET_OFFSET, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + assertFalse( + "The sheet should not be set to animate.", mSwipeableBottomSheet.shouldBeAnimating); + } + + /** Test that the sheet does not move when scrolled up from max height. */ + @Test + public void testScrollToolbarUp_maxHeight() { + // Init the sheet to be full height. + mSwipeableBottomSheet.setSheetOffset(SCREEN_HEIGHT, false); + + assertEquals("The sheet should be at the maximum state.", SCREEN_HEIGHT, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + + performScroll(0, 0, 0, -500, mSwipeDetector, true); + + assertEquals("The sheet should still be at the maximum state.", SCREEN_HEIGHT, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + assertFalse( + "The sheet should not be set to animate.", mSwipeableBottomSheet.shouldBeAnimating); + } + + /** Test that the sheet does not move when scrolled down from min height. */ + @Test + public void testScrollToolbarDown_minHeight() { + assertEquals("The sheet should be at the minimum state.", MIN_SHEET_OFFSET, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + + performScroll(0, SCREEN_HEIGHT, 0, SCREEN_HEIGHT + 500, mSwipeDetector, true); + + assertEquals("The sheet should still be at the minimum state.", MIN_SHEET_OFFSET, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + assertFalse( + "The sheet should not be set to animate.", mSwipeableBottomSheet.shouldBeAnimating); + } + + /** Test that the sheet moves when scrolled down from max height. */ + @Test + public void testScrollToolbarDown_maxHeight() { + // Init the sheet to be full height. + mSwipeableBottomSheet.setSheetOffset(SCREEN_HEIGHT, false); + + assertEquals("The sheet should be at the maximum state.", SCREEN_HEIGHT, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + final float halfScreenHeight = SCREEN_HEIGHT / 2f; + + // Scrolling down half the screen should put the sheet at half height. + performScroll(0, 0, 0, halfScreenHeight, mSwipeDetector, true); + + assertEquals("The sheet is not at the correct height.", halfScreenHeight, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + assertTrue("The sheet should be set to animate.", mSwipeableBottomSheet.shouldBeAnimating); + } + + /** + * Test that the sheet moves when scrolled down from max height while the content has been + * scrolled. + */ + @Test + public void testScrollToolbarDown_maxHeight_contentScrolled() { + // Init the sheet to be full height. + mSwipeableBottomSheet.setSheetOffset(SCREEN_HEIGHT, false); + + assertEquals("The sheet should be at the maximum state.", SCREEN_HEIGHT, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + final float halfScreenHeight = SCREEN_HEIGHT / 2f; + + // Scrolling down half the screen should put the sheet at half height, regardless of the + // state of the content. + performScroll(0, 0, 0, halfScreenHeight, mSwipeDetector, true); + + assertEquals("The sheet is not at the correct height.", halfScreenHeight, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + assertTrue("The sheet should be set to animate.", mSwipeableBottomSheet.shouldBeAnimating); + } + + /** Test that the sheet does not move when a scroll is not sufficiently in the up direction. */ + @Test + public void testScrollToolbarDiagonal_minHeight() { + assertEquals("The sheet should be at the minimum state.", MIN_SHEET_OFFSET, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + final float halfScreenHeight = SCREEN_HEIGHT / 2f; + + performScroll( + 0, halfScreenHeight, halfScreenHeight, halfScreenHeight, mSwipeDetector, true); + + assertEquals("The sheet should still be at the minimum state.", MIN_SHEET_OFFSET, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + assertFalse( + "The sheet should not be set to animate.", mSwipeableBottomSheet.shouldBeAnimating); + } + + /** + * Test that the sheet does not move when the content is scrolled up and the sheet is at max + * height. + */ + @Test + public void testScrollContent_maxHeight() { + // Init the sheet to be full height. + mSwipeableBottomSheet.setSheetOffset(SCREEN_HEIGHT, false); + + // Content is scrolled some amount. + mSwipeableBottomSheet.isContentScrolledToTop = false; + + final float halfScreenHeight = SCREEN_HEIGHT / 2f; + + // Scroll down half the screen. The sheet should not move since the content is scrolled. + performScroll(0, halfScreenHeight, 0, SCREEN_HEIGHT, mSwipeDetector, true); + + assertEquals("The sheet should still be at the maximum state.", SCREEN_HEIGHT, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + } + + /** + * Test that the sheet moves when a scroll occurs on the body of the sheet. Content should only + * scroll if the sheet is at max height. + */ + @Test + public void testScrollContent_halfHeight() { + final float halfScreenHeight = SCREEN_HEIGHT / 2f; + + // Init the sheet to be half height. + mSwipeableBottomSheet.setSheetOffset(halfScreenHeight, false); + + // Content is scrolled some amount. + mSwipeableBottomSheet.isContentScrolledToTop = false; + + // Scroll down on the content, the sheet should move. + performScroll(0, halfScreenHeight / 2f, 0, SCREEN_HEIGHT, mSwipeDetector, true); + + assertEquals("The sheet should be at the minimum state.", MIN_SHEET_OFFSET, + mSwipeableBottomSheet.getCurrentOffsetPx(), MathUtils.EPSILON); + } +} diff --git a/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java new file mode 100644 index 00000000000..45d7f47cc8e --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/TouchRestrictingFrameLayout.java @@ -0,0 +1,50 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.FrameLayout; + +/** + * A specialized FrameLayout that is capable of ignoring all user input based on the state of + * the bottom sheet. + */ +class TouchRestrictingFrameLayout extends FrameLayout { + /** A handle to the bottom sheet. */ + private BottomSheet mBottomSheet; + + public TouchRestrictingFrameLayout(Context context, AttributeSet atts) { + super(context, atts); + } + + /** + * @param sheet The bottom sheet. + */ + public void setBottomSheet(BottomSheet sheet) { + mBottomSheet = sheet; + } + + /** + * @return Whether touch is enabled. + */ + private boolean isTouchDisabled() { + return mBottomSheet == null + || mBottomSheet.getSheetState() == BottomSheetController.SheetState.SCROLLING; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + if (isTouchDisabled()) return false; + return super.onInterceptTouchEvent(event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (isTouchDisabled()) return false; + return super.onTouchEvent(event); + } +} diff --git a/chromium/components/browser_ui/android/bottomsheet/java/res/layout/bottom_sheet.xml b/chromium/components/browser_ui/android/bottomsheet/java/res/layout/bottom_sheet.xml new file mode 100644 index 00000000000..37ce7fcab57 --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/java/res/layout/bottom_sheet.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + diff --git a/chromium/components/browser_ui/android/bottomsheet/java/res/values/dimens.xml b/chromium/components/browser_ui/android/bottomsheet/java/res/values/dimens.xml new file mode 100644 index 00000000000..d795be2ea38 --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/java/res/values/dimens.xml @@ -0,0 +1,12 @@ + + + + + 140dp + 56dp + 16dp + 10dp + + diff --git a/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java b/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java new file mode 100644 index 00000000000..d24e41afe7b --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java @@ -0,0 +1,158 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import androidx.annotation.IntDef; + +import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator; +import org.chromium.ui.modelutil.PropertyModel; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The public interface for the bottom sheet's controller. Features wishing to show content in the + * sheet UI must implement {@link BottomSheetContent} and call + * {@link #requestShowContent(BottomSheetContent, boolean)} which will return true if the content + * was actually shown (see full doc on method). + */ +public interface BottomSheetController { + /** The different states that the bottom sheet can have. */ + @IntDef({SheetState.NONE, SheetState.HIDDEN, SheetState.PEEK, SheetState.HALF, SheetState.FULL, + SheetState.SCROLLING}) + @Retention(RetentionPolicy.SOURCE) + @interface SheetState { + /** + * NONE is for internal use only and indicates the sheet is not currently + * transitioning between states. + */ + int NONE = -1; + // Values are used for indexing mStateRatios, should start from 0 + // and can't have gaps. Additionally order is important for these, + // they go from smallest to largest. + int HIDDEN = 0; + int PEEK = 1; + int HALF = 2; + int FULL = 3; + + int SCROLLING = 4; + } + + /** + * The different reasons that the sheet's state can change. + * + * Needs to stay in sync with BottomSheet.StateChangeReason in enums.xml. These values are + * persisted to logs. Entries should not be renumbered and numeric values should never be + * reused. + */ + @IntDef({StateChangeReason.NONE, StateChangeReason.SWIPE, StateChangeReason.BACK_PRESS, + StateChangeReason.TAP_SCRIM, StateChangeReason.NAVIGATION, + StateChangeReason.COMPOSITED_UI, StateChangeReason.VR, StateChangeReason.PROMOTE_TAB, + StateChangeReason.OMNIBOX_FOCUS, StateChangeReason.MAX_VALUE}) + @Retention(RetentionPolicy.SOURCE) + @interface StateChangeReason { + int NONE = 0; + int SWIPE = 1; + int BACK_PRESS = 2; + int TAP_SCRIM = 3; + int NAVIGATION = 4; + int COMPOSITED_UI = 5; + int VR = 6; + int PROMOTE_TAB = 7; + int OMNIBOX_FOCUS = 8; + int MAX_VALUE = OMNIBOX_FOCUS; + } + + /** + * Request that some content be shown in the bottom sheet. + * @param content The content to be shown in the bottom sheet. + * @param animate Whether the appearance of the bottom sheet should be animated. + * @return True if the content was shown, false if it was suppressed. Content is suppressed if + * higher priority content is in the sheet, the sheet is expanded beyond the peeking + * state, or the browser is in a mode that does not support showing the sheet. + */ + boolean requestShowContent(BottomSheetContent content, boolean animate); + + /** + * Hide content shown in the bottom sheet. If the content is not showing, this call retracts the + * request to show it. + * @param content The content to be hidden. + * @param animate Whether the sheet should animate when hiding. + * @param hideReason The reason that the content is being hidden. + */ + void hideContent( + BottomSheetContent content, boolean animate, @StateChangeReason int hideReason); + + void hideContent(BottomSheetContent content, boolean animate); + + /** @param observer The observer to add. */ + void addObserver(BottomSheetObserver observer); + + /** @param observer The observer to remove. */ + void removeObserver(BottomSheetObserver observer); + + /** + * Expand the sheet. If there is no content in the sheet, this is a noop. + */ + void expandSheet(); + + /** + * Collapse the current sheet to peek state. Sheet may not change the state if the state + * is not allowed. + * @param animate {@code true} for animation effect. + * @return {@code true} if the sheet could go to the peek state. + */ + boolean collapseSheet(boolean animate); + + /** @return The content currently showing in the bottom sheet. */ + BottomSheetContent getCurrentSheetContent(); + + /** @return The current state of the bottom sheet. */ + @SheetState + int getSheetState(); + + /** @return The target state of the bottom sheet (usually during animations). */ + @SheetState + int getTargetSheetState(); + + /** @return Whether the bottom sheet is currently open (expanded beyond peek state). */ + boolean isSheetOpen(); + + /** @return Whether the bottom sheet is in the process of hiding. */ + boolean isSheetHiding(); + + /** @return The current offset from the bottom of the screen that the sheet is in px. */ + int getCurrentOffset(); + + /** + * @return The height of the bottom sheet's container in px. This will return 0 if the sheet has + * not been initialized (content has not been requested). + */ + int getContainerHeight(); + + /** @return The height of the shadow above the bottom sheet in px. */ + int getTopShadowHeight(); + + /** + * @return The srcim's coordinator. This can be used to customize the bottom sheet's interaction + * with the scrim if the default behavior is not desired -- fading in behind the sheet + * as the sheet is expanded. + */ + ScrimCoordinator getScrimCoordinator(); + + /** + * This method provides a property model that can be used to show the scrim behind the bottom + * sheet. This can be used in conjunction with {@link #getScrimCoordinator()} to customize the + * scrim's behavior. While this method is not required to show the scrim, this method returns + * a model set up to appear behnind the sheet. Common usage is the following: + * + * PropertyModel params = controller.createScrimParams(); + * // further modify params + * controller.getScrimCoordinator().showScrim(params); + * + * @return A property model used to show the scrim behind the bottom sheet. + */ + PropertyModel createScrimParams(); +} diff --git a/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java b/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java new file mode 100644 index 00000000000..e5c2ae2687c --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java @@ -0,0 +1,57 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import androidx.annotation.Nullable; + +import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason; + +/** + * An interface for notifications about the state of the bottom sheet. + */ +public interface BottomSheetObserver { + /** + * A notification that the sheet has been opened, meaning the sheet is any height greater + * than its peeking state. + * @param reason The {@link StateChangeReason} that the sheet was opened. + */ + void onSheetOpened(@StateChangeReason int reason); + + /** + * A notification that the sheet has closed, meaning the sheet has reached its peeking state. + * @param reason The {@link StateChangeReason} that the sheet was closed. + */ + void onSheetClosed(@StateChangeReason int reason); + + /** + * An event for when the sheet's offset from the bottom of the screen changes. + * + * @param heightFraction The fraction of the way to the fully expanded state that the sheet + * is. This will be 0.0f when the sheet is hidden or scrolled off-screen + * and 1.0f when the sheet is completely expanded. + * @param offsetPx The offset of the top of the sheet from the bottom of the screen in pixels. + */ + void onSheetOffsetChanged(float heightFraction, float offsetPx); + + /** + * An event for when the sheet changes state. + * @param newState The new sheet state. See {@link SheetState}. + */ + void onSheetStateChanged(@SheetState int newState); + + /** + * An event for when the sheet reaches its full peeking height. This is called when the sheet + * is finished being scrolled back on-screen or finishes animating to its peeking state. This + * is also called when going back to the peeking state after the sheet has been opened. + */ + void onSheetFullyPeeked(); + + /** + * An event for when the sheet content changes. + * @param newContent The new {@link BottomSheetContent}, or null if the sheet has no content. + */ + void onSheetContentChanged(@Nullable BottomSheetContent newContent); +} diff --git a/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/EmptyBottomSheetObserver.java b/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/EmptyBottomSheetObserver.java new file mode 100644 index 00000000000..c758da5acf5 --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/EmptyBottomSheetObserver.java @@ -0,0 +1,30 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason; + +/** + * An empty base implementation of the {@link BottomSheetObserver} interface. + */ +public class EmptyBottomSheetObserver implements BottomSheetObserver { + @Override + public void onSheetOpened(@StateChangeReason int reason) {} + + @Override + public void onSheetClosed(@StateChangeReason int reason) {} + + @Override + public void onSheetOffsetChanged(float heightFraction, float offsetPx) {} + + @Override + public void onSheetStateChanged(int newState) {} + + @Override + public void onSheetFullyPeeked() {} + + @Override + public void onSheetContentChanged(BottomSheetContent newContent) {} +} diff --git a/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java b/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java new file mode 100644 index 00000000000..aa045e501f5 --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java @@ -0,0 +1,54 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.bottomsheet; + +import org.chromium.ui.util.AccessibilityUtil; + +/** + * An interface for the owning object to manage interaction between the bottom sheet and the rest + * of the system. + */ +public interface ManagedBottomSheetController extends BottomSheetController { + /** + * Temporarily suppress the bottom sheet while other UI is showing. This will not itself change + * the content displayed by the sheet. + * @param reason The reason the sheet was suppressed. + * @return A token to unsuppress the sheet with. + */ + int suppressSheet(@StateChangeReason int reason); + + /** + * Unsuppress the bottom sheet. This may or may not affect the sheet depending on the state of + * the browser (i.e. the tab switcher may be showing). + * @param token The token that was received from suppressing the sheet. + */ + void unsuppressSheet(int token); + + /** + * For all contents that don't have a custom lifecycle, we remove them from show requests or + * hide it if it is currently shown. + */ + void clearRequestsAndHide(); + + /** + * Handle a back press event. By default this will return the bottom sheet to it's minimum / + * peeking state if it is open. However, the sheet's content has the opportunity to intercept + * this event and block the default behavior {@see BottomSheetContent#handleBackPress()}. + * @return {@code true} if the sheet or content handled the back press. + */ + boolean handleBackPress(); + + /** + * Set the hidden ratio of the browser controls. + * @param ratio The hidden ratio of the browser controls in range [0, 1]. + */ + void setBrowserControlsHiddenRatio(float ratio); + + /** @param accessibilityUtil A mechanism for testing whether accessibility is enabled. */ + void setAccssibilityUtil(AccessibilityUtil accessibilityUtil); + + /** Clean up any state maintained by the controller. */ + void destroy(); +} diff --git a/chromium/components/browser_ui/android/bottomsheet/test/BUILD.gn b/chromium/components/browser_ui/android/bottomsheet/test/BUILD.gn new file mode 100644 index 00000000000..f8c7f8b22fc --- /dev/null +++ b/chromium/components/browser_ui/android/bottomsheet/test/BUILD.gn @@ -0,0 +1,16 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_library("java") { + sources = [ "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetTestSupport.java" ] + + deps = [ + "..:java", + "..:manager_java", + "../internal:java", + "//third_party/android_deps:androidx_annotation_annotation_java", + ] +} diff --git a/chromium/components/browser_ui/client_certificate/OWNERS b/chromium/components/browser_ui/client_certificate/OWNERS new file mode 100644 index 00000000000..bd8c1538b60 --- /dev/null +++ b/chromium/components/browser_ui/client_certificate/OWNERS @@ -0,0 +1,7 @@ +# Primary: +dmcardle@chromium.org + +# Secondary: +file://chrome/android/OWNERS + +# COMPONENT: Internals>Network>Auth diff --git a/chromium/components/browser_ui/client_certificate/android/BUILD.gn b/chromium/components/browser_ui/client_certificate/android/BUILD.gn new file mode 100644 index 00000000000..68a7aaf93bf --- /dev/null +++ b/chromium/components/browser_ui/client_certificate/android/BUILD.gn @@ -0,0 +1,58 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_resources("java_resources") { + custom_package = "org.chromium.components.browser_ui.client_certificate" + deps = [ + "//components/browser_ui/strings/android:browser_ui_strings_grd", + "//components/browser_ui/styles/android:java_resources", + ] +} + +generate_jni("jni_headers") { + sources = [ "java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java" ] +} + +android_library("java") { + sources = [ "java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java" ] + deps = [ + ":java_resources", + "//base:base_java", + "//base:jni_java", + "//content/public/android:content_java", + "//third_party/android_deps:androidx_appcompat_appcompat_java", + "//ui/android:ui_java", + ] + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] +} + +source_set("android") { + sources = [ + "ssl_client_certificate_request.cc", + "ssl_client_certificate_request.h", + ] + deps = [ + ":jni_headers", + "//base", + "//content/public/browser", + "//net", + "//ui/android", + ] +} + +java_library("junit") { + # Platform checks are broken for Robolectric. See https://crbug.com/1071638. + bypass_platform_checks = true + testonly = true + sources = [ "java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequestTest.java" ] + deps = [ + ":java", + "//base:base_junit_test_support", + "//third_party/android_deps:robolectric_all_java", + "//third_party/junit", + "//third_party/mockito:mockito_java", + ] +} diff --git a/chromium/components/browser_ui/client_certificate/android/DEPS b/chromium/components/browser_ui/client_certificate/android/DEPS new file mode 100644 index 00000000000..20a2f7177eb --- /dev/null +++ b/chromium/components/browser_ui/client_certificate/android/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+content/public/android/java/src/org/chromium/content_public", + "+content/public/browser", + "+net", + "+ui/android", +] diff --git a/chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java b/chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java new file mode 100644 index 00000000000..800cf88e521 --- /dev/null +++ b/chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequest.java @@ -0,0 +1,320 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.client_certificate; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.DialogInterface.OnClickListener; +import android.security.KeyChain; +import android.security.KeyChainAliasCallback; +import android.security.KeyChainException; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.app.AlertDialog; + +import org.chromium.base.ContextUtils; +import org.chromium.base.ThreadUtils; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; +import org.chromium.base.task.AsyncTask; +import org.chromium.base.task.PostTask; +import org.chromium.content_public.browser.UiThreadTaskTraits; +import org.chromium.ui.UiUtils; +import org.chromium.ui.base.WindowAndroid; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import javax.security.auth.x500.X500Principal; + +/** + * Handles selection of client certificate on the Java side. This class is responsible for selection + * of the client certificate to be used for authentication and retrieval of the private key and full + * certificate chain. + * + * The entry point is selectClientCertificate() and it will be called on the UI thread. Then the + * class will construct and run an appropriate CertAsyncTask, that will run in background, and + * finally pass the results back to the UI thread, which will return to the native code. + */ +@JNINamespace("browser_ui") +public class SSLClientCertificateRequest { + static final String TAG = "SSLClientCertificateRequest"; + + /** + * Implementation for anynchronous task of handling the certificate request. This + * AsyncTask retrieves the authentication material from the system key store. + * The key store is accessed in background, as the APIs being exercised + * may be blocking. The results are posted back to native on the UI thread. + */ + private static class CertAsyncTaskKeyChain extends AsyncTask { + // These fields will store the results computed in doInBackground so that they can be posted + // back in onPostExecute. + private byte[][] mEncodedChain; + private PrivateKey mPrivateKey; + + // Pointer to the native certificate request needed to return the results. + private final long mNativePtr; + + @SuppressLint("StaticFieldLeak") // TODO(crbug.com/807729): Remove and fix. + final Context mContext; + final String mAlias; + + CertAsyncTaskKeyChain(Context context, long nativePtr, String alias) { + mNativePtr = nativePtr; + mContext = context; + assert alias != null; + mAlias = alias; + } + + @Override + protected Void doInBackground() { + String alias = getAlias(); + if (alias == null) return null; + + PrivateKey key = getPrivateKey(alias); + X509Certificate[] chain = getCertificateChain(alias); + + if (key == null || chain == null || chain.length == 0) { + Log.w(TAG, "Empty client certificate chain?"); + return null; + } + + // Encode the certificate chain. + byte[][] encodedChain = new byte[chain.length][]; + try { + for (int i = 0; i < chain.length; ++i) { + encodedChain[i] = chain[i].getEncoded(); + } + } catch (CertificateEncodingException e) { + Log.w(TAG, "Could not retrieve encoded certificate chain: " + e); + return null; + } + + mEncodedChain = encodedChain; + mPrivateKey = key; + return null; + } + + @Override + protected void onPostExecute(Void result) { + ThreadUtils.assertOnUiThread(); + SSLClientCertificateRequestJni.get().onSystemRequestCompletion( + mNativePtr, mEncodedChain, mPrivateKey); + } + + private String getAlias() { + return mAlias; + } + + private PrivateKey getPrivateKey(String alias) { + try { + return KeyChain.getPrivateKey(mContext, alias); + } catch (KeyChainException e) { + Log.w(TAG, "KeyChainException when looking for '" + alias + "' certificate"); + return null; + } catch (InterruptedException e) { + Log.w(TAG, "InterruptedException when looking for '" + alias + "'certificate"); + return null; + } + } + + private X509Certificate[] getCertificateChain(String alias) { + try { + return KeyChain.getCertificateChain(mContext, alias); + } catch (KeyChainException e) { + Log.w(TAG, "KeyChainException when looking for '" + alias + "' certificate"); + return null; + } catch (InterruptedException e) { + Log.w(TAG, "InterruptedException when looking for '" + alias + "'certificate"); + return null; + } + } + } + + /** + * The system KeyChain API will call us back on the alias() method, passing the alias of the + * certificate selected by the user. + */ + private static class KeyChainCertSelectionCallback implements KeyChainAliasCallback { + private final long mNativePtr; + private final Context mContext; + + KeyChainCertSelectionCallback(Context context, long nativePtr) { + mContext = context; + mNativePtr = nativePtr; + } + + @Override + public void alias(final String alias) { + // This is called by KeyChainActivity in a background thread. Post task to + // handle the certificate selection on the UI thread. + PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> { + if (alias == null) { + // No certificate was selected. + PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, + () + -> SSLClientCertificateRequestJni.get() + .onSystemRequestCompletion(mNativePtr, null, null)); + } else { + new CertAsyncTaskKeyChain(mContext, mNativePtr, alias) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + } + } + + /** + * Wrapper class for the static KeyChain#choosePrivateKeyAlias method to facilitate testing. + */ + @VisibleForTesting + static class KeyChainCertSelectionWrapper { + private final Activity mActivity; + private final KeyChainAliasCallback mCallback; + private final String[] mKeyTypes; + private final Principal[] mPrincipalsForCallback; + private final String mHostName; + private final int mPort; + private final String mAlias; + + public KeyChainCertSelectionWrapper(Activity activity, KeyChainAliasCallback callback, + String[] keyTypes, Principal[] principalsForCallback, String hostName, int port, + String alias) { + mActivity = activity; + mCallback = callback; + mKeyTypes = keyTypes; + mPrincipalsForCallback = principalsForCallback; + mHostName = hostName; + mPort = port; + mAlias = alias; + } + + /** + * Calls KeyChain#choosePrivateKeyAlias with the provided arguments. + */ + public void choosePrivateKeyAlias() throws ActivityNotFoundException { + KeyChain.choosePrivateKeyAlias(mActivity, mCallback, mKeyTypes, mPrincipalsForCallback, + mHostName, mPort, mAlias); + } + } + + /** + * Dialog that explains to the user that client certificates aren't supported on their operating + * system. Separated out into its own class to allow Robolectric unit testing of + * maybeShowCertSelection without depending on Chrome resources. + */ + @VisibleForTesting + static class CertSelectionFailureDialog { + private final Context mContext; + + public CertSelectionFailureDialog(Context context) { + mContext = context; + } + + /** + * Builds and shows the dialog. + */ + public void show() { + final AlertDialog.Builder builder = new UiUtils.CompatibleAlertDialogBuilder( + mContext, R.style.Theme_Chromium_AlertDialog); + builder.setTitle(R.string.client_cert_unsupported_title) + .setMessage(R.string.client_cert_unsupported_message) + .setNegativeButton(R.string.close, + (OnClickListener) (dialog, which) + -> { + // Do nothing + }); + builder.show(); + } + } + + /** + * Create a new asynchronous request to select a client certificate. + * + * @param nativePtr The native object responsible for this request. + * @param window A WindowAndroid instance. + * @param keyTypes The list of supported key exchange types. + * @param encodedPrincipals The list of CA DistinguishedNames. + * @param hostName The server host name is available (empty otherwise). + * @param port The server port if available (0 otherwise). + * @return true on success. + * Note that nativeOnSystemRequestComplete will be called iff this method returns true. + */ + @CalledByNative + private static boolean selectClientCertificate(final long nativePtr, final WindowAndroid window, + final String[] keyTypes, byte[][] encodedPrincipals, final String hostName, + final int port) { + ThreadUtils.assertOnUiThread(); + + // Use the context for the failure dialog in case the activity doesn't have the correct + // resources. + final Context context = window.getContext().get(); + final Activity activity = ContextUtils.activityFromContext(context); + if (activity == null) { + Log.w(TAG, "Certificate request on GC'd activity."); + return false; + } + + // Build the list of principals from encoded versions. + Principal[] principals = null; + if (encodedPrincipals.length > 0) { + principals = new X500Principal[encodedPrincipals.length]; + try { + for (int n = 0; n < encodedPrincipals.length; n++) { + principals[n] = new X500Principal(encodedPrincipals[n]); + } + } catch (Exception e) { + Log.w(TAG, "Exception while decoding issuers list: " + e); + return false; + } + } + + KeyChainCertSelectionCallback callback = + new KeyChainCertSelectionCallback(activity.getApplicationContext(), nativePtr); + KeyChainCertSelectionWrapper keyChain = new KeyChainCertSelectionWrapper( + activity, callback, keyTypes, principals, hostName, port, null); + maybeShowCertSelection(keyChain, callback, new CertSelectionFailureDialog(context)); + + // We've taken ownership of the native ssl request object. + return true; + } + + /** + * Attempt to show the certificate selection dialog and shows the provided + * CertSelectionFailureDialog if the platform's cert selection activity can't be found. + */ + @VisibleForTesting + static void maybeShowCertSelection(KeyChainCertSelectionWrapper keyChain, + KeyChainAliasCallback callback, CertSelectionFailureDialog failureDialog) { + try { + keyChain.choosePrivateKeyAlias(); + } catch (ActivityNotFoundException e) { + // This exception can be hit when a platform is missing the activity to select + // a client certificate. It gets handled here to avoid a crash. + // Complete the callback without selecting a certificate. + callback.alias(null); + // Show a dialog letting the user know that the system does not support + // client certificate selection. + failureDialog.show(); + } + } + + public static void notifyClientCertificatesChangedOnIOThread() { + Log.d(TAG, "ClientCertificatesChanged!"); + SSLClientCertificateRequestJni.get().notifyClientCertificatesChangedOnIOThread(); + } + + @NativeMethods + interface Natives { + void notifyClientCertificatesChangedOnIOThread(); + // Called to pass request results to native side. + void onSystemRequestCompletion(long requestPtr, byte[][] certChain, PrivateKey privateKey); + } +} diff --git a/chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequestTest.java b/chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequestTest.java new file mode 100644 index 00000000000..2f3135f4106 --- /dev/null +++ b/chromium/components/browser_ui/client_certificate/android/java/src/org/chromium/components/browser_ui/client_certificate/SSLClientCertificateRequestTest.java @@ -0,0 +1,63 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.client_certificate; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.content.ActivityNotFoundException; +import android.security.KeyChainAliasCallback; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.components.browser_ui.client_certificate.SSLClientCertificateRequest.CertSelectionFailureDialog; +import org.chromium.components.browser_ui.client_certificate.SSLClientCertificateRequest.KeyChainCertSelectionWrapper; + +/** + * Unit tests for the SSLClientCertificateRequest class. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class SSLClientCertificateRequestTest { + @Mock + private KeyChainCertSelectionWrapper mKeyChainMock; + @Mock + private KeyChainAliasCallback mCallbackMock; + @Mock + private CertSelectionFailureDialog mFailureDialogMock; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testSelectCertActivityNotFound() { + doThrow(new ActivityNotFoundException()).when(mKeyChainMock).choosePrivateKeyAlias(); + + SSLClientCertificateRequest.maybeShowCertSelection( + mKeyChainMock, mCallbackMock, mFailureDialogMock); + + verify(mKeyChainMock).choosePrivateKeyAlias(); + verify(mCallbackMock).alias(null); + verify(mFailureDialogMock).show(); + } + + @Test + public void testSelectCertActivityFound() { + SSLClientCertificateRequest.maybeShowCertSelection( + mKeyChainMock, mCallbackMock, mFailureDialogMock); + + verify(mKeyChainMock).choosePrivateKeyAlias(); + verifyZeroInteractions(mFailureDialogMock); + } +} diff --git a/chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.cc b/chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.cc new file mode 100644 index 00000000000..13cb4583889 --- /dev/null +++ b/chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.cc @@ -0,0 +1,394 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/browser_ui/client_certificate/android/ssl_client_certificate_request.h" + +#include +#include + +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/containers/queue.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "components/browser_ui/client_certificate/android/jni_headers/SSLClientCertificateRequest_jni.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/client_certificate_delegate.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" +#include "net/base/host_port_pair.h" +#include "net/cert/cert_database.h" +#include "net/cert/x509_certificate.h" +#include "net/ssl/ssl_cert_request_info.h" +#include "net/ssl/ssl_client_cert_type.h" +#include "net/ssl/ssl_platform_key_android.h" +#include "net/ssl/ssl_private_key.h" +#include "ui/android/view_android.h" +#include "ui/android/window_android.h" + +namespace browser_ui { +namespace { +using base::android::JavaParamRef; +using base::android::ScopedJavaLocalRef; + +class SSLClientCertPendingRequests; + +class ClientCertRequest { + public: + ClientCertRequest( + base::WeakPtr pending_requests, + const scoped_refptr& cert_request_info, + std::unique_ptr delegate) + : pending_requests_(pending_requests), + cert_request_info_(cert_request_info), + delegate_(std::move(delegate)) {} + + base::OnceClosure GetCancellationCallback() { + return base::BindOnce(&ClientCertRequest::OnCancel, + weak_factory_.GetWeakPtr()); + } + + void CertificateSelected(scoped_refptr cert, + scoped_refptr key); + + void OnCancel(); + + net::SSLCertRequestInfo* cert_request_info() const { + return cert_request_info_.get(); + } + + private: + base::WeakPtr pending_requests_; + scoped_refptr cert_request_info_; + std::unique_ptr delegate_; + base::WeakPtrFactory weak_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(ClientCertRequest); +}; + +class SSLClientCertPendingRequests + : public content::WebContentsUserData, + public content::WebContentsObserver { + public: + explicit SSLClientCertPendingRequests(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) {} + ~SSLClientCertPendingRequests() override {} + + void AddRequest(std::unique_ptr request); + + void RequestComplete(net::SSLCertRequestInfo* info, + scoped_refptr cert, + scoped_refptr key); + + // Remove pending requests when |should_keep| returns false. Calls |on_drop| + // before dropping a request. + void FilterPendingRequests( + std::function should_keep, + std::function on_drop); + + base::WeakPtr GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + void ReadyToCommitNavigation( + content::NavigationHandle* navigation_handle) override; + + class CertificateDialogPolicy { + public: + // Has the maximum number of cert dialogs been exceeded? + bool MaxExceeded() { return count_ >= k_max_displayed_dialogs; } + // Resets counter. Should be called on navigation. + void ResetCount() { count_ = 0; } + // Increment the counter. + void IncrementCount() { count_++; } + + private: + size_t count_ = 0; + const size_t k_max_displayed_dialogs = 5; + }; + + private: + void PumpRequests(); + + bool active_request_ = false; + + CertificateDialogPolicy dialog_policy_; + base::queue> pending_requests_; + base::WeakPtrFactory weak_factory_{this}; + + friend class content::WebContentsUserData; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); +}; + +ui::WindowAndroid* GetWindowFromWebContents( + content::WebContents* web_contents) { + ui::ViewAndroid* view = web_contents->GetNativeView(); + if (view == nullptr) { + LOG(ERROR) << "Could not get ViewAndroid"; + return nullptr; + } + // May return nullptr. + return view->GetWindowAndroid(); +} + +WEB_CONTENTS_USER_DATA_KEY_IMPL(SSLClientCertPendingRequests) + +static void StartClientCertificateRequest( + std::unique_ptr request, + content::WebContents* web_contents) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + ui::WindowAndroid* window = GetWindowFromWebContents(web_contents); + if (window == nullptr) { + LOG(ERROR) << "Could not get Window"; + return; + } + + // Build the |key_types| JNI parameter, as a String[] + std::vector key_types; + for (size_t n = 0; n < request->cert_request_info()->cert_key_types.size(); + ++n) { + switch (request->cert_request_info()->cert_key_types[n]) { + case net::CLIENT_CERT_RSA_SIGN: + key_types.push_back("RSA"); + break; + case net::CLIENT_CERT_ECDSA_SIGN: + key_types.push_back("EC"); + break; + default: + // Ignore unknown types. + break; + } + } + + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef key_types_ref = + base::android::ToJavaArrayOfStrings(env, key_types); + if (key_types_ref.is_null()) { + LOG(ERROR) << "Could not create key types array (String[])"; + return; + } + + // Build the |encoded_principals| JNI parameter, as a byte[][] + ScopedJavaLocalRef principals_ref = + base::android::ToJavaArrayOfByteArray( + env, request->cert_request_info()->cert_authorities); + if (principals_ref.is_null()) { + LOG(ERROR) << "Could not create principals array (byte[][])"; + return; + } + + // Build the |host_name| and |port| JNI parameters, as a String and + // a jint. + ScopedJavaLocalRef host_name_ref = + base::android::ConvertUTF8ToJavaString( + env, request->cert_request_info()->host_and_port.host()); + + // Pass the address of the delegate through to Java. + jlong request_id = reinterpret_cast(request.get()); + + if (!Java_SSLClientCertificateRequest_selectClientCertificate( + env, request_id, window->GetJavaObject(), key_types_ref, + principals_ref, host_name_ref, + request->cert_request_info()->host_and_port.port())) { + return; + } + + // Ownership was transferred to Java. + ignore_result(request.release()); +} + +void SSLClientCertPendingRequests::AddRequest( + std::unique_ptr request) { + pending_requests_.push(std::move(request)); + PumpRequests(); +} + +// Note that the default value for |on_drop| is a no-op. +void SSLClientCertPendingRequests::FilterPendingRequests( + std::function should_keep, + std::function on_drop = [](auto* unused) {}) { + base::queue> new_pending_requests; + while (!pending_requests_.empty()) { + std::unique_ptr next = + std::move(pending_requests_.front()); + pending_requests_.pop(); + if (should_keep(next.get())) { + new_pending_requests.push(std::move(next)); + } else { + on_drop(next.get()); + } + } + pending_requests_.swap(new_pending_requests); +} + +void SSLClientCertPendingRequests::RequestComplete( + net::SSLCertRequestInfo* info, + scoped_refptr cert, + scoped_refptr key) { + active_request_ = false; + + // Deduplicate pending requests. Only keep pending requests whose host and + // port differ from those of the completed request. + const std::string host_and_port = info->host_and_port.ToString(); + auto should_keep = [host_and_port](ClientCertRequest* req) { + return host_and_port != req->cert_request_info()->host_and_port.ToString(); + }; + auto on_drop = [cert, key](ClientCertRequest* req) { + req->CertificateSelected(cert, key); + }; + FilterPendingRequests(should_keep, on_drop); + + PumpRequests(); +} + +void SSLClientCertPendingRequests::PumpRequests() { + if (active_request_ || pending_requests_.empty()) { + return; + } + + active_request_ = true; + std::unique_ptr next = + std::move(pending_requests_.front()); + pending_requests_.pop(); + + // Check if this page is allowed to show any more client cert dialogs. + if (!dialog_policy_.MaxExceeded()) { + dialog_policy_.IncrementCount(); + StartClientCertificateRequest(std::move(next), web_contents()); + } +} + +void SSLClientCertPendingRequests::ReadyToCommitNavigation( + content::NavigationHandle* navigation_handle) { + // Be careful to only reset the the client certificate dialog counter when the + // navigation is user-initiated. Note that |HasUserGesture| does not capture + // browser-initiated navigations. The negation of |IsRendererInitiated| tells + // us whether the navigation is browser-generated. + if (navigation_handle->IsInMainFrame() && + (navigation_handle->HasUserGesture() || + !navigation_handle->IsRendererInitiated())) { + // Flush any remaining dialogs before resetting the counter. + auto should_keep = [](auto* req) { return false; }; + FilterPendingRequests(should_keep); + dialog_policy_.ResetCount(); + } +} + +void ClientCertRequest::CertificateSelected( + scoped_refptr cert, + scoped_refptr key) { + delegate_->ContinueWithCertificate(cert, key); + if (pending_requests_) { + pending_requests_->RequestComplete(cert_request_info(), cert, key); + } +} + +void ClientCertRequest::OnCancel() { + // When we receive an OnCancel message, we remove this ClientCertRequest from + // the queue of pending requests. + auto should_keep = [this](auto* req) { return req != this; }; + if (pending_requests_) { + pending_requests_->FilterPendingRequests(should_keep); + } +} + +} // namespace + +// Called from JNI on request completion/result. +// |env| is the current thread's JNIEnv. +// |clazz| is the SSLClientCertificateRequest JNI class reference. +// |request_id| is the id passed to +// Java_SSLClientCertificateRequest_selectClientCertificate() in Start(). +// |encoded_chain_ref| is a JNI reference to a Java array of byte arrays, +// each item holding a DER-encoded X.509 certificate. +// |private_key_ref| is the platform PrivateKey object JNI reference for +// the client certificate. +// Note: both |encoded_chain_ref| and |private_key_ref| will be NULL if +// the user didn't select a certificate. +static void JNI_SSLClientCertificateRequest_OnSystemRequestCompletion( + JNIEnv* env, + jlong request_id, + const JavaParamRef& encoded_chain_ref, + const JavaParamRef& private_key_ref) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + // Take back ownership of the request object. + std::unique_ptr request( + reinterpret_cast(request_id)); + + if (encoded_chain_ref == NULL || private_key_ref == NULL) { + LOG(ERROR) << "No client certificate selected"; + request->CertificateSelected(nullptr, nullptr); + return; + } + + // Convert the encoded chain to a vector of strings. + std::vector encoded_chain_strings; + if (encoded_chain_ref) { + base::android::JavaArrayOfByteArrayToStringVector(env, encoded_chain_ref, + &encoded_chain_strings); + } + + std::vector encoded_chain; + for (size_t n = 0; n < encoded_chain_strings.size(); ++n) + encoded_chain.push_back(encoded_chain_strings[n]); + + // Create the X509Certificate object from the encoded chain. + scoped_refptr client_cert( + net::X509Certificate::CreateFromDERCertChain(encoded_chain)); + if (!client_cert.get()) { + LOG(ERROR) << "Could not decode client certificate chain"; + return; + } + + // Create an SSLPrivateKey wrapper for the private key JNI reference. + scoped_refptr private_key = + net::WrapJavaPrivateKey(client_cert.get(), private_key_ref); + if (!private_key) { + LOG(ERROR) << "Could not create OpenSSL wrapper for private key"; + return; + } + + request->CertificateSelected(std::move(client_cert), std::move(private_key)); +} + +static void NotifyClientCertificatesChanged() { + net::CertDatabase::GetInstance()->NotifyObserversCertDBChanged(); +} + +static void +JNI_SSLClientCertificateRequest_NotifyClientCertificatesChangedOnIOThread( + JNIEnv* env) { + if (content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) { + NotifyClientCertificatesChanged(); + } else { + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&NotifyClientCertificatesChanged)); + } +} + +base::OnceClosure ShowSSLClientCertificateSelector( + content::WebContents* contents, + net::SSLCertRequestInfo* cert_request_info, + std::unique_ptr delegate) { + SSLClientCertPendingRequests::CreateForWebContents(contents); + SSLClientCertPendingRequests* active_requests = + SSLClientCertPendingRequests::FromWebContents(contents); + + auto client_cert_request = std::make_unique( + active_requests->GetWeakPtr(), cert_request_info, std::move(delegate)); + base::OnceClosure cancellation_callback = + client_cert_request->GetCancellationCallback(); + active_requests->AddRequest(std::move(client_cert_request)); + return cancellation_callback; +} + +} // namespace browser_ui diff --git a/chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.h b/chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.h new file mode 100644 index 00000000000..1ac2090b23c --- /dev/null +++ b/chromium/components/browser_ui/client_certificate/android/ssl_client_certificate_request.h @@ -0,0 +1,32 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_BROWSER_UI_CLIENT_CERTIFICATE_ANDROID_SSL_CLIENT_CERTIFICATE_REQUEST_H_ +#define COMPONENTS_BROWSER_UI_CLIENT_CERTIFICATE_ANDROID_SSL_CLIENT_CERTIFICATE_REQUEST_H_ + +#include + +#include "base/callback.h" + +namespace content { +class ClientCertificateDelegate; +class WebContents; +} // namespace content + +namespace net { +class SSLCertRequestInfo; +} + +namespace browser_ui { + +// Opens a SSL client certificate selection dialog. Returns a callback that will +// cancel the dialog. +base::OnceClosure ShowSSLClientCertificateSelector( + content::WebContents* contents, + net::SSLCertRequestInfo* cert_request_info, + std::unique_ptr delegate); + +} // namespace browser_ui + +#endif // COMPONENTS_BROWSER_UI_CLIENT_CERTIFICATE_ANDROID_SSL_CLIENT_CERTIFICATE_REQUEST_H_ diff --git a/chromium/components/browser_ui/http_auth/android/BUILD.gn b/chromium/components/browser_ui/http_auth/android/BUILD.gn new file mode 100644 index 00000000000..0184556b59c --- /dev/null +++ b/chromium/components/browser_ui/http_auth/android/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_library("java") { + sources = [ + "java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java", + ] + + deps = [ + ":java_resources", + "//base:base_java", + "//components/browser_ui/widget/android:java", + "//components/strings:components_strings_grd", + "//third_party/android_deps:androidx_appcompat_appcompat_java", + "//third_party/android_deps:com_google_android_material_material_java", + "//ui/android:ui_java", + ] +} + +android_resources("java_resources") { + custom_package = "org.chromium.components.browser_ui.http_auth" + sources = [ "java/res/layout/http_auth_dialog.xml" ] + + deps = [ + "//components/browser_ui/styles/android:java_resources", + "//components/browser_ui/widget/android:java_resources", + "//components/strings:components_strings_grd", + "//ui/android:ui_java_resources", + ] +} diff --git a/chromium/components/browser_ui/http_auth/android/DEPS b/chromium/components/browser_ui/http_auth/android/DEPS new file mode 100644 index 00000000000..d21e0729f9c --- /dev/null +++ b/chromium/components/browser_ui/http_auth/android/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+ui/android", +] diff --git a/chromium/components/browser_ui/http_auth/android/OWNERS b/chromium/components/browser_ui/http_auth/android/OWNERS new file mode 100644 index 00000000000..697c69edec7 --- /dev/null +++ b/chromium/components/browser_ui/http_auth/android/OWNERS @@ -0,0 +1 @@ +file://chrome/android/OWNERS diff --git a/chromium/components/browser_ui/http_auth/android/java/res/OWNERS b/chromium/components/browser_ui/http_auth/android/java/res/OWNERS new file mode 100644 index 00000000000..7bb5d6933c5 --- /dev/null +++ b/chromium/components/browser_ui/http_auth/android/java/res/OWNERS @@ -0,0 +1,9 @@ +# This restriction is in place to avoid accidential addition to our top level +# layout files, such as add duplicated assets, or introducing new colors when +# we don't want them. +set noparent + +file://ui/android/java/res/LAYOUT_OWNERS + +# COMPONENT: UI>Browser>Mobile +# OS: Android diff --git a/chromium/components/browser_ui/http_auth/android/java/res/layout/http_auth_dialog.xml b/chromium/components/browser_ui/http_auth/android/java/res/layout/http_auth_dialog.xml new file mode 100644 index 00000000000..f55df1e32a5 --- /dev/null +++ b/chromium/components/browser_ui/http_auth/android/java/res/layout/http_auth_dialog.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + diff --git a/chromium/components/browser_ui/http_auth/android/java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java b/chromium/components/browser_ui/http_auth/android/java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java new file mode 100644 index 00000000000..a97b221d24d --- /dev/null +++ b/chromium/components/browser_ui/http_auth/android/java/src/org/chromium/components/browser_ui/http_auth/LoginPrompt.java @@ -0,0 +1,142 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.http_auth; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.TextView; + +import androidx.appcompat.app.AlertDialog; + +import org.chromium.components.browser_ui.widget.text.AlertDialogEditText; +import org.chromium.ui.UiUtils; + +/** + * HTTP Authentication Dialog + * + * This borrows liberally from android.browser.HttpAuthenticationDialog. + */ +public class LoginPrompt { + private final Context mContext; + private final String mMessageBody; + private final Observer mObserver; + + private AlertDialog mDialog; + private AlertDialogEditText mUsernameView; + private AlertDialogEditText mPasswordView; + + /** + * This is a public interface that provides the result of the prompt. + */ + public static interface Observer { + /** + * Cancel the authorization request. + */ + public void cancel(); + + /** + * Proceed with the authorization with the given credentials. + */ + public void proceed(String username, String password); + } + + /** + * Constructs an http auth prompt. + * + * @param context The Context to use. + * @param messageBody The text to show to the user. + * @param autofillUrl If not null, Android Autofill support is enabled for the form with the + * given url being set as the web domain for the View control. + * @param observer An interface to receive the result of the prompt. + */ + public LoginPrompt(Context context, String messageBody, String autofillUrl, Observer observer) { + mContext = context; + mMessageBody = messageBody; + mObserver = observer; + createDialog(autofillUrl); + } + + private void createDialog(String autofillUrl) { + View v = LayoutInflater.from(mContext).inflate(R.layout.http_auth_dialog, null); + mUsernameView = (AlertDialogEditText) v.findViewById(R.id.username); + mPasswordView = (AlertDialogEditText) v.findViewById(R.id.password); + if (autofillUrl != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // By default Android Autofill support is turned off for these controls because Chrome + // uses its own autofill provider (Chrome Sync). If an app is using Android Autofill + // then we need to enable Android Autofill for the controls. + mUsernameView.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES); + mPasswordView.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES); + mUsernameView.setUrl(autofillUrl); + mPasswordView.setUrl(autofillUrl); + } + mPasswordView.setOnEditorActionListener((v1, actionId, event) -> { + if (actionId == EditorInfo.IME_ACTION_DONE) { + mDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick(); + return true; + } + return false; + }); + + TextView explanationView = (TextView) v.findViewById(R.id.explanation); + explanationView.setText(mMessageBody); + + mDialog = + new UiUtils + .CompatibleAlertDialogBuilder(mContext, R.style.Theme_Chromium_AlertDialog) + .setTitle(R.string.login_dialog_title) + .setView(v) + .setPositiveButton(R.string.login_dialog_ok_button_label, + (DialogInterface.OnClickListener) (dialog, whichButton) + -> mObserver.proceed(getUsername(), getPassword())) + .setNegativeButton(R.string.cancel, + (DialogInterface.OnClickListener) (dialog, + whichButton) -> mObserver.cancel()) + .setOnCancelListener(dialog -> mObserver.cancel()) + .create(); + mDialog.getDelegate().setHandleNativeActionModesEnabled(false); + + // Make the IME appear when the dialog is displayed if applicable. + mDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + } + + /** + * Shows the dialog. + */ + public void show() { + mDialog.show(); + mUsernameView.requestFocus(); + } + + /** + * Dismisses the dialog. + */ + public void dismiss() { + mDialog.dismiss(); + } + + /** Return whether the dialog is being shown. */ + public boolean isShowing() { + return mDialog != null && mDialog.isShowing(); + } + + private String getUsername() { + return mUsernameView.getText().toString(); + } + + private String getPassword() { + return mPasswordView.getText().toString(); + } + + public void onAutofillDataAvailable(String username, String password) { + mUsernameView.setText(username); + mPasswordView.setText(password); + mUsernameView.selectAll(); + } +} diff --git a/chromium/components/browser_ui/media/OWNERS b/chromium/components/browser_ui/media/OWNERS new file mode 100644 index 00000000000..28f64e589e9 --- /dev/null +++ b/chromium/components/browser_ui/media/OWNERS @@ -0,0 +1,4 @@ +mlamouri@chromium.org + +# TEAM: media-dev@chromium.org +# COMPONENT: Internals>Media>UI diff --git a/chromium/components/browser_ui/media/android/BUILD.gn b/chromium/components/browser_ui/media/android/BUILD.gn new file mode 100644 index 00000000000..31b7a047305 --- /dev/null +++ b/chromium/components/browser_ui/media/android/BUILD.gn @@ -0,0 +1,95 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_library("java") { + sources = [ + "java/src/org/chromium/components/browser_ui/media/MediaImageCallback.java", + "java/src/org/chromium/components/browser_ui/media/MediaImageManager.java", + "java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java", + "java/src/org/chromium/components/browser_ui/media/MediaNotificationImageUtils.java", + "java/src/org/chromium/components/browser_ui/media/MediaNotificationInfo.java", + "java/src/org/chromium/components/browser_ui/media/MediaNotificationListener.java", + "java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java", + "java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java", + "java/src/org/chromium/components/browser_ui/media/MediaSessionUma.java", + ] + + deps = [ + ":java_resources", + "//base:base_java", + "//components/browser_ui/notifications/android:java", + "//components/url_formatter/android:url_formatter_java", + "//content/public/android:content_java", + "//services/media_session/public/cpp/android:media_session_java", + "//services/media_session/public/mojom:mojom_java", + "//third_party/android_deps:android_support_v4_java", + "//ui/android:ui_full_java", + "//url:gurl_java", + ] +} + +android_resources("java_resources") { + custom_package = "org.chromium.components.browser_ui.media" + sources = [ + "java/res/drawable-hdpi/audio_playing.png", + "java/res/drawable-hdpi/audio_playing_square.png", + "java/res/drawable-hdpi/ic_fast_forward_white_36dp.png", + "java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png", + "java/res/drawable-hdpi/ic_skip_next_white_36dp.png", + "java/res/drawable-hdpi/ic_skip_previous_white_36dp.png", + "java/res/drawable-mdpi/audio_playing.png", + "java/res/drawable-mdpi/audio_playing_square.png", + "java/res/drawable-mdpi/ic_fast_forward_white_36dp.png", + "java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png", + "java/res/drawable-mdpi/ic_skip_next_white_36dp.png", + "java/res/drawable-mdpi/ic_skip_previous_white_36dp.png", + "java/res/drawable-xhdpi/audio_playing.png", + "java/res/drawable-xhdpi/audio_playing_square.png", + "java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png", + "java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png", + "java/res/drawable-xhdpi/ic_skip_next_white_36dp.png", + "java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png", + "java/res/drawable-xxhdpi/audio_playing.png", + "java/res/drawable-xxhdpi/audio_playing_square.png", + "java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png", + "java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png", + "java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png", + "java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png", + "java/res/drawable-xxxhdpi/audio_playing.png", + "java/res/drawable-xxxhdpi/audio_playing_square.png", + "java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png", + "java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png", + "java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png", + "java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png", + ] + deps = [ + "//components/browser_ui/strings/android:browser_ui_strings_grd", + "//components/browser_ui/styles/android:java_resources", + ] +} + +java_library("junit") { + # Skip platform checks since Robolectric depends on requires_android targets. + bypass_platform_checks = true + testonly = true + sources = [ + "java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java", + "java/src/org/chromium/components/browser_ui/media/MediaNotificationButtonComputationTest.java", + ] + deps = [ + ":java", + "//base:base_java", + "//base:base_java_test_support", + "//base:base_junit_test_support", + "//base/test:test_support_java", + "//content/public/android:content_java", + "//services/media_session/public/cpp/android:media_session_java", + "//services/media_session/public/mojom:mojom_java", + "//third_party/android_deps:robolectric_all_java", + "//third_party/junit", + "//third_party/mockito:mockito_java", + ] +} diff --git a/chromium/components/browser_ui/media/android/DEPS b/chromium/components/browser_ui/media/android/DEPS new file mode 100644 index 00000000000..652050725b0 --- /dev/null +++ b/chromium/components/browser_ui/media/android/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+components/url_formatter/android", + "+content/public/android", + "+services/media_session/public/cpp/android", + "+ui/android", +] diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing.png b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing.png new file mode 100644 index 00000000000..75d13258a70 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing_square.png b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing_square.png new file mode 100644 index 00000000000..94108427da0 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/audio_playing_square.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 00000000000..6a7db4b8c80 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 00000000000..656d0220963 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png new file mode 100644 index 00000000000..cf68df833a9 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 00000000000..da1c1c958f2 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing.png b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing.png new file mode 100644 index 00000000000..a9ccbc15101 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing_square.png b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing_square.png new file mode 100644 index 00000000000..acbe6dbea23 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/audio_playing_square.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 00000000000..f890f113715 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 00000000000..9d02d436605 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png new file mode 100644 index 00000000000..9032328d4df Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 00000000000..23faeeb0264 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing.png b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing.png new file mode 100644 index 00000000000..7fcb219818e Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing_square.png b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing_square.png new file mode 100644 index 00000000000..212aa900ffb Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/audio_playing_square.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 00000000000..f7d810f1248 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 00000000000..12ff39ab480 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png new file mode 100644 index 00000000000..972192d3937 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 00000000000..1181ec926a8 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing.png new file mode 100644 index 00000000000..a743b72ac6b Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing_square.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing_square.png new file mode 100644 index 00000000000..403f6945051 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/audio_playing_square.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 00000000000..b41b3de40b6 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 00000000000..253833bbc36 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png new file mode 100644 index 00000000000..4652215ccc8 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 00000000000..c8db47f6635 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing.png new file mode 100644 index 00000000000..5139cbdba20 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing_square.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing_square.png new file mode 100644 index 00000000000..e994ee339dd Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/audio_playing_square.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png new file mode 100644 index 00000000000..b111f7d2541 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png new file mode 100644 index 00000000000..e1baaa33127 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png new file mode 100644 index 00000000000..00b29dd1703 Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png new file mode 100644 index 00000000000..9e52d5001ea Binary files /dev/null and b/chromium/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png differ diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageCallback.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageCallback.java new file mode 100644 index 00000000000..117373ad0ef --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageCallback.java @@ -0,0 +1,22 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import android.graphics.Bitmap; + +import androidx.annotation.Nullable; + +/** + * The callback when an image is downloaded. This class is different with + * {@link ImageDownloadCallback} and is only used by {@link MediaImageManager}. + */ +public interface MediaImageCallback { + /** + * Called when image downloading is complete. + * @param bitmap The downloaded image. |null| indicates there is no available src for download + * or image download failed. + */ + void onImageDownloaded(@Nullable Bitmap image); +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManager.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManager.java new file mode 100644 index 00000000000..b2fa480fe90 --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManager.java @@ -0,0 +1,235 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.text.TextUtils; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.FileUtils; +import org.chromium.content_public.browser.ImageDownloadCallback; +import org.chromium.content_public.browser.WebContents; +import org.chromium.services.media_session.MediaImage; + +import java.util.Iterator; +import java.util.List; + +/** + * A class for managing the MediaImage download process. + * + * The manager takes a list of {@link MediaMetadata.MediaImage} as input, and + * selects one of them based on scoring and start download through + * {@link WebContents} asynchronously. When the download successfully finishes, + * the manager runs the callback function to notify the completion and pass the + * downloaded Bitmap. + * + * The scoring works as follows: + * - A image score is computed by multiplying the type score with the size score. + * - The type score lies in [0, 1] and is based on the image MIME type/file extension. + * - PNG and JPEG are prefered than others. + * - If unspecified, use the default type score (0.6). + * - The size score lies in [0, 1] and is computed by multiplying the dominant size score and aspect + * ratio score: + * - The dominant size score lies in [0, 1] and is computed using |mMinimumSize| and |mIdealSize|: + * - If size < |mMinimumSize| (too small), the size score is 0. + * - If |mMinimumSize| <= size <= |mIdealSize|, the score increases linearly from 0.2 to 1. + * - If size > |mIdealSize|, the score is |mIdealSize| / size, which drops from 1 to 0. + * - When the size is "any", the size score is 0.8. + * - If unspecified, use the default size score (0.4). + * - The aspect ratio score lies in [0, 1] and is computed by dividing the short edge length by + * the long edge. + */ +public class MediaImageManager implements ImageDownloadCallback { + // The default score of unknown image size. + private static final double DEFAULT_IMAGE_SIZE_SCORE = 0.4; + // The scores for different image types. Keep them sorted by value. + private static final double TYPE_SCORE_DEFAULT = 0.6; + private static final double TYPE_SCORE_PNG = 1.0; + private static final double TYPE_SCORE_JPEG = 0.7; + private static final double TYPE_SCORE_BMP = 0.5; + private static final double TYPE_SCORE_XICON = 0.4; + private static final double TYPE_SCORE_GIF = 0.3; + + @VisibleForTesting + static final int MAX_BITMAP_SIZE_FOR_DOWNLOAD = 2048; + + private WebContents mWebContents; + // The minimum image size. Images that are smaller than |mMinimumSize| will be ignored. + final int mMinimumSize; + // The ideal image size. Images that are too large than |mIdealSize| will be ignored. + final int mIdealSize; + // The pending download image request id, which is set when calling + // {@link WebContents#downloadImage()}, and reset when image download completes or + // {@link #clearRequests()} is called. + private int mRequestId; + // The callback to be called when the pending download image request completes. + private MediaImageCallback mCallback; + + // The last image src for download, used for avoiding fetching the same src when artwork is set + // multiple times but the same src is chosen. + // + // Will be reset when initiating a new download request. + private String mLastImageSrc; + + /** + * MediaImageManager constructor. + * @param minimumSize The minimum size of images to download. + * @param idealSize The ideal size of images to download. + */ + public MediaImageManager(int minimumSize, int idealSize) { + mMinimumSize = minimumSize; + mIdealSize = idealSize; + clearRequests(); + } + + /** + * Called when the WebContent changes. + * @param contents The new WebContents. + */ + public void setWebContents(WebContents contents) { + mWebContents = contents; + clearRequests(); + } + + /** + * Select the best image from |images| and start download. + * @param images The list of images to choose from. Null is equivalent to empty list. + * @param callback The callback when image download completes. + */ + public void downloadImage(List images, MediaImageCallback callback) { + if (mWebContents == null) return; + + mCallback = callback; + MediaImage image = selectImage(images); + if (image == null) { + mLastImageSrc = null; + mCallback.onImageDownloaded(null); + clearRequests(); + return; + } + + // Avoid fetching the same image twice. + if (TextUtils.equals(image.getSrc(), mLastImageSrc)) return; + mLastImageSrc = image.getSrc(); + + // Limit |maxBitmapSize| to |MAX_BITMAP_SIZE_FOR_DOWNLOAD| to avoid passing huge bitmaps + // through JNI. |maxBitmapSize| does not prevent huge images to be downloaded. It is used to + // filter/rescale the download images. See documentation of + // {@link WebContents#downloadImage()} for details. + mRequestId = mWebContents.downloadImage(image.getSrc(), // url + false, // isFavicon + MAX_BITMAP_SIZE_FOR_DOWNLOAD, // maxBitmapSize + false, // bypassCache + this); // callback + } + + /** + * ImageDownloadCallback implementation. This method is called when an download image request is + * completed. The class will only keep the latest request. If some call to this method is + * corresponding to a previous request, it will be ignored. + */ + @Override + public void onFinishDownloadImage(int id, int httpStatusCode, String imageUrl, + List bitmaps, List originalImageSizes) { + if (id != mRequestId) return; + + Iterator iterBitmap = bitmaps.iterator(); + Iterator iterSize = originalImageSizes.iterator(); + + Bitmap bestBitmap = null; + double bestScore = 0; + while (iterBitmap.hasNext() && iterSize.hasNext()) { + Bitmap bitmap = iterBitmap.next(); + Rect size = iterSize.next(); + double newScore = getImageSizeScore(size); + if (bestScore < newScore) { + bestBitmap = bitmap; + bestScore = newScore; + } + } + mCallback.onImageDownloaded(bestBitmap); + clearRequests(); + } + + /** + * Select the best image from the |images|. + * @param images The list of images to select from. Null is equivalent to empty list. + */ + private MediaImage selectImage(List images) { + if (images == null) return null; + + MediaImage selectedImage = null; + double bestScore = 0; + for (MediaImage image : images) { + double newScore = getImageScore(image); + if (newScore > bestScore) { + bestScore = newScore; + selectedImage = image; + } + } + return selectedImage; + } + + private void clearRequests() { + mRequestId = -1; + mCallback = null; + } + + private double getImageScore(MediaImage image) { + if (image == null) return 0; + if (image.getSizes().isEmpty()) return DEFAULT_IMAGE_SIZE_SCORE; + + double bestSizeScore = 0; + for (Rect size : image.getSizes()) { + bestSizeScore = Math.max(bestSizeScore, getImageSizeScore(size)); + } + double typeScore = getImageTypeScore(image.getSrc(), image.getType()); + return bestSizeScore * typeScore; + } + + private double getImageSizeScore(Rect size) { + return getImageDominantSizeScore(size.width(), size.height()) + * getImageAspectRatioScore(size.width(), size.height()); + } + + private double getImageDominantSizeScore(int width, int height) { + int dominantSize = Math.max(width, height); + // When the size is "any". + if (dominantSize == 0) return 0.8; + // Ignore images that are too small. + if (dominantSize < mMinimumSize) return 0; + + if (dominantSize <= mIdealSize) { + return 0.8 * (dominantSize - mMinimumSize) / (mIdealSize - mMinimumSize) + 0.2; + } + return 1.0 * mIdealSize / dominantSize; + } + + private double getImageAspectRatioScore(int width, int height) { + double longEdge = Math.max(width, height); + double shortEdge = Math.min(width, height); + return shortEdge / longEdge; + } + + private double getImageTypeScore(String url, String type) { + String extension = FileUtils.getExtension(url); + + if ("bmp".equals(extension) || "image/bmp".equals(type)) { + return TYPE_SCORE_BMP; + } else if ("gif".equals(extension) || "image/gif".equals(type)) { + return TYPE_SCORE_GIF; + } else if ("icon".equals(extension) || "image/x-icon".equals(type)) { + return TYPE_SCORE_XICON; + } else if ("png".equals(extension) || "image/png".equals(type)) { + return TYPE_SCORE_PNG; + } else if ("jpeg".equals(extension) || "jpg".equals(extension) + || "image/jpeg".equals(type)) { + return TYPE_SCORE_JPEG; + } + return TYPE_SCORE_DEFAULT; + } +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java new file mode 100644 index 00000000000..c45340a77e9 --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java @@ -0,0 +1,298 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNotNull; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.graphics.Bitmap; +import android.graphics.Rect; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowLog; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.content_public.browser.WebContents; +import org.chromium.services.media_session.MediaImage; + +import java.util.ArrayList; + +/** + * Robolectric tests for MediaImageManager. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class MediaImageManagerTest { + private static final int TINY_IMAGE_SIZE_PX = 50; + private static final int MIN_IMAGE_SIZE_PX = 100; + private static final int IDEAL_IMAGE_SIZE_PX = 200; + private static final int REQUEST_ID_1 = 1; + private static final int REQUEST_ID_2 = 2; + private static final String IMAGE_URL_1 = "http://example.com/foo.png"; + private static final String IMAGE_URL_2 = "http://example.com/bar.png"; + + @Mock + private WebContents mWebContents; + @Mock + private MediaImageCallback mCallback; + + private MediaImageManager mMediaImageManager; + + // Prepared data for feeding. + private ArrayList mImages; + private ArrayList mBitmaps; + private ArrayList mOriginalImageSizes; + + @Before + public void setUp() { + ShadowLog.stream = System.out; + MockitoAnnotations.initMocks(this); + doReturn(REQUEST_ID_1) + .when(mWebContents) + .downloadImage(anyString(), anyBoolean(), anyInt(), anyBoolean(), + any(MediaImageManager.class)); + mMediaImageManager = new MediaImageManager(MIN_IMAGE_SIZE_PX, IDEAL_IMAGE_SIZE_PX); + mMediaImageManager.setWebContents(mWebContents); + + mImages = new ArrayList(); + mImages.add(new MediaImage(IMAGE_URL_1, "", new ArrayList())); + + mBitmaps = new ArrayList(); + mBitmaps.add(Bitmap.createBitmap( + IDEAL_IMAGE_SIZE_PX, IDEAL_IMAGE_SIZE_PX, Bitmap.Config.ARGB_8888)); + + mOriginalImageSizes = new ArrayList(); + mOriginalImageSizes.add(new Rect(0, 0, IDEAL_IMAGE_SIZE_PX, IDEAL_IMAGE_SIZE_PX)); + } + + @Test + public void testDownloadImage() { + mMediaImageManager.downloadImage(mImages, mCallback); + verify(mWebContents) + .downloadImage(eq(IMAGE_URL_1), eq(false), + eq(MediaImageManager.MAX_BITMAP_SIZE_FOR_DOWNLOAD), eq(false), + eq(mMediaImageManager)); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + verify(mCallback).onImageDownloaded((Bitmap) isNotNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNull()); + } + + @Test + public void testDownloadSameImageTwice() { + // First download. + mMediaImageManager.downloadImage(mImages, mCallback); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + // Second download. + doReturn(REQUEST_ID_2) + .when(mWebContents) + .downloadImage(anyString(), anyBoolean(), anyInt(), anyBoolean(), + any(MediaImageManager.class)); + mMediaImageManager.downloadImage(mImages, mCallback); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_2, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + verify(mWebContents, times(1)) + .downloadImage(eq(IMAGE_URL_1), eq(false), + eq(MediaImageManager.MAX_BITMAP_SIZE_FOR_DOWNLOAD), eq(false), + eq(mMediaImageManager)); + verify(mCallback, times(1)).onImageDownloaded((Bitmap) isNotNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNull()); + } + + @Test + public void testDownloadSameImageTwiceButFailed() { + // First download. + mBitmaps.clear(); + mOriginalImageSizes.clear(); + + mMediaImageManager.downloadImage(mImages, mCallback); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 404, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + // Second download. + mMediaImageManager.downloadImage(mImages, mCallback); + // The second download request will never be initiated and the callback + // will be ignored. + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + verify(mWebContents, times(1)) + .downloadImage(eq(IMAGE_URL_1), eq(false), + eq(MediaImageManager.MAX_BITMAP_SIZE_FOR_DOWNLOAD), eq(false), + eq(mMediaImageManager)); + verify(mCallback, times(1)).onImageDownloaded((Bitmap) isNull()); + } + + @Test + public void testDownloadDifferentImagesTwice() { + // First download. + mMediaImageManager.downloadImage(mImages, mCallback); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + // Second download. + doReturn(REQUEST_ID_2) + .when(mWebContents) + .downloadImage(anyString(), anyBoolean(), anyInt(), anyBoolean(), + any(MediaImageManager.class)); + mImages.clear(); + mImages.add(new MediaImage(IMAGE_URL_2, "", new ArrayList())); + + mMediaImageManager.downloadImage(mImages, mCallback); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_2, 200, IMAGE_URL_2, mBitmaps, mOriginalImageSizes); + + verify(mWebContents, times(1)) + .downloadImage(eq(IMAGE_URL_1), eq(false), + eq(MediaImageManager.MAX_BITMAP_SIZE_FOR_DOWNLOAD), eq(false), + eq(mMediaImageManager)); + verify(mWebContents, times(1)) + .downloadImage(eq(IMAGE_URL_2), eq(false), + eq(MediaImageManager.MAX_BITMAP_SIZE_FOR_DOWNLOAD), eq(false), + eq(mMediaImageManager)); + verify(mCallback, times(2)).onImageDownloaded((Bitmap) isNotNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNull()); + } + + @Test + public void testDownloadAnotherImageBeforeResponse() { + // First download. + mMediaImageManager.downloadImage(mImages, mCallback); + + // Second download. + doReturn(REQUEST_ID_2) + .when(mWebContents) + .downloadImage(anyString(), anyBoolean(), anyInt(), anyBoolean(), + any(MediaImageManager.class)); + mImages.clear(); + mImages.add(new MediaImage(IMAGE_URL_2, "", new ArrayList())); + + mMediaImageManager.downloadImage(mImages, mCallback); + + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_2, 200, IMAGE_URL_2, mBitmaps, mOriginalImageSizes); + + // This reply should not be sent to the client. + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + verify(mWebContents, times(1)) + .downloadImage(eq(IMAGE_URL_1), eq(false), + eq(MediaImageManager.MAX_BITMAP_SIZE_FOR_DOWNLOAD), eq(false), + eq(mMediaImageManager)); + verify(mWebContents, times(1)) + .downloadImage(eq(IMAGE_URL_2), eq(false), + eq(MediaImageManager.MAX_BITMAP_SIZE_FOR_DOWNLOAD), eq(false), + eq(mMediaImageManager)); + + verify(mCallback, times(1)).onImageDownloaded((Bitmap) isNotNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNull()); + } + + @Test + public void testDuplicateResponce() { + mMediaImageManager.downloadImage(mImages, mCallback); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + verify(mCallback, times(1)).onImageDownloaded((Bitmap) isNotNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNull()); + } + + @Test + public void testWrongResponceId() { + mMediaImageManager.downloadImage(mImages, mCallback); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_2, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNotNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNull()); + } + + @Test + public void testTinyImagesRemovedBeforeDownloading() { + mImages.clear(); + ArrayList sizes = new ArrayList(); + sizes.add(new Rect(0, 0, TINY_IMAGE_SIZE_PX, TINY_IMAGE_SIZE_PX)); + mImages.add(new MediaImage(IMAGE_URL_1, "", sizes)); + mMediaImageManager.downloadImage(mImages, mCallback); + + verify(mWebContents, times(0)) + .downloadImage(anyString(), anyBoolean(), anyInt(), anyBoolean(), + any(MediaImageManager.class)); + verify(mCallback).onImageDownloaded((Bitmap) isNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNotNull()); + } + + @Test + public void testTinyImagesRemovedAfterDownloading() { + mMediaImageManager.downloadImage(mImages, mCallback); + + // Reset the data for feeding. + mBitmaps.clear(); + mBitmaps.add(Bitmap.createBitmap( + TINY_IMAGE_SIZE_PX, TINY_IMAGE_SIZE_PX, Bitmap.Config.ARGB_8888)); + mOriginalImageSizes.clear(); + mOriginalImageSizes.add(new Rect(0, 0, TINY_IMAGE_SIZE_PX, TINY_IMAGE_SIZE_PX)); + + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 200, IMAGE_URL_1, mBitmaps, mOriginalImageSizes); + + verify(mCallback).onImageDownloaded((Bitmap) isNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNotNull()); + } + + @Test + public void testDownloadImageFails() { + mMediaImageManager.downloadImage(mImages, mCallback); + mMediaImageManager.onFinishDownloadImage( + REQUEST_ID_1, 404, IMAGE_URL_1, new ArrayList(), new ArrayList()); + + verify(mCallback).onImageDownloaded((Bitmap) isNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNotNull()); + } + + @Test + public void testEmptyImageList() { + mImages.clear(); + mMediaImageManager.downloadImage(mImages, mCallback); + + verify(mWebContents, times(0)) + .downloadImage(anyString(), anyBoolean(), anyInt(), anyBoolean(), + any(MediaImageManager.class)); + verify(mCallback).onImageDownloaded((Bitmap) isNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNotNull()); + } + + @Test + public void testNullImageList() { + mMediaImageManager.downloadImage(null, mCallback); + + verify(mWebContents, times(0)) + .downloadImage(anyString(), anyBoolean(), anyInt(), anyBoolean(), + any(MediaImageManager.class)); + verify(mCallback).onImageDownloaded((Bitmap) isNull()); + verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNotNull()); + } +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationButtonComputationTest.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationButtonComputationTest.java new file mode 100644 index 00000000000..94432be5b16 --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationButtonComputationTest.java @@ -0,0 +1,96 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.Feature; +import org.chromium.media_session.mojom.MediaSessionAction; + +import java.util.ArrayList; + +/** + * Robolectric tests for compact view button computation in {@link MediaNotificationController}. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class MediaNotificationButtonComputationTest { + @Test + @Feature({"MediaNotification"}) + public void testLessThanThreeActionsWillBeAllShownInCompactView() { + ArrayList actions = new ArrayList<>(); + actions.add(MediaSessionAction.NEXT_TRACK); + actions.add(MediaSessionAction.SEEK_FORWARD); + actions.add(MediaSessionAction.PLAY); + + int[] compactViewActions = + MediaNotificationController.computeCompactViewActionIndices(actions); + + assertEquals(3, compactViewActions.length); + assertEquals(0, compactViewActions[0]); + assertEquals(1, compactViewActions[1]); + assertEquals(2, compactViewActions[2]); + } + + @Test + @Feature({"MediaNotification"}) + public void testCompactViewPrefersActionPairs_SwitchTrack() { + ArrayList actions = new ArrayList<>(); + actions.add(MediaSessionAction.PREVIOUS_TRACK); + actions.add(MediaSessionAction.NEXT_TRACK); + actions.add(MediaSessionAction.SEEK_FORWARD); + actions.add(MediaSessionAction.PLAY); + + int[] compactViewActions = + MediaNotificationController.computeCompactViewActionIndices(actions); + + assertEquals(3, compactViewActions.length); + assertEquals(0, compactViewActions[0]); + assertEquals(3, compactViewActions[1]); + assertEquals(1, compactViewActions[2]); + } + + @Test + @Feature({"MediaNotification"}) + public void testCompactViewPrefersActionPairs_Seek() { + ArrayList actions = new ArrayList<>(); + actions.add(MediaSessionAction.NEXT_TRACK); + actions.add(MediaSessionAction.SEEK_BACKWARD); + actions.add(MediaSessionAction.SEEK_FORWARD); + actions.add(MediaSessionAction.PLAY); + + int[] compactViewActions = + MediaNotificationController.computeCompactViewActionIndices(actions); + + assertEquals(3, compactViewActions.length); + assertEquals(1, compactViewActions[0]); + assertEquals(3, compactViewActions[1]); + assertEquals(2, compactViewActions[2]); + } + + @Test + @Feature({"MediaNotification"}) + public void testCompactViewPreferSwitchTrackWhenBothPairsExist() { + ArrayList actions = new ArrayList<>(); + actions.add(MediaSessionAction.PREVIOUS_TRACK); + actions.add(MediaSessionAction.NEXT_TRACK); + actions.add(MediaSessionAction.SEEK_BACKWARD); + actions.add(MediaSessionAction.SEEK_FORWARD); + actions.add(MediaSessionAction.PLAY); + + int[] compactViewActions = + MediaNotificationController.computeCompactViewActionIndices(actions); + + assertEquals(3, compactViewActions.length); + assertEquals(0, compactViewActions[0]); + assertEquals(4, compactViewActions[1]); + assertEquals(1, compactViewActions[2]); + } +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java new file mode 100644 index 00000000000..819ddde1e61 --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java @@ -0,0 +1,905 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.media.AudioManager; +import android.os.Build; +import android.os.Handler; +import android.support.v4.media.MediaMetadataCompat; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; +import android.text.TextUtils; +import android.util.SparseArray; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import org.chromium.base.CollectionUtil; +import org.chromium.base.ContextUtils; +import org.chromium.base.Log; +import org.chromium.components.browser_ui.notifications.ChromeNotification; +import org.chromium.components.browser_ui.notifications.ChromeNotificationBuilder; +import org.chromium.components.browser_ui.notifications.ForegroundServiceUtils; +import org.chromium.components.browser_ui.notifications.NotificationManagerProxy; +import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl; +import org.chromium.media_session.mojom.MediaSessionAction; +import org.chromium.services.media_session.MediaMetadata; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A class that manages the notification, foreground service, and {@link MediaSessionCompat} for a + * specific type of media. + */ +public class MediaNotificationController { + private static final String TAG = "MediaNotification"; + + // The maximum number of actions in CompactView media notification. + private static final int COMPACT_VIEW_ACTIONS_COUNT = 3; + + // The maximum number of actions in BigView media notification. + private static final int BIG_VIEW_ACTIONS_COUNT = 5; + + // These string values reflect legacy class hierarchy. + public static final String ACTION_PLAY = "MediaNotificationManager.ListenerService.PLAY"; + public static final String ACTION_PAUSE = "MediaNotificationManager.ListenerService.PAUSE"; + public static final String ACTION_STOP = "MediaNotificationManager.ListenerService.STOP"; + public static final String ACTION_SWIPE = "MediaNotificationManager.ListenerService.SWIPE"; + public static final String ACTION_CANCEL = "MediaNotificationManager.ListenerService.CANCEL"; + public static final String ACTION_PREVIOUS_TRACK = + "MediaNotificationManager.ListenerService.PREVIOUS_TRACK"; + public static final String ACTION_NEXT_TRACK = + "MediaNotificationManager.ListenerService.NEXT_TRACK"; + public static final String ACTION_SEEK_FORWARD = + "MediaNotificationManager.ListenerService.SEEK_FORWARD"; + public static final String ACTION_SEEK_BACKWARD = + "MediaNotificationmanager.ListenerService.SEEK_BACKWARD"; + + // Overrides N detection. The production code will use |null|, which uses the Android version + // code. Otherwise, |isRunningAtLeastN()| will return whatever value is set. + @VisibleForTesting + public static Boolean sOverrideIsRunningNForTesting; + + // ListenerService running for the notification. Only non-null when showing. + @VisibleForTesting + public Service mService; + + @VisibleForTesting + public Delegate mDelegate; + + private SparseArray mActionToButtonInfo; + + @VisibleForTesting + public ChromeNotificationBuilder mNotificationBuilder; + + @VisibleForTesting + public Bitmap mDefaultNotificationLargeIcon; + + // |mMediaNotificationInfo| should be not null if and only if the notification is showing. + @VisibleForTesting + public MediaNotificationInfo mMediaNotificationInfo; + + @VisibleForTesting + public MediaSessionCompat mMediaSession; + + @VisibleForTesting + public Throttler mThrottler; + + @VisibleForTesting + public static class Throttler { + @VisibleForTesting + public static final int THROTTLE_MILLIS = 500; + + @VisibleForTesting + public MediaNotificationController mController; + + private final Handler mHandler; + + @VisibleForTesting + public Throttler(@NonNull MediaNotificationController manager) { + mController = manager; + mHandler = new Handler(); + } + + // When |mTask| is non-null, it will always be queued in mHandler. When |mTask| is non-null, + // all notification updates will be throttled and their info will be stored as + // mLastPendingInfo. When |mTask| fires, it will call {@link showNotification()} with + // the latest queued notification info. + @VisibleForTesting + public Runnable mTask; + + // The last pending info. If non-null, it will be the latest notification info. + // Otherwise, the latest notification info will be |mController.mMediaNotificationInfo|. + @VisibleForTesting + public MediaNotificationInfo mLastPendingInfo; + + /** + * Queue |mediaNotificationInfo| for update. In unthrottled state (i.e. |mTask| != null), + * the notification will be updated immediately and enter the throttled state. In + * unthrottled state, the method will only update the pending notification info, which will + * be used for updating the notification when |mTask| is fired. + * + * @param mediaNotificationInfo The notification info to be queued. + */ + public void queueNotification(MediaNotificationInfo mediaNotificationInfo) { + assert mediaNotificationInfo != null; + + MediaNotificationInfo latestMediaNotificationInfo = mLastPendingInfo != null + ? mLastPendingInfo + : mController.mMediaNotificationInfo; + + if (shouldIgnoreMediaNotificationInfo( + latestMediaNotificationInfo, mediaNotificationInfo)) { + return; + } + + if (mTask == null) { + showNotificationImmediately(mediaNotificationInfo); + } else { + mLastPendingInfo = mediaNotificationInfo; + } + } + + /** + * Clears the pending notification and enter unthrottled state. + */ + public void clearPendingNotifications() { + mHandler.removeCallbacks(mTask); + mLastPendingInfo = null; + mTask = null; + } + + @VisibleForTesting + public void showNotificationImmediately(MediaNotificationInfo mediaNotificationInfo) { + // If no notification hasn't been updated in the last THROTTLE_MILLIS, update + // immediately and queue a task for blocking further updates. + mController.showNotification(mediaNotificationInfo); + mTask = new Runnable() { + @Override + public void run() { + if (mLastPendingInfo != null) { + // If any notification info is pended during the throttling time window, + // update the notification. + showNotificationImmediately(mLastPendingInfo); + mLastPendingInfo = null; + } else { + // Otherwise, clear the task so further update is unthrottled. + mTask = null; + } + } + }; + if (!mHandler.postDelayed(mTask, THROTTLE_MILLIS)) { + Log.w(TAG, "Failed to post the throttler task."); + mTask = null; + } + } + } + + private final MediaSessionCompat + .Callback mMediaSessionCallback = new MediaSessionCompat.Callback() { + @Override + public void onPlay() { + MediaNotificationController.this.onPlay( + MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION); + } + + @Override + public void onPause() { + MediaNotificationController.this.onPause( + MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION); + } + + @Override + public void onSkipToPrevious() { + MediaNotificationController.this.onMediaSessionAction( + MediaSessionAction.PREVIOUS_TRACK); + } + + @Override + public void onSkipToNext() { + MediaNotificationController.this.onMediaSessionAction(MediaSessionAction.NEXT_TRACK); + } + + @Override + public void onFastForward() { + MediaNotificationController.this.onMediaSessionAction(MediaSessionAction.SEEK_FORWARD); + } + + @Override + public void onRewind() { + MediaNotificationController.this.onMediaSessionAction(MediaSessionAction.SEEK_BACKWARD); + } + + @Override + public void onSeekTo(long pos) { + MediaNotificationController.this.onMediaSessionSeekTo(pos); + } + }; + + /** + * Finishes starting the service on O+. + * + * If startForegroundService() was called, the app MUST call startForeground on the created + * service no matter what or it will crash. + * + * @param service the {@link Service} on which {@link Context#startForegroundService()} has been + * called. + * @param notification a minimal version of the notification associated with the service. + * @return true if {@link Service#startForeground()} was called. + */ + public static boolean finishStartingForegroundServiceOnO( + Service service, ChromeNotification notification) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false; + ForegroundServiceUtils.getInstance().startForeground(service, notification.getMetadata().id, + notification.getNotification(), 0 /* foregroundServiceType */); + return true; + } + + private PendingIntent createPendingIntent(String action) { + Intent intent = mDelegate.createServiceIntent().setAction(action); + return PendingIntent.getService(getContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + } + + private static boolean isRunningAtLeastN() { + return (sOverrideIsRunningNForTesting != null) + ? sOverrideIsRunningNForTesting + : Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; + } + + /** + * The class containing all the information for adding a button in the notification for an + * action. + */ + private static final class MediaButtonInfo { + /** The resource ID of this media button icon. */ + public int iconResId; + + /** The resource ID of this media button description. */ + public int descriptionResId; + + /** The intent string to be fired when this media button is clicked. */ + public String intentString; + + public MediaButtonInfo(int buttonResId, int descriptionResId, String intentString) { + this.iconResId = buttonResId; + this.descriptionResId = descriptionResId; + this.intentString = intentString; + } + } + + /** An interface for separating embedder-specific logic. */ + public interface Delegate { + /** Returns an intent that will start a Service which listens to notification actions. */ + Intent createServiceIntent(); + + /** Returns the name of the embedding app. */ + String getAppName(); + + /** Returns the notification group name used to prevent automatic grouping. */ + String getNotificationGroupName(); + + /** Returns a builder suitable as a starting point for creating the notification. */ + ChromeNotificationBuilder createChromeNotificationBuilder(); + + /** Called when the Android MediaSession has been updated. */ + void onMediaSessionUpdated(MediaSessionCompat session); + + /** Called when a notification has been shown and should be logged in UMA. */ + void logNotificationShown(ChromeNotification notification); + } + + public MediaNotificationController(Delegate delegate) { + mDelegate = delegate; + + mActionToButtonInfo = new SparseArray<>(); + + mActionToButtonInfo.put(MediaSessionAction.PLAY, + new MediaButtonInfo(R.drawable.ic_play_arrow_white_36dp, + R.string.accessibility_play, ACTION_PLAY)); + mActionToButtonInfo.put(MediaSessionAction.PAUSE, + new MediaButtonInfo(R.drawable.ic_pause_white_36dp, R.string.accessibility_pause, + ACTION_PAUSE)); + mActionToButtonInfo.put(MediaSessionAction.STOP, + new MediaButtonInfo( + R.drawable.ic_stop_white_36dp, R.string.accessibility_stop, ACTION_STOP)); + mActionToButtonInfo.put(MediaSessionAction.PREVIOUS_TRACK, + new MediaButtonInfo(R.drawable.ic_skip_previous_white_36dp, + R.string.accessibility_previous_track, ACTION_PREVIOUS_TRACK)); + mActionToButtonInfo.put(MediaSessionAction.NEXT_TRACK, + new MediaButtonInfo(R.drawable.ic_skip_next_white_36dp, + R.string.accessibility_next_track, ACTION_NEXT_TRACK)); + mActionToButtonInfo.put(MediaSessionAction.SEEK_FORWARD, + new MediaButtonInfo(R.drawable.ic_fast_forward_white_36dp, + R.string.accessibility_seek_forward, ACTION_SEEK_FORWARD)); + mActionToButtonInfo.put(MediaSessionAction.SEEK_BACKWARD, + new MediaButtonInfo(R.drawable.ic_fast_rewind_white_36dp, + R.string.accessibility_seek_backward, ACTION_SEEK_BACKWARD)); + + mThrottler = new Throttler(this); + } + + /** + * Registers the started {@link Service} with the manager and creates the notification. + * + * @param service the service that was started + */ + public void onServiceStarted(Service service) { + if (mService == service) return; + + mService = service; + updateNotification(true /*serviceStarting*/, true /*shouldLogNotification*/); + } + + /** Handles the service destruction. */ + public void onServiceDestroyed() { + mService = null; + } + + public boolean processIntent(Service service, Intent intent) { + if (intent == null || mMediaNotificationInfo == null) return false; + + if (intent.getAction() == null) { + // The intent comes from {@link AppHooks#startForegroundService}. + onServiceStarted(service); + } else { + // The intent comes from the notification. In this case, {@link onServiceStarted()} + // does need to be called. + processAction(intent.getAction()); + } + return true; + } + + public void processAction(String action) { + if (ACTION_STOP.equals(action) || ACTION_SWIPE.equals(action) + || ACTION_CANCEL.equals(action)) { + onStop(MediaNotificationListener.ACTION_SOURCE_MEDIA_NOTIFICATION); + stopListenerService(); + } else if (ACTION_PLAY.equals(action)) { + onPlay(MediaNotificationListener.ACTION_SOURCE_MEDIA_NOTIFICATION); + } else if (ACTION_PAUSE.equals(action)) { + onPause(MediaNotificationListener.ACTION_SOURCE_MEDIA_NOTIFICATION); + } else if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(action)) { + onPause(MediaNotificationListener.ACTION_SOURCE_HEADSET_UNPLUG); + } else if (ACTION_PREVIOUS_TRACK.equals(action)) { + onMediaSessionAction(MediaSessionAction.PREVIOUS_TRACK); + } else if (ACTION_NEXT_TRACK.equals(action)) { + onMediaSessionAction(MediaSessionAction.NEXT_TRACK); + } else if (ACTION_SEEK_FORWARD.equals(action)) { + onMediaSessionAction(MediaSessionAction.SEEK_FORWARD); + } else if (ACTION_SEEK_BACKWARD.equals(action)) { + onMediaSessionAction(MediaSessionAction.SEEK_BACKWARD); + } + } + + @VisibleForTesting + public void onPlay(int actionSource) { + // MediaSessionCompat calls this sometimes when `mMediaNotificationInfo` + // is no longer available. It's unclear if it is a Support Library issue + // or something that isn't properly cleaned up but given that the + // crashes are rare and the fix is simple, null check was enough. + if (mMediaNotificationInfo == null || !mMediaNotificationInfo.isPaused) return; + mMediaNotificationInfo.listener.onPlay(actionSource); + } + + @VisibleForTesting + public void onPause(int actionSource) { + // MediaSessionCompat calls this sometimes when `mMediaNotificationInfo` + // is no longer available. It's unclear if it is a Support Library issue + // or something that isn't properly cleaned up but given that the + // crashes are rare and the fix is simple, null check was enough. + if (mMediaNotificationInfo == null || mMediaNotificationInfo.isPaused) return; + mMediaNotificationInfo.listener.onPause(actionSource); + } + + @VisibleForTesting + public void onStop(int actionSource) { + // MediaSessionCompat calls this sometimes when `mMediaNotificationInfo` + // is no longer available. It's unclear if it is a Support Library issue + // or something that isn't properly cleaned up but given that the + // crashes are rare and the fix is simple, null check was enough. + if (mMediaNotificationInfo == null) return; + mMediaNotificationInfo.listener.onStop(actionSource); + } + + @VisibleForTesting + public void onMediaSessionAction(int action) { + // MediaSessionCompat calls this sometimes when `mMediaNotificationInfo` + // is no longer available. It's unclear if it is a Support Library issue + // or something that isn't properly cleaned up but given that the + // crashes are rare and the fix is simple, null check was enough. + if (mMediaNotificationInfo == null) return; + mMediaNotificationInfo.listener.onMediaSessionAction(action); + } + + @VisibleForTesting + void onMediaSessionSeekTo(long pos) { + // MediaSessionCompat calls this sometimes when `mMediaNotificationInfo` + // is no longer available. It's unclear if it is a Support Library issue + // or something that isn't properly cleaned up but given that the + // crashes are rare and the fix is simple, null check was enough. + if (mMediaNotificationInfo == null) return; + mMediaNotificationInfo.listener.onMediaSessionSeekTo(pos); + } + + @VisibleForTesting + public void showNotification(MediaNotificationInfo mediaNotificationInfo) { + if (shouldIgnoreMediaNotificationInfo(mMediaNotificationInfo, mediaNotificationInfo)) { + return; + } + + mMediaNotificationInfo = mediaNotificationInfo; + + // If there's no pending service start request, don't try to start service. If there is a + // pending service start request but the service haven't started yet, only update the + // |mMediaNotificationInfo|. The service will update the notification later once it's + // started. + if (mService == null && mediaNotificationInfo.isPaused) return; + + if (mService == null) { + updateMediaSession(); + updateNotificationBuilder(); + ForegroundServiceUtils.getInstance().startForegroundService( + mDelegate.createServiceIntent()); + } else { + updateNotification(false, false); + } + } + + private static boolean shouldIgnoreMediaNotificationInfo( + MediaNotificationInfo oldInfo, MediaNotificationInfo newInfo) { + // If we don't have actions then we shouldn't display the notification. + if (newInfo.mediaSessionActions.isEmpty()) return true; + + return newInfo.equals(oldInfo) + || ((newInfo.isPaused && oldInfo != null + && newInfo.instanceId != oldInfo.instanceId)); + } + + public void clearNotification() { + mThrottler.clearPendingNotifications(); + if (mMediaNotificationInfo == null) return; + + NotificationManagerCompat.from(getContext()).cancel(mMediaNotificationInfo.id); + + if (mMediaSession != null) { + mMediaSession.setCallback(null); + mMediaSession.setActive(false); + mMediaSession.release(); + mMediaSession = null; + } + stopListenerService(); + mMediaNotificationInfo = null; + mNotificationBuilder = null; + } + + public void queueNotification(MediaNotificationInfo mediaNotificationInfo) { + mThrottler.queueNotification(mediaNotificationInfo); + } + + public void hideNotification(int instanceId) { + if (mMediaNotificationInfo == null || instanceId != mMediaNotificationInfo.instanceId) { + return; + } + clearNotification(); + } + + @VisibleForTesting + public void stopListenerService() { + if (mService == null) return; + + ForegroundServiceUtils.getInstance().stopForeground( + mService, Service.STOP_FOREGROUND_REMOVE); + mService.stopSelf(); + } + + @NonNull + @VisibleForTesting + public MediaMetadataCompat createMetadata() { + // Can't return null as {@link MediaSessionCompat#setMetadata()} will crash in some versions + // of the Android compat library. + MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Builder(); + if (mMediaNotificationInfo.isPrivate) return metadataBuilder.build(); + + metadataBuilder.putString( + MediaMetadataCompat.METADATA_KEY_TITLE, mMediaNotificationInfo.metadata.getTitle()); + metadataBuilder.putString( + MediaMetadataCompat.METADATA_KEY_ARTIST, mMediaNotificationInfo.origin); + + if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getArtist())) { + metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, + mMediaNotificationInfo.metadata.getArtist()); + } + if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getAlbum())) { + metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, + mMediaNotificationInfo.metadata.getAlbum()); + } + if (mMediaNotificationInfo.mediaSessionImage != null) { + metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, + mMediaNotificationInfo.mediaSessionImage); + } + if (mMediaNotificationInfo.mediaPosition != null) { + metadataBuilder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, + mMediaNotificationInfo.mediaPosition.getDuration()); + } + + return metadataBuilder.build(); + } + + @VisibleForTesting + public void updateNotification(boolean serviceStarting, boolean shouldLogNotification) { + if (mService == null) return; + + if (mMediaNotificationInfo == null) { + if (serviceStarting) { + finishStartingForegroundServiceOnO(mService, + mDelegate.createChromeNotificationBuilder().buildChromeNotification()); + ForegroundServiceUtils.getInstance().stopForeground( + mService, Service.STOP_FOREGROUND_REMOVE); + } + return; + } + updateMediaSession(); + updateNotificationBuilder(); + + ChromeNotification notification = mNotificationBuilder.buildChromeNotification(); + + // On O, finish starting the foreground service nevertheless, or Android will + // crash Chrome. + boolean finishedForegroundingService = + serviceStarting && finishStartingForegroundServiceOnO(mService, notification); + + // We keep the service as a foreground service while the media is playing. When it is not, + // the service isn't stopped but is no longer in foreground, thus at a lower priority. + // While the service is in foreground, the associated notification can't be swipped away. + // Moving it back to background allows the user to remove the notification. + if (mMediaNotificationInfo.supportsSwipeAway() && mMediaNotificationInfo.isPaused) { + ForegroundServiceUtils.getInstance().stopForeground( + mService, Service.STOP_FOREGROUND_DETACH); + NotificationManagerProxy manager = new NotificationManagerProxyImpl(getContext()); + manager.notify(notification); + } else if (!finishedForegroundingService) { + ForegroundServiceUtils.getInstance().startForeground(mService, + mMediaNotificationInfo.id, notification.getNotification(), + 0 /*foregroundServiceType*/); + } + if (shouldLogNotification) { + mDelegate.logNotificationShown(notification); + } + } + + @VisibleForTesting + public void updateNotificationBuilder() { + assert (mMediaNotificationInfo != null); + + mNotificationBuilder = mDelegate.createChromeNotificationBuilder(); + setMediaStyleLayoutForNotificationBuilder(mNotificationBuilder); + + // TODO(zqzhang): It's weird that setShowWhen() doesn't work on K. Calling setWhen() to + // force removing the time. + mNotificationBuilder.setShowWhen(false).setWhen(0); + mNotificationBuilder.setSmallIcon(mMediaNotificationInfo.notificationSmallIcon); + mNotificationBuilder.setAutoCancel(false); + mNotificationBuilder.setLocalOnly(true); + mNotificationBuilder.setGroup(mDelegate.getNotificationGroupName()); + mNotificationBuilder.setGroupSummary(true); + + if (mMediaNotificationInfo.supportsSwipeAway()) { + mNotificationBuilder.setOngoing(!mMediaNotificationInfo.isPaused); + mNotificationBuilder.setDeleteIntent(createPendingIntent(ACTION_SWIPE)); + } + + // The intent will currently only be null when using a custom tab. + // TODO(avayvod) work out what we should do in this case. See https://crbug.com/585395. + if (mMediaNotificationInfo.contentIntent != null) { + mNotificationBuilder.setContentIntent(PendingIntent.getActivity(getContext(), + mMediaNotificationInfo.instanceId, mMediaNotificationInfo.contentIntent, + PendingIntent.FLAG_UPDATE_CURRENT)); + // Set FLAG_UPDATE_CURRENT so that the intent extras is updated, otherwise the + // intent extras will stay the same for the same tab. + } + + mNotificationBuilder.setVisibility(mMediaNotificationInfo.isPrivate + ? NotificationCompat.VISIBILITY_PRIVATE + : NotificationCompat.VISIBILITY_PUBLIC); + } + + @VisibleForTesting + public void updateMediaSession() { + if (!mMediaNotificationInfo.supportsPlayPause()) return; + + if (mMediaSession == null) mMediaSession = createMediaSession(); + + activateAndroidMediaSession(mMediaNotificationInfo.instanceId); + + mDelegate.onMediaSessionUpdated(mMediaSession); + + mMediaSession.setMetadata(createMetadata()); + + mMediaSession.setPlaybackState(createPlaybackState()); + } + + @VisibleForTesting + public PlaybackStateCompat createPlaybackState() { + PlaybackStateCompat.Builder playbackStateBuilder = + new PlaybackStateCompat.Builder().setActions(computeMediaSessionActions()); + + int state = mMediaNotificationInfo.isPaused ? PlaybackStateCompat.STATE_PAUSED + : PlaybackStateCompat.STATE_PLAYING; + + if (mMediaNotificationInfo.mediaPosition != null) { + playbackStateBuilder.setState(state, mMediaNotificationInfo.mediaPosition.getPosition(), + mMediaNotificationInfo.mediaPosition.getPlaybackRate(), + mMediaNotificationInfo.mediaPosition.getLastUpdatedTime()); + } else { + playbackStateBuilder.setState( + state, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f); + } + + return playbackStateBuilder.build(); + } + + private long computeMediaSessionActions() { + assert mMediaNotificationInfo != null; + + long actions = PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE; + if (mMediaNotificationInfo.mediaSessionActions.contains( + MediaSessionAction.PREVIOUS_TRACK)) { + actions |= PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; + } + if (mMediaNotificationInfo.mediaSessionActions.contains(MediaSessionAction.NEXT_TRACK)) { + actions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT; + } + if (mMediaNotificationInfo.mediaSessionActions.contains(MediaSessionAction.SEEK_FORWARD)) { + actions |= PlaybackStateCompat.ACTION_FAST_FORWARD; + } + if (mMediaNotificationInfo.mediaSessionActions.contains(MediaSessionAction.SEEK_BACKWARD)) { + actions |= PlaybackStateCompat.ACTION_REWIND; + } + if (mMediaNotificationInfo.mediaSessionActions.contains(MediaSessionAction.SEEK_TO)) { + actions |= PlaybackStateCompat.ACTION_SEEK_TO; + } + return actions; + } + + private MediaSessionCompat createMediaSession() { + MediaSessionCompat mediaSession = + new MediaSessionCompat(getContext(), mDelegate.getAppName()); + mediaSession.setCallback(mMediaSessionCallback); + mediaSession.setActive(true); + return mediaSession; + } + + /** + * Activates the media session. + * @param instanceId the instance of the notification to activate. If it doesn't match the + * active notification, this method will no-op. + */ + public void activateAndroidMediaSession(int instanceId) { + if (mMediaNotificationInfo == null) return; + if (mMediaNotificationInfo.instanceId != instanceId) return; + if (!mMediaNotificationInfo.supportsPlayPause() || mMediaNotificationInfo.isPaused) return; + if (mMediaSession == null) return; + mMediaSession.setActive(true); + } + + private void setMediaStyleLayoutForNotificationBuilder(ChromeNotificationBuilder builder) { + setMediaStyleNotificationText(builder); + if (!mMediaNotificationInfo.supportsPlayPause()) { + // Non-playback (Cast) notification will not use MediaStyle, so not + // setting the large icon is fine. + builder.setLargeIcon(null); + // Notifications in incognito shouldn't show an icon to avoid leaking information. + } else if (mMediaNotificationInfo.notificationLargeIcon != null + && !mMediaNotificationInfo.isPrivate) { + builder.setLargeIcon(mMediaNotificationInfo.notificationLargeIcon); + } else if (!isRunningAtLeastN()) { + if (mDefaultNotificationLargeIcon == null + && mMediaNotificationInfo.defaultNotificationLargeIcon != 0) { + mDefaultNotificationLargeIcon = + MediaNotificationImageUtils.downscaleIconToIdealSize( + BitmapFactory.decodeResource(getContext().getResources(), + mMediaNotificationInfo.defaultNotificationLargeIcon)); + } + builder.setLargeIcon(mDefaultNotificationLargeIcon); + } + + addNotificationButtons(builder); + } + + private void addNotificationButtons(ChromeNotificationBuilder builder) { + Set actions = new HashSet<>(); + + // TODO(zqzhang): handle other actions when play/pause is not supported? See + // https://crbug.com/667500 + if (mMediaNotificationInfo.supportsPlayPause()) { + actions.addAll(mMediaNotificationInfo.mediaSessionActions); + if (mMediaNotificationInfo.isPaused) { + actions.remove(MediaSessionAction.PAUSE); + actions.add(MediaSessionAction.PLAY); + } else { + actions.remove(MediaSessionAction.PLAY); + actions.add(MediaSessionAction.PAUSE); + } + } + + if (mMediaNotificationInfo.supportsStop()) { + actions.add(MediaSessionAction.STOP); + } else { + actions.remove(MediaSessionAction.STOP); + } + + List bigViewActions = computeBigViewActions(actions); + + for (int action : bigViewActions) { + MediaButtonInfo buttonInfo = mActionToButtonInfo.get(action); + builder.addAction(buttonInfo.iconResId, + getContext().getResources().getString(buttonInfo.descriptionResId), + createPendingIntent(buttonInfo.intentString)); + } + + // Only apply MediaStyle when NotificationInfo supports play/pause. + if (mMediaNotificationInfo.supportsPlayPause()) { + builder.setMediaStyle(mMediaSession, computeCompactViewActionIndices(bigViewActions), + createPendingIntent(ACTION_CANCEL), true); + } + } + + private void setMediaStyleNotificationText(ChromeNotificationBuilder builder) { + if (mMediaNotificationInfo.isPrivate) { + // Notifications in incognito shouldn't show what is playing to avoid leaking + // information. + if (isRunningAtLeastN()) { + builder.setContentTitle(getContext().getResources().getString( + R.string.media_notification_incognito)); + builder.setSubText( + getContext().getResources().getString(R.string.notification_incognito_tab)); + } else { + // App name is automatically added to the title from Android N, + // but needs to be added explicitly for prior versions. + builder.setContentTitle(mDelegate.getAppName()) + .setContentText(getContext().getResources().getString( + R.string.media_notification_incognito)); + } + return; + } + + builder.setContentTitle(mMediaNotificationInfo.metadata.getTitle()); + String artistAndAlbumText = getArtistAndAlbumText(mMediaNotificationInfo.metadata); + if (isRunningAtLeastN() || !artistAndAlbumText.isEmpty()) { + builder.setContentText(artistAndAlbumText); + builder.setSubText(mMediaNotificationInfo.origin); + } else { + // Leaving ContentText empty looks bad, so move origin up to the ContentText. + builder.setContentText(mMediaNotificationInfo.origin); + } + } + + private static String getArtistAndAlbumText(MediaMetadata metadata) { + String artist = (metadata.getArtist() == null) ? "" : metadata.getArtist(); + String album = (metadata.getAlbum() == null) ? "" : metadata.getAlbum(); + if (artist.isEmpty() || album.isEmpty()) { + return artist + album; + } + return artist + " - " + album; + } + + /** + * Compute the actions to be shown in BigView media notification. + * + * The method assumes STOP cannot coexist with switch track actions and seeking actions. It also + * assumes PLAY and PAUSE cannot coexist. + */ + private static List computeBigViewActions(Set actions) { + // STOP cannot coexist with switch track actions and seeking actions. + assert !actions.contains(MediaSessionAction.STOP) + || !(actions.contains(MediaSessionAction.PREVIOUS_TRACK) + && actions.contains(MediaSessionAction.NEXT_TRACK) + && actions.contains(MediaSessionAction.SEEK_BACKWARD) + && actions.contains(MediaSessionAction.SEEK_FORWARD)); + // PLAY and PAUSE cannot coexist. + assert !actions.contains(MediaSessionAction.PLAY) + || !actions.contains(MediaSessionAction.PAUSE); + + int[] actionByOrder = { + MediaSessionAction.PREVIOUS_TRACK, + MediaSessionAction.SEEK_BACKWARD, + MediaSessionAction.PLAY, + MediaSessionAction.PAUSE, + MediaSessionAction.SEEK_FORWARD, + MediaSessionAction.NEXT_TRACK, + MediaSessionAction.STOP, + }; + + // Sort the actions based on the expected ordering in the UI. + List sortedActions = new ArrayList<>(); + for (int action : actionByOrder) { + if (actions.contains(action)) sortedActions.add(action); + } + + // There can't be move actions than BIG_VIEW_ACTIONS_COUNT. We do this check after we have + // sorted the actions since there may be more actions that we do not support. + assert sortedActions.size() <= BIG_VIEW_ACTIONS_COUNT; + + return sortedActions; + } + + /** + * Compute the actions to be shown in CompactView media notification. + * + * The method assumes STOP cannot coexist with switch track actions and seeking actions. It also + * assumes PLAY and PAUSE cannot coexist. + * + * Actions in pairs are preferred if there are more actions than |COMPACT_VIEW_ACTIONS_COUNT|. + */ + @VisibleForTesting + static int[] computeCompactViewActionIndices(List actions) { + // STOP cannot coexist with switch track actions and seeking actions. + assert !actions.contains(MediaSessionAction.STOP) + || !(actions.contains(MediaSessionAction.PREVIOUS_TRACK) + && actions.contains(MediaSessionAction.NEXT_TRACK) + && actions.contains(MediaSessionAction.SEEK_BACKWARD) + && actions.contains(MediaSessionAction.SEEK_FORWARD)); + // PLAY and PAUSE cannot coexist. + assert !actions.contains(MediaSessionAction.PLAY) + || !actions.contains(MediaSessionAction.PAUSE); + + if (actions.size() <= COMPACT_VIEW_ACTIONS_COUNT) { + // If the number of actions is less than |COMPACT_VIEW_ACTIONS_COUNT|, just return an + // array of 0, 1, ..., |actions.size()|-1. + int[] actionsArray = new int[actions.size()]; + for (int i = 0; i < actions.size(); ++i) actionsArray[i] = i; + return actionsArray; + } + + if (actions.contains(MediaSessionAction.STOP)) { + List compactActions = new ArrayList<>(); + if (actions.contains(MediaSessionAction.PLAY)) { + compactActions.add(actions.indexOf(MediaSessionAction.PLAY)); + } + compactActions.add(actions.indexOf(MediaSessionAction.STOP)); + return CollectionUtil.integerListToIntArray(compactActions); + } + + int[] actionsArray = new int[COMPACT_VIEW_ACTIONS_COUNT]; + if (actions.contains(MediaSessionAction.PREVIOUS_TRACK) + && actions.contains(MediaSessionAction.NEXT_TRACK)) { + actionsArray[0] = actions.indexOf(MediaSessionAction.PREVIOUS_TRACK); + if (actions.contains(MediaSessionAction.PLAY)) { + actionsArray[1] = actions.indexOf(MediaSessionAction.PLAY); + } else { + actionsArray[1] = actions.indexOf(MediaSessionAction.PAUSE); + } + actionsArray[2] = actions.indexOf(MediaSessionAction.NEXT_TRACK); + return actionsArray; + } + + assert actions.contains(MediaSessionAction.SEEK_BACKWARD) + && actions.contains(MediaSessionAction.SEEK_FORWARD); + actionsArray[0] = actions.indexOf(MediaSessionAction.SEEK_BACKWARD); + if (actions.contains(MediaSessionAction.PLAY)) { + actionsArray[1] = actions.indexOf(MediaSessionAction.PLAY); + } else { + actionsArray[1] = actions.indexOf(MediaSessionAction.PAUSE); + } + actionsArray[2] = actions.indexOf(MediaSessionAction.SEEK_FORWARD); + + return actionsArray; + } + + private static Context getContext() { + return ContextUtils.getApplicationContext(); + } +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationImageUtils.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationImageUtils.java new file mode 100644 index 00000000000..d5ce90feedb --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationImageUtils.java @@ -0,0 +1,74 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; + +import androidx.annotation.Nullable; + +import org.chromium.base.SysUtils; + +/** A collection of utilities and constants for the images used in MediaSession notifications. */ +public class MediaNotificationImageUtils { + public static final int MINIMAL_MEDIA_IMAGE_SIZE_PX = 114; + + // The media artwork image resolution on high-end devices. + private static final int HIGH_IMAGE_SIZE_PX = 512; + + // The media artwork image resolution on high-end devices. + private static final int LOW_IMAGE_SIZE_PX = 256; + + /** + * Downscale |icon| for display in the notification if needed. Returns null if |icon| is null. + * If |icon| is larger than {@link getIdealMediaImageSize()}, scale it down to + * {@link getIdealMediaImageSize()} and return. Otherwise return the original |icon|. + * @param icon The icon to be scaled. + */ + @Nullable + public static Bitmap downscaleIconToIdealSize(@Nullable Bitmap icon) { + if (icon == null) return null; + + int targetSize = getIdealMediaImageSize(); + + Matrix m = new Matrix(); + int dominantLength = Math.max(icon.getWidth(), icon.getHeight()); + + if (dominantLength < getIdealMediaImageSize()) return icon; + + // Move the center to (0,0). + m.postTranslate(icon.getWidth() / -2.0f, icon.getHeight() / -2.0f); + // Scale to desired size. + float scale = 1.0f * targetSize / dominantLength; + m.postScale(scale, scale); + // Move to the desired place. + m.postTranslate(targetSize / 2.0f, targetSize / 2.0f); + + // Draw the image. + Bitmap paddedBitmap = Bitmap.createBitmap(targetSize, targetSize, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(paddedBitmap); + Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + canvas.drawBitmap(icon, m, paint); + return paddedBitmap; + } + + /** + * @return The ideal size of the media image. + */ + public static int getIdealMediaImageSize() { + return SysUtils.isLowEndDevice() ? LOW_IMAGE_SIZE_PX : HIGH_IMAGE_SIZE_PX; + } + + /** + * @param icon The icon to be checked. + * @return Whether |icon| is suitable as the media image, i.e. bigger than the minimal size. + */ + public static boolean isBitmapSuitableAsMediaImage(Bitmap icon) { + return icon != null && icon.getWidth() >= MINIMAL_MEDIA_IMAGE_SIZE_PX + && icon.getHeight() >= MINIMAL_MEDIA_IMAGE_SIZE_PX; + } +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfo.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfo.java new file mode 100644 index 00000000000..855ed9a50e2 --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationInfo.java @@ -0,0 +1,354 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.text.TextUtils; + +import androidx.annotation.Nullable; + +import org.chromium.services.media_session.MediaMetadata; +import org.chromium.services.media_session.MediaPosition; + +import java.util.HashSet; +import java.util.Set; + +/** + * Exposes information about the current media notification to the external clients. + */ +public class MediaNotificationInfo { + // Bits defining various user actions supported by the media notification. + + /** + * If set, play/pause controls are shown and handled via notification UI and MediaSession. + */ + public static final int ACTION_PLAY_PAUSE = 1 << 0; + + /** + * If set, a stop button is shown and handled via the notification UI. + */ + public static final int ACTION_STOP = 1 << 1; + + /** + * If set, a user can swipe the notification away when it's paused. + * If notification swipe is not supported, it will behave like {@link #ACTION_STOP}. + */ + public static final int ACTION_SWIPEAWAY = 1 << 2; + + /** + * A value that represents an invalid ID. + */ + public static final int INVALID_ID = -1; + + /** + * Use this class to construct an instance of {@link MediaNotificationInfo}. + */ + public static final class Builder { + private MediaMetadata mMetadata; + private boolean mIsPaused; + private String mOrigin = ""; + private int mInstanceId = INVALID_ID; + private boolean mIsPrivate = true; + private int mNotificationSmallIcon; + private Bitmap mNotificationLargeIcon; + private int mDefaultNotificationLargeIcon; + private Bitmap mMediaSessionImage; + private int mActions = ACTION_PLAY_PAUSE | ACTION_SWIPEAWAY; + private int mId = INVALID_ID; + private Intent mContentIntent; + private MediaNotificationListener mListener; + private Set mMediaSessionActions; + private @Nullable MediaPosition mMediaPosition; + + /** + * Initializes the builder with the default values. + */ + public Builder() {} + + public MediaNotificationInfo build() { + assert mMetadata != null; + assert mOrigin != null; + assert mListener != null; + + return new MediaNotificationInfo(mMetadata, mIsPaused, mOrigin, mInstanceId, mIsPrivate, + mNotificationSmallIcon, mNotificationLargeIcon, mDefaultNotificationLargeIcon, + mMediaSessionImage, mActions, mId, mContentIntent, mListener, + mMediaSessionActions, mMediaPosition); + } + + public Builder setMetadata(MediaMetadata metadata) { + mMetadata = metadata; + return this; + } + + public Builder setPaused(boolean isPaused) { + mIsPaused = isPaused; + return this; + } + + public Builder setOrigin(String origin) { + mOrigin = origin; + return this; + } + + public Builder setInstanceId(int instanceId) { + mInstanceId = instanceId; + return this; + } + + public Builder setPrivate(boolean isPrivate) { + mIsPrivate = isPrivate; + return this; + } + + public Builder setNotificationSmallIcon(int icon) { + mNotificationSmallIcon = icon; + return this; + } + + public Builder setNotificationLargeIcon(Bitmap icon) { + mNotificationLargeIcon = icon; + return this; + } + + public Builder setDefaultNotificationLargeIcon(int icon) { + mDefaultNotificationLargeIcon = icon; + return this; + } + + public Builder setMediaSessionImage(Bitmap image) { + mMediaSessionImage = image; + return this; + } + + public Builder setActions(int actions) { + mActions = actions; + return this; + } + + public Builder setId(int id) { + mId = id; + return this; + } + + public Builder setContentIntent(Intent intent) { + mContentIntent = intent; + return this; + } + + public Builder setListener(MediaNotificationListener listener) { + mListener = listener; + return this; + } + + public Builder setMediaSessionActions(Set actions) { + mMediaSessionActions = actions; + return this; + } + + public Builder setMediaPosition(@Nullable MediaPosition position) { + mMediaPosition = position; + return this; + } + } + + /** + * The bitset defining user actions handled by the notification. + */ + private final int mActions; + + /** + * The metadata associated with the media. + */ + public final MediaMetadata metadata; + + /** + * The current state of the media, paused or not. + */ + public final boolean isPaused; + + /** + * The origin of the tab containing the media. + */ + public final String origin; + + /** + * An identifier that helps distinguish different instances of the same type of media + * notification. The {@link id} is shared by different MediaNotificationInfo instances for the + * same media type, but this identifier provides an extra layer of differentiation. In Chrome, + * for example, this corresponds to the source tab. + */ + public final int instanceId; + + /** + * Whether the media notification should be considered as private. + */ + public final boolean isPrivate; + + /** + * The id of the notification small icon from R.drawable. + */ + public final int notificationSmallIcon; + + /** + * The Bitmap resource used as the notification large icon. + */ + public final Bitmap notificationLargeIcon; + + /** + * The id of the default notification large icon from R.drawable. + */ + public final int defaultNotificationLargeIcon; + + /** + * The Bitmap resource used for Android MediaSession image, which will be used on lock screen + * and wearable devices. + */ + public final Bitmap mediaSessionImage; + + /** + * The id to use for the Android Notification. + */ + public final int id; + + /** + * The intent to send when the notification is selected. + */ + public final Intent contentIntent; + + /** + * The listener for the control events. + */ + public final MediaNotificationListener listener; + + /** + * The actions enabled in MediaSession. + */ + public final Set mediaSessionActions; + + /** + * The current position of the media session. + */ + public final @Nullable MediaPosition mediaPosition; + + /** + * @return if play/pause actions are supported by this notification. + */ + public boolean supportsPlayPause() { + return (mActions & ACTION_PLAY_PAUSE) != 0; + } + + /** + * @return if stop action is supported by this notification. + */ + public boolean supportsStop() { + return (mActions & ACTION_STOP) != 0; + } + + /** + * @return if notification should be dismissable by swiping it away when paused. + */ + public boolean supportsSwipeAway() { + return (mActions & ACTION_SWIPEAWAY) != 0; + } + + /** + * Create a new MediaNotificationInfo. + * @param metadata The metadata associated with the media. + * @param isPaused The current state of the media, paused or not. + * @param origin The origin of the tab containing the media. + * @param instanceId The id of the tab containing the media. + * @param isPrivate Whether the media notification should be considered as private. + * @param notificationSmallIcon The small icon used in the notification. + * @param notificationLargeIcon The large icon used in the notification. + * @param defaultNotificationLargeIcon The fallback large icon when |notificationLargeIcon| is + * improper to use. + * @param mediaSessionImage The artwork image to be used in Android MediaSession. + * @param actions The actions supported in this notification. + * @param id The id of this notification, which is used for distinguishing media playback, cast + * and media remote. + * @param contentIntent The intent to send when the notification is selected. + * @param listener The listener for the control events. + * @param mediaSessionActions The actions supported by the page. + * @param mediaPosition The current position of the media. + */ + private MediaNotificationInfo(MediaMetadata metadata, boolean isPaused, String origin, + int instanceId, boolean isPrivate, int notificationSmallIcon, + Bitmap notificationLargeIcon, int defaultNotificationLargeIcon, + Bitmap mediaSessionImage, int actions, int id, Intent contentIntent, + MediaNotificationListener listener, Set mediaSessionActions, + @Nullable MediaPosition mediaPosition) { + this.metadata = metadata; + this.isPaused = isPaused; + this.origin = origin; + assert instanceId != INVALID_ID; + this.instanceId = instanceId; + this.isPrivate = isPrivate; + this.notificationSmallIcon = notificationSmallIcon; + this.notificationLargeIcon = notificationLargeIcon; + this.defaultNotificationLargeIcon = defaultNotificationLargeIcon; + this.mediaSessionImage = mediaSessionImage; + this.mActions = actions; + assert id != INVALID_ID; + this.id = id; + this.contentIntent = contentIntent; + this.listener = listener; + this.mediaSessionActions = (mediaSessionActions != null) + ? new HashSet(mediaSessionActions) + : new HashSet(); + this.mediaPosition = mediaPosition; + } + + @Override + @SuppressWarnings("ReferenceEquality") + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof MediaNotificationInfo)) return false; + + MediaNotificationInfo other = (MediaNotificationInfo) obj; + return isPaused == other.isPaused && isPrivate == other.isPrivate + && instanceId == other.instanceId + && notificationSmallIcon == other.notificationSmallIcon + && (notificationLargeIcon == other.notificationLargeIcon + || (notificationLargeIcon != null + && notificationLargeIcon.sameAs(other.notificationLargeIcon))) + && defaultNotificationLargeIcon == other.defaultNotificationLargeIcon + && mediaSessionImage == other.mediaSessionImage && mActions == other.mActions + && id == other.id + && (metadata == other.metadata + || (metadata != null && metadata.equals(other.metadata))) + && TextUtils.equals(origin, other.origin) + && (contentIntent == other.contentIntent + || (contentIntent != null && contentIntent.equals(other.contentIntent))) + && (listener == other.listener + || (listener != null && listener.equals(other.listener))) + && (mediaSessionActions == other.mediaSessionActions + || (mediaSessionActions != null + && mediaSessionActions.equals(other.mediaSessionActions))) + && mediaPosition == other.mediaPosition; + } + + @Override + public int hashCode() { + int result = isPaused ? 1 : 0; + result = 31 * result + (isPrivate ? 1 : 0); + result = 31 * result + (metadata == null ? 0 : metadata.hashCode()); + result = 31 * result + (origin == null ? 0 : origin.hashCode()); + result = 31 * result + (contentIntent == null ? 0 : contentIntent.hashCode()); + result = 31 * result + instanceId; + result = 31 * result + notificationSmallIcon; + result = 31 * result + + (notificationLargeIcon == null ? 0 : notificationLargeIcon.hashCode()); + result = 31 * result + defaultNotificationLargeIcon; + result = 31 * result + (mediaSessionImage == null ? 0 : mediaSessionImage.hashCode()); + result = 31 * result + mActions; + result = 31 * result + id; + result = 31 * result + listener.hashCode(); + result = 31 * result + mediaSessionActions.hashCode(); + result = 31 * result + mediaPosition.hashCode(); + return result; + } +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationListener.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationListener.java new file mode 100644 index 00000000000..f4ae70e4e24 --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationListener.java @@ -0,0 +1,57 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +/** + * Interface for classes that need to be notified about media events. + */ +public interface MediaNotificationListener { + /** + * The media action was caused by direct interaction with the notification. + */ + public static final int ACTION_SOURCE_MEDIA_NOTIFICATION = 1000; + + /** + * The media action was received via the MediaSession Android API, e.g. a headset, a watch, etc. + */ + public static final int ACTION_SOURCE_MEDIA_SESSION = 1001; + + /** + * The media action was received by unplugging the headset, + * which broadcasts an ACTION_AUDIO_BECOMING_NOISY intent. + */ + public static final int ACTION_SOURCE_HEADSET_UNPLUG = 1002; + + /** + * Called when the user wants to resume the playback. + * @param actionSource The source the listener got the action from. + */ + void onPlay(int actionSource); + + /** + * Called when the user wants to pause the playback. + * @param actionSource The source the listener got the action from. + */ + void onPause(int actionSource); + + /** + * Called when the user wants to stop the playback. + * @param actionSource The source the listener got the action from. + */ + void onStop(int actionSource); + + /** + * Called when the user performed one of the media actions (like fast forward or next track) + * supported by MediaSession. + * @param action The kind of the initated action. + */ + void onMediaSessionAction(int action); + + /** + * Called when the user performed a seek action through Media Session. + * @param action The position to seek to in ms. + */ + void onMediaSessionSeekTo(long pos); +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java new file mode 100644 index 00000000000..83c25162906 --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationUma.java @@ -0,0 +1,47 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import android.content.Intent; + +import androidx.annotation.IntDef; + +import org.chromium.base.metrics.RecordHistogram; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Helper class to record which kind of media notifications does the user click to go back to + * Chrome. + */ +public class MediaNotificationUma { + @IntDef({Source.INVALID, Source.MEDIA, Source.PRESENTATION, Source.MEDIA_FLING}) + @Retention(RetentionPolicy.SOURCE) + public @interface Source { + int INVALID = -1; + int MEDIA = 0; + int PRESENTATION = 1; + int MEDIA_FLING = 2; + int NUM_ENTRIES = 3; + } + + public static final String INTENT_EXTRA_NAME = + "org.chromium.chrome.browser.metrics.MediaNotificationUma.EXTRA_CLICK_SOURCE"; + + /** + * Record the UMA as specified by {@link intent}. The {@link intent} should contain intent extra + * of name {@link INTENT_EXTRA_NAME} indicating the type. + * @param intent The intent starting the activity. + */ + public static void recordClickSource(Intent intent) { + if (intent == null) return; + @Source + int source = intent.getIntExtra(INTENT_EXTRA_NAME, Source.INVALID); + if (source == Source.INVALID || source >= Source.NUM_ENTRIES) return; + RecordHistogram.recordEnumeratedHistogram( + "Media.Notification.Click", source, Source.NUM_ENTRIES); + } +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java new file mode 100644 index 00000000000..7c9422e2248 --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java @@ -0,0 +1,573 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.media.AudioManager; +import android.os.Build; +import android.os.Handler; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.SysUtils; +import org.chromium.components.url_formatter.UrlFormatter; +import org.chromium.content_public.browser.MediaSession; +import org.chromium.content_public.browser.MediaSessionObserver; +import org.chromium.content_public.browser.NavigationHandle; +import org.chromium.content_public.browser.WebContents; +import org.chromium.content_public.browser.WebContentsObserver; +import org.chromium.media_session.mojom.MediaSessionAction; +import org.chromium.services.media_session.MediaImage; +import org.chromium.services.media_session.MediaMetadata; +import org.chromium.services.media_session.MediaPosition; +import org.chromium.ui.base.WindowAndroid; + +import java.util.List; +import java.util.Set; + +/** + * Glue code that relays events from the {@link org.chromium.content.browser.MediaSession} for a + * WebContents to a delegate (ultimately, to {@link MediaNotificationController}). + */ +public class MediaSessionHelper implements MediaImageCallback { + private static final String TAG = "MediaSession"; + + private static final String UNICODE_PLAY_CHARACTER = "\u25B6"; + @VisibleForTesting + public static final int HIDE_NOTIFICATION_DELAY_MILLIS = 2500; + + private Delegate mDelegate; + private WebContents mWebContents; + @VisibleForTesting + public WebContentsObserver mWebContentsObserver; + @VisibleForTesting + public MediaSessionObserver mMediaSessionObserver; + private MediaImageManager mMediaImageManager; + private Bitmap mPageMediaImage; + @VisibleForTesting + public Bitmap mFavicon; + private Bitmap mCurrentMediaImage; + private String mOrigin; + private int mPreviousVolumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE; + @VisibleForTesting + public MediaNotificationInfo.Builder mNotificationInfoBuilder; + // The fallback title if |mPageMetadata| is null or its title is empty. + private String mFallbackTitle; + // Set to true if favicon update callback was called at least once. + private boolean mMaybeHasFavicon; + // The metadata set by the page. + private MediaMetadata mPageMetadata; + // The currently showing metadata. + private MediaMetadata mCurrentMetadata; + private Set mMediaSessionActions; + private @Nullable MediaPosition mMediaPosition; + private Handler mHandler; + // The delayed task to hide notification. Hiding notification can be immediate or delayed. + // Delayed hiding will schedule this delayed task to |mHandler|. The task will be canceled when + // showing or immediate hiding. + private Runnable mHideNotificationDelayedTask; + + // Used to override the MediaSession object get from WebContents. This is to work around the + // static getter {@link MediaSession#fromWebContents()}. + @VisibleForTesting + public static MediaSession sOverriddenMediaSession; + + private MediaNotificationListener mControlsListener = new MediaNotificationListener() { + @Override + public void onPlay(int actionSource) { + if (isNotificationHidingOrHidden()) return; + + MediaSessionUma.recordPlay( + MediaSessionHelper.convertMediaActionSourceToUMA(actionSource)); + + if (mMediaSessionObserver.getMediaSession() == null) return; + + mMediaSessionObserver.getMediaSession().resume(); + } + + @Override + public void onPause(int actionSource) { + if (isNotificationHidingOrHidden()) return; + + MediaSessionUma.recordPause( + MediaSessionHelper.convertMediaActionSourceToUMA(actionSource)); + + if (mMediaSessionObserver.getMediaSession() == null) return; + + mMediaSessionObserver.getMediaSession().suspend(); + } + + @Override + public void onStop(int actionSource) { + if (isNotificationHidingOrHidden()) return; + + MediaSessionUma.recordStop( + MediaSessionHelper.convertMediaActionSourceToUMA(actionSource)); + + if (mMediaSessionObserver.getMediaSession() != null) { + mMediaSessionObserver.getMediaSession().stop(); + } + } + + @Override + public void onMediaSessionAction(int action) { + if (!MediaSessionAction.isKnownValue(action)) return; + if (mMediaSessionObserver != null) { + mMediaSessionObserver.getMediaSession().didReceiveAction(action); + } + } + + @Override + public void onMediaSessionSeekTo(long pos) { + if (mMediaSessionObserver == null) return; + mMediaSessionObserver.getMediaSession().seekTo(pos); + } + }; + + private void hideNotificationDelayed() { + if (mWebContentsObserver == null) return; + if (mHideNotificationDelayedTask != null) return; + + mHideNotificationDelayedTask = new Runnable() { + @Override + public void run() { + mHideNotificationDelayedTask = null; + hideNotificationInternal(); + } + }; + mHandler.postDelayed(mHideNotificationDelayedTask, HIDE_NOTIFICATION_DELAY_MILLIS); + + mNotificationInfoBuilder = null; + mFavicon = null; + } + + private void hideNotificationImmediately() { + if (mWebContentsObserver == null) return; + if (mHideNotificationDelayedTask != null) { + mHandler.removeCallbacks(mHideNotificationDelayedTask); + mHideNotificationDelayedTask = null; + } + + hideNotificationInternal(); + mNotificationInfoBuilder = null; + } + + /** + * This method performs the common steps for hiding the notification. It should only be called + * by {@link #hideNotificationDelayed()} and {@link #hideNotificationImmediately()}. + */ + private void hideNotificationInternal() { + mDelegate.hideMediaNotification(); + Activity activity = getActivity(); + if (activity != null) { + activity.setVolumeControlStream(mPreviousVolumeControlStream); + } + } + + private void showNotification() { + assert mNotificationInfoBuilder != null; + if (mHideNotificationDelayedTask != null) { + mHandler.removeCallbacks(mHideNotificationDelayedTask); + mHideNotificationDelayedTask = null; + } + mDelegate.showMediaNotification(mNotificationInfoBuilder.build()); + } + + private MediaSessionObserver createMediaSessionObserver(MediaSession mediaSession) { + return new MediaSessionObserver(mediaSession) { + @Override + public void mediaSessionDestroyed() { + hideNotificationImmediately(); + cleanupMediaSessionObserver(); + } + + @Override + public void mediaSessionStateChanged(boolean isControllable, boolean isPaused) { + if (!isControllable) { + hideNotificationDelayed(); + return; + } + + Intent contentIntent = mDelegate.createBringTabToFrontIntent(); + if (contentIntent != null) { + contentIntent.putExtra(MediaNotificationUma.INTENT_EXTRA_NAME, + MediaNotificationUma.Source.MEDIA); + } + + if (mFallbackTitle == null) mFallbackTitle = sanitizeMediaTitle(mOrigin); + + mCurrentMetadata = getMetadata(); + mCurrentMediaImage = getCachedNotificationImage(); + mNotificationInfoBuilder = + mDelegate.createMediaNotificationInfoBuilder() + .setMetadata(mCurrentMetadata) + .setPaused(isPaused) + .setOrigin(mOrigin) + .setPrivate(mWebContents.isIncognito()) + .setNotificationSmallIcon(R.drawable.audio_playing) + .setNotificationLargeIcon(mCurrentMediaImage) + .setMediaSessionImage(mPageMediaImage) + .setActions(MediaNotificationInfo.ACTION_PLAY_PAUSE + | MediaNotificationInfo.ACTION_SWIPEAWAY) + .setContentIntent(contentIntent) + .setListener(mControlsListener) + .setMediaSessionActions(mMediaSessionActions) + .setMediaPosition(mMediaPosition); + + // Show a default icon in incognito contents, as they don't show the media icon. + // Also show a default icon if we won't get a favicon from {@link mDelegate}. If the + // delegate will pass a favicon later, show nothing for now; we expect the favicon + // to arrive quickly. + if (mWebContents.isIncognito() + || (mCurrentMediaImage == null && !fetchLargeFaviconImage())) { + mNotificationInfoBuilder.setDefaultNotificationLargeIcon( + R.drawable.audio_playing_square); + } + showNotification(); + Activity activity = getActivity(); + if (activity != null) { + activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + } + } + + @Override + public void mediaSessionMetadataChanged(MediaMetadata metadata) { + mPageMetadata = metadata; + updateNotificationMetadata(); + } + + @Override + public void mediaSessionActionsChanged(Set actions) { + mMediaSessionActions = actions; + updateNotificationActions(); + } + + @Override + public void mediaSessionArtworkChanged(List images) { + mMediaImageManager.downloadImage(images, MediaSessionHelper.this); + updateNotificationMetadata(); + } + + @Override + public void mediaSessionPositionChanged(@Nullable MediaPosition position) { + mMediaPosition = position; + updateNotificationPosition(); + } + }; + } + + public void setWebContents(@NonNull WebContents webContents) { + if (mWebContents == webContents) return; + + mWebContents = webContents; + + if (mWebContentsObserver != null) mWebContentsObserver.destroy(); + mWebContentsObserver = new WebContentsObserver(webContents) { + @Override + public void didFinishNavigation(NavigationHandle navigation) { + if (!navigation.hasCommitted() || !navigation.isInMainFrame() + || navigation.isSameDocument()) { + return; + } + + mOrigin = UrlFormatter.formatUrlForDisplayOmitSchemeOmitTrivialSubdomains( + webContents.getVisibleUrl().getOrigin().getSpec()); + mFavicon = null; + mPageMediaImage = null; + mPageMetadata = null; + // |mCurrentMetadata| selects either |mPageMetadata| or |mFallbackTitle|. As + // there is no guarantee {@link #titleWasSet()} will be called before or + // after this method, |mFallbackTitle| is not reset in this callback, i.e. + // relying solely on + // {@link #titleWasSet()}. The following assignment is to keep + // |mCurrentMetadata| up to date as |mPageMetadata| may have changed. + mCurrentMetadata = getMetadata(); + mMediaSessionActions = null; + + if (isNotificationHidingOrHidden()) return; + + mNotificationInfoBuilder.setOrigin(mOrigin); + mNotificationInfoBuilder.setNotificationLargeIcon(mFavicon); + mNotificationInfoBuilder.setMediaSessionImage(mPageMediaImage); + mNotificationInfoBuilder.setMetadata(mCurrentMetadata); + mNotificationInfoBuilder.setMediaSessionActions(mMediaSessionActions); + showNotification(); + } + + @Override + public void titleWasSet(String title) { + String newFallbackTitle = sanitizeMediaTitle(title); + if (!TextUtils.equals(mFallbackTitle, newFallbackTitle)) { + mFallbackTitle = newFallbackTitle; + updateNotificationMetadata(); + } + } + + @Override + public void wasShown() { + mDelegate.activateAndroidMediaSession(); + } + }; + + MediaSession mediaSession = getMediaSession(webContents); + if (mMediaSessionObserver != null + && mediaSession == mMediaSessionObserver.getMediaSession()) { + return; + } + + cleanupMediaSessionObserver(); + mMediaImageManager.setWebContents(webContents); + if (mediaSession != null) { + mMediaSessionObserver = createMediaSessionObserver(mediaSession); + } + } + + private void cleanupMediaSessionObserver() { + if (mMediaSessionObserver == null) return; + mMediaSessionObserver.stopObserving(); + mMediaSessionObserver = null; + mMediaSessionActions = null; + } + + /** An interface for dispatching embedder-specific behavior. */ + public interface Delegate { + /** Returns an intent that brings the associated web contents to the front. */ + Intent createBringTabToFrontIntent(); + + /** + * Called to asynchronously fetch a larger favicon image. + * + * Normal, smaller favicons are passed in automatically. This call triggers lookup of a + * larger icon, which will also be passed in via {@link updateFavicon()}, or not at all if + * this method returns false. + * @return true if the favicon will be updated. + */ + boolean fetchLargeFaviconImage(); + + /** + * Creates a {@link MediaNotificationInfo.Builder} with basic embedder-specific + * initialization. + */ + public MediaNotificationInfo.Builder createMediaNotificationInfoBuilder(); + + /** Shows a notification with the given metadata. */ + void showMediaNotification(MediaNotificationInfo notificationInfo); + + /** Hides the active notification. */ + void hideMediaNotification(); + + /** Activates the Android MediaSession. */ + void activateAndroidMediaSession(); + } + + public MediaSessionHelper(@NonNull WebContents webContents, @NonNull Delegate delegate) { + mDelegate = delegate; + mMediaImageManager = + new MediaImageManager(MediaNotificationImageUtils.MINIMAL_MEDIA_IMAGE_SIZE_PX, + MediaNotificationImageUtils.getIdealMediaImageSize()); + mHandler = new Handler(); + setWebContents(webContents); + + Activity activity = getActivity(); + if (activity != null) { + mPreviousVolumeControlStream = activity.getVolumeControlStream(); + } + } + + /** + * Called when this object should no longer manage a media session because owning code no longer + * requires it. + */ + public void destroy() { + cleanupMediaSessionObserver(); + hideNotificationImmediately(); + if (mWebContentsObserver != null) mWebContentsObserver.destroy(); + mWebContentsObserver = null; + } + + /** + * Removes all the leading/trailing white spaces and the quite common unicode play character. + * It improves the visibility of the title in the notification. + * + * @param title The original tab title, e.g. " ▶ Foo - Bar " + * @return The sanitized tab title, e.g. "Foo - Bar" + */ + private String sanitizeMediaTitle(String title) { + title = title.trim(); + return title.startsWith(UNICODE_PLAY_CHARACTER) ? title.substring(1).trim() : title; + } + + /** + * Converts the {@link MediaNotificationListener} action source enum into the + * {@link MediaSessionUma} one to ensure matching the histogram values. + * @param source the source id, must be one of the ACTION_SOURCE_* constants defined in the + * {@link MediaNotificationListener} interface. + * @return the corresponding histogram value. + */ + public static @MediaSessionUma.MediaSessionActionSource int convertMediaActionSourceToUMA( + int source) { + if (source == MediaNotificationListener.ACTION_SOURCE_MEDIA_NOTIFICATION) { + return MediaSessionUma.MediaSessionActionSource.MEDIA_NOTIFICATION; + } else if (source == MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION) { + return MediaSessionUma.MediaSessionActionSource.MEDIA_SESSION; + } else if (source == MediaNotificationListener.ACTION_SOURCE_HEADSET_UNPLUG) { + return MediaSessionUma.MediaSessionActionSource.HEADSET_UNPLUG; + } + + assert false; + return MediaSessionUma.MediaSessionActionSource.NUM_ENTRIES; + } + + private Activity getActivity() { + assert mWebContents != null; + WindowAndroid windowAndroid = mWebContents.getTopLevelNativeWindow(); + if (windowAndroid == null) return null; + + return windowAndroid.getActivity().get(); + } + + /** Returns true if a large favicon might be found. */ + private boolean fetchLargeFaviconImage() { + // The page does not have a favicon yet to fetch since onFaviconUpdated was never called. + // Don't waste time trying to find it. + if (!mMaybeHasFavicon) return false; + + return mDelegate.fetchLargeFaviconImage(); + } + + /** + * Updates the best favicon if the given icon is better and the favicon is shown in + * notification. + */ + public void updateFavicon(Bitmap icon) { + if (icon == null) return; + + mMaybeHasFavicon = true; + + // Store the favicon only if notification is being shown. Otherwise the favicon is + // obtained from large icon bridge when needed. + if (isNotificationHidingOrHidden() || mPageMediaImage != null) return; + + // Disable favicons in notifications for low memory devices on O + // where the notification icon is optional. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && SysUtils.isLowEndDevice()) return; + + if (!MediaNotificationImageUtils.isBitmapSuitableAsMediaImage(icon)) return; + if (mFavicon != null + && (icon.getWidth() < mFavicon.getWidth() + || icon.getHeight() < mFavicon.getHeight())) { + return; + } + mFavicon = MediaNotificationImageUtils.downscaleIconToIdealSize(icon); + updateNotificationImage(mFavicon); + } + + /** Sets an icon which will preferentially be used in place of a smaller favicon. */ + public void setLargeIcon(Bitmap icon) { + if (isNotificationHidingOrHidden()) return; + + if (icon == null) { + // If we do not have any favicon then make sure we show default sound icon. This + // icon is used by notification manager only if we do not show any icon. + mNotificationInfoBuilder.setDefaultNotificationLargeIcon( + R.drawable.audio_playing_square); + showNotification(); + } else { + updateFavicon(icon); + } + } + + /** + * Updates the metadata in media notification. This method should be called whenever + * |mPageMetadata| or |mFallbackTitle| is changed. + */ + private void updateNotificationMetadata() { + if (isNotificationHidingOrHidden()) return; + + MediaMetadata newMetadata = getMetadata(); + if (mCurrentMetadata.equals(newMetadata)) return; + + mCurrentMetadata = newMetadata; + mNotificationInfoBuilder.setMetadata(mCurrentMetadata); + showNotification(); + } + + /** + * @return The up-to-date MediaSession metadata. Returns the cached object like |mPageMetadata| + * or |mCurrentMetadata| if it reflects the current state. Otherwise will return a new + * {@link MediaMetadata} object. + */ + private MediaMetadata getMetadata() { + String title = mFallbackTitle; + String artist = ""; + String album = ""; + if (mPageMetadata != null) { + if (!TextUtils.isEmpty(mPageMetadata.getTitle())) return mPageMetadata; + + artist = mPageMetadata.getArtist(); + album = mPageMetadata.getAlbum(); + } + + if (mCurrentMetadata != null && TextUtils.equals(title, mCurrentMetadata.getTitle()) + && TextUtils.equals(artist, mCurrentMetadata.getArtist()) + && TextUtils.equals(album, mCurrentMetadata.getAlbum())) { + return mCurrentMetadata; + } + + return new MediaMetadata(title, artist, album); + } + + private void updateNotificationActions() { + if (isNotificationHidingOrHidden()) return; + + mNotificationInfoBuilder.setMediaSessionActions(mMediaSessionActions); + showNotification(); + } + + private void updateNotificationPosition() { + if (isNotificationHidingOrHidden()) return; + + mNotificationInfoBuilder.setMediaPosition(mMediaPosition); + showNotification(); + } + + @Override + public void onImageDownloaded(Bitmap image) { + mPageMediaImage = MediaNotificationImageUtils.downscaleIconToIdealSize(image); + mFavicon = null; + updateNotificationImage(mPageMediaImage); + } + + private void updateNotificationImage(Bitmap newMediaImage) { + if (mCurrentMediaImage == newMediaImage) return; + + mCurrentMediaImage = newMediaImage; + + if (isNotificationHidingOrHidden()) return; + mNotificationInfoBuilder.setNotificationLargeIcon(mCurrentMediaImage); + mNotificationInfoBuilder.setMediaSessionImage(mPageMediaImage); + showNotification(); + } + + private Bitmap getCachedNotificationImage() { + if (mPageMediaImage != null) return mPageMediaImage; + if (mFavicon != null) return mFavicon; + return null; + } + + private boolean isNotificationHidingOrHidden() { + return mNotificationInfoBuilder == null; + } + + private MediaSession getMediaSession(WebContents contents) { + return (sOverriddenMediaSession != null) ? sOverriddenMediaSession + : MediaSession.fromWebContents(contents); + } +} diff --git a/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionUma.java b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionUma.java new file mode 100644 index 00000000000..58f489fc1e8 --- /dev/null +++ b/chromium/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionUma.java @@ -0,0 +1,42 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.media; + +import androidx.annotation.IntDef; + +import org.chromium.base.metrics.RecordHistogram; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Centralizes UMA data collection for Android-specific MediaSession features. */ +public class MediaSessionUma { + // MediaSessionAction defined in tools/metrics/histograms/histograms.xml. + @IntDef({MediaSessionActionSource.MEDIA_NOTIFICATION, MediaSessionActionSource.MEDIA_SESSION, + MediaSessionActionSource.HEADSET_UNPLUG}) + @Retention(RetentionPolicy.SOURCE) + public @interface MediaSessionActionSource { + int MEDIA_NOTIFICATION = 0; + int MEDIA_SESSION = 1; + int HEADSET_UNPLUG = 2; + + int NUM_ENTRIES = 3; + } + + public static void recordPlay(@MediaSessionActionSource int action) { + RecordHistogram.recordEnumeratedHistogram( + "Media.Session.Play", action, MediaSessionActionSource.NUM_ENTRIES); + } + + public static void recordPause(@MediaSessionActionSource int action) { + RecordHistogram.recordEnumeratedHistogram( + "Media.Session.Pause", action, MediaSessionActionSource.NUM_ENTRIES); + } + + public static void recordStop(@MediaSessionActionSource int action) { + RecordHistogram.recordEnumeratedHistogram( + "Media.Session.Stop", action, MediaSessionActionSource.NUM_ENTRIES); + } +} diff --git a/chromium/components/browser_ui/modaldialog/android/BUILD.gn b/chromium/components/browser_ui/modaldialog/android/BUILD.gn index e2f4d4f4829..88ddb752e03 100644 --- a/chromium/components/browser_ui/modaldialog/android/BUILD.gn +++ b/chromium/components/browser_ui/modaldialog/android/BUILD.gn @@ -56,9 +56,9 @@ android_library("javatests") { "//base:base_java_test_support", "//content/public/test/android:content_java_test_support", "//third_party/android_deps:androidx_core_core_java", + "//third_party/android_deps:espresso_java", "//third_party/android_support_test_runner:rules_java", "//third_party/android_support_test_runner:runner_java", - "//third_party/espresso:espresso_all_java", "//third_party/hamcrest:hamcrest_java", "//third_party/junit:junit", "//ui/android:ui_java", diff --git a/chromium/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/AppModalPresenterTest.java b/chromium/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/AppModalPresenterTest.java index 291cd1a6062..89e3e1cc702 100644 --- a/chromium/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/AppModalPresenterTest.java +++ b/chromium/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/AppModalPresenterTest.java @@ -4,11 +4,11 @@ package org.chromium.components.browser_ui.modaldialog; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils.checkCurrentPresenter; import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils.checkDialogDismissalCause; @@ -16,8 +16,8 @@ import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtil import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils.createDialog; import static org.chromium.components.browser_ui.modaldialog.ModalDialogTestUtils.showDialog; -import android.support.test.espresso.Espresso; -import android.support.test.filters.SmallTest; +import androidx.test.espresso.Espresso; +import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/chromium/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java b/chromium/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java index ffa19f5974b..225470cf110 100644 --- a/chromium/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java +++ b/chromium/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java @@ -4,29 +4,31 @@ package org.chromium.components.browser_ui.modaldialog; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; -import static android.support.test.espresso.matcher.ViewMatchers.withChild; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withParent; -import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isEnabled; +import static androidx.test.espresso.matcher.ViewMatchers.withChild; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withParent; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.not; import android.app.Activity; import android.content.res.Resources; -import android.support.test.filters.MediumTest; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.TextView; +import androidx.test.filters.MediumTest; + import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; diff --git a/chromium/components/browser_ui/notifications/android/BUILD.gn b/chromium/components/browser_ui/notifications/android/BUILD.gn index 78dcfecfc03..220f0f987cd 100644 --- a/chromium/components/browser_ui/notifications/android/BUILD.gn +++ b/chromium/components/browser_ui/notifications/android/BUILD.gn @@ -8,6 +8,7 @@ android_library("java") { sources = [ "java/src/org/chromium/components/browser_ui/notifications/ChromeNotification.java", "java/src/org/chromium/components/browser_ui/notifications/ChromeNotificationBuilder.java", + "java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java", "java/src/org/chromium/components/browser_ui/notifications/NotificationBuilder.java", "java/src/org/chromium/components/browser_ui/notifications/NotificationCompatBuilder.java", "java/src/org/chromium/components/browser_ui/notifications/NotificationManagerProxy.java", diff --git a/chromium/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java b/chromium/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java new file mode 100644 index 00000000000..ca6227ee7b0 --- /dev/null +++ b/chromium/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java @@ -0,0 +1,91 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.notifications; + +import android.app.Notification; +import android.app.Service; +import android.content.Intent; +import android.os.Build; + +import androidx.annotation.VisibleForTesting; +import androidx.core.app.ServiceCompat; +import androidx.core.content.ContextCompat; + +import org.chromium.base.ContextUtils; +import org.chromium.base.Log; + +/** + * Utility functions that call into Android foreground service related API, and provides + * compatibility for older Android versions and work around for Android API bugs. + */ +public class ForegroundServiceUtils { + private static final String TAG = "ForegroundService"; + private ForegroundServiceUtils() {} + + /** + * Gets the singleton instance of ForegroundServiceUtils. + */ + public static ForegroundServiceUtils getInstance() { + return ForegroundServiceUtils.LazyHolder.sInstance; + } + + /** + * Sets a mocked instance for testing. + */ + @VisibleForTesting + public static void setInstanceForTesting(ForegroundServiceUtils instance) { + ForegroundServiceUtils.LazyHolder.sInstance = instance; + } + + private static class LazyHolder { + private static ForegroundServiceUtils sInstance = new ForegroundServiceUtils(); + } + + /** + * Starts a service from {@code intent} with the expectation that it will make itself a + * foreground service with {@link android.app.Service#startForeground(int, Notification)}. + * + * @param intent The {@link Intent} to fire to start the service. + */ + public void startForegroundService(Intent intent) { + ContextCompat.startForegroundService(ContextUtils.getApplicationContext(), intent); + } + + /** + * Upgrades a service from background to foreground after calling + * {@link #startForegroundService(Intent)}. + * @param service The service to be foreground. + * @param id The notification id. + * @param notification The notification attached to the foreground service. + * @param foregroundServiceType The type of foreground service. Must be a subset of the + * foreground service types defined in AndroidManifest.xml. + * Use 0 if no foregroundServiceType attribute is defined. + */ + public void startForeground( + Service service, int id, Notification notification, int foregroundServiceType) { + // If android fail to build the notification, do nothing. + if (notification == null) return; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + service.startForeground(id, notification, foregroundServiceType); + } else { + service.startForeground(id, notification); + } + } + + /** + * Stops the foreground service. See {@link ServiceCompat#stopForeground(Service, int)}. + * @param service The foreground service to stop. + * @param flags The flags to stop foreground service. + */ + public void stopForeground(Service service, int flags) { + // OnePlus devices may throw NullPointerException, see https://crbug.com/992347. + try { + ServiceCompat.stopForeground(service, flags); + } catch (NullPointerException e) { + Log.e(TAG, "Failed to stop foreground service, ", e); + } + } +} diff --git a/chromium/components/browser_ui/settings/android/BUILD.gn b/chromium/components/browser_ui/settings/android/BUILD.gn index 806560d5d19..3f907927a63 100644 --- a/chromium/components/browser_ui/settings/android/BUILD.gn +++ b/chromium/components/browser_ui/settings/android/BUILD.gn @@ -16,6 +16,7 @@ android_library("java") { "widget/java/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreference.java", "widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java", "widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java", + "widget/java/src/org/chromium/components/browser_ui/settings/IconPreference.java", "widget/java/src/org/chromium/components/browser_ui/settings/LearnMorePreference.java", "widget/java/src/org/chromium/components/browser_ui/settings/SpinnerPreference.java", "widget/java/src/org/chromium/components/browser_ui/settings/TextMessagePreference.java", @@ -25,6 +26,7 @@ android_library("java") { "//base:base_java", "//third_party/android_deps:android_support_v7_appcompat_java", "//third_party/android_deps:androidx_annotation_annotation_java", + "//third_party/android_deps:androidx_appcompat_appcompat_resources_java", "//third_party/android_deps:androidx_preference_preference_java", "//ui/android:ui_java", ] @@ -55,9 +57,11 @@ android_resources("java_resources") { "java/res/layout/preference_spinner.xml", "java/res/layout/preference_spinner_single_line.xml", "java/res/layout/preference_spinner_single_line_item.xml", + "java/res/layout/settings_action_bar_shadow.xml", "java/res/values/attrs.xml", "java/res/values/dimens.xml", "java/res/values/styles.xml", ] + custom_package = "org.chromium.components.browser_ui.settings" } diff --git a/chromium/components/browser_ui/settings/android/java/res/layout/settings_action_bar_shadow.xml b/chromium/components/browser_ui/settings/android/java/res/layout/settings_action_bar_shadow.xml new file mode 100644 index 00000000000..9b326a9178a --- /dev/null +++ b/chromium/components/browser_ui/settings/android/java/res/layout/settings_action_bar_shadow.xml @@ -0,0 +1,19 @@ + + + + + + + + + \ No newline at end of file diff --git a/chromium/components/browser_ui/settings/android/java/res/values/styles.xml b/chromium/components/browser_ui/settings/android/java/res/values/styles.xml index dd02c6822a8..c0435c8a02a 100644 --- a/chromium/components/browser_ui/settings/android/java/res/values/styles.xml +++ b/chromium/components/browser_ui/settings/android/java/res/values/styles.xml @@ -29,12 +29,6 @@ @style/TextAppearance.TextMedium.Secondary - - - + + + + + + + + + + + + + + diff --git a/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java b/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java index 04837e74d81..6c5476db8ad 100644 --- a/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java +++ b/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java @@ -51,7 +51,6 @@ public class ChromeBasePreference extends Preference { public ChromeBasePreference(Context context, AttributeSet attrs) { super(context, attrs); - setLayoutResource(R.layout.preference_compat); setSingleLineTitle(false); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChromeBasePreference); diff --git a/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/IconPreference.java b/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/IconPreference.java new file mode 100644 index 00000000000..919a222f44c --- /dev/null +++ b/chromium/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/IconPreference.java @@ -0,0 +1,38 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.settings; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ImageView; + +import androidx.core.view.ViewCompat; +import androidx.preference.PreferenceViewHolder; + +/** + * A preference with a horizontally centered icon. + */ +public class IconPreference extends ChromeBasePreference { + /** + * Constructor for inflating from XML. + */ + public IconPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + // Horizontally center the preference icon. + + // TODO(crbug.com/1095981): move this logic to ChromeBasePreference and + // find a way to center it without a hard-coded padding. + int padding = getContext().getResources().getDimensionPixelSize(R.dimen.pref_icon_padding); + ImageView icon = (ImageView) holder.findViewById(android.R.id.icon); + ViewCompat.setPaddingRelative( + icon, padding, icon.getPaddingTop(), 0, icon.getPaddingBottom()); + } +} diff --git a/chromium/components/browser_ui/share/DEPS b/chromium/components/browser_ui/share/DEPS new file mode 100644 index 00000000000..876f1347c5f --- /dev/null +++ b/chromium/components/browser_ui/share/DEPS @@ -0,0 +1,7 @@ +include_rules = [ + "+chrome/android/java/src/org/chromium/chrome/browser/FileProviderHelper.java", + "+components/dom_distiller/core/android", + "+content/public/android", + "+content/public/test/android/javatests", + "+ui/android", +] diff --git a/chromium/components/browser_ui/share/OWNERS b/chromium/components/browser_ui/share/OWNERS new file mode 100644 index 00000000000..109f4d68517 --- /dev/null +++ b/chromium/components/browser_ui/share/OWNERS @@ -0,0 +1,5 @@ +dtrainor@chromium.org + +file://components/send_tab_to_self/OWNERS + +# COMPONENT: UI>Browser>Sharing diff --git a/chromium/components/browser_ui/share/android/BUILD.gn b/chromium/components/browser_ui/share/android/BUILD.gn new file mode 100644 index 00000000000..6c370e7cba3 --- /dev/null +++ b/chromium/components/browser_ui/share/android/BUILD.gn @@ -0,0 +1,53 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_library("java") { + sources = [ + "java/src/org/chromium/components/browser_ui/share/ShareDialogAdapter.java", + "java/src/org/chromium/components/browser_ui/share/ShareHelper.java", + "java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java", + "java/src/org/chromium/components/browser_ui/share/ShareParams.java", + ] + deps = [ + ":java_resources", + "//base:base_java", + "//components/browser_ui/util/android:java", + "//components/dom_distiller/core/android:dom_distiller_core_java", + "//content/public/android:content_java", + "//third_party/android_deps:androidx_annotation_annotation_java", + "//third_party/android_deps:androidx_appcompat_appcompat_java", + "//third_party/android_deps:androidx_core_core_java", + "//ui/android:ui_java", + ] +} + +android_resources("java_resources") { + custom_package = "org.chromium.components.browser_ui.share" + sources = [ "java/res/layout/share_dialog_item.xml" ] + deps = [ + "//components/browser_ui/strings/android:browser_ui_strings_grd", + "//components/browser_ui/styles/android:java_resources", + ] +} + +android_library("javatests") { + testonly = true + + sources = [ "java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java" ] + deps = [ + ":java", + "//base:base_java", + "//base:base_java_test_support", + "//chrome/android:chrome_java", + "//content/public/test/android:content_java_test_support", + "//third_party/android_deps:androidx_appcompat_appcompat_java", + "//third_party/android_deps:androidx_core_core_java", + "//third_party/hamcrest:hamcrest_java", + "//third_party/junit", + "//ui/android:ui_java", + "//ui/android:ui_java_test_support", + ] +} diff --git a/chromium/components/browser_ui/share/android/java/res/layout/share_dialog_item.xml b/chromium/components/browser_ui/share/android/java/res/layout/share_dialog_item.xml new file mode 100644 index 00000000000..7a716c41522 --- /dev/null +++ b/chromium/components/browser_ui/share/android/java/res/layout/share_dialog_item.xml @@ -0,0 +1,29 @@ + + + + + + + \ No newline at end of file diff --git a/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareDialogAdapter.java b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareDialogAdapter.java new file mode 100644 index 00000000000..811dfdd3d81 --- /dev/null +++ b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareDialogAdapter.java @@ -0,0 +1,52 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.share; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.List; + +/** + * Adapter that provides the list of activities via which a web page can be shared. + */ +class ShareDialogAdapter extends ArrayAdapter { + private final LayoutInflater mInflater; + private final PackageManager mManager; + + /** + * @param context Context used to for layout inflation. + * @param manager PackageManager used to query for activity information. + * @param objects The list of possible share intents. + */ + public ShareDialogAdapter(Context context, PackageManager manager, List objects) { + super(context, R.layout.share_dialog_item, objects); + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mManager = manager; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView == null) { + view = mInflater.inflate(R.layout.share_dialog_item, parent, false); + } else { + view = convertView; + } + TextView text = (TextView) view.findViewById(R.id.text); + ImageView icon = (ImageView) view.findViewById(R.id.icon); + + text.setText(getItem(position).loadLabel(mManager)); + icon.setImageDrawable(ShareHelper.loadIconForResolveInfo(getItem(position), mManager)); + return view; + } +} diff --git a/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java new file mode 100644 index 00000000000..993e5d7c165 --- /dev/null +++ b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java @@ -0,0 +1,394 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.share; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ClipData; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnDismissListener; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.text.TextUtils; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.app.AlertDialog; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.ContextUtils; +import org.chromium.base.PackageManagerUtils; +import org.chromium.components.browser_ui.share.ShareParams.TargetChosenCallback; +import org.chromium.ui.UiUtils; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.base.WindowAndroid.IntentCallback; + +import java.util.Collections; +import java.util.List; + +/** + * A helper class that helps to start an intent to share titles and URLs. + */ +public class ShareHelper { + /** Interface that receives intents for testing (to fake out actually sending them). */ + public interface FakeIntentReceiver { + /** Sets the intent to send back in the broadcast. */ + public void setIntentToSendBack(Intent intent); + + /** Called when a custom chooser dialog is shown. */ + public void onCustomChooserShown(AlertDialog dialog); + + /** + * Simulates firing the given intent, without actually doing so. + * + * @param context The context that will receive broadcasts from the simulated activity. + * @param intent The intent to send to the system. + */ + public void fireIntent(Context context, Intent intent); + } + + /** The task ID of the activity that triggered the share action. */ + public static final String EXTRA_TASK_ID = "org.chromium.chrome.extra.TASK_ID"; + + private static final String EXTRA_SHARE_SCREENSHOT_AS_STREAM = "share_screenshot_as_stream"; + + /** Force the use of a Chrome-specific intent chooser, not the system chooser. */ + private static boolean sForceCustomChooserForTesting; + + /** If non-null, will be used instead of the real activity. */ + private static FakeIntentReceiver sFakeIntentReceiverForTesting; + + protected ShareHelper() {} + + /** + * Fire the intent to share content with the target app. + * + * @param window The current window. + * @param intent The intent to fire. + * @param callback The callback to be triggered when the calling activity has finished. This + * allows the target app to identify Chrome as the source. + */ + protected static void fireIntent( + WindowAndroid window, Intent intent, @Nullable IntentCallback callback) { + if (sFakeIntentReceiverForTesting != null) { + sFakeIntentReceiverForTesting.fireIntent(ContextUtils.getApplicationContext(), intent); + } else if (callback != null) { + window.showIntent(intent, callback, null); + } else { + // TODO(tedchoc): Allow startActivity w/o intent via Window. + Activity activity = window.getActivity().get(); + activity.startActivity(intent); + } + } + + /** + * Force the use of a Chrome-specific intent chooser, not the system chooser. + * + * This emulates the behavior on pre Lollipop-MR1 systems, where the system chooser is not + * available. + */ + public static void setForceCustomChooserForTesting(boolean enabled) { + sForceCustomChooserForTesting = enabled; + } + + /** + * Uses a FakeIntentReceiver instead of actually sending intents to the system. + * + * @param receiver The object to send intents to. If null, resets back to the default behavior + * (really send intents). + */ + public static void setFakeIntentReceiverForTesting(FakeIntentReceiver receiver) { + sFakeIntentReceiverForTesting = receiver; + } + + /** + * Receiver to record the chosen component when sharing an Intent. + */ + public static class TargetChosenReceiver extends BroadcastReceiver implements IntentCallback { + private static final String EXTRA_RECEIVER_TOKEN = "receiver_token"; + private static final Object LOCK = new Object(); + + private static String sTargetChosenReceiveAction; + private static TargetChosenReceiver sLastRegisteredReceiver; + + @Nullable + private TargetChosenCallback mCallback; + + private TargetChosenReceiver(@Nullable TargetChosenCallback callback) { + mCallback = callback; + } + + public static boolean isSupported() { + return !sForceCustomChooserForTesting + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1; + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1) + public static void sendChooserIntent(WindowAndroid window, Intent sharingIntent, + @Nullable TargetChosenCallback callback) { + final Context context = ContextUtils.getApplicationContext(); + final String packageName = context.getPackageName(); + synchronized (LOCK) { + if (sTargetChosenReceiveAction == null) { + sTargetChosenReceiveAction = + packageName + "/" + TargetChosenReceiver.class.getName() + "_ACTION"; + } + if (sLastRegisteredReceiver != null) { + context.unregisterReceiver(sLastRegisteredReceiver); + // Must cancel the callback (to satisfy guarantee that exactly one method of + // TargetChosenCallback is called). + sLastRegisteredReceiver.cancel(); + } + sLastRegisteredReceiver = new TargetChosenReceiver(callback); + context.registerReceiver( + sLastRegisteredReceiver, new IntentFilter(sTargetChosenReceiveAction)); + } + + Intent intent = new Intent(sTargetChosenReceiveAction); + intent.setPackage(packageName); + intent.putExtra(EXTRA_RECEIVER_TOKEN, sLastRegisteredReceiver.hashCode()); + Activity activity = window.getActivity().get(); + final PendingIntent pendingIntent = PendingIntent.getBroadcast(activity, 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); + Intent chooserIntent = Intent.createChooser(sharingIntent, + context.getString(R.string.share_link_chooser_title), + pendingIntent.getIntentSender()); + if (sFakeIntentReceiverForTesting != null) { + sFakeIntentReceiverForTesting.setIntentToSendBack(intent); + } + fireIntent(window, chooserIntent, sLastRegisteredReceiver); + } + + @Override + public void onReceive(Context context, Intent intent) { + synchronized (LOCK) { + if (sLastRegisteredReceiver != this) return; + ContextUtils.getApplicationContext().unregisterReceiver(sLastRegisteredReceiver); + sLastRegisteredReceiver = null; + } + if (!intent.hasExtra(EXTRA_RECEIVER_TOKEN) + || intent.getIntExtra(EXTRA_RECEIVER_TOKEN, 0) != this.hashCode()) { + return; + } + + ComponentName target = intent.getParcelableExtra(Intent.EXTRA_CHOSEN_COMPONENT); + if (mCallback != null) { + mCallback.onTargetChosen(target); + mCallback = null; + } + } + + @Override + public void onIntentCompleted(WindowAndroid window, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_CANCELED) { + cancel(); + } + } + + private void cancel() { + if (mCallback != null) { + mCallback.onCancel(); + mCallback = null; + } + } + } + + /** + * Creates and shows a custom share intent picker dialog. + * + * @param params The container holding the share parameters. + */ + static void showCompatShareDialog(final ShareParams params) { + Intent intent = getShareLinkAppCompatibilityIntent(); + List resolveInfoList = PackageManagerUtils.queryIntentActivities(intent, 0); + assert resolveInfoList.size() > 0; + if (resolveInfoList.size() == 0) return; + + final Context context = params.getWindow().getContext().get(); + final PackageManager manager = context.getPackageManager(); + Collections.sort(resolveInfoList, new ResolveInfo.DisplayNameComparator(manager)); + + final ShareDialogAdapter adapter = + new ShareDialogAdapter(context, manager, resolveInfoList); + AlertDialog.Builder builder = new UiUtils.CompatibleAlertDialogBuilder( + context, R.style.Theme_Chromium_AlertDialog); + builder.setTitle(context.getString(R.string.share_link_chooser_title)); + builder.setAdapter(adapter, null); + + final TargetChosenCallback callback = params.getCallback(); + // Need a mutable object to record whether the callback has been fired. + final boolean[] callbackCalled = new boolean[1]; + + final AlertDialog dialog = builder.create(); + dialog.show(); + dialog.getListView().setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + ResolveInfo info = adapter.getItem(position); + ActivityInfo ai = info.activityInfo; + ComponentName component = + new ComponentName(ai.applicationInfo.packageName, ai.name); + + if (callback != null && !callbackCalled[0]) { + callback.onTargetChosen(component); + callbackCalled[0] = true; + } + shareDirectly(params, component); + dialog.dismiss(); + } + }); + + dialog.setOnDismissListener(new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (callback != null && !callbackCalled[0]) { + callback.onCancel(); + callbackCalled[0] = true; + } + } + }); + + if (sFakeIntentReceiverForTesting != null) { + sFakeIntentReceiverForTesting.onCustomChooserShown(dialog); + } + } + + /** + * Shares the params using the system share sheet, or skipping the sheet and sharing directl if + * the target component is specified. + */ + static void shareWithSystemSheet(ShareParams params) { + assert TargetChosenReceiver.isSupported(); + TargetChosenReceiver.sendChooserIntent( + params.getWindow(), getShareLinkIntent(params), params.getCallback()); + } + + /** + * Shows a picker and allows the user to choose a share target. + * + * @param params The container holding the share parameters. + */ + public static void shareWithUi(ShareParams params) { + if (TargetChosenReceiver.isSupported()) { + // On L+ open system share sheet. + shareWithSystemSheet(params); + } else { + // On K and below open custom share dialog. + showCompatShareDialog(params); + } + } + + /** + * Share directly with the provied share target. + * @param params The container holding the share parameters. + * @param component The component to share to, bypassing any UI. + */ + public static void shareDirectly( + @NonNull ShareParams params, @NonNull ComponentName component) { + Intent intent = getShareLinkIntent(params); + intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); + intent.setComponent(component); + fireIntent(params.getWindow(), intent, null); + } + + @VisibleForTesting + public static Intent getShareLinkIntent(ShareParams params) { + final boolean isFileShare = (params.getFileUris() != null); + final boolean isMultipleFileShare = isFileShare && (params.getFileUris().size() > 1); + final String action = + isMultipleFileShare ? Intent.ACTION_SEND_MULTIPLE : Intent.ACTION_SEND; + Intent intent = new Intent(action); + intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag() + | Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); + intent.putExtra(EXTRA_TASK_ID, params.getWindow().getActivity().get().getTaskId()); + + Uri screenshotUri = params.getScreenshotUri(); + if (screenshotUri != null) { + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + // To give read access to an Intent target, we need to put |screenshotUri| in clipData + // because adding Intent.FLAG_GRANT_READ_URI_PERMISSION doesn't work for + // EXTRA_SHARE_SCREENSHOT_AS_STREAM. + intent.setClipData(ClipData.newRawUri("", screenshotUri)); + intent.putExtra(EXTRA_SHARE_SCREENSHOT_AS_STREAM, screenshotUri); + } + + if (params.getOfflineUri() != null) { + intent.putExtra(Intent.EXTRA_SUBJECT, params.getTitle()); + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.putExtra(Intent.EXTRA_STREAM, params.getOfflineUri()); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setType("multipart/related"); + } else { + if (!TextUtils.equals(params.getText(), params.getTitle())) { + intent.putExtra(Intent.EXTRA_SUBJECT, params.getTitle()); + } + intent.putExtra(Intent.EXTRA_TEXT, params.getText()); + + if (isFileShare) { + intent.setType(params.getFileContentType()); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + if (isMultipleFileShare) { + intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, params.getFileUris()); + } else { + intent.putExtra(Intent.EXTRA_STREAM, params.getFileUris().get(0)); + } + } else { + intent.setType("text/plain"); + } + } + + return intent; + } + + /** + * Convenience method to create an Intent to retrieve all the apps support sharing text. + */ + public static Intent getShareLinkAppCompatibilityIntent() { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); + intent.putExtra(Intent.EXTRA_SUBJECT, ""); + intent.putExtra(Intent.EXTRA_TEXT, ""); + intent.setType("text/plain"); + return intent; + } + + /** + * Loads the icon for the provided ResolveInfo. + * @param info The ResolveInfo to load the icon for. + * @param manager The package manager to use to load the icon. + */ + public static Drawable loadIconForResolveInfo(ResolveInfo info, PackageManager manager) { + try { + final int iconRes = info.getIconResource(); + if (iconRes != 0) { + Resources res = manager.getResourcesForApplication(info.activityInfo.packageName); + Drawable icon = ApiCompatibilityUtils.getDrawable(res, iconRes); + return icon; + } + } catch (NameNotFoundException | NotFoundException e) { + // Could not find the icon. loadIcon call below will return the default app icon. + } + return info.loadIcon(manager); + } +} diff --git a/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java new file mode 100644 index 00000000000..246b0c7a9b3 --- /dev/null +++ b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtils.java @@ -0,0 +1,449 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.share; + +import android.annotation.TargetApi; +import android.app.DownloadManager; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.text.TextUtils; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.ApplicationState; +import org.chromium.base.ApplicationStatus; +import org.chromium.base.BuildInfo; +import org.chromium.base.Callback; +import org.chromium.base.ContentUriUtils; +import org.chromium.base.ContextUtils; +import org.chromium.base.FileUtils; +import org.chromium.base.Log; +import org.chromium.base.StreamUtil; +import org.chromium.base.task.AsyncTask; +import org.chromium.components.browser_ui.util.DownloadUtils; +import org.chromium.content_public.browser.RenderWidgetHostView; +import org.chromium.content_public.browser.WebContents; +import org.chromium.ui.UiUtils; +import org.chromium.ui.base.Clipboard; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Locale; + +/** + * Utility class for file operations for image data. + */ +public class ShareImageFileUtils { + private static final String TAG = "share"; + + /** + * Directory name for shared images. + * + * Named "screenshot" for historical reasons as we only initially shared screenshot images. + * TODO(crbug.com/1055886): consider changing the directory name. + */ + private static final String SHARE_IMAGES_DIRECTORY_NAME = "screenshot"; + private static final String JPEG_EXTENSION = ".jpg"; + private static final String FILE_NUMBER_FORMAT = " (%d)"; + private static final String MIME_TYPE = "image/JPEG"; + + /** + * Check if the file related to |fileUri| is in the |folder|. + * + * @param fileUri The {@link Uri} related to the file to be checked. + * @param folder The folder that may contain the |fileUrl|. + * @return Whether the |fileUri| is in the |folder|. + */ + private static boolean isUriInDirectory(Uri fileUri, File folder) { + if (fileUri == null) return false; + + Uri chromeUriPrefix = ContentUriUtils.getContentUriFromFile(folder); + if (chromeUriPrefix == null) return false; + + return fileUri.toString().startsWith(chromeUriPrefix.toString()); + } + + /** + * Check if the system clipboard contains a Uri that comes from Chrome. If yes, return the file + * name from the Uri, otherwise return null. + * + * @return The file name if system clipboard contains a Uri from Chrome, otherwise return null. + */ + private static String getClipboardCurrentFilepath() throws IOException { + Uri clipboardUri = Clipboard.getInstance().getImageUri(); + if (isUriInDirectory(clipboardUri, getSharedFilesDirectory())) { + return clipboardUri.getPath(); + } + return null; + } + + /** + * Returns the directory where temporary files are stored to be shared with external + * applications. These files are deleted on startup and when there are no longer any active + * Activities. + * + * @return The directory where shared files are stored. + */ + public static File getSharedFilesDirectory() throws IOException { + File imagePath = UiUtils.getDirectoryForImageCapture(ContextUtils.getApplicationContext()); + return new File(imagePath, SHARE_IMAGES_DIRECTORY_NAME); + } + + /** + * Clears all shared image files. + */ + public static void clearSharedImages() { + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + try { + String clipboardFilepath = getClipboardCurrentFilepath(); + FileUtils.recursivelyDeleteFile(getSharedFilesDirectory(), (filepath) -> { + return filepath == null || clipboardFilepath == null + || !filepath.endsWith(clipboardFilepath); + }); + } catch (IOException ie) { + // Ignore exception. + } + }); + } + + /** + * Temporarily saves the given set of image bytes and provides that URI to a callback for + * sharing. + * + * @param context The context used to trigger the share action. + * @param imageData The image data to be shared in |fileExtension| format. + * @param fileExtension File extension which |imageData| encoded to. + * @param callback A provided callback function which will act on the generated URI. + */ + public static void generateTemporaryUriFromData(final Context context, final byte[] imageData, + String fileExtension, Callback callback) { + if (imageData.length == 0) { + Log.w(TAG, "Share failed -- Received image contains no data."); + return; + } + OnImageSaveListener listener = new OnImageSaveListener() { + @Override + public void onImageSaved(Uri uri, String displayName) { + callback.onResult(uri); + } + @Override + public void onImageSaveError(String displayName) {} + }; + + String fileName = String.valueOf(System.currentTimeMillis()); + // Path is passed as a function because in some cases getting the path should be run on a + // background thread. + saveImage(fileName, + () + -> { return ""; }, + listener, (fos) -> { writeImageData(fos, imageData); }, true, fileExtension); + } + + /** + * Saves bitmap to external storage directory. + * + * @param context The Context to use for determining download location. + * @param filename The filename without extension. + * @param bitmap The Bitmap to download. + * @param listener The OnImageSaveListener to notify the download results. + */ + public static void saveBitmapToExternalStorage( + final Context context, String fileName, Bitmap bitmap, OnImageSaveListener listener) { + // Passing the path as a function so that it can be called on a background thread in + // |saveImage|. + saveImage(fileName, + () + -> { + return context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath(); + }, + listener, (fos) -> { writeBitmap(fos, bitmap); }, false, JPEG_EXTENSION); + } + + /** + * Interface for notifying image download result. + */ + public interface OnImageSaveListener { + void onImageSaved(Uri uri, String displayName); + void onImageSaveError(String displayName); + } + + /** + * Interface for writing image information to a output stream. + */ + private interface FileOutputStreamWriter { + void write(FileOutputStream fos) throws IOException; + } + + /** + * Interface for providing file path. This is used for passing a function for getting the path + * to other function to be called while on a background thread. Should be used on a background + * thread. + */ + private interface FilePathProvider { + String getPath(); + } + + /** + * Saves image to the given file. + * + * @param fileName The File instance of a destination file. + * @param filePathProvider The FilePathProvider for obtaining destination file path. + * @param listener The OnImageSaveListener to notify the download results. + * @param writer The FileOutputStreamWriter that writes to given stream. + * @param isTemporary Indicates whether image should be save to a temporary file. + * @param fileExtension The file's extension. + */ + private static void saveImage(String fileName, FilePathProvider filePathProvider, + OnImageSaveListener listener, FileOutputStreamWriter writer, boolean isTemporary, + String fileExtension) { + new AsyncTask() { + @Override + protected Uri doInBackground() { + FileOutputStream fOut = null; + File destFile = null; + try { + destFile = createFile( + fileName, filePathProvider.getPath(), isTemporary, fileExtension); + if (destFile != null && destFile.exists()) { + fOut = new FileOutputStream(destFile); + writer.write(fOut); + } else { + Log.w(TAG, + "Share failed -- Unable to create or write to destination file."); + } + } catch (IOException ie) { + cancel(true); + } finally { + StreamUtil.closeQuietly(fOut); + } + + Uri uri = null; + if (!isTemporary) { + if (BuildInfo.isAtLeastQ()) { + uri = addToMediaStore(destFile); + } else { + long downloadId = addCompletedDownload(destFile); + DownloadManager manager = + (DownloadManager) ContextUtils.getApplicationContext() + .getSystemService(Context.DOWNLOAD_SERVICE); + return manager.getUriForDownloadedFile(downloadId); + } + } else { + uri = FileUtils.getUriForFile(destFile); + } + return uri; + } + + @Override + protected void onCancelled() { + listener.onImageSaveError(fileName); + } + + @Override + protected void onPostExecute(Uri uri) { + if (uri == null) { + listener.onImageSaveError(fileName); + return; + } + + if (ApplicationStatus.getStateForApplication() + == ApplicationState.HAS_DESTROYED_ACTIVITIES) { + return; + } + + listener.onImageSaved(uri, fileName); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + /** + * Creates file with specified path, name and extension. + * + * @param filePath The file path a destination file. + * @param fileName The file name a destination file. + * @param isTemporary Indicates whether image should be save to a temporary file. + * @param fileExtension The file's extension. + * + * @return The new File object. + */ + private static File createFile(String fileName, String filePath, boolean isTemporary, + String fileExtension) throws IOException { + File path; + if (filePath.isEmpty()) { + path = getSharedFilesDirectory(); + } else { + path = new File(filePath); + } + + File newFile = null; + if (path.exists() || path.mkdir()) { + if (isTemporary) { + newFile = File.createTempFile(fileName, fileExtension, path); + } else { + newFile = getNextAvailableFile(filePath, fileName, fileExtension); + } + } + + return newFile; + } + + /** + * Returns next available file for the given fileName. + * + * @param filePath The file path a destination file. + * @param fileName The file name a destination file. + * @param extension The extension a destination file. + * + * @return The new File object. + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public static File getNextAvailableFile(String filePath, String fileName, String extension) + throws IOException { + File destFile = new File(filePath, fileName + extension); + int num = 0; + while (destFile.exists()) { + destFile = new File(filePath, + fileName + String.format(Locale.getDefault(), FILE_NUMBER_FORMAT, ++num) + + extension); + } + destFile.createNewFile(); + + return destFile; + } + + /** + * Writes given bitmap to into the given fos. + * + * @param fos The FileOutputStream to write to. + * @param bitmap The Bitmap to write. + */ + private static void writeBitmap(FileOutputStream fos, Bitmap bitmap) throws IOException { + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); + } + + /** + * Writes given data to into the given fos. + * + * @param fos The FileOutputStream to write to. + * @param byte[] The byte[] to write. + */ + private static void writeImageData(FileOutputStream fos, final byte[] data) throws IOException { + fos.write(data); + } + + /** + * This is a pass through to the {@link AndroidDownloadManager} function of the same name. + * @param file The File corresponding to the download. + * @return the download ID of this item as assigned by the download manager. + */ + public static long addCompletedDownload(File file) { + String title = file.getName(); + String path = file.getPath(); + long length = file.length(); + + return DownloadUtils.addCompletedDownload( + title, title, MIME_TYPE, path, length, null, null); + } + + @TargetApi(29) + public static Uri addToMediaStore(File file) { + assert BuildInfo.isAtLeastQ(); + + final ContentValues contentValues = new ContentValues(); + contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName()); + contentValues.put(MediaStore.MediaColumns.MIME_TYPE, MIME_TYPE); + contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); + + ContentResolver database = ContextUtils.getApplicationContext().getContentResolver(); + Uri insertUri = database.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues); + + InputStream input = null; + OutputStream output = null; + try { + input = new FileInputStream(file); + if (insertUri != null) { + output = database.openOutputStream(insertUri); + } + if (output != null) { + byte[] buffer = new byte[4096]; + int byteCount = 0; + while ((byteCount = input.read(buffer)) != -1) { + output.write(buffer, 0, byteCount); + } + } + file.delete(); + } catch (IOException e) { + } finally { + StreamUtil.closeQuietly(input); + StreamUtil.closeQuietly(output); + } + return insertUri; + } + + /** + * Captures a screenshot for the provided web contents, persists it and notifies the file + * provider that the file is ready to be accessed by the client. + * + * The screenshot is compressed to JPEG before being written to the file. + * + * @param contents The WebContents instance for which to capture a screenshot. + * @param width The desired width of the resulting screenshot, or 0 for "auto." + * @param height The desired height of the resulting screenshot, or 0 for "auto." + * @param callback The callback that will be called once the screenshot is saved. + */ + public static void captureScreenshotForContents( + WebContents contents, int width, int height, Callback callback) { + RenderWidgetHostView rwhv = contents.getRenderWidgetHostView(); + if (rwhv == null) { + callback.onResult(null); + return; + } + try { + String path = UiUtils.getDirectoryForImageCapture(ContextUtils.getApplicationContext()) + + File.separator + SHARE_IMAGES_DIRECTORY_NAME; + rwhv.writeContentBitmapToDiskAsync( + width, height, path, new ExternallyVisibleUriCallback(callback)); + } catch (IOException e) { + Log.e(TAG, "Error getting content bitmap: ", e); + callback.onResult(null); + } + } + + private static class ExternallyVisibleUriCallback implements Callback { + private Callback mComposedCallback; + ExternallyVisibleUriCallback(Callback cb) { + mComposedCallback = cb; + } + + @Override + public void onResult(final String path) { + if (TextUtils.isEmpty(path)) { + mComposedCallback.onResult(null); + return; + } + + new AsyncTask() { + @Override + protected Uri doInBackground() { + return ContentUriUtils.getContentUriFromFile(new File(path)); + } + + @Override + protected void onPostExecute(Uri uri) { + mComposedCallback.onResult(uri); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } +} diff --git a/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java new file mode 100644 index 00000000000..087d53556ea --- /dev/null +++ b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareImageFileUtilsTest.java @@ -0,0 +1,348 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.share; + +import android.annotation.TargetApi; +import android.app.DownloadManager; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.net.Uri; +import android.os.Environment; +import android.os.Looper; +import android.provider.MediaStore; + +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.BuildInfo; +import org.chromium.base.Callback; +import org.chromium.base.ContentUriUtils; +import org.chromium.base.ContextUtils; +import org.chromium.base.task.AsyncTask; +import org.chromium.base.test.BaseJUnit4ClassRunner; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.DisableIf; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.chrome.browser.FileProviderHelper; +import org.chromium.content_public.browser.test.util.Criteria; +import org.chromium.content_public.browser.test.util.CriteriaHelper; +import org.chromium.ui.base.Clipboard; +import org.chromium.ui.test.util.DummyUiActivityTestCase; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Tests of {@link ShareImageFileUtils}. + */ +@RunWith(BaseJUnit4ClassRunner.class) +public class ShareImageFileUtilsTest extends DummyUiActivityTestCase { + private static final long WAIT_TIMEOUT_SECONDS = 30L; + private static final byte[] TEST_IMAGE_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + private static final String TEST_IMAGE_FILE_NAME = "chrome-test-bitmap"; + private static final String TEST_GIF_IMAGE_FILE_EXTENSION = ".gif"; + private static final String TEST_JPG_IMAGE_FILE_EXTENSION = ".jpg"; + private static final String TEST_PNG_IMAGE_FILE_EXTENSION = ".png"; + + private class GenerateUriCallback extends CallbackHelper implements Callback { + private Uri mImageUri; + + public Uri getImageUri() { + return mImageUri; + } + + @Override + public void onResult(Uri uri) { + mImageUri = uri; + notifyCalled(); + } + } + + private class AsyncTaskRunnableHelper extends CallbackHelper implements Runnable { + @Override + public void run() { + notifyCalled(); + } + } + + @Override + public void setUpTest() throws Exception { + super.setUpTest(); + Looper.prepare(); + ContentUriUtils.setFileProviderUtil(new FileProviderHelper()); + } + + @Override + public void tearDownTest() throws Exception { + Clipboard.getInstance().setText(""); + clearSharedImages(); + deleteAllTestImages(); + super.tearDownTest(); + } + + private int fileCount(File file) { + if (file.isFile()) { + return 1; + } + + int count = 0; + if (file.isDirectory()) { + for (File f : file.listFiles()) count += fileCount(f); + } + return count; + } + + private boolean filepathExists(File file, String filepath) { + if (file.isFile() && filepath.endsWith(file.getName())) { + return true; + } + + if (file.isDirectory()) { + for (File f : file.listFiles()) { + if (filepathExists(f, filepath)) return true; + } + } + return false; + } + + private Uri generateAnImageToClipboard(String fileExtension) throws TimeoutException { + GenerateUriCallback imageCallback = new GenerateUriCallback(); + ShareImageFileUtils.generateTemporaryUriFromData( + getActivity(), TEST_IMAGE_DATA, fileExtension, imageCallback); + imageCallback.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); + Clipboard.getInstance().setImageUri(imageCallback.getImageUri()); + CriteriaHelper.pollInstrumentationThread(() -> { + Criteria.checkThat(Clipboard.getInstance().getImageUri(), + Matchers.is(imageCallback.getImageUri())); + }); + return imageCallback.getImageUri(); + } + + private Uri generateAnImageToClipboard() throws TimeoutException { + return generateAnImageToClipboard(TEST_JPG_IMAGE_FILE_EXTENSION); + } + + private void clearSharedImages() throws TimeoutException { + ShareImageFileUtils.clearSharedImages(); + + // ShareImageFileUtils::clearSharedImages uses AsyncTask.SERIAL_EXECUTOR to schedule a + // clearing the shared folder job, so schedule a new job and wait for the new job finished + // to make sure ShareImageFileUtils::clearSharedImages's clearing folder job finished. + waitForAsync(); + } + + private void waitForAsync() throws TimeoutException { + AsyncTaskRunnableHelper runnableHelper = new AsyncTaskRunnableHelper(); + AsyncTask.SERIAL_EXECUTOR.execute(runnableHelper); + runnableHelper.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + AsyncTask.THREAD_POOL_EXECUTOR.execute(runnableHelper); + runnableHelper.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + + private void deleteAllTestImages() throws TimeoutException { + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + if (BuildInfo.isAtLeastQ()) { + deleteMediaStoreFiles(); + } + deleteExternalStorageFiles(); + }); + waitForAsync(); + } + + @TargetApi(29) + private void deleteMediaStoreFiles() { + ContentResolver contentResolver = ContextUtils.getApplicationContext().getContentResolver(); + Cursor cursor = + contentResolver.query(MediaStore.Downloads.EXTERNAL_CONTENT_URI, null, null, null); + while (cursor.moveToNext()) { + long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID)); + Uri uri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, id); + contentResolver.delete(uri, null, null); + } + } + + public void deleteExternalStorageFiles() { + File externalStorageDir = ContextUtils.getApplicationContext().getExternalFilesDir( + Environment.DIRECTORY_DOWNLOADS); + String[] children = externalStorageDir.list(); + for (int i = 0; i < children.length; i++) { + new File(externalStorageDir, children[i]).delete(); + } + } + + private int fileCountInShareDirectory() throws IOException { + return fileCount(ShareImageFileUtils.getSharedFilesDirectory()); + } + + private boolean fileExistsInShareDirectory(Uri fileUri) throws IOException { + return filepathExists(ShareImageFileUtils.getSharedFilesDirectory(), fileUri.getPath()); + } + + private Bitmap getTestBitmap() { + int size = 10; + Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(); + paint.setColor(android.graphics.Color.GREEN); + canvas.drawRect(0F, 0F, (float) size, (float) size, paint); + return bitmap; + } + + @Test + @SmallTest + public void clipboardUriDoNotClearTest() throws TimeoutException, IOException { + Uri clipboardUri = generateAnImageToClipboard(TEST_GIF_IMAGE_FILE_EXTENSION); + Assert.assertTrue(clipboardUri.getPath().endsWith(TEST_GIF_IMAGE_FILE_EXTENSION)); + clipboardUri = generateAnImageToClipboard(TEST_JPG_IMAGE_FILE_EXTENSION); + Assert.assertTrue(clipboardUri.getPath().endsWith(TEST_JPG_IMAGE_FILE_EXTENSION)); + clipboardUri = generateAnImageToClipboard(TEST_PNG_IMAGE_FILE_EXTENSION); + Assert.assertTrue(clipboardUri.getPath().endsWith(TEST_PNG_IMAGE_FILE_EXTENSION)); + Assert.assertEquals(3, fileCountInShareDirectory()); + + clearSharedImages(); + Assert.assertEquals(1, fileCountInShareDirectory()); + Assert.assertTrue(fileExistsInShareDirectory(clipboardUri)); + } + + @Test + @SmallTest + public void clearEverythingIfNoClipboardImageTest() throws TimeoutException, IOException { + generateAnImageToClipboard(); + generateAnImageToClipboard(); + generateAnImageToClipboard(); + Assert.assertEquals(3, fileCountInShareDirectory()); + + Clipboard.getInstance().setText(""); + clearSharedImages(); + Assert.assertEquals(0, fileCountInShareDirectory()); + } + + @Test + @SmallTest + @DisabledTest(message = "crbug.com/1056059") + public void testSaveBitmap() throws IOException, TimeoutException { + String fileName = TEST_IMAGE_FILE_NAME + "_save_bitmap"; + ShareImageFileUtils.OnImageSaveListener listener = + new ShareImageFileUtils.OnImageSaveListener() { + @Override + public void onImageSaved(Uri uri, String displayName) { + Assert.assertNotNull(uri); + Assert.assertEquals(fileName, displayName); + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + File file = new File(uri.getPath()); + Assert.assertTrue(file.exists()); + Assert.assertTrue(file.isFile()); + }); + + // Wait for the above checks to complete. + try { + waitForAsync(); + } catch (TimeoutException ex) { + } + } + + @Override + public void onImageSaveError(String displayName) { + Assert.fail(); + } + }; + ShareImageFileUtils.saveBitmapToExternalStorage( + getActivity(), fileName, getTestBitmap(), listener); + waitForAsync(); + } + + @Test + @SmallTest + @DisableIf.Build(sdk_is_less_than = 29) + public void testSaveBitmapAndMediaStore() throws IOException, TimeoutException { + String fileName = TEST_IMAGE_FILE_NAME + "_mediastore"; + ShareImageFileUtils.OnImageSaveListener listener = + new ShareImageFileUtils.OnImageSaveListener() { + @Override + public void onImageSaved(Uri uri, String displayName) { + Assert.assertNotNull(uri); + Assert.assertEquals(fileName, displayName); + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + Cursor cursor = getActivity().getContentResolver().query( + uri, null, null, null, null); + Assert.assertNotNull(cursor); + Assert.assertTrue(cursor.moveToFirst()); + Assert.assertEquals(fileName + TEST_JPG_IMAGE_FILE_EXTENSION, + cursor.getString(cursor.getColumnIndex( + MediaStore.MediaColumns.DISPLAY_NAME))); + }); + + // Wait for the above checks to complete. + try { + waitForAsync(); + } catch (TimeoutException ex) { + } + } + + @Override + public void onImageSaveError(String displayName) { + Assert.fail(); + } + }; + ShareImageFileUtils.saveBitmapToExternalStorage( + getActivity(), fileName, getTestBitmap(), listener); + waitForAsync(); + } + + @Test + @SmallTest + public void testGetNextAvailableFile() throws IOException { + String fileName = TEST_IMAGE_FILE_NAME + "_next_availble"; + File externalStorageDir = + getActivity().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); + File imageFile = ShareImageFileUtils.getNextAvailableFile( + externalStorageDir.getPath(), fileName, TEST_JPG_IMAGE_FILE_EXTENSION); + Assert.assertTrue(imageFile.exists()); + + File imageFile2 = ShareImageFileUtils.getNextAvailableFile( + externalStorageDir.getPath(), fileName, TEST_JPG_IMAGE_FILE_EXTENSION); + Assert.assertTrue(imageFile2.exists()); + Assert.assertNotEquals(imageFile.getPath(), imageFile2.getPath()); + } + + @Test + @SmallTest + @DisableIf.Build(sdk_is_greater_than = 28) + public void testAddCompletedDownload() throws IOException { + String filename = + TEST_IMAGE_FILE_NAME + "_add_completed_download" + TEST_JPG_IMAGE_FILE_EXTENSION; + File externalStorageDir = + getActivity().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); + File qrcodeFile = new File(externalStorageDir, filename); + Assert.assertTrue(qrcodeFile.createNewFile()); + + long downloadId = ShareImageFileUtils.addCompletedDownload(qrcodeFile); + Assert.assertNotEquals(0L, downloadId); + + DownloadManager downloadManager = + (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE); + DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); + Cursor c = downloadManager.query(query); + + Assert.assertNotNull(c); + Assert.assertTrue(c.moveToFirst()); + Assert.assertEquals( + filename, c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TITLE))); + c.close(); + } +} diff --git a/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareParams.java b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareParams.java new file mode 100644 index 00000000000..f26655eb4a8 --- /dev/null +++ b/chromium/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareParams.java @@ -0,0 +1,246 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.share; + +import android.content.ComponentName; +import android.net.Uri; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils; +import org.chromium.ui.base.WindowAndroid; + +import java.util.ArrayList; + +/** + * A container object for passing share parameters to {@link ShareHelper}. + */ +public class ShareParams { + /** The window that triggered the share action. */ + private final WindowAndroid mWindow; + + /** The title of the page to be shared. */ + private final String mTitle; + + /** + * The text to be shared. If both |text| and |url| are supplied, they are concatenated with a + * space. + */ + private final String mText; + + /** The URL of the page to be shared. */ + private final String mUrl; + + /** The common MIME type of the files to be shared. A wildcard if they have differing types. */ + private final String mFileContentType; + + /** The list of Uris of the files to be shared. */ + private final ArrayList mFileUris; + + /** The Uri to the offline MHTML file to be shared. */ + private final Uri mOfflineUri; + + /** The Uri of the screenshot of the page to be shared. */ + private final Uri mScreenshotUri; + + /** + * Optional callback to be called when user makes a choice. Will not be called if receiving a + * response when the user makes a choice is not supported (on older Android versions). + */ + private TargetChosenCallback mCallback; + + private ShareParams(WindowAndroid window, String title, String text, String url, + @Nullable String fileContentType, @Nullable ArrayList fileUris, + @Nullable Uri offlineUri, @Nullable Uri screenshotUri, + @Nullable TargetChosenCallback callback) { + mWindow = window; + mTitle = title; + mText = text; + mUrl = url; + mFileContentType = fileContentType; + mFileUris = fileUris; + mOfflineUri = offlineUri; + mScreenshotUri = screenshotUri; + mCallback = callback; + } + + /** + * @return The window that triggered share. + */ + public WindowAndroid getWindow() { + return mWindow; + } + + /** + * @return The title of the page to be shared. + */ + public String getTitle() { + return mTitle; + } + + /** + * @return The text to be shared. + */ + public String getText() { + return mText; + } + + /** + * @return The URL of the page to be shared. + */ + public String getUrl() { + return mUrl; + } + + /** + * @return The MIME type to the arbitrary files to be shared. + */ + @Nullable + public String getFileContentType() { + return mFileContentType; + } + + /** + * @return The Uri to the arbitrary files to be shared. + */ + @Nullable + public ArrayList getFileUris() { + return mFileUris; + } + + /** + * @return The Uri to the offline MHTML file to be shared. + */ + @Nullable + public Uri getOfflineUri() { + return mOfflineUri; + } + + /** + * @return The Uri of the screenshot of the page to be shared. + */ + @Nullable + public Uri getScreenshotUri() { + return mScreenshotUri; + } + + /** + * @return The callback to be called when user makes a choice. + */ + @Nullable + public TargetChosenCallback getCallback() { + return mCallback; + } + + /** + * @param callback To be called when user makes a choice. + */ + public void setCallback(@Nullable TargetChosenCallback callback) { + mCallback = callback; + } + + /** The builder for {@link ShareParams} objects. */ + public static class Builder { + private WindowAndroid mWindow; + private String mTitle; + private String mText; + private String mUrl; + private String mFileContentType; + private ArrayList mFileUris; + private Uri mOfflineUri; + private Uri mScreenshotUri; + private TargetChosenCallback mCallback; + + public Builder(@NonNull WindowAndroid window, @NonNull String title, @NonNull String url) { + mWindow = window; + mUrl = url; + mTitle = title; + } + + /** + * Sets the text to be shared. + */ + public Builder setText(@NonNull String text) { + mText = text; + return this; + } + + /** + * Sets the MIME type of the arbitrary files to be shared. + */ + public Builder setFileContentType(@NonNull String fileContentType) { + mFileContentType = fileContentType; + return this; + } + + /** + * Sets the Uri of the arbitrary files to be shared. + */ + public Builder setFileUris(@Nullable ArrayList fileUris) { + mFileUris = fileUris; + return this; + } + + /** + * Sets the Uri of the offline MHTML file to be shared. + */ + public Builder setOfflineUri(@Nullable Uri offlineUri) { + mOfflineUri = offlineUri; + return this; + } + + /** + * Sets the Uri of the screenshot of the page to be shared. + */ + public Builder setScreenshotUri(@Nullable Uri screenshotUri) { + mScreenshotUri = screenshotUri; + return this; + } + + /** + * Sets the callback to be called when user makes a choice. + */ + public Builder setCallback(@Nullable TargetChosenCallback callback) { + mCallback = callback; + return this; + } + + /** @return A fully constructed {@link ShareParams} object. */ + public ShareParams build() { + if (!TextUtils.isEmpty(mUrl)) { + mUrl = DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(mUrl); + if (!TextUtils.isEmpty(mText)) { + // Concatenate text and URL with a space. + mText = mText + " " + mUrl; + } else { + mText = mUrl; + } + } + return new ShareParams(mWindow, mTitle, mText, mUrl, mFileContentType, mFileUris, + mOfflineUri, mScreenshotUri, mCallback); + } + } + + /** + * Callback interface for when a target is chosen. + */ + public static interface TargetChosenCallback { + /** + * Called when the user chooses a target in the share dialog. + * + * Note that if the user cancels the share dialog, this callback is never called. + */ + public void onTargetChosen(ComponentName chosenComponent); + + /** + * Called when the user cancels the share dialog. + * + * Guaranteed that either this, or onTargetChosen (but not both) will be called, eventually. + */ + public void onCancel(); + } +} diff --git a/chromium/components/browser_ui/site_settings/DEPS b/chromium/components/browser_ui/site_settings/DEPS index 29ca6c45af1..62df9f80480 100644 --- a/chromium/components/browser_ui/site_settings/DEPS +++ b/chromium/components/browser_ui/site_settings/DEPS @@ -7,6 +7,7 @@ include_rules = [ "+components/embedder_support/android", "+components/location/android", "+components/permissions", + "+components/prefs/android", "+components/subresource_filter/android", "+components/url_formatter/android", "+components/user_prefs", diff --git a/chromium/components/browser_ui/site_settings/android/BUILD.gn b/chromium/components/browser_ui/site_settings/android/BUILD.gn index 894e9a34438..64150f04e4c 100644 --- a/chromium/components/browser_ui/site_settings/android/BUILD.gn +++ b/chromium/components/browser_ui/site_settings/android/BUILD.gn @@ -65,7 +65,6 @@ android_library("java") { "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsFeatureList.java", "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsHelpClient.java", "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPrefClient.java", - "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreference.java", "java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreferenceFragment.java", "java/src/org/chromium/components/browser_ui/site_settings/StorageInfo.java", "java/src/org/chromium/components/browser_ui/site_settings/TriStateSiteSettingsPreference.java", @@ -89,8 +88,10 @@ android_library("java") { "//components/embedder_support/android:util_java", "//components/location/android:location_java", "//components/permissions/android:java", + "//components/prefs/android:java", "//components/subresource_filter/android:java", "//components/url_formatter/android:url_formatter_java", + "//components/user_prefs/android:java", "//content/public/android:content_java", "//services/device/public/java:device_feature_list_java", "//third_party/android_deps:android_support_v7_appcompat_java", @@ -98,6 +99,8 @@ android_library("java") { "//ui/android:ui_full_java", "//ui/android:ui_utils_java", ] + srcjar_deps = + [ "//components/content_settings/android:java_pref_names_srcjar" ] } android_library("javatests") { @@ -105,7 +108,9 @@ android_library("javatests") { sources = [ "javatests/src/org/chromium/components/browser_ui/site_settings/WebsiteAddressTest.java" ] deps = [ ":java", + "//base:base_java", "//base:base_java_test_support", + "//third_party/junit", ] } @@ -182,6 +187,7 @@ android_resources("java_resources") { "java/res/drawable-xxxhdpi/settings_usb.png", "java/res/drawable-xxxhdpi/web_asset.png", "java/res/drawable/ic_person_24dp.xml", + "java/res/drawable/settings_bluetooth.xml", "java/res/layout/add_site_dialog.xml", "java/res/layout/clear_data_dialog.xml", "java/res/layout/clear_storage.xml", diff --git a/chromium/components/browser_ui/site_settings/android/java/res/drawable/settings_bluetooth.xml b/chromium/components/browser_ui/site_settings/android/java/res/drawable/settings_bluetooth.xml new file mode 100644 index 00000000000..0fe09abbbc9 --- /dev/null +++ b/chromium/components/browser_ui/site_settings/android/java/res/drawable/settings_bluetooth.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/chromium/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml b/chromium/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml index 9f20c01689b..b9b237e08e1 100644 --- a/chromium/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml +++ b/chromium/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml @@ -7,94 +7,98 @@ - - - - - - - - - - - - - - - - - - + + - - - diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ContentSettingsResources.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ContentSettingsResources.java index a8471043b22..89b1ee327c6 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ContentSettingsResources.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ContentSettingsResources.java @@ -118,6 +118,16 @@ public class ContentSettingsResources { R.string.background_sync_permission_title, ContentSettingValues.ALLOW, ContentSettingValues.BLOCK, R.string.website_settings_category_allowed_recommended, 0)); + localMap.put(ContentSettingsType.BLUETOOTH_CHOOSER_DATA, + new ResourceItem(R.drawable.settings_bluetooth, 0, 0, ContentSettingValues.ASK, + ContentSettingValues.BLOCK, 0, 0)); + localMap.put(ContentSettingsType.BLUETOOTH_GUARD, + new ResourceItem(R.drawable.settings_bluetooth, + R.string.website_settings_bluetooth, + R.string.website_settings_bluetooth, ContentSettingValues.ASK, + ContentSettingValues.BLOCK, + R.string.website_settings_category_bluetooth_ask, + R.string.website_settings_category_bluetooth_blocked)); localMap.put(ContentSettingsType.BLUETOOTH_SCANNING, new ResourceItem(R.drawable.ic_bluetooth_searching_black_24dp, R.string.website_settings_bluetooth_scanning, diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/PermissionInfo.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/PermissionInfo.java index 488c062dc20..8f11649a727 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/PermissionInfo.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/PermissionInfo.java @@ -20,7 +20,7 @@ import java.lang.annotation.RetentionPolicy; */ public class PermissionInfo implements Serializable { @IntDef({Type.AUGMENTED_REALITY, Type.CAMERA, Type.CLIPBOARD, Type.GEOLOCATION, Type.MICROPHONE, - Type.MIDI, Type.NOTIFICATION, Type.PROTECTED_MEDIA_IDENTIFIER, Type.SENSORS, + Type.MIDI, Type.NFC, Type.NOTIFICATION, Type.PROTECTED_MEDIA_IDENTIFIER, Type.SENSORS, Type.VIRTUAL_REALITY}) @Retention(RetentionPolicy.SOURCE) public @interface Type { @@ -45,15 +45,22 @@ public class PermissionInfo implements Serializable { } private final boolean mIsIncognito; + private final boolean mIsEmbargoed; private final String mEmbedder; private final String mOrigin; private final @Type int mType; public PermissionInfo(@Type int type, String origin, String embedder, boolean isIncognito) { + this(type, origin, embedder, isIncognito, false); + } + + public PermissionInfo(@Type int type, String origin, String embedder, boolean isIncognito, + boolean isEmbargoed) { mOrigin = origin; mEmbedder = embedder; mIsIncognito = isIncognito; mType = type; + mIsEmbargoed = isEmbargoed; } public @Type int getType() { @@ -76,6 +83,10 @@ public class PermissionInfo implements Serializable { return mEmbedder != null ? mEmbedder : mOrigin; } + public boolean isEmbargoed() { + return mIsEmbargoed; + } + /** * Returns the ContentSetting value for this origin. */ @@ -205,4 +216,36 @@ public class PermissionInfo implements Serializable { return ContentSettingsType.DEFAULT; } } + + /** + * If conversion is not possible, method returns null. + */ + public static @Type Integer getPermissionInfoType(@ContentSettingsType int type) { + switch (type) { + case ContentSettingsType.AR: + return Type.AUGMENTED_REALITY; + case ContentSettingsType.MEDIASTREAM_CAMERA: + return Type.CAMERA; + case ContentSettingsType.CLIPBOARD_READ_WRITE: + return Type.CLIPBOARD; + case ContentSettingsType.GEOLOCATION: + return Type.GEOLOCATION; + case ContentSettingsType.MEDIASTREAM_MIC: + return Type.MICROPHONE; + case ContentSettingsType.MIDI_SYSEX: + return Type.MIDI; + case ContentSettingsType.NFC: + return Type.NFC; + case ContentSettingsType.NOTIFICATIONS: + return Type.NOTIFICATION; + case ContentSettingsType.PROTECTED_MEDIA_IDENTIFIER: + return Type.PROTECTED_MEDIA_IDENTIFIER; + case ContentSettingsType.SENSORS: + return Type.SENSORS; + case ContentSettingsType.VR: + return Type.VIRTUAL_REALITY; + default: + return null; + } + } } diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java index 68503a37405..6ab6038f9c0 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java @@ -6,6 +6,8 @@ package org.chromium.components.browser_ui.site_settings; import static org.chromium.components.browser_ui.settings.SearchUtils.handleSearchNavigation; import static org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge.SITE_WILDCARD; +import static org.chromium.components.content_settings.PrefNames.BLOCK_THIRD_PARTY_COOKIES; +import static org.chromium.components.content_settings.PrefNames.COOKIE_CONTROLS_MODE; import android.content.Context; import android.content.DialogInterface; @@ -53,6 +55,8 @@ import org.chromium.components.content_settings.ContentSettingsType; import org.chromium.components.content_settings.CookieControlsMode; import org.chromium.components.embedder_support.browser_context.BrowserContextHandle; import org.chromium.components.embedder_support.util.UrlUtilities; +import org.chromium.components.prefs.PrefService; +import org.chromium.components.user_prefs.UserPrefs; import org.chromium.ui.widget.Toast; import java.util.ArrayList; @@ -518,6 +522,7 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment public boolean onPreferenceChange(Preference preference, Object newValue) { BrowserContextHandle browserContextHandle = getSiteSettingsClient().getBrowserContextHandle(); + PrefService prefService = UserPrefs.get(browserContextHandle); if (BINARY_TOGGLE_KEY.equals(preference.getKey())) { assert !mCategory.isManaged(); @@ -551,7 +556,7 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment setCookieSettingsPreference((CookieSettingsState) newValue); getInfoForOrigins(); } else if (THIRD_PARTY_COOKIES_TOGGLE_KEY.equals(preference.getKey())) { - getPrefs().setBlockThirdPartyCookies((boolean) newValue); + prefService.setBoolean(BLOCK_THIRD_PARTY_COOKIES, (boolean) newValue); } else if (NOTIFICATIONS_VIBRATE_TOGGLE_KEY.equals(preference.getKey())) { getPrefs().setNotificationsVibrateEnabled((boolean) newValue); } else if (NOTIFICATIONS_QUIET_UI_TOGGLE_KEY.equals(preference.getKey())) { @@ -595,8 +600,10 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment WebsitePreferenceBridge.setCategoryEnabled( getSiteSettingsClient().getBrowserContextHandle(), ContentSettingsType.COOKIES, allowCookies); - getPrefs().setCookieControlsMode(mode); - getPrefs().setBlockThirdPartyCookies(mode == CookieControlsMode.BLOCK_THIRD_PARTY); + PrefService prefService = UserPrefs.get(getSiteSettingsClient().getBrowserContextHandle()); + prefService.setInteger(COOKIE_CONTROLS_MODE, mode); + prefService.setBoolean( + BLOCK_THIRD_PARTY_COOKIES, mode == CookieControlsMode.BLOCK_THIRD_PARTY); } private boolean cookieSettingsExceptionShouldBlock() { @@ -865,7 +872,7 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment for (Website site : sites) { for (ChosenObjectInfo info : site.getChosenObjectInfo()) { if (mSearch == null || mSearch.isEmpty() - || info.getName().toLowerCase().contains(mSearch)) { + || info.getName().toLowerCase(Locale.getDefault()).contains(mSearch)) { Pair, ArrayList> entry = objects.get(info.getObject()); if (entry == null) { @@ -890,8 +897,8 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment extras.putString(EXTRA_TITLE, getActivity().getTitle().toString()); extras.putSerializable(ChosenObjectSettings.EXTRA_OBJECT_INFOS, entry.first); extras.putSerializable(ChosenObjectSettings.EXTRA_SITES, entry.second); - preference.setIcon( - ContentSettingsResources.getIcon(mCategory.getContentSettingsType())); + preference.setIcon(SettingsUtils.getTintedIcon(getActivity(), + ContentSettingsResources.getIcon(mCategory.getContentSettingsType()))); preference.setTitle(entry.first.get(0).getName()); preference.setFragment(ChosenObjectSettings.class.getCanonicalName()); getPreferenceScreen().addPreference(preference); @@ -1016,7 +1023,8 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment } // Only show the link that explains protected content settings when needed. - if (mCategory.showSites(SiteSettingsCategory.Type.PROTECTED_MEDIA)) { + if (mCategory.showSites(SiteSettingsCategory.Type.PROTECTED_MEDIA) + && getSiteSettingsClient().getSiteSettingsHelpClient().isHelpAndFeedbackEnabled()) { explainProtectedMediaKey.setOnPreferenceClickListener(preference -> { getSiteSettingsClient() .getSiteSettingsHelpClient() @@ -1075,10 +1083,12 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment new FourStateCookieSettingsPreference.Params(); params.allowCookies = WebsitePreferenceBridge.isCategoryEnabled( getSiteSettingsClient().getBrowserContextHandle(), ContentSettingsType.COOKIES); - params.blockThirdPartyCookies = getPrefs().getBlockThirdPartyCookies(); - params.cookieControlsMode = getPrefs().getCookieControlsMode(); + PrefService prefService = UserPrefs.get(getSiteSettingsClient().getBrowserContextHandle()); + params.blockThirdPartyCookies = prefService.getBoolean(BLOCK_THIRD_PARTY_COOKIES); + params.cookieControlsMode = prefService.getInteger(COOKIE_CONTROLS_MODE); params.cookiesContentSettingEnforced = mCategory.isManaged(); - params.thirdPartyBlockingEnforced = getPrefs().isBlockThirdPartyCookiesManaged(); + params.thirdPartyBlockingEnforced = + prefService.isManagedPreference(BLOCK_THIRD_PARTY_COOKIES); fourStateCookieToggle.setState(params); } @@ -1125,14 +1135,15 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment ChromeBaseCheckBoxPreference thirdPartyCookiesPref = (ChromeBaseCheckBoxPreference) getPreferenceScreen().findPreference( THIRD_PARTY_COOKIES_TOGGLE_KEY); - thirdPartyCookiesPref.setChecked(getPrefs().getBlockThirdPartyCookies()); + PrefService prefService = UserPrefs.get(getSiteSettingsClient().getBrowserContextHandle()); + thirdPartyCookiesPref.setChecked(prefService.getBoolean(BLOCK_THIRD_PARTY_COOKIES)); thirdPartyCookiesPref.setEnabled(WebsitePreferenceBridge.isCategoryEnabled( getSiteSettingsClient().getBrowserContextHandle(), ContentSettingsType.COOKIES)); thirdPartyCookiesPref.setManagedPreferenceDelegate(new ForwardingManagedPreferenceDelegate( getSiteSettingsClient().getManagedPreferenceDelegate()) { @Override public boolean isPreferenceControlledByPolicy(Preference preference) { - return getPrefs().isBlockThirdPartyCookiesManaged(); + return prefService.isManagedPreference(BLOCK_THIRD_PARTY_COOKIES); } }); } diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java index edcf1a9ebb9..c1a5bb603e6 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java @@ -350,7 +350,9 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment } else if (i == ContentSettingException.Type.JAVASCRIPT) { setUpJavascriptPreference(preference); } else { - setUpListPreference(preference, mSite.getContentSettingPermission(i)); + // ContentSettingException can not be embargoed. + setUpListPreference(preference, mSite.getContentSettingPermission(i), + false /* isEmbargoed */); } return; } @@ -362,17 +364,23 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment if (i == PermissionInfo.Type.GEOLOCATION) { setUpLocationPreference(preference); } else if (i == PermissionInfo.Type.NOTIFICATION) { - setUpNotificationsPreference(preference); + setUpNotificationsPreference( + preference, isPermissionEmbargoed(PermissionInfo.Type.NOTIFICATION)); } else { setUpListPreference(preference, mSite.getPermission( - getSiteSettingsClient().getBrowserContextHandle(), i)); + getSiteSettingsClient().getBrowserContextHandle(), i), + isPermissionEmbargoed(i)); } return; } } } + private boolean isPermissionEmbargoed(@PermissionInfo.Type int type) { + return mSite.getPermissionInfo(type) != null && mSite.getPermissionInfo(type).isEmbargoed(); + } + private void setUpClearDataPreference(ClearWebsiteStorage preference) { long usage = mSite.getTotalUsage(); if (usage > 0) { @@ -452,7 +460,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment }); } - private void setUpNotificationsPreference(Preference preference) { + private void setUpNotificationsPreference(Preference preference, boolean isEmbargoed) { WebappSettingsClient client = getSiteSettingsClient().getWebappSettingsClient(); Origin origin = Origin.create(mSite.getAddress().getOrigin()); if (origin != null) { @@ -482,14 +490,15 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment getPreferenceScreen().removePreference(preference); return; } - String overrideSummary; if (isPermissionControlledByDSE(ContentSettingsType.NOTIFICATIONS)) { overrideSummary = getString(value != null && value == ContentSettingValues.ALLOW ? R.string.website_settings_permissions_allow_dse : R.string.website_settings_permissions_block_dse); } else { - overrideSummary = getString(ContentSettingsResources.getSiteSummary(value)); + overrideSummary = isEmbargoed + ? getString(R.string.automatically_blocked) + : getString(ContentSettingsResources.getSiteSummary(value)); } // On Android O this preference is read-only, so we replace the existing pref with a @@ -503,7 +512,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment return true; }); } else { - setUpListPreference(preference, value); + setUpListPreference(preference, value, isEmbargoed); if (isPermissionControlledByDSE(ContentSettingsType.NOTIFICATIONS) && value != null) { updatePreferenceForDSESetting(preference); } @@ -512,14 +521,9 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment // This is implemented as a public utility function to better facilitate testing. public void launchOsChannelSettingsFromPreference(Preference preference) { - final boolean blockedByEmbargo = - (WebsitePreferenceBridgeJni.get().isNotificationEmbargoedForOrigin( - getSiteSettingsClient().getBrowserContextHandle(), - mSite.getAddress().getOrigin())); - // There is no notification channel if the origin is merely embargoed. Create it // just-in-time if the user tries to change to setting. - if (blockedByEmbargo) { + if (isPermissionEmbargoed(PermissionInfo.Type.NOTIFICATION)) { mSite.setPermission(getSiteSettingsClient().getBrowserContextHandle(), PermissionInfo.Type.NOTIFICATION, ContentSettingValues.BLOCK); } @@ -567,7 +571,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment findPreference(PERMISSION_PREFERENCE_KEYS[PermissionInfo.Type.NOTIFICATION + ContentSettingException.Type.NUM_ENTRIES]); if (notificationsPreference != null) { - setUpNotificationsPreference(notificationsPreference); + setUpNotificationsPreference(notificationsPreference, false /* isEmbargoed */); } // To ensure UMA receives notification revocations, we detect if the setting has changed @@ -608,7 +612,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment new ChromeImageViewPreference(getStyledContext()); preference.setKey(CHOOSER_PERMISSION_PREFERENCE_KEY); - preference.setIcon(ContentSettingsResources.getIcon(info.getContentSettingsType())); + preference.setIcon(SettingsUtils.getTintedIcon(getActivity(), + ContentSettingsResources.getIcon(info.getContentSettingsType()))); preference.setOrder(maxPermissionOrder); preference.setTitle(info.getName()); preference.setImageView(R.drawable.ic_delete_white_24dp, @@ -745,8 +750,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment * @param preference The ListPreference to initialize. * @param value The value to initialize it to. */ - private void setUpListPreference( - Preference preference, @ContentSettingValues @Nullable Integer value) { + private void setUpListPreference(Preference preference, + @ContentSettingValues @Nullable Integer value, boolean isEmbargoed) { if (value == null) { getPreferenceScreen().removePreference(preference); return; @@ -764,12 +769,12 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment getString(ContentSettingsResources.getSiteSummary(ContentSettingValues.BLOCK)); listPreference.setEntryValues(keys); listPreference.setEntries(descriptions); + listPreference.setOnPreferenceChangeListener(this); + listPreference.setSummary(isEmbargoed ? getString(R.string.automatically_blocked) : "%s"); // TODO(crbug.com/735110): Figure out if this is the correct thing to do - here we are // effectively treating non-ALLOW values as BLOCK. int index = (value == ContentSettingValues.ALLOW ? 0 : 1); listPreference.setValueIndex(index); - listPreference.setOnPreferenceChangeListener(this); - listPreference.setSummary("%s"); } /** @@ -803,7 +808,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment @Nullable Integer permission = mSite.getPermission( getSiteSettingsClient().getBrowserContextHandle(), PermissionInfo.Type.GEOLOCATION); - setUpListPreference(preference, permission); + setUpListPreference( + preference, permission, isPermissionEmbargoed(PermissionInfo.Type.GEOLOCATION)); if (isPermissionControlledByDSE(ContentSettingsType.GEOLOCATION) && permission != null) { updatePreferenceForDSESetting(preference); } @@ -823,7 +829,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment ? ContentSettingValues.ALLOW : ContentSettingValues.BLOCK; } - setUpListPreference(preference, currentValue); + // Not possible to embargo SOUND. + setUpListPreference(preference, currentValue, false /* isEmbargoed */); } private void setUpJavascriptPreference(Preference preference) { @@ -839,7 +846,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment ContentSettingsType.JAVASCRIPT)) { currentValue = ContentSettingValues.BLOCK; } - setUpListPreference(preference, currentValue); + // Not possible to embargo JAVASCRIPT. + setUpListPreference(preference, currentValue, false /* isEmbargoed */); } /** @@ -852,7 +860,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment private void setUpAdsPreference(Preference preference) { // Do not show the setting if the category is not enabled. if (!SiteSettingsCategory.adsCategoryEnabled()) { - setUpListPreference(preference, null); + setUpListPreference(preference, null, false); return; } // If the ad blocker is activated, then this site will have ads blocked unless there is an @@ -867,7 +875,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment // If the site is not considered a candidate for blocking, do the standard thing and remove // the preference. if (permission == null && !activated) { - setUpListPreference(preference, null); + setUpListPreference(preference, null, false); return; } @@ -880,7 +888,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment ? ContentSettingValues.ALLOW : ContentSettingValues.BLOCK; } - setUpListPreference(preference, permission); + // Not possible to embargo ADS. + setUpListPreference(preference, permission, false /* isEmbargoed */); // The subresource filter permission has a custom BLOCK string. ListPreference listPreference = (ListPreference) preference; @@ -935,6 +944,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment public boolean onPreferenceChange(Preference preference, Object newValue) { @ContentSettingValues int permission = ContentSetting.fromString((String) newValue); + // Embargoed permission preserves summary. Refresh it manually. + preference.setSummary("%s"); BrowserContextHandle browserContextHandle = getSiteSettingsClient().getBrowserContextHandle(); for (int i = 0; i < PERMISSION_PREFERENCE_KEYS.length; i++) { diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java index a03499bd5ac..2c75d1125f0 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java @@ -4,6 +4,8 @@ package org.chromium.components.browser_ui.site_settings; +import static org.chromium.components.content_settings.PrefNames.BLOCK_THIRD_PARTY_COOKIES; + import android.os.Bundle; import androidx.preference.Preference; @@ -12,6 +14,7 @@ import org.chromium.components.browser_ui.settings.SettingsUtils; import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory.Type; import org.chromium.components.content_settings.ContentSettingValues; import org.chromium.components.embedder_support.browser_context.BrowserContextHandle; +import org.chromium.components.user_prefs.UserPrefs; /** * The main Site Settings screen, which shows all the site settings categories: All sites, Location, @@ -96,9 +99,7 @@ public class SiteSettings // Show 'disabled' message when permission is not granted in Android. p.setSummary(ContentSettingsResources.getCategorySummary(contentType, false)); } else if (Type.COOKIES == prefCategory && checked - && getSiteSettingsClient() - .getSiteSettingsPrefClient() - .getBlockThirdPartyCookies()) { + && UserPrefs.get(browserContextHandle).getBoolean(BLOCK_THIRD_PARTY_COOKIES)) { p.setSummary(ContentSettingsResources.getCookieAllowedExceptThirdPartySummary()); } else if (Type.DEVICE_LOCATION == prefCategory && checked && WebsitePreferenceBridge.isLocationAllowedByPolicy(browserContextHandle)) { diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java index f38e075c2a1..ae11552e0ed 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java @@ -39,10 +39,10 @@ import java.lang.annotation.RetentionPolicy; */ public class SiteSettingsCategory { @IntDef({Type.ALL_SITES, Type.ADS, Type.AUGMENTED_REALITY, Type.AUTOMATIC_DOWNLOADS, - Type.BACKGROUND_SYNC, Type.BLUETOOTH_SCANNING, Type.CAMERA, Type.CLIPBOARD, - Type.COOKIES, Type.DEVICE_LOCATION, Type.JAVASCRIPT, Type.MICROPHONE, Type.NFC, - Type.NOTIFICATIONS, Type.POPUPS, Type.PROTECTED_MEDIA, Type.SENSORS, Type.SOUND, - Type.USB, Type.VIRTUAL_REALITY, Type.USE_STORAGE}) + Type.BACKGROUND_SYNC, Type.BLUETOOTH, Type.BLUETOOTH_SCANNING, Type.CAMERA, + Type.CLIPBOARD, Type.COOKIES, Type.DEVICE_LOCATION, Type.JAVASCRIPT, Type.MICROPHONE, + Type.NFC, Type.NOTIFICATIONS, Type.POPUPS, Type.PROTECTED_MEDIA, Type.SENSORS, + Type.SOUND, Type.USB, Type.VIRTUAL_REALITY, Type.USE_STORAGE}) @Retention(RetentionPolicy.SOURCE) public @interface Type { // Values used to address array index - should be enumerated from 0 and can't have gaps. @@ -67,12 +67,13 @@ public class SiteSettingsCategory { int SENSORS = 16; int SOUND = 17; int USB = 18; - int VIRTUAL_REALITY = 19; - int USE_STORAGE = 20; // Always last as it should appear in the UI at the bottom. + int BLUETOOTH = 19; + int VIRTUAL_REALITY = 20; + int USE_STORAGE = 21; // Always last as it should appear in the UI at the bottom. /** * Number of handled categories used for calculating array sizes. */ - int NUM_ENTRIES = 21; + int NUM_ENTRIES = 22; } private final BrowserContextHandle mBrowserContextHandle; @@ -156,6 +157,8 @@ public class SiteSettingsCategory { return ContentSettingsType.AUTOMATIC_DOWNLOADS; case Type.BACKGROUND_SYNC: return ContentSettingsType.BACKGROUND_SYNC; + case Type.BLUETOOTH: + return ContentSettingsType.BLUETOOTH_GUARD; case Type.BLUETOOTH_SCANNING: return ContentSettingsType.BLUETOOTH_SCANNING; case Type.CAMERA: @@ -201,6 +204,8 @@ public class SiteSettingsCategory { switch (type) { case ContentSettingsType.USB_GUARD: return ContentSettingsType.USB_CHOOSER_DATA; + case ContentSettingsType.BLUETOOTH_GUARD: + return ContentSettingsType.BLUETOOTH_CHOOSER_DATA; default: return -1; // Conversion unavailable. } @@ -221,6 +226,8 @@ public class SiteSettingsCategory { return "automatic_downloads"; case Type.BACKGROUND_SYNC: return "background_sync"; + case Type.BLUETOOTH: + return "bluetooth"; case Type.BLUETOOTH_SCANNING: return "bluetooth_scanning"; case Type.CAMERA: diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPrefClient.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPrefClient.java index ab24de08ddb..605da219084 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPrefClient.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPrefClient.java @@ -12,13 +12,6 @@ package org.chromium.components.browser_ui.site_settings; */ // TODO(crbug.com/1071603): Remove this once PrefServiceBridge is componentized. public interface SiteSettingsPrefClient { - boolean getBlockThirdPartyCookies(); - void setBlockThirdPartyCookies(boolean newValue); - boolean isBlockThirdPartyCookiesManaged(); - - int getCookieControlsMode(); - void setCookieControlsMode(int newValue); - boolean getEnableQuietNotificationPermissionUi(); void setEnableQuietNotificationPermissionUi(boolean newValue); void clearEnableNotificationPermissionUi(); diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreference.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreference.java deleted file mode 100644 index 0e642c7943d..00000000000 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsPreference.java +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.browser_ui.site_settings; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.ImageView; - -import androidx.core.view.ViewCompat; -import androidx.preference.PreferenceViewHolder; - -import org.chromium.components.browser_ui.settings.ChromeBasePreference; - -/** - * A custom preference for drawing Site Settings entries. - */ -public class SiteSettingsPreference extends ChromeBasePreference { - /** - * Constructor for inflating from XML. - */ - public SiteSettingsPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public void onBindViewHolder(PreferenceViewHolder holder) { - super.onBindViewHolder(holder); - - int padding = getContext().getResources().getDimensionPixelSize(R.dimen.pref_icon_padding); - ImageView icon = (ImageView) holder.findViewById(android.R.id.icon); - ViewCompat.setPaddingRelative( - icon, padding, icon.getPaddingTop(), 0, icon.getPaddingBottom()); - } -} diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java index f6ba7418ff3..bddd75f7b38 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java @@ -131,21 +131,37 @@ public class WebsitePermissionsFetcher { queue.add(new PermissionInfoFetcher(PermissionInfo.Type.CLIPBOARD)); // Sensors permission is per-origin. queue.add(new PermissionInfoFetcher(PermissionInfo.Type.SENSORS)); + + // There are two Bluetooth related permissions: Bluetooth scanning and + // Bluetooth guard. + // + // The Bluetooth Scanning permission controls access to the Web Bluetooth + // Scanning API, which enables sites to scan for and receive events for + // advertisement packets received from nearby Bluetooth devices. CommandLine commandLine = CommandLine.getInstance(); if (commandLine.hasSwitch(ContentSwitches.ENABLE_EXPERIMENTAL_WEB_PLATFORM_FEATURES)) { // Bluetooth scanning permission is per-origin. queue.add(new ExceptionInfoFetcher(ContentSettingsType.BLUETOOTH_SCANNING)); } + // The Bluetooth guard permission controls access to the Web Bluetooth + // API, which enables sites to request access to connect to specific + // Bluetooth devices. Users are presented with a chooser prompt in which + // they must select the Bluetooth device that they would like to allow + // the site to connect to. Therefore, this permission also displays a + // list of permitted Bluetooth devices that each site can connect to. + if (ContentFeatureList.isEnabled( + ContentFeatureList.WEB_BLUETOOTH_NEW_PERMISSIONS_BACKEND)) { + // Bluetooth device permission is per-origin and per-embedder. + queue.add(new ChooserExceptionInfoFetcher(ContentSettingsType.BLUETOOTH_GUARD)); + } if (ContentFeatureList.isEnabled(ContentFeatureList.WEB_NFC)) { // NFC permission is per-origin and per-embedder. queue.add(new PermissionInfoFetcher(PermissionInfo.Type.NFC)); } - if (ContentFeatureList.isEnabled(ContentFeatureList.WEBXR_PERMISSIONS_API)) { - // VIRTUAL_REALITY permission is per-origin and per-embedder. - queue.add(new PermissionInfoFetcher(PermissionInfo.Type.VIRTUAL_REALITY)); - // AR permission is per-origin and per-embedder. - queue.add(new PermissionInfoFetcher(PermissionInfo.Type.AUGMENTED_REALITY)); - } + // VIRTUAL_REALITY permission is per-origin and per-embedder. + queue.add(new PermissionInfoFetcher(PermissionInfo.Type.VIRTUAL_REALITY)); + // AR permission is per-origin and per-embedder. + queue.add(new PermissionInfoFetcher(PermissionInfo.Type.AUGMENTED_REALITY)); queue.add(new PermissionsAvailableCallbackRunner(callback)); @@ -215,6 +231,9 @@ public class WebsitePermissionsFetcher { } else if (category.showSites(SiteSettingsCategory.Type.USB)) { // USB device permission is per-origin. queue.add(new ChooserExceptionInfoFetcher(ContentSettingsType.USB_GUARD)); + } else if (category.showSites(SiteSettingsCategory.Type.BLUETOOTH)) { + // Bluetooth device permission is per-origin. + queue.add(new ChooserExceptionInfoFetcher(ContentSettingsType.BLUETOOTH_GUARD)); } else if (category.showSites(SiteSettingsCategory.Type.CLIPBOARD)) { // Clipboard permission is per-origin. queue.add(new PermissionInfoFetcher(PermissionInfo.Type.CLIPBOARD)); @@ -233,15 +252,11 @@ public class WebsitePermissionsFetcher { queue.add(new PermissionInfoFetcher(PermissionInfo.Type.NFC)); } } else if (category.showSites(SiteSettingsCategory.Type.VIRTUAL_REALITY)) { - if (ContentFeatureList.isEnabled(ContentFeatureList.WEBXR_PERMISSIONS_API)) { - // VIRTUAL_REALITY permission is per-origin and per-embedder. - queue.add(new PermissionInfoFetcher(PermissionInfo.Type.VIRTUAL_REALITY)); - } + // VIRTUAL_REALITY permission is per-origin and per-embedder. + queue.add(new PermissionInfoFetcher(PermissionInfo.Type.VIRTUAL_REALITY)); } else if (category.showSites(SiteSettingsCategory.Type.AUGMENTED_REALITY)) { - if (ContentFeatureList.isEnabled(ContentFeatureList.WEBXR_PERMISSIONS_API)) { - // AUGMENTED_REALITY permission is per-origin and per-embedder. - queue.add(new PermissionInfoFetcher(PermissionInfo.Type.AUGMENTED_REALITY)); - } + // AUGMENTED_REALITY permission is per-origin and per-embedder. + queue.add(new PermissionInfoFetcher(PermissionInfo.Type.AUGMENTED_REALITY)); } queue.add(new PermissionsAvailableCallbackRunner(callback)); queue.next(); diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreference.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreference.java index a307e6550f1..71b2b84ec8b 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreference.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreference.java @@ -86,8 +86,18 @@ class WebsitePreference extends ChromeImageViewPreference { private void refresh() { setTitle(mSite.getTitle()); - if (mSite.getEmbedder() == null) return; - + if (mSite.getEmbedder() == null) { + @PermissionInfo.Type + Integer permissionInfoType = + PermissionInfo.getPermissionInfoType(mCategory.getContentSettingsType()); + PermissionInfo permissionInfo = null; + if (permissionInfoType != null + && (permissionInfo = mSite.getPermissionInfo(permissionInfoType)) != null + && permissionInfo.isEmbargoed()) { + setSummary(getContext().getString(R.string.automatically_blocked)); + } + return; + } String subtitleText; if (mSite.representsThirdPartiesOnSite()) { subtitleText = getContext().getString( diff --git a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java index f8c470922aa..b40b44ea050 100644 --- a/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java +++ b/chromium/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java @@ -77,7 +77,7 @@ public class WebsitePreferenceBridge { } private static void insertInfoIntoList(@PermissionInfo.Type int type, - ArrayList list, String origin, String embedder) { + ArrayList list, String origin, String embedder, boolean isEmbargoed) { if (type == PermissionInfo.Type.CAMERA || type == PermissionInfo.Type.MICROPHONE) { for (PermissionInfo info : list) { if (info.getOrigin().equals(origin) && info.getEmbedder().equals(embedder)) { @@ -85,67 +85,69 @@ public class WebsitePreferenceBridge { } } } - list.add(new PermissionInfo(type, origin, embedder, false)); + list.add(new PermissionInfo(type, origin, embedder, false, isEmbargoed)); } @CalledByNative private static void insertArInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.AUGMENTED_REALITY, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList( + PermissionInfo.Type.AUGMENTED_REALITY, list, origin, embedder, isEmbargoed); } @CalledByNative private static void insertCameraInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.CAMERA, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList(PermissionInfo.Type.CAMERA, list, origin, embedder, isEmbargoed); } @CalledByNative private static void insertClipboardInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.CLIPBOARD, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList(PermissionInfo.Type.CLIPBOARD, list, origin, embedder, isEmbargoed); } @CalledByNative private static void insertGeolocationInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.GEOLOCATION, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList(PermissionInfo.Type.GEOLOCATION, list, origin, embedder, isEmbargoed); } @CalledByNative private static void insertMicrophoneInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.MICROPHONE, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList(PermissionInfo.Type.MICROPHONE, list, origin, embedder, isEmbargoed); } @CalledByNative private static void insertMidiInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.MIDI, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList(PermissionInfo.Type.MIDI, list, origin, embedder, isEmbargoed); } @CalledByNative private static void insertNfcInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.NFC, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList(PermissionInfo.Type.NFC, list, origin, embedder, isEmbargoed); } @CalledByNative private static void insertNotificationIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.NOTIFICATION, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList(PermissionInfo.Type.NOTIFICATION, list, origin, embedder, isEmbargoed); } @CalledByNative private static void insertProtectedMediaIdentifierInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.PROTECTED_MEDIA_IDENTIFIER, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList(PermissionInfo.Type.PROTECTED_MEDIA_IDENTIFIER, list, origin, embedder, + isEmbargoed); } @CalledByNative private static void insertSensorsInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.SENSORS, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList(PermissionInfo.Type.SENSORS, list, origin, embedder, isEmbargoed); } @CalledByNative @@ -156,8 +158,9 @@ public class WebsitePreferenceBridge { @CalledByNative private static void insertVrInfoIntoList( - ArrayList list, String origin, String embedder) { - insertInfoIntoList(PermissionInfo.Type.VIRTUAL_REALITY, list, origin, embedder); + ArrayList list, String origin, String embedder, boolean isEmbargoed) { + insertInfoIntoList( + PermissionInfo.Type.VIRTUAL_REALITY, list, origin, embedder, isEmbargoed); } @CalledByNative @@ -354,6 +357,7 @@ public class WebsitePreferenceBridge { switch (contentSettingsType) { case ContentSettingsType.ADS: + case ContentSettingsType.BLUETOOTH_GUARD: case ContentSettingsType.BLUETOOTH_SCANNING: case ContentSettingsType.JAVASCRIPT: case ContentSettingsType.POPUPS: @@ -422,6 +426,9 @@ public class WebsitePreferenceBridge { case ContentSettingsType.POPUPS: // Returns true if websites are allowed to request permission to access USB devices. case ContentSettingsType.USB_GUARD: + // Returns true if websites are allowed to request permission to access Bluetooth + // devices. + case ContentSettingsType.BLUETOOTH_GUARD: case ContentSettingsType.BLUETOOTH_SCANNING: return isContentSettingEnabled(browserContextHandle, contentSettingsType); case ContentSettingsType.AR: diff --git a/chromium/components/browser_ui/site_settings/android/javatests/src/org/chromium/components/browser_ui/site_settings/WebsiteAddressTest.java b/chromium/components/browser_ui/site_settings/android/javatests/src/org/chromium/components/browser_ui/site_settings/WebsiteAddressTest.java index a799614c5a5..bf9d8e381a7 100644 --- a/chromium/components/browser_ui/site_settings/android/javatests/src/org/chromium/components/browser_ui/site_settings/WebsiteAddressTest.java +++ b/chromium/components/browser_ui/site_settings/android/javatests/src/org/chromium/components/browser_ui/site_settings/WebsiteAddressTest.java @@ -4,7 +4,7 @@ package org.chromium.components.browser_ui.site_settings; -import android.support.test.filters.SmallTest; +import androidx.test.filters.SmallTest; import org.junit.Assert; import org.junit.Before; diff --git a/chromium/components/browser_ui/site_settings/android/site_settings_feature_list.cc b/chromium/components/browser_ui/site_settings/android/site_settings_feature_list.cc index ce85744a8b5..70c94e7c417 100644 --- a/chromium/components/browser_ui/site_settings/android/site_settings_feature_list.cc +++ b/chromium/components/browser_ui/site_settings/android/site_settings_feature_list.cc @@ -4,6 +4,7 @@ #include "base/android/jni_string.h" #include "base/feature_list.h" +#include "base/notreached.h" #include "base/stl_util.h" #include "components/browser_ui/site_settings/android/features.h" diff --git a/chromium/components/browser_ui/site_settings/android/storage_info_fetcher.cc b/chromium/components/browser_ui/site_settings/android/storage_info_fetcher.cc index f11a27d6198..14ff1dfe39c 100644 --- a/chromium/components/browser_ui/site_settings/android/storage_info_fetcher.cc +++ b/chromium/components/browser_ui/site_settings/android/storage_info_fetcher.cc @@ -5,7 +5,6 @@ #include "components/browser_ui/site_settings/android/storage_info_fetcher.h" #include "base/bind.h" -#include "base/task/post_task.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -33,8 +32,8 @@ void StorageInfoFetcher::FetchStorageInfo(FetchCallback fetch_callback) { // QuotaManager must be called on IO thread, but the callback must then be // called on the UI thread. - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &StorageInfoFetcher::GetUsageInfo, this, base::BindOnce(&StorageInfoFetcher::OnGetUsageInfoInternal, this))); @@ -49,8 +48,8 @@ void StorageInfoFetcher::ClearStorage(const std::string& host, clear_callback_ = std::move(clear_callback); type_to_delete_ = type; - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &storage::QuotaManager::DeleteHostData, quota_manager_, host, type, storage::AllQuotaClientTypes(), @@ -68,8 +67,8 @@ void StorageInfoFetcher::OnGetUsageInfoInternal( entries_ = std::move(entries); - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&StorageInfoFetcher::OnFetchCompleted, this)); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&StorageInfoFetcher::OnFetchCompleted, this)); } void StorageInfoFetcher::OnFetchCompleted() { @@ -86,8 +85,8 @@ void StorageInfoFetcher::OnUsageClearedInternal( quota_manager_->ResetUsageTracker(type_to_delete_); - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&StorageInfoFetcher::OnClearCompleted, this, code)); } diff --git a/chromium/components/browser_ui/site_settings/android/website_preference_bridge.cc b/chromium/components/browser_ui/site_settings/android/website_preference_bridge.cc index 8f279ee726e..bfc9188b16c 100644 --- a/chromium/components/browser_ui/site_settings/android/website_preference_bridge.cc +++ b/chromium/components/browser_ui/site_settings/android/website_preference_bridge.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include #include #include @@ -125,7 +126,8 @@ typedef void (*InfoListInsertionFunction)( JNIEnv*, const base::android::JavaRef&, const base::android::JavaRef&, - const base::android::JavaRef&); + const base::android::JavaRef&, + jboolean); void GetOrigins(JNIEnv* env, const JavaParamRef& jbrowser_context_handle, @@ -166,7 +168,8 @@ void GetOrigins(JNIEnv* env, jembedder = ConvertUTF8ToJavaString(env, embedder); seen_origins.push_back(origin); - insertionFunc(env, list, ConvertOriginToJavaString(env, origin), jembedder); + insertionFunc(env, list, ConvertOriginToJavaString(env, origin), jembedder, + /*is_embargoed=*/false); } // Add any origins which have a default content setting value (thus skipped @@ -188,7 +191,7 @@ void GetOrigins(JNIEnv* env, .content_setting == CONTENT_SETTING_BLOCK) { seen_origins.push_back(origin); insertionFunc(env, list, ConvertOriginToJavaString(env, origin), - jembedder); + jembedder, /*is_embargoed=*/true); } } } @@ -1039,7 +1042,8 @@ static jboolean JNI_WebsitePreferenceBridge_IsContentSettingEnabled( type == ContentSettingsType::ADS || type == ContentSettingsType::CLIPBOARD_READ_WRITE || type == ContentSettingsType::USB_GUARD || - type == ContentSettingsType::BLUETOOTH_SCANNING); + type == ContentSettingsType::BLUETOOTH_SCANNING || + type == ContentSettingsType::BLUETOOTH_GUARD); return GetBooleanForContentSetting(jbrowser_context_handle, type); } @@ -1057,12 +1061,14 @@ static void JNI_WebsitePreferenceBridge_SetContentSettingEnabled( type == ContentSettingsType::POPUPS || type == ContentSettingsType::ADS || type == ContentSettingsType::USB_GUARD || - type == ContentSettingsType::BLUETOOTH_SCANNING); + type == ContentSettingsType::BLUETOOTH_SCANNING || + type == ContentSettingsType::BLUETOOTH_GUARD); ContentSetting value = CONTENT_SETTING_BLOCK; if (allow) { if (type == ContentSettingsType::USB_GUARD || - type == ContentSettingsType::BLUETOOTH_SCANNING) { + type == ContentSettingsType::BLUETOOTH_SCANNING || + type == ContentSettingsType::BLUETOOTH_GUARD) { value = CONTENT_SETTING_ASK; } else { value = CONTENT_SETTING_ALLOW; diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings.grd b/chromium/components/browser_ui/strings/android/browser_ui_strings.grd index 7f2795e9058..66257c44a71 100644 --- a/chromium/components/browser_ui/strings/android/browser_ui_strings.grd +++ b/chromium/components/browser_ui/strings/android/browser_ui_strings.grd @@ -192,6 +192,9 @@ Details + + Change + Done @@ -255,6 +258,9 @@ Add + + Change + Share @@ -378,6 +384,9 @@ Help & feedback + + Automatically blocked + @@ -435,6 +444,9 @@ Lite page provided by Google + + To save you data, this page's images have been optimized by Google. + Open Online @@ -472,47 +484,90 @@ Blocked for current search engine - - Cookies - + + Cookies + + + + + %1$sChrome - %2$sAccessing video input + + + A site is using your camera + + + A site is using your microphone + + + A site is using your camera and microphone + + + Tap to return to %1$shttps://apprtc.appspot.com + + + Tap to return to the site + + + Stop + + + Incognito tab + + + Completed downloads + + + Active downloads + + + Playing media + + + Camera and microphone use + + + Sharing your screen + + + + Share via + - - - %1$sChrome - %2$sAccessing video input - - - A site is using your camera - - - A site is using your microphone - - - A site is using your camera and microphone - - - Tap to return to %1$shttps://apprtc.appspot.com - - - Tap to return to the site - - - Stop - - - Incognito tab - - - Completed downloads - - - Active downloads - - - Camera and microphone use - - - Sharing your screen - + + + Play + + + Pause + + + Previous track + + + Next track + + + Seek forward + + + Seek backward + + + A site is playing media + + + + + Swipe down to close. + + + + + Unable to select certificate. + + + Client side certificate selection is not supported by the operating system. + diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_PAGE_INFO_LITE_MODE_HTTPS_IMAGE_COMPRESSION.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_PAGE_INFO_LITE_MODE_HTTPS_IMAGE_COMPRESSION.png.sha1 new file mode 100644 index 00000000000..a86aeb47184 --- /dev/null +++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_PAGE_INFO_LITE_MODE_HTTPS_IMAGE_COMPRESSION.png.sha1 @@ -0,0 +1 @@ +212a781d980587ffb65c0d71cc96ce0bdb3832bb \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/scheduled_for_later.png.sha1 b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/scheduled_for_later.png.sha1 new file mode 100644 index 00000000000..1670826c602 --- /dev/null +++ b/chromium/components/browser_ui/strings/android/browser_ui_strings_grd/scheduled_for_later.png.sha1 @@ -0,0 +1 @@ +611e2667da59528924617439be19de090c2c8f32 \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/site_settings.grdp b/chromium/components/browser_ui/strings/android/site_settings.grdp index 037a8dacd6e..f1cc5249032 100644 --- a/chromium/components/browser_ui/strings/android/site_settings.grdp +++ b/chromium/components/browser_ui/strings/android/site_settings.grdp @@ -269,6 +269,18 @@ + + Ask before allowing sites to connect to a device (recommended) + + + Block sites from connecting to devices + + + Bluetooth + + + + Ask when a site wants to discover nearby Bluetooth devices (recommended) diff --git a/chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_BLUETOOTH.png.sha1 b/chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_BLUETOOTH.png.sha1 new file mode 100644 index 00000000000..c3deb3069de --- /dev/null +++ b/chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_BLUETOOTH.png.sha1 @@ -0,0 +1 @@ +5c2db6cbf8fa317258407d2947394406dae689e3 \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_ASK.png.sha1 b/chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_ASK.png.sha1 new file mode 100644 index 00000000000..303690df77c --- /dev/null +++ b/chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_ASK.png.sha1 @@ -0,0 +1 @@ +1bba94995f4b58a0f16751fda0a6fcdc38282c43 \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_BLOCKED.png.sha1 b/chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_BLOCKED.png.sha1 new file mode 100644 index 00000000000..4f13c68d7e5 --- /dev/null +++ b/chromium/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_BLOCKED.png.sha1 @@ -0,0 +1 @@ +2645ff62b62780091b64d3380eb84bd92bd373b6 \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_af.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_af.xtb index 57f8b531a20..f5f78abdc12 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_af.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_af.xtb @@ -10,6 +10,7 @@ Volgende Ingebed op Stop +Hierdie bladsy se prente is geoptimeer om vir jou data te bespaar. Lêers wat deur webwerwe gestoor is, verskyn hier Werf bygevoeg @@ -42,6 +43,7 @@ Vra voordat werwe toegelaat word om 'n 3D-kaart van jou omgewing te skep of kameraposisie na te spoor (aanbeveel) Laat webkoekies toe Vra wanneer 'n werf Bluetooth-toestelle in die omtrek wil ontdek (aanbeveel) +Bedryfstelsel steun nie die sertifikaat wat die kliënt gekies het nie. Gaan voort MB Aflaai is voltooi @@ -61,10 +63,12 @@ Gekopieer Is jy seker jy wil alle werftoestemmings vir terugstel? Werf +Vorige snit Vra voordat werwe toegelaat word om beskermde inhoud te speel Laat werwe toe om webkoekiedata te stoor en te lees (aanbeveel) Sertifikaatbekyker Dit sal al se webwerfberging uitvee. +Outomaties geblokkeer Wys inligting Geblokkeer op sommige werwe Skakel toestemmings vir Chrome aan in Android-instellings. @@ -83,16 +87,19 @@ Agtergrondsinkronisering Vee werfberging uit? Terug +Soek vorentoe Laat werwe toe om beskermde inhoud te speel (aanbeveel) Kopieer skakel van ? Titel Laat webkoekies vir 'n spesifieke werf toe. +Volgende snit Werfinstellings Kennisgewings kan die toestel laat vibreer Maak instellings oop Navigasie is geblokkeer: Toegelaat +'n Werf speel tans media Ligte bladsy deur Google verskaf Opspringers en herleidings Laat werwe toe om beskermde inhoud te speel @@ -107,6 +114,7 @@ Herroep alle toestemmings vir toestel Skakel ook kamera in Android-instellings aan sodat Chrome AR kan gebruik. Gaan vorentoe +Deel via Laat toe Laat werwe toegang tot bewegingsensors toe (aanbeveel) Gebruik @@ -163,7 +171,9 @@ Vee uit Aktiewe aflaaie Skuif af +Swiep ondertoe om toe te maak Keer derdeparty-webwerwe om webkoekiedata te stoor en te lees +Speel Kies items Hierdie toestel kan nie NFC lees nie Uitgevee @@ -197,6 +207,7 @@ 'n Werf gebruik tans jou kamera Jy sal op hierdie werf afgemeld word. Webkoekies is lêers wat geskep word deur webwerwe wat jy besoek. Werwe kan hulle gebruik om jou voorkeure te onthou. Derdepartywebkoekies word deur ander werwe geskep. Hierdie werwe besit van die inhoud, soos advertensies of prente, wat jy sien op die webbladsy wat jy besoek. +Speel tans media Kanselleer keuse Jy sal op alle werwe afgemeld word. Laat klank toe vir 'n spesifieke werf. @@ -204,6 +215,7 @@ Is jy seker dat jy alle plaaslike data, insluitend webkoekies, wil uitvee en alle toestemmings vir hierdie webwerf wil terugstel? Afgeskakel vir hierdie toestel Kon nie aflaai nie +Kan nie sertifikaat kies nie. Hierdie inhoud kom van af, afgelewer deur Google. Gedemp Bevestig @@ -216,6 +228,8 @@ 'n Werf gebruik tans jou kamera en mikrofoon Hierdie werf wys indringerige of misleidende advertensies Tik om na werf terug te keer +Verander +Soek agtertoe Herroep toesteltoestemming Werf is vinnig Keer dat werwe aan toestelle koppel @@ -236,6 +250,7 @@ Blokkeer webkoekies vir 'n spesifieke werf. Navigasie is onbereikbaar: Deur jou ouers bestuur +Bluetooth Toegang tot jou mikrofoon Blokkeer as werf indringerige of misleidende advertensies wys (aanbeveel) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_am.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_am.xtb index b0bba485b3b..aecb9f7689c 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_am.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_am.xtb @@ -10,6 +10,7 @@ ቀጣይ ላይ ተካትቷል አቁም +ውሂብ እንዲቆጥብልዎ የዚህ ገጽ ምስሎች በGoogle እንዲተቡ ተደርገዋል። በድር ጣቢያዎች የተቀመጡ ፋይሎች እዚህ ይታያሉ ጣቢያ ተክሏል @@ -42,6 +43,7 @@ ጣቢያዎች የዙሪያዎ የ3ል ካርታ እንዲፈጥሩ ወይም የካሜራ ቦታን እንዲከታተል ከመፍቀድ በፊት ጠይቅ (የሚመከር) ኩኪዎችን ፍቀድ አንድ ጣቢያ በአቅራቢያ ያሉ ብሉቱዝ መሣሪያዎችን ፈልጎ ለማግኘት ሲፈልግ ጠይቅ (የሚመከር) +የደንበኛ ወገን ዕውቅና ማረጋገጫ ምርጫ በስርዓተ-ክወናው አይደገፍም። ቀጥል ሜባ ማውረድ ተጠናቅቋል @@ -61,10 +63,12 @@ ተቀድቷል እርግጠኛ ነዎት ሁሉንም የ ጣቢያ ፈቃዶችን ዳግም ማስጀመር ይፈልጋሉ? ጣቢያ +ቀዳሚ ትራክ ጣቢያዎች ጥበቃ የሚደረግለትን ይዘትን እንዲያጫውቱ ከመፍቀድ በፊት ጠይቅ ጣቢያዎች የኩኪ ውሂብ እንዲያስቀምጡ እና እንዲያነቡ ይፍቀዱ (የሚመከር) የእውቅና ማረጋገጫ መመልከቻ ይህ ሁሉንም የድር ጣቢያ ማከማቻ ያጸዳል። +በራስ-ሰር ታግዷል መረጃ አሳይ በአንዳንድ ጣቢያዎች ላይ ታግዷል Android ቅንብሮች ውስጥ ፍቃዶችን ለChrome ያብሩ። @@ -83,16 +87,19 @@ የዳራ ስምረት የጣቢያ ማከማቻ ይጽዳ? ተመለስ +ወደፊት ፈልግ ጥበቃ የሚደረግበትን ይዘት እንዲያጫውቱ ለጣቢያዎች ፍቀድ (የሚመከር) አገናኝ ቅዳ / ? ርዕስ የተወሰነ ጣቢያ ኩኪዎችን ፍቀድ። +ቀጣይ ትራክ የጣቢያ ቅንብሮች ማሳወቂያዎች መሣሪያውን እንዲነዝር ሊያደርጉት ይችላሉ ቅንብሮችን ክፈት ዳሰሳ ታግዷል፦ ተፈቅዷል +አንድ ጣቢያ ሚዲያን በማጫወት ላይ ነው ቀላል ገጹ በGoogle ቀርቧል። ብቅ-ባዮች እና አቅጣጫ ማዞሮች ጣቢያዎች የተጠበቀ ይዘትን እንዲያጫውት ይፍቀዱ @@ -107,6 +114,7 @@ ሁሉንም የመሣሪያ ፈቃዶች ይሻሩ Chrome ኤአርን እንዲጠቀም ለማስቻል፣ እንዲሁም በየAndroid ቅንብሮች ውስጥ ካሜራውን ያብሩ። ወደ ፊት ሂድ +ያጋሩ በ ፍቀድ ጣቢያዎች የእንቅስቃሴ ዳሳሾችን እንዲደርሱባቸው ይፍቀዱ (የሚመከር) አጠቃቀም @@ -163,7 +171,9 @@ አጽዳ ንቁ ውርዶች ወደታች አውርድ +ለመዝጋት ወደታች ያንሸራትቱ። የሦስተኛ ወገን ድር ጣቢያዎች የኩኪ ውሂብን እንዳያስቀምጡ እና እንዳያነብቡ ከልክል +አጫውት ንጥሎችን ይምረጡ ይህ መሣሪያ NFCን ማንበብ አይችልም ተሰርዟል @@ -197,6 +207,7 @@ አንድ ጣቢያ ካሜራዎን እየተጠቀመ ነው ከዚህ ጣቢያ ዘግተው እንዲወጡ ይደረጋሉ። ኩኪዎች እርስዎ በሚጎበኟቸው ድር ጣቢያዎች የሚፈጠሩ ፋይሎች ናቸው። ጣቢያዎች የእርስዎን ምርጫዎች ለማስታወስ ይጠቀሙባቸዋል። የሦስተኛ ወገን ኩኪዎች በሌሎች ጣቢያዎች የሚፈጠሩ ናቸው። እነዚህ ጣቢያዎች እንደ ማስታወቂያዎች ወይም ምስሎች ያሉ እርስዎ በሚጎበኙት ድረ-ገጽ ላይ ያለ ይዘትን በባለቤትነት የያዙ ናቸው። +ሚዲያን በማጫወት ላይ ምርጫ ሰርዝ ከሁሉም ጣቢያዎች ዘግተው እንዲወጡ ይደረጋሉ። ለተወሰነ ጣቢያ ድምጽ ፍቀድ። @@ -204,6 +215,7 @@ እርግጠኛ ነዎት ለዚህ ጣቢያ ኩኪዎችንም ጨምሮ ሁሉንም አካባቢያዊ ውሂብ ማጽዳት እና ሁሉም ፍቃዶችን ዳግም ማስጀመር ይፈልጋሉ? ለዚህ መሣሪያ ጠፍቷል ማውረድ አልተሳካም +የዕውቅና ማረጋገጫን መምረጥ አልተቻለም። ይህ ይዘት ከ የመጣ ነው፣ የተላከው በGoogle ነው። ድምፀ ከል ተደርጓል አረጋግጥ @@ -216,6 +228,8 @@ አንድ ጣቢያ የእርስዎን ካሜራ እና ማይክሮፎን እየተጠቀመ ነው ይህ ጣቢያ ረባሽ ወይም አሳሳች ማስታወቂያዎችን ያሳያል ወደ ጣቢያው ለመመለስ መታ ያድርጉ +ለውጥ +ወደኋላ ፈልግ የመሣሪያ ፈቃድ ሻር ጣቢያ ፈጣን ነው ጣቢያዎች ከመሣሪያዎች ጋር እንዳይገናኙ አግድ @@ -236,6 +250,7 @@ የአንድ የተወሰነ ጣቢያ ኩኪዎችን ያግዱ። ዳሰሳ ሊደረስበት አይችልም፦ በእርስዎ ወላጆች የሚቀናበር +ብሉቱዝ የእርስዎን ማይክሮፎን ይድረሱ ጣቢያው ረባሽ ወይም አሳሳች ማስታወቂያዎችን የሚያሳይ ከሆነ ማገድ (የሚመከር) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb index 8f92b64fe96..d930585f828 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ar.xtb @@ -10,6 +10,7 @@ التالي مضمّن في إيقاف +‏لحفظ بياناتك، حسَّنت Google الصور في هذه الصفحة. تظهر هنا الملفات التي تحفظها المواقع الإلكترونية - تمت إضافة الموقع @@ -42,6 +43,7 @@ طلب الإذن قبل السماح لموقع إلكتروني بإنشاء خريطة ثلاثية الأبعاد للبيئة المحيطة بك أو تتبُّع موضع الكاميرا (مقترَح) السماح بملفات تعريف الارتباط طلب الإذن عند محاولة موقع إلكتروني العثور على أجهزة البلوتوث المجاورة (إجراء موصَى به) +لا يدعم نظام التشغيل تحديد الشهادة من جانب العميل. متابعة ميغابايت اكتمل التنزيل @@ -61,10 +63,12 @@ تم النسخ هل تريد فعلاً إعادة ضبط جميع أذونات الموقع الإلكتروني للكائن ؟ الموقع +المقطع الصوتي السابق السؤال قبل السماح للمواقع الإلكترونية بتشغيل المحتوى المحمي السماح للمواقع الإلكترونية بحفظ بيانات ملفات تعريف الارتباط وقراءتها (موصى به) عارض الشهادات سيؤدي هذا إلى محو مساحة التخزين البالغة بأكملها من مساحة تخزين المواقع الإلكترونية. +تم الحظر تلقائيًا عرض المعلومات حظر الإعلانات في بعض المواقع ‏تفعيل الأذونات لـ Chrome في إعدادات Android. @@ -83,16 +87,19 @@ المزامنة في الخلفية هل تريد محو مساحة تخزين المواقع؟ رجوع +الانتقال للأمام السماح للمواقع الإلكترونية بتشغيل المحتوى المحمي (مُستحسَن) نسخ الرابط / ? العنوان السماح لموقع إلكتروني معيّن بتشغيل ملفات تعريف الارتباط +المقطع الصوتي التالي إعدادات المواقع الإلكترونية يمكن أن تؤدي الإشعارات إلى اهتزاز الجهاز فتح الإعدادات التنقل محظور: منح الإذن +تفعيل موقع إلكتروني للوسائط ‏نسخة خفيفة تقدِّمها Google النوافذ المنبثقة وإعادة التوجيه السماح للمواقع الإلكترونية بتشغيل المحتوى المَحمي @@ -107,6 +114,7 @@ إبطال جميع الأذونات للجهاز ‏للسماح لمتصفِّح Chrome باستخدام خدمة الواقع المعزّز (AR)، يُرجى أيضًا تفعيل الكاميرا في إعدادات Android. انتقال للأمام +المشاركة عن طريق سماح السماح للمواقع الإلكترونية بالوصول إلى مستشعرات الحركة (مُقترَح) الاستخدام @@ -163,7 +171,9 @@ محو عمليات التنزيل النشِطة الانتقال إلى الأسفل +يمكنك التمرير السريع للأسفل للإغلاق. منع المواقع الإلكترونية التابعة لجهات خارجية من حفظ بيانات ملفات تعريف الارتباط وقراءتها +التشغيل اختيار عناصر ‏يتعذّر على هذا الجهاز قراءة NFC. تم الحذف @@ -197,6 +207,7 @@ هناك موقع إلكتروني يستخدم الكاميرا. سيتم تسجيل خروجك من هذا الموقع الإلكتروني. ملفات تعريف الارتباط هي ملفات تُنشئها المواقع الإلكترونية التي تزورها. وتستخدمها المواقع الإلكترونية لتتذكّر الإعدادات المفضّلة. أمّا ملفات تعريف الارتباط التابعة لجهات خارجية، فتُنشئها مواقع إلكترونية أخرى. وتمتلك هذه المواقع الإلكترونية بعضًا من المحتوى الذي تراه على صفحة الويب التي تزورها، مثل الإعلانات أو الصور. +تشغيل الوسائط إلغاء الاختيار سيتم تسجيل خروجك من جميع المواقع الإلكترونية. السماح بتشغيل الصوت لموقع معين. @@ -204,6 +215,7 @@ هل أنت متأكّد أنك تريد محو كل البيانات المحلية، بما في ذلك ملفات تعريف الارتباط، وإعادة ضبط كافة الأذونات لهذا الموقع الإلكتروني؟ تم إيقافه لهذا الجهاز تعذّر التنزيل +يتعذر تحديد الشهادة. ‏هذا المحتوى من ، وتم عرضه من قبل Google. المواقع الإلكترونية التي تم كتم الصوت فيها التأكيد @@ -216,6 +228,8 @@ هناك موقع إلكتروني يستخدم الميكروفون والكاميرا. يعرض هذا الموقع الإلكتروني إعلانات مضلِّلة أو غير مرغوب فيها يُرجى النقر للرجوع إلى الموقع الإلكتروني. +تغيير +الرجوع للوراء إبطال إذن الجهاز الموقع الإلكتروني سريع حظر المواقع الإلكترونية من الاتصال بأجهزة @@ -236,6 +250,7 @@ حظر ملفات تعريف الارتباط لموقع إلكتروني مُحدَّد. التنقل غير قابل للوصول: يديره والداك +بلوتوث الدخول إلى الميكروفون الحظر في حال كان الموقع الإلكتروني يعرض إعلانات مضلِّلة أو غير مرغوب فيها (مُستحسَن) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb index 54d7596299a..febece3ad44 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_as.xtb @@ -10,6 +10,7 @@ পৰৱৰ্তী ত এম্বেড কৰা হৈছে বন্ধ কৰক +আপোনাৰ ডেটা ৰাহি কৰিবলৈ Googleএ এই পৃষ্ঠাখনৰ প্ৰতিচ্ছবিসমূহ অপ্টিমাইজ কৰিছে। ৱেবছাইটে ছেভ কৰা ফাইলসমূহ ইয়াত দেখা পোৱা যায় - ছাইট যোগ কৰা হ’ল @@ -42,6 +43,7 @@ ছাইটসমূহক আপোনাৰ চৌপাশৰ এখন 3D মেপ সৃষ্টি কৰিবলৈ অথবা কেমেৰাৰ স্থান ট্ৰেক কৰিবলৈ অনুমতি দিয়াৰ পূর্বে সোধক (চুপাৰিছ কৰা হয়) কুকিসমূহক অনুমতি দিয়ক কোনো ছাইটে নিকটৱৰ্তী ডিভাইচ ব্লুটুথ ডিভাইচসমূহ পাব বিচাৰিলে সোধক (আমি চুপাৰিছ কৰোঁ) +অপাৰেটিং ছিষ্টেমটোৱে ক্লায়েণ্টৰ ফালৰ প্ৰমাণপত্ৰ বাছনি কৰা সুবিধাটো সমৰ্থন নকৰে। অব্যাহত ৰাখক এম. বি. ডাউনল’ড সম্পূর্ণ হৈছে @@ -61,10 +63,12 @@ প্ৰতিলিপি কৰা হ'ল আপুনি ৰ বাবে ছাইটৰ সকলো অনুমতি ৰিছেট কৰিবলৈ বিচাৰে বুলি নিশ্চিতনে? ছাইট +পূৰ্বৱৰ্তী ট্ৰেক ছাইটক প্রতিবন্ধিত সমল প্লে' কৰিব দিয়াৰ আগত সোধক ছাইটসমূহক কুকি ডেটা ছেভ কৰিবলৈ আৰু পঢ়িবলৈ অনুমতি দিয়ক (আমি চুপাৰিছ কৰোঁ) প্ৰমাণপত্ৰ ভিউৱাৰ এইটোৱে ৱেবছাইটৰ ষ্ট’ৰেজৰ সম্পূৰ্ণ মচি পেলাব। +স্বয়ংক্ৰিয়ভাৱে অৱৰোধ কৰা হৈছে তথ্য দেখুৱাওক কিছুমান ছাইটত অৱৰোধ কৰা আছে Android ছেটিংসমূহলৈ গৈ Chromeক অনুমতি প্ৰদান কৰক। @@ -83,16 +87,19 @@ নেপথ্যত ছিংক কৰা ছাইটৰ ষ্ট’ৰেজ মচিবনে? উভতি যাওক +আগলৈ যাওক ছাইটসমূহক সুৰক্ষিত সমল প্লে’ কৰিবলৈ অনুমতি দিয়ক (চুপাৰিছ কৰা হয়) লিংক প্ৰতিলিপি কৰক / ? শিৰোনাম কোনো নিৰ্দিষ্ট ছাইটৰ বাবে কুকিসমূহক অনুমতি দিয়ক। +পৰৱৰ্তী ট্ৰেক ছাইটৰ ছেটিংসমূহ জাননীয়ে ডিভাইচটো কম্পন কৰিব পাৰে ছেটিংসমূহ খোলক নেভিগে’শ্বন অৱৰোধ কৰা আছে: অনুমতি দিয়া হৈছে +ছাইটটোৱে মিডিয়া প্লে’ কৰি আছে Googleএ প্ৰদান কৰা লাইট পৃষ্ঠা পপ-আপ আৰু পুনৰ নির্দেশ ছাইটসমূহক সুৰক্ষিত সমল প্লে' কৰিবলৈ অনুমতি দিয়ক @@ -107,6 +114,7 @@ ডিভাইচৰ বাবে সকলো অনুমতি প্ৰত্যাহাৰ কৰক Chromeক AR ব্যৱহাৰ কৰিবলৈ দিবলৈ Android ছেটিংসমূহত কেমেৰাও অন কৰক। আগুৱাই যাওক +ইয়াৰ জৰিয়তে শ্বেয়াৰ কৰক অনুমতি দিয়ক ম'শ্বন ছেন্সৰসমূহলৈ এক্সেছ পাবলৈ ছাইটসমূহক অনুমতি দিয়ক (চুপাৰিছ কৰা) ব্যৱহাৰ @@ -163,7 +171,9 @@ মচক সক্রিয় ডাউনল’ডসমূহ তললৈ নিয়ক +বন্ধ কৰিবলৈ তললৈ ছোৱাইপ কৰক। তৃতীয় পক্ষৰ ৱেবছাইটসমূহে কুকি ডেটা ছেভ কৰা আৰু পঢ়াত বাধা দিয়ক +প্লে’ কৰক বস্তু বাছনি কৰক এই ডিভাইচটোৱে NFC পঢ়িব নোৱাৰে মচা হ’ল @@ -197,6 +207,7 @@ এটা ছাইটে আপোনাৰ কেমেৰা ব্যৱহাৰ কৰি আছে আপোনাক এই ছাইটটোৰ পৰা ছাইন আউট কৰোৱা হ’ব। কুকিবোৰ হৈছে আপুনি চোৱা ৱেবছাইটসমূহে সৃষ্টি কৰা ফাইল। ছাইটসমূহে আপোনাৰ অগ্ৰাধিকাৰসমূহ মনত ৰাখিবলৈ সেইবোৰ ব্যৱহাৰ কৰে। তৃতীয় পক্ষৰ কুকিসমূহ অন্য ছাইটসমূহে সৃষ্টি কৰে। এই ছাইটসমূহ আপুনি চোৱা ৱেবছাইটটোত দেখা পোৱা বিজ্ঞাপন অথবা প্ৰতিচ্ছবিবোৰৰ দৰে কিছুমান সমলৰ গৰাকী। +মিডিয়া প্লে’ হৈ আছে বাছনি বাতিল কৰক আপোনাক সকলো ছাইটৰ পৰা ছাইন আউট কৰোৱা হ’ব। নির্দিষ্ট ছাইটৰ বাবে ধ্বনিৰ অনুমতি দিয়ক। @@ -204,6 +215,7 @@ আপুনি এই ৱেবছাইটটোৰ কুকিকে ধৰি সকলো স্থানীয় ডেটা মচিব আৰু সকলো অনুমতি ৰিছেট কৰিব বিচাৰে বুলি নিশ্চিতনে? এই ডিভাইচটোৰ বাবে অফ কৰা হৈছে ডাউনল’ড কৰিব পৰা নগ’ল +প্ৰমাণপত্ৰ বাছনি কৰিব পৰা নগ'ল। এই সমল Googleএ যোগান ধৰা ৰ। মিউট আছে নিশ্চিত কৰক @@ -216,6 +228,8 @@ এটা ছাইটে আপোনাৰ কেমেৰা আৰু মাইক্ৰ’ফ’ন ব্যৱহাৰ কৰি আছে এই ছাইটটোৱে বিনা অনুমতিত বা কোনো বিভ্ৰান্তিকৰ বিজ্ঞাপন দেখুৱায় এই ছাইটটোলৈ উভতি যাবলৈ টিপক +সলনি কৰক +পিছলৈ ছীক কৰক ডিভাইচৰ অনুমতি প্ৰত্যাহাৰ কৰক ছাইটটো দ্ৰুত ডিভাইচৰ সৈতে সংযোগ কৰাৰ পৰা ছাইটসমূহক অৱৰোধ কৰক @@ -236,6 +250,7 @@ কোনো বিশেষ ছাইটৰ বাবে কুকিসমূহ অৱৰোধ কৰক। ইয়ালৈ নেভিগেশ্বন পাব পৰা অৱস্থাত নাই: আপোনাৰ অভিভাৱকৰ দ্বাৰা পৰিচালিত +ব্লুটুথ আপোনাৰ মাইক্ৰ'ফ'ন এক্সেছ কৰক কোনো ছাইটে অননুমোদিত বা বিভ্ৰান্তিকৰ বিজ্ঞাপন দেখুৱালে সেয়া অৱৰোধ কৰক ( চুপাৰিছ কৰা) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_az.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_az.xtb index 2df2b230b58..026fc13eceb 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_az.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_az.xtb @@ -10,6 +10,7 @@ Növbəti daxilində yerləşdirilib Dayandırın +Datanızı yadda saxlamaq üçün bu səhifənin şəkilləri Google tərəfindən optimallaşdırılıb. Veb saytların yadda saxladığı fayllar burada görünür - saytı əlavə edildi @@ -42,6 +43,7 @@ Saytların ətrafınızdakı sahələrin 3D xəritəsini yaratmasına və ya kamera mövqeyini izləməsinə icazə verməzdən əvvəl icazə tələb edin (tövsiyə edilir) Kukilərə icazə verin Sayt yaxınlıqdakı Bluetooth cihazlarını kəşf etmək istədikdə soruşun (tövsiyə edilir) +Müştəri sayt sertifikatı seçimi əməliyyat sistemi tərəfindən dəstəklənmir. Davam edin MB Endirmə tamamlandı @@ -61,10 +63,12 @@ Kopyalandı üçün bütün sayt icazələrini sıfırlamaq istədiyinizə əminsiniz? Sayt +Öncəki trek Saytlar qorunan kontenti göstərmədən öncə icazə tələb edilsin Saytlara kuki datanı saxlamağa və oxumağa imkan verir (tövsiyə olunur) Sertifikat izləyici Bu, veb sayt yaddaşının hissəsini siləcək +Avtomatik olaraq blok edildi Məlumatı göstərin Bəzi saytlarda bloklandı Chrome üçün icazələrin yandırmaq Android Settings . @@ -83,16 +87,19 @@ Arxa fon sinx Sayt yaddaşı silinsin? Geri +İrəli axtarın Saytlara qorunan datanı oxumağa icazə verin (tövsiyə olunur) Linki kopyalayın / ? Başlıq Xüsusi sayt üçün kukilərə icazə verin. +Növbəti trek Sayt ayarları Bildirişlər cihazı titrədə bilər Ayarları açın Naviqasiya bloklandı: İcazə verilib +Sayt media oxudur Google'un dəstəklədiyi lite səhifəsi Popap və yönləndirmələr Saytlara qorunan kontenti oxutmağa icazə verin @@ -107,6 +114,7 @@ Cihaz üçün bütün icazələri ləğv edin Chrome'un artırılmış reallıqdan istifadə etməsi üçün Android Ayarlarında kameranı da aktiv edin. İrəli +Paylaşma vasitəsi: İcazə verin Saytların hərəkət sensorlarınıza daxil olmasına icazə verin (tövsiyə edilir) Istifadə @@ -163,7 +171,9 @@ Silin Aktiv endirmələr Aşağı köçürün +Bağlamaq üçün aşağı sürüşdürün. Üçüncü tərəf veb saytları tərəfindən kuki datasının yadda saxlanılması və oxunmasının qarşısını alın +Oxudun Element seçin Bu cihazda NFC dəstəklənmir Silinib @@ -197,6 +207,7 @@ Sayt kameranızdan istifadə edir Bu saytdan çıxacaqsınız. Kukilər daxil olduğunuz veb saytların yaratdığı fayllardır. Saytlar tərcihlərinizi yadda saxlamaq üçün onlardan istifadə edir. Üçüncü tərəf kukiləri başqa saytlar tərəfindən yaradılır. Bu saytlar daxil olduğunuz veb səhifədə gördüyünüz reklam və ya şəkillər kimi bəzi məzmunların sahibidir. +Medianın oxudulması Seçimi ləğv edin Bütün saytlardan çıxacaqsınız. Xüsusi sayt üçün səsə icazə verin. @@ -204,6 +215,7 @@ Bütün lokal datanı silmək və bu sayt üçün bütün icazələri sıfırlamaq istəyirsiniz? Bu cihaz üçün söndürüldükdə Endirmə alınmadı +Sertifikatı seçmək mümkün deyil. Bu məzmun domenindəndir, Google tərəfindən çatdırılıb. Səssiz Təsdiq edin @@ -216,6 +228,8 @@ Sayt kamera və mikrofonunuzdan istifadə edir Bu sayt inadçı və ya aldadıcı reklamlar göstərir Sayta qayıtmaq üçün toxunun +Dəyişin +Geri axtarı Cihaz icazəsini ləğv edin Sayt sürətlidir Saytların cihazlara qoşulmasını blok edin @@ -236,6 +250,7 @@ Xüsusi sayt üçün kukiləri blok edin. Naviqasiya əlçatmazdır: Valideynləriniz tərəfindən idarə olunur +Bluetooth Mikrofonunuza giriş Sayt inadçı və ya aldadıcı reklamlar göstərirsə, blok edin (tövsiyə olunur) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_be.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_be.xtb index 3e03d353ff7..3ff039933cd 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_be.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_be.xtb @@ -10,6 +10,7 @@ Далей Убудавана на сайце Спыніць +Відарысы на гэтай старонцы былі аптымізаваны з дапамогай Google для эканоміі трафіка. Тут паказваюцца файлы, захаваныя вэб-сайтамі . Сайт дададзены @@ -42,6 +43,7 @@ Пытацца, перш чым дазволіць сайтам ствараць 3D-карту вашага асяроддзя і адсочваць становішча камеры (рэкамендуецца) Дазволіць файлы cookie Пытацца, калі сайт спрабуе выявіць прылады Bluetooth паблізу (рэкамендуецца) +Аперацыйная сістэма не падтрымлівае выбар сертыфіката кліента. Працягнуць  МБ Спампоўванне завершана @@ -61,10 +63,12 @@ Скапіравана Сапраўды скінуць усе дазволы сайта для ? Сайт +Папярэдні трэк Пытаць, перш чым дазволіць прайграванне абароненага змесціва на сайтах Дазволіць сайтам захоўваць і чытаць даныя файлаў cookie (рэкамендуецца) Звесткі пра сертыфікат Гэта вызваліць у сховішчы вэб-сайтаў. +Заблакіравана аўтаматычна Паказаць інфармацыю Заблакіравана на некаторых сайтах Уключыце дазволы для Chrome у Наладах Android. @@ -83,16 +87,19 @@ Фонавая сінхранізацыя Ачысц. сховішча сайтаў? Назад +Перайсці ўперад Дазволіць сайтам прайграваць абароненае змесціва (рэкамендуецца) Скапіраваць спасылку / ? Назва Дазвол выкарыстання файлаў cookie ад канкрэтнага сайта. +Наступны трэк Налады сайта Апавяшчэнні могуць уключаць вібрацыю прылады Адкрыць налады Пераход па наступным адрасе заблакіраваны: Дазволена +Сайт прайграе мультымедыя Старонка, спрошчаная алгарытмамі Google Усплыв. вокны і перанакіраванні Дазволіць сайтам прайграваць абароненае змесціва @@ -107,6 +114,7 @@ Адклікаць усе дазволы для прылады Каб Chrome мог выкарыстоўваць дапоўненую рэальнасць, уключыце таксама камеру ў Наладах Android. Наперад +Абагуліць праз Дазволіць Дазваляць сайтам доступ да датчыкаў руху (рэкамендуецца) Выкарыстанне @@ -163,7 +171,9 @@ Ачысціць Актыўныя спампоўкі Перамясціць ніжэй +Правядзіце пальцам уніз, каб закрыць. Не даваць староннім вэб-сайтам захоўваць і чытаць даныя файлаў cookie +Прайграць Выберыце элементы Гэта прылада не падтрымлівае функцыю NFC Выдалена @@ -197,6 +207,7 @@ Сайт выкарыстоўвае камеру Будзе выкананы выхад з гэтага сайта. Файлы cookie – гэта файлы, якія ствараюцца наведанымі вамі вэб-сайтамі, каб запамінаць вашы налады. Староннія файлы cookie ствараюцца іншымі сайтамі. Гэтым сайтам належыць пэўнае змесціва (напрыклад рэклама або відарысы), размешчанае на вэб-старонцы, якую вы наведваеце. +Прайграванне мультымедыя Скасаваць выбар Будзе выкананы выхад з усіх сайтаў. Дазвол гуку на канкрэтным сайце. @@ -204,6 +215,7 @@ Вы сапраўды хочаце ачысціць усе лакальныя даныя, у тым ліку файлы cookie, і скінуць усе дазволы для гэтага вэб-сайта? Выключана для гэтай прылады Збой спампоўкі +Не ўдаецца выбраць сертыфікат. Змесціва з , пастаўшчык – Google. Без гуку Пацвердзіць @@ -216,6 +228,8 @@ Сайт выкарыстоўвае камеру і мікрафон Гэты сайт паказвае назойлівую рэкламу або рэкламу, якая ўводзіць у зман Каб вярнуцца на сайт, націсніце тут +Змяніць +Перайсці назад Адклікаць дазвол на доступ да прылады Сайт хуткі Блакіраваць сайтам падключэнне да прылад @@ -236,6 +250,7 @@ Блакіраваць файлы cookie для пэўнага сайта. Пераход па наступным адрасе недаступны: Пад кіраваннем вашых бацькоў +Bluetooth Доступ да мікрафона Блакіраваць, калі сайт паказвае назойлівую рэкламу або рэкламу, якая ўводзіць у зман (рэкамендуецца) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bg.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bg.xtb index 5617ffb7c19..9e7ed388933 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bg.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bg.xtb @@ -10,6 +10,7 @@ Напред Вграден в Стоп +Изображенията на тази страница бяха оптимизирани от Google с цел пестене на данни. Тук се показват файловете, запазени от уебсайтове  – Сайтът е добавен @@ -42,6 +43,7 @@ Извеждане на запитване, преди да се разреши на сайтовете да създават триизмерна карта на заобикалящата ви среда или да следят позицията на камерата (препоръчително) Разрешаване на „бисквитките“ Извеждане на запитване, когато сайт иска да открива устройства с Bluetooth в близост (препоръчително) +Избраният сертификат от страната на клиента не се поддържа от операционната система. Напред МБ Изтеглянето завърши @@ -61,10 +63,12 @@ Копирано Наистина ли искате да зададете повторно всички разрешения за сайта за ? Сайт +Предишен запис Запитване преди разрешаване на сайтовете да възпроизвеждат защитено съдържание Разрешаване на сайтовете да запазват „бисквитки“ и да четат данни от такива (препоръчително) Визуализатор на сертификатите Така ще се изчистят всички съхранявани данни от уебсайтове (). +Автоматично блокирано Показване на информацията Блокиране на някои сайтове Включете разрешенията за Chrome от настройките на Android. @@ -83,16 +87,19 @@ Синхронизиране на заден план Изчистване на данните? Назад +Придвижване напред Разрешаване на сайтовете да възпроизвеждат защитено съдържание (препоръчително) Връзка: Коп. от ? Заглавие Разрешаване на „бисквитките“ за конкретен сайт. +Следващ запис Настройки за сайта Възможно е устройството да вибрира при известия Отваряне на настройките Навигирането е блокирано: Разрешено +Сайт възпроизвежда мултимедийно съдържание Олекотена страница, предоставена от Google Изскач. прозорци и пренасочвания Разрешаване на сайтовете да възпроизвеждат защитено съдържание @@ -107,6 +114,7 @@ Отмяна на всички разрешения за устройството За да разрешите на Chrome да използва AR, трябва да включите камерата от настройките на Android. Преминаване напред +Споделяне чрез Разрешаване Разрешаване на достъпа на сайтовете до сензорите за движение (препоръчително) Употреба @@ -163,7 +171,9 @@ Изчистване Активни изтегляния Придвижване надолу +Прекарайте пръст надолу, за да затворите. Забраняване на уебсайтовете на трети страни да запазват и четат данни в „бисквитки“ +Пускане Избор на елементи Това устройство не може да чете NFC Изтрито @@ -197,6 +207,7 @@ Сайт използва камерата ви Ще излезете от профила си в този сайт. Това са файлове, създавани от уебсайтовете, които посещавате. Сайтовете ги използват, за да запомнят предпочитанията ви. „Бисквитките“ на трети страни се създават от други сайтове. Тези сайтове притежават част от съдържанието, като например реклами или изображения, което виждате на посетената от вас уеб страница. +Възпроизвеждане на мултимедия Анулиране на избора Ще излезете от профила си във всички сайтове. Разрешаване на звука за конкретен сайт. @@ -204,6 +215,7 @@ Наистина ли искате да изчистите всички локални данни, включително „бисквитките“, и да нулирате всички разрешения за този уебсайт? Изключено за това устройство Изтеглянето не бе успешно +Сертификатът не може да бъде избран. Това съдържание се показва от Google, а източникът му е . Заглушени Потвърждаване @@ -216,6 +228,8 @@ Сайт използва камерата и микрофона ви На този сайт се показват натрапчиви или подвеждащи реклами Докоснете, за да се върнете към сайта +Промяна +Придвижване назад Отмяна на разрешението за достъп до устройството Сайтът е бърз Блокиране на сайтовете, така че да не се свързват с устройства @@ -236,6 +250,7 @@ Блокиране на „бисквитките“ за конкретен сайт. Навигирането не е възможно: Управлява се от родителите ви +Bluetooth Достъп до микрофона Блокиране, ако на сайта се показват натрапчиви или подвеждащи реклами (препоръчително) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bn.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bn.xtb index f5b37691a23..34e259b7e0e 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bn.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bn.xtb @@ -10,6 +10,7 @@ পরের -এ এম্বেড করা হয়েছে বন্ধ +আপনার ডেটা সেভ করার জন্য, এই পৃষ্ঠার ছবিগুলি Google অপ্টিমাইজ করেছে। ওয়েবসাইট যেসব ফাইল সেভ করে সেগুলি এখানে দেখা যায় - সাইট যোগ করা হয়েছে @@ -42,6 +43,7 @@ আপনার আশেপাশের এলাকার একটি 3D ম্যাপ তৈরি করতে বা ক্যামেরার অবস্থান ট্র্যাক করতে কোনও সাইট অনুমোদন দেওয়ার আগে, একবার জিজ্ঞাসা করে নিন (সাজেস্ট করা হয়েছে) কুকিগুলিকে অনুমতি দিন কাছাকাছি ব্লুটুথ ডিভাইস আছে কিনা তা কোনও সাইট খুঁজতে চাইলে আমাকে জিজ্ঞাসা করুন (সাজেস্ট করা হচ্ছে) +ক্লায়েন্ট সাইড সার্টিফিকেট নির্বাচন অপারেটিং সিসটেম দ্বারা সমর্থিত নয়। চালিয়ে যান এমবি ডাউনলোড সম্পূর্ণ হয়েছে @@ -61,10 +63,12 @@ প্রতিলিপি করা হয়েছে আপনি কি -এর জন্য সাইটের সব অনুমতি রিসেট করার ব্যাপারে নিশ্চিত? সাইট +পূর্ববর্তী ট্র্যাক কোনও সাইটে সুরক্ষিত কন্টেন্ট চালু হওয়ার আগে আমায় জিজ্ঞেস করা হোক সাইটগুলিকে কুকি ডেটা পড়ার এবং সংরক্ষণ করার অনুমতি দিন (প্রস্তাবিত) সার্টিফিকেট প্রদর্শনকারী এটা ওয়েবসাইট স্টোরেজের -এর পুরোটা সাফ করবে। +স্বয়ংক্রিয়ভাবে ব্লক করা হয়েছে তথ্য দেখুন কিছু সাইটে ব্লক করা হয়েছে Android সেটিংসে Chrome এর জন্য অনুমতিগুলি চালু করুন। @@ -83,16 +87,19 @@ পটভূমি সিঙ্ক সাইটের সঞ্চয়স্থান সাফ করবেন? ফিরুন +সামনে এগোন সুরক্ষিত কন্টেন্ট প্লে করতে সাইটগুলিকে মঞ্জুরি দিন (প্রস্তাবিত) লিঙ্ক কপি করুন / ? শিরোনাম কোনও নির্দিষ্ট সাইটের জন্য কুকিকে অনুমতি দিন। +পরবর্তী ট্র্যাক সাইটের সেটিংস বিজ্ঞপ্তি আসলে ডিভাইস ভাইব্রেট হতে পারে সেটিংস খুলুন নেভিগেশন অবরুদ্ধ করা হয়েছে: মঞ্জুরিপ্রাপ্ত +একটি সাইট মিডিয়া চালাচ্ছে Google লাইট পৃষ্ঠা পাঠিয়েছে পপ-আপ এবং রিডাইরেক্ট সাইটকে সুরক্ষিত কন্টেন্ট চালানোর জন্য অনুমতি দিন @@ -107,6 +114,7 @@ ডিভাইসের ক্ষেত্রে সমস্ত অনুমতি সরিয়ে দিন এছাড়াও, Chrome-কে এ আর (AR) ব্যবহার করতে দিতে চাইলে, Android সেটিংস থেকে ক্যামেরা চালু করতে হবে। অগ্রবর্তী করুন +এর মাধ্যমে শেয়ার করুন অনুমতি দিন সাইটকে মোশন সেন্সর অ্যাক্সেস করার অনুমতি দিন (সাজেস্ট করা হয়) ব্যবহার @@ -163,7 +171,9 @@ সাফ করুন ডাউনলোড করা হচ্ছে নিচে যান +বন্ধ করতে নিচের দিকে সোয়াইপ করুন। কুকি ডেটা সংরক্ষণ করা এবং পড়া থেকে তৃতীয় পক্ষের ওয়েবসাইটগুলিকে আটকান +চালু করুন আইটেম বেছে নিন এই ডিভাইস NFC পড়তে পারছে না মোছা হয়েছে @@ -197,6 +207,7 @@ একটি সাইট আপনার ক্যামেরা ব্যবহার করছে আপনি এই সাইট থেকে সাইন-আউট হয়ে যাবেন। আপনার ব্রাউজ করা ওয়েবসাইটের তৈরি করা ফাইলগুলিকে 'কুকি' বলা হয়। সাইট আপনার পছন্দগুলি মনে রাখতে, কুকি ব্যবহার করে। অন্যান্য সাইট 'থার্ড-পার্টি কুকি' তৈরি করে। এই সাইটগুলিতে বিজ্ঞাপন এবং ছবির মতো কিছু কন্টেন্ট থাকে, যেগুলি আপনার ভিজিট করা ওয়েবপেজে আপনি দেখতে পান। +মিডিয়া চালানো হচ্ছে নির্বাচন বাতিল করুন আপনি সমস্ত সাইট থেকে সাইন-আউট হয়ে যাবেন। একটি নির্দিষ্ট সাইটের জন্য সাউন্ড প্লে করার অনুমতি দিন। @@ -204,6 +215,7 @@ আপনি কি এই ওয়েবসাইটের কুকি সহ সমস্ত ডেটা পরিষ্কার করার এবং এটির সমস্ত অনুমতি রিসেট করার বিষয়ে নিশ্চিত? এই ডিভাইসের জন্য বন্ধ করা আছে ডাউনলোড করা যায়নি +সার্টিফিকেট বেছে নিতে পারেনি। এই কন্টেন্ট Google দ্বারা বিতরণ করা থেকে এসেছে। মিউট করা আছে নিশ্চিত হন @@ -216,6 +228,8 @@ একটি সাইট আপনার ক্যামেরা ও মাইক্রোফোন ব্যবহার করছে এই সাইট ব্যাঘাত সৃষ্টিকারী বা বিভ্রান্তিকর বিজ্ঞাপন দেখায় সাইটে ফিরে যেতে ট্যাপ করুন +পরিবর্তন +পেছনে যান ডিভাইসের অনুমতি প্রত্যাহার করুন সাইট খুব দ্রুত লোড হয় সাইটকে ডিভাইসের সাথে কানেক্ট করা থেকে ব্লক করুন @@ -236,6 +250,7 @@ কোনও নির্দিষ্ট সাইটের জন্য কুকি ব্লক করুন। নেভিগেশানে পৌছানো যাচ্ছে না: আপনার পিতামাতার দ্বারা পরিচালিত +ব্লুটুথ আপনার মাইক্রোফোন অ্যাক্সেস করুন সাইটে থাকা ব্যাঘাত সৃষ্টিকারী বা বিভ্রান্তিকর বিজ্ঞাপন ব্লক করুন (প্রস্তাবিত) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bs.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bs.xtb index 7a6eccbc8e4..bc05e78e3b4 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bs.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_bs.xtb @@ -10,6 +10,7 @@ Naprijed Ugrađeno na Zaustavi +Da sačuvate podatke, Google je optimizirao slike ove stranice. Fajlovi koje sačuvaju web lokacije će se pojaviti ovdje Web lokacija je dodana @@ -42,6 +43,7 @@ Web lokacije moraju tražiti odobrenje za kreiranje 3D mape okruženja i praćenje položaja kamere (preporučeno) Dozvoli kolačiće Pitaj kada web lokacija želi otkriti Bluetooth uređaje u blizini (preporučeno) +Operativni sistem ne podržava odabir potvrde na strani klijenta. Nastavi MB Preuzimanje je završeno @@ -61,10 +63,12 @@ Kopirano Jeste li sigurni da želite poništiti sva odobrenja web lokacije za ? Web lokacija +Prethodna numera Web lokacije moraju tražiti dozvolu za reprodukciju zaštićenog sadržaja Omogućite web lokacijama da sačuvaju i čitaju podatke o kolačićima (preporučeno) Preglednik certifikata Time će se izbrisati cijela pohrana web-lokacije veličine . +Automatski blokirano Prikaži informacije Blokirano na nekim web lokacijama Uključite odobrenja za Chrome u Postavkama Androida. @@ -83,16 +87,19 @@ Sinhronizacija u pozadini Obrisati pohranu? Nazad +Pomakni naprijed Dozvoljava web lokacijama da reproduciraju zaštićeni sadržaj (preporučeno) Kopiraj link /? Naslov Omogućavanje kolačića za određenu web lokaciju. +Sljedeća pjesma Postavke web-lokacije Obavještenja mogu aktivirati vibraciju uređaja Otvori postavke Navigacija je blokirana: Dozvoljeno +Web lokacija reproducira medij Jednostavnu stranicu omogućava Google Skočni proz. i preusmjeravanja Dozvoljava web lokacijama da reproduciraju zaštićen sadržaj @@ -107,6 +114,7 @@ Opoziv svih dopuštenja za uređaj Da Chromeu dozvolite korištenje AR-a, također uključite kameru u Postavkama Androida. Idi naprijed +Dijeljenje koristeći Dozvoli Dozvoli web lokacijama pristup senzorima pokreta (preporučeno) Korištenje @@ -163,7 +171,9 @@ Obriši Aktivna preuzimanja Pomjeri dolje +Prevucite prema dolje da zatvorite. Spriječi web lokacije treće strane da pohranjuju i čitaju podatke kolačića +Pokreni Odaberite stavke Ovaj uređaj ne može očitavati NFC Izbrisano @@ -197,6 +207,7 @@ Web lokacija koristi vašu kameru Odjavit ćete se s ove web lokacije. Kolačići su fajlovi koje kreiraju web lokacije koje posjetite. Web lokacije ih koriste da zapamte vaše preference. Kolačiće treće strane kreiraju druge web lokacije. Te web lokacije posjeduju dio sadržaja, kao što su oglasi ili slike, koji se prikazuje na web stranici koju posjećujete. +Reproduciranje medija Otkazivanje odabira Odjavit ćete se sa svih web lokacija. Omogućavanje zvuka za određenu web lokaciju. @@ -204,6 +215,7 @@ Jeste li sigurni da želite obrisati sve lokalne podatke, uključujući kolačiće i poništiti sva odobrenja za ovu web lokaciju? Isključeno za ovaj uređaj Preuzimanje nije uspjelo +Nije moguće odabrati potvrdu. Ovaj sadržaj je iz domene , a isporučio ga je Google. Isključen zvuk Potvrdi @@ -216,6 +228,8 @@ Web lokacija koristi vašu kameru i mikrofon Ova web lokacija prikazuje ometajuće ili obmanjujuće oglase Dodirnite da se vratite na web lokaciju +Izmijeni +Pomicanje unazad Opozivanje dozvole za uređaj Web lokacija je brza Blokiraj povezivanje web-lokacija s uređajima @@ -236,6 +250,7 @@ Blokiranje kolačića za određenu web lokaciju. Navigacija je nedostupna: Upravljaju tvoji roditelji +Bluetooth Pristup mikrofonu Blokirajte ako web lokacija prikazuje nametljive ili obmanjujuće oglase (preporučeno) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ca.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ca.xtb index 60f52a6329f..a6a6ab9f147 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ca.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ca.xtb @@ -10,6 +10,7 @@ Següent Incrustada a Atura +Perquè estalviïs dades, Google ha optimitzat les imatges d'aquesta pàgina. Els fitxers desats pels llocs web es mostren aquí : El lloc s'ha afegit @@ -42,6 +43,7 @@ Pregunta abans de permetre que els llocs web creïn un mapa en 3D del teu entorn o facin un seguiment de la posició de la càmera (opció recomanada) Permet les galetes Pregunta'm quan un lloc web vulgui descobrir dispositius Bluetooth propers (opció recomanada) +El sistema operatiu no permet seleccionar el certificat del client. Continua  MB S'ha completat la baixada @@ -61,10 +63,12 @@ Copiada Confirmes que vols restablir tots els permisos del lloc web concedits a ? Lloc web +Pista anterior Pregunta abans de permetre que els llocs web reprodueixin contingut protegit Permet que els llocs web desin i llegeixin les dades de les galetes (opció recomanada) Lector de certificats Amb aquesta acció s'esborraran d'emmagatzematge del lloc web. +Bloquejada automàticament Mostra la informació Bloquejat en alguns llocs web Activa els permisos per a Chrome a la configuració d'Android. @@ -83,16 +87,19 @@ Sincronització en segon pla Esborrem emmagatz. lloc web? Enrere +Avança Permet que els llocs web reprodueixin contingut protegit (opció recomanada) Copia l'enllaç /? Títol Permet les galetes d'un lloc web concret. +Pista següent Configuració del lloc web És possible que les notificacions facin vibrar el dispositiu Obre la configuració S'ha bloquejat la navegació: Permès +Un lloc web està reproduint contingut multimèdia Pàgina en mode bàsic oferida per Google Finestres emergents i redireccions Permet que els llocs web reprodueixin contingut protegit @@ -107,6 +114,7 @@ Revoca tots els permisos del dispositiu Per permetre que Chrome utilitzi RA, activa també la càmera a la configuració d'Android. Ves endavant +Comparteix mitjançant Permet Permet que els llocs web accedeixin als sensors de moviment (opció recomanada) Ús @@ -163,7 +171,9 @@ Esborra Baixades actives Mou avall +Llisca cap avall per tancar el full. Impedeix que els llocs web de tercers desin i llegeixin les dades de les galetes +Reprodueix Selecciona elements Aquest dispositiu no pot llegir NFC Suprimit @@ -197,6 +207,7 @@ Un lloc web està utilitzant la càmera Se't tancarà la sessió d'aquest lloc web. Les galetes són fitxers que creen els llocs web que visites. Els llocs web les fan servir per recordar les teves preferències. Les galetes de tercers són les que creen altres llocs web. Part del contingut d'aquests llocs, com ara els anuncis o les imatges, es mostra a la pàgina web que visites. +Reproduint contingut multimèdia Cancel·la la selecció Se't tancarà la sessió de tots els llocs web. Permet el so d'un lloc web concret. @@ -204,6 +215,7 @@ Confirmes que vols esborrar d'aquest lloc web totes les dades locals, incloses les galetes, i restablir-ne tots els permisos? Aquesta ubicació està desactivada per a aquest dispositiu Error de baixada +No es pot seleccionar el certificat. Aquest contingut és del domini , oferit per Google. Silenciats Confirma @@ -216,6 +228,8 @@ Un lloc web està utilitzant la càmera i el micròfon Aquest lloc web mostra anuncis intrusius o enganyosos Toca per tornar al lloc web +Canvia +Retrocedeix Revoca el permís d'accés al dispositiu El lloc web és ràpid Impedeix que els llocs web es connectin a dispositius @@ -236,6 +250,7 @@ Bloqueja les galetes d'un lloc web concret. No es pot accedir a la navegació: Gestionat pels pares +Bluetooth Accés al micròfon Bloqueja els anuncis si el lloc web mostra publicitat intrusiva o enganyosa (opció recomanada) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cs.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cs.xtb index 44d4b5b7285..16850d2dce3 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cs.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_cs.xtb @@ -10,6 +10,7 @@ Další Vloženo do Zastavit +Google obrázky na této stránce optimalizoval, abyste ušetřili data. Zde se budou zobrazovat soubory uložené webovými stránkami Byl přidán web @@ -42,6 +43,7 @@ Předtím, než webům bude povoleno vytvořit 3D mapu vašeho okolí nebo sledovat polohu kamery, se zeptat (doporučeno) Povolit cookies Zeptat se, když chce web objevit zařízení Bluetooth v okolí (doporučeno) +Volbu certifikátu na straně klienta operační systém nepodporuje. Pokračovat  MB Stažení bylo dokončeno @@ -61,10 +63,12 @@ Zkopírováno Opravdu chcete resetovat všechna oprávnění webů pro objekt ? Stránky +Předchozí skladba Před povolením spuštění chráněného obsahu na webu se zeptat Povolit webům ukládat a číst data souborů cookie (doporučeno) Prohlížeč certifikátů Tímto vymažete celé úložiště webů (). +Automaticky blokováno Zobrazit informace Na některých webech blokováno Oprávnění pro Chrome zapnete v Nastavení pro Android. @@ -83,16 +87,19 @@ Synchronizace na pozadí Vymazat úložiště webů? Zpět +Přetočit dopředu Povolit webům přehrávat chráněný obsah (doporučeno) Kopírovat odkaz / ? Název Povolit soubory cookie pro konkrétní web. +Další skladba Nastavení webu Oznámení mohou aktivovat vibraci Otevřít Nastavení Navigace je blokována: Povoleno +Web přehrává média Zjednodušenou stránku poskytuje Google Vyskakovací okna a přesměrování Povolit webům přehrávat chráněný obsah @@ -107,6 +114,7 @@ Zrušit všechna oprávnění pro zařízení Chcete-li Chromu povolit používání rozšířené reality, zapněte v Nastavení Android fotoaparát. Vpřed +Sdílet prostřednictvím Povolit Povolit webům přístup k senzorům pohybu (doporučeno) Použití @@ -163,7 +171,9 @@ Vymazat Aktivní stahování Posunout dolů +Zavřete přejetím prstem dolů. Zabránit webům třetích stran v ukládání a čtení dat souborů cookie +Přehrát Výběr položek Toto zařízení neumí číst NFC Smazáno @@ -197,6 +207,7 @@ Web používá váš fotoaparát Budete odhlášeni z tohoto webu. Soubory cookie jsou vytvářeny webovými stránkami, které navštěvujete. Weby je používají k tomu, aby si zapamatovaly vaše preference. Soubory cookie třetích stran jsou vytvářeny ostatními weby. Tyto weby na navštívené stránce vlastní nějaký obsah, např. reklamy nebo obrázky. +Přehrávání médií Zrušit výběr Budete odhlášeni ze všech webů. Zapne zvuk na konkrétním webu. @@ -204,6 +215,7 @@ Opravdu chcete vymazat všechna místní data tohoto webu, včetně souborů cookie, a resetovat všechna jeho oprávnění? Vypnuto v tomto zařízení Stažení se nezdařilo +Certifikát nelze vybrat. Tento obsah pochází z domény . Poskytováno společností Google. Ztlumeno Potvrdit @@ -216,6 +228,8 @@ Webové stránky používají váš fotoaparát a mikrofon Tento web zobrazuje rušivé nebo zavádějící reklamy Klepnutím se vrátíte na web +Změnit +Přetočit dozadu Zrušit oprávnění zařízení Web je rychlý Bránit webům v připojení k zařízením @@ -236,6 +250,7 @@ Blokovat soubory cookie pro konkrétní web. Navigace není dosažitelná: Spravováno vašimi rodiči +Bluetooth Přístup k mikrofonu Blokovat, pokud web zobrazuje rušivé nebo zavádějící reklamy (doporučeno) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_da.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_da.xtb index d24448ee744..e7f799d8333 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_da.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_da.xtb @@ -10,6 +10,7 @@ Næste Indlejret på Stop +Billederne på denne side er blevet optimeret af Google, så du sparer data. Filer, der gemmes af websites, vises her Websitet blev tilføjet @@ -42,6 +43,7 @@ Spørg, inden websites kan oprette et 3D-kort over dine omgivelser eller registrere kamerapositionen (anbefales) Tillad cookies Spørg, når et website vil søge efter Bluetooth-enheder i nærheden (anbefalet) +Klientens certifikatvalg understøttes ikke af operativsystemet. Fortsæt MB Downloaden er fuldført @@ -61,10 +63,12 @@ Kopieret Er du sikker på, at du vil nulstille alle websitetilladelser for ? Website +Forrige nummer Spørg, før websites begynder at afspille beskyttet indhold Tillad, at websites gemmer og læser cookiedata (anbefales) Certifikatfremviser Dette rydder alle i websitelagerpladsen. +Automatisk blokering Vis info Blokeret på visse websites Aktivér tilladelser for Chrome i Android-indstillingerne. @@ -83,16 +87,19 @@ Synkronisering i baggrunden Vil du rydde websitelagerpladsen? Tilbage +Spol fremad Tillad, at websites afspiller beskyttet indhold (anbefales) Kopiér linket /? Titel Tillad cookies for et bestemt website. +Næste nummer Indstillinger for websites Notifikationer kan få enheden til at vibrere Åbn Indstillinger Navigationen er blokeret: Tilladt +Et website afspiller medier Lite-side leveret af Google Pop op-vinduer og omdirigeringer Tillad, at websites afspiller beskyttet indhold @@ -107,6 +114,7 @@ Tilbagekald alle tilladelser for enheden Hvis du vil give Chrome tilladelse til at bruge AR, skal du også aktivere kameraet i Android-indstillingerne. Gå fremad +Del via Tillad Tillad, at websites kan få adgang til bevægelsessensorer (anbefales) Databrug @@ -163,7 +171,9 @@ Ryd Aktive downloads Flyt ned +Stryg ned for at lukke. Undgå, at tredjepartswebsites gemmer og læser cookiedata +Afspil Vælg elementer Denne enhed kan ikke læse NFC Slettet @@ -197,6 +207,7 @@ Et website anvender dit kamera Du bliver logget ud af dette website. Cookies er filer, som oprettes af de websites, du besøger. Websites anvender dem til at huske dine præferencer. Tredjepartscookies oprettes af andre websites. Disse websites ejer noget af indholdet, f.eks. de annoncer eller billeder, der vises på den webside, du besøger. +Afspilning af medier Annuller valg Du bliver logget ud af alle websites. Tillad, at et bestemt website afspiller lyd. @@ -204,6 +215,7 @@ Er du sikker på, at du vil slette alle lokale data, herunder cookies, og nulstille alle tilladelser for dette website? Deaktiveret på denne enhed Download mislykkedes +Certifikatet kunne ikke vælges. Dette indhold er fra , som leveres af Google. Websites, hvor lyden er slået fra Bekræft @@ -216,6 +228,8 @@ Et website anvender dit kamera og din mikrofon Dette website viser påtrængende eller vildledende annoncer Tryk for at vende tilbage til websitet +Skift +Spol tilbage Tilbagekald adgangstilladelsen til enheden Websitet er hurtigt Bloker websites fra at oprette forbindelse til enheder @@ -236,6 +250,7 @@ Bloker cookies for et bestemt website. Navigationen er ikke mulig: Administreret af dine forældre +Bluetooth Adgang til din mikrofon Bloker, hvis websitet viser påtrængende eller vildledende annoncer (anbefales) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_de.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_de.xtb index 3d7a5595e1c..8e20b0e0412 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_de.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_de.xtb @@ -10,6 +10,7 @@ Weiter Auf eingebettet Stoppen +Die Bilder auf dieser Seite wurden von Google optimiert, um den Datenverbrauch zu reduzieren. Hier werden von Websites gespeicherte Dateien angezeigt  – Website "" hinzugefügt @@ -42,6 +43,7 @@ Nachfragen, bevor Websites erlaubt wird, eine 3D-Karte meiner Umgebung zu erstellen oder die Kameraposition zu verfolgen (empfohlen) Cookies zulassen Nachfragen, wenn eine Website nach Bluetooth-Geräten in der Nähe suchen möchte (empfohlen) +Die clientseitige Zertifikatauswahl wird vom Betriebssystem nicht unterstützt. Weiter  MB Download abgeschlossen @@ -61,10 +63,12 @@ Kopiert Möchten Sie wirklich alle Websiteberechtigungen für zurücksetzen? Website +Vorheriger Titel Fragen, bevor die Wiedergabe geschützter Inhalte auf Websites zugelassen wird Websites dürfen Cookiedaten speichern und lesen (empfohlen) Zertifikats-Viewer Der gesamte Websitespeicher () wird gelöscht. +Automatisch blockiert Informationen anzeigen Auf einigen Websites blockiert Berechtigungen für Chrome in den Android-Einstellungen aktivieren @@ -83,16 +87,19 @@ Hintergrundsynchronisierung Websitespeicher löschen? Zurück +Nach vorne navigieren Wiedergabe geschützter Inhalte auf Websites zulassen (empfohlen) Link kopieren von ? Titel Cookies für eine bestimmte Website werden zugelassen. +Nächster Titel Website-Einstellungen Bei Benachrichtigungen kann das Gerät vibrieren Einstellungen öffnen Die Navigation zu ist blockiert. Zugelassen +Eine Website gibt Medien wieder Lite-Modus-Seite von Google bereitgestellt Pop-ups und Weiterleitungen Websites erlauben, geschützte Inhalte wiederzugeben @@ -107,6 +114,7 @@ Alle Berechtigungen für Gerät entziehen Damit Chrome AR verwenden kann, muss auch die Kamera in den Android-Einstellungen aktiviert sein. Weiter +Teilen über Zulassen Websites den Zugriff auf Bewegungssensoren erlauben (empfohlen) Verwendung @@ -163,7 +171,9 @@ Löschen Aktive Downloads Nach unten +Zum Schließen nach unten wischen. Websites von Drittanbietern am Speichern und Lesen von Cookiedaten hindern +Wiedergabe Einträge auswählen NFC wird von diesem Gerät nicht unterstützt Gelöscht @@ -197,6 +207,7 @@ Eine Website verwendet Ihre Kamera Sie werden von dieser Website abgemeldet. Cookies sind Dateien, die von Websites erstellt werden, die Sie besuchen. Sie werden zum Speichern Ihrer Einstellungen verwendet. Drittanbieter-Cookies werden von anderen Websites erstellt. Diesen Websites gehören einige der Inhalte, wie z. B. Werbeanzeigen oder Bilder, die Sie auf der besuchten Webseite sehen. +Medien werden wiedergegeben Auswahl aufheben Sie werden von allen Websites abgemeldet. Wiedergabe von Ton auf einer bestimmten Website zulassen. @@ -204,6 +215,7 @@ Möchten Sie wirklich alle lokalen Daten einschließlich Cookies löschen und alle Berechtigungen für diese Website zurücksetzen? Für dieses Gerät deaktiviert Downloadfehler +Zertifikat kann nicht ausgewählt werden. Dieser Inhalt ist von und wurde von Google bereitgestellt. Stummgeschaltet Bestätigen @@ -216,6 +228,8 @@ Eine Website verwendet Ihre Kamera und Ihr Mikrofon Diese Website zeigt aufdringliche oder irreführende Werbung an Tippen, um zur Website zurückzukehren +Ändern +Zurück navigieren Zugriffsberechtigung auf Gerät widerrufen Website ist schnell Verhindern, dass Websites eine Verbindung zu Geräten herstellen @@ -236,6 +250,7 @@ Cookies für eine bestimmte Website werden blockiert. Navigation nicht möglich: ist nicht erreichbar. Von deinen Eltern verwaltet +Bluetooth Mikrofonzugriff Blockieren, wenn Website aufdringliche oder irreführende Werbung anzeigt (empfohlen) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_el.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_el.xtb index 30d38b535d8..63d3b55ba1e 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_el.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_el.xtb @@ -10,6 +10,7 @@ Επόμενο Ενσωματωμένο σε Διακοπή +Οι εικόνες αυτής της σελίδας έχουν βελτιστοποιηθεί από την Google για την εξοικονόμηση των δεδομένων σας. Τα αρχεία που αποθηκεύονται από ιστοτόπους εμφανίζονται εδώ - Προστέθηκε ο ιστότοπος @@ -42,6 +43,7 @@ Να γίνεται ερώτηση προτού επιτραπεί σε ιστοτόπους η δημιουργία τρισδιάστατου χάρτη του περιβάλλοντα χώρου σας και η παρακολούθηση της θέσης της κάμερας (συνιστάται) Αποδοχή cookie Να γίνεται ερώτηση όταν ένας ιστότοπος επιθυμεί να εντοπίσει κοντινές συσκευες Bluetooth (συνιστάται) +Η επιλογή πιστοποιητικού από τον πελάτη δεν υποστηρίζεται από το λειτουργικό σύστημα. Συνέχεια MB Η λήψη ολοκληρώθηκε @@ -61,10 +63,12 @@ Αντιγράφ. Είστε βέβαοι ότι θέλετε να αναιρεθούν όλες οι άδειες ιστοτόπου για το αντικείμενο ; Ιστότοπος +Προηγούμενο κομμάτι Να γίνεται ερώτηση προτού επιτραπεί στους ιστοτόπους η αναπαραγωγή προστατευόμενου περιεχομένου Να επιτρέπεται στους ιστότοπους η αποθήκευση και η ανάγνωση δεδομένων cookie (συνιστάται) Πρόγρ. προβολής πιστοποιητικού Αυτό θα διαγράψει και τα του αποθηκευτικού χώρου ιστοτόπων. +Αποκλείστηκε αυτόματα Πληροφορίες εκπομπής Αποκλεισμός σε ορισμένους ιστοτόπους Ενεργοποίηση των αδειών για το Chrome στις Ρυθμίσεις Android. @@ -83,16 +87,19 @@ Συγχρονισμός παρασκηνίου Διαγ.αποθ.χώρου ιστότ.; Πίσω +Αναζήτηση προς τα εμπρός Να επιτρέπεται στους ιστοτόπους να αναπαράγουν προστατευμένο περιεχόμενο (συνιστάται) Αντ. συνδ. / ? Τίτλος Επιτρέπει τα cookie για έναν συγκεκριμένο ιστότοπο. +Επόμενο κομμάτι Ρυθμίσεις ιστότοπου Κατά τη λήψη ειδοποιήσεων ενδέχεται να δονείται η συσκευή Ανοίξτε τις ρυθμίσεις Αποκλείστηκε η μετάβαση στη διεύθυνση: Επιτρέπεται +Ένας ιστότοπος κάνει αναπαραγωγή μέσων Σελίδα Lite που παρέχεται από την Google Αναδυόμενα παράθυρα και ανακατευθύνσεις Να επιτρέπεται στους ιστοτόπους η αναπαραγωγή προστατευμένου περιεχομένου @@ -107,6 +114,7 @@ Ανάκληση όλων των αδειών για τη συσκευή Για να επιτρέπεται στο Chrome να χρησιμοποιεί AR, ενεργοποιήστε επίσης την κάμερα στις Ρυθμίσεις Android. Μετάβαση προς τα εμπρός +Μοιραστείτε μέσω Επιτρέπεται Να επιτρέπεται στους ιστοτόπους η πρόσβαση στους αισθητήρες κίνησης (συνιστάται) Χρήση @@ -163,7 +171,9 @@ Διαγραφή Ενεργές λήψεις Μετακίνηση προς τα κάτω +Σύρετε προς τα κάτω για κλείσιμο. Αποτροπή αποθήκευσης και ανάγνωσης δεδομένων cookie από ιστότοπους τρίτων +Αναπαραγωγή Επιλέξτε στοιχεία Αυτή η συσκευή δεν μπορεί να διαβάσει NFC Διαγράφηκε @@ -197,6 +207,7 @@ Ένας ιστότοπος χρησιμοποιεί την κάμερά σας. Θα αποσυνδεθείτε από αυτόν τον ιστότοπο. Τα cookie είναι αρχεία που δημιουργούνται από ιστοτόπους που επισκέπτεστε. Οι ιστότοποι τα χρησιμοποιούν για να απομνημονεύουν τις προτιμήσεις σας. Τα cookie τρίτου μέρους δημιουργούνται από άλλους ιστοτόπους. Αυτοί οι ιστότοποι διαθέτουν κάποιο περιεχόμενο, όπως διαφημίσεις ή εικόνες, το οποίο μπορείτε να δείτε στην ιστοσελίδα που επισκέπτεστε. +Αναπαραγωγή πολυμέσων Ακύρωση επιλογής Θα αποσυνδεθείτε από όλους τους ιστοτόπους. Να επιτρέπεται ο ήχος για έναν συγκεκριμένο ιστότοπο. @@ -204,6 +215,7 @@ Είστε βέβαιοι ότι θέλετε να διαγράψετε όλα τα δεδομένα γι' αυτόν τον ιστότοπο, όπως τα cookie, και να επαναφέρετε όλα τα δικαιώματά του; Έχει απενεργοποιηθεί για αυτήν τη συσκευή Η λήψη απέτυχε +Δεν είναι δυνατή η επιλογή του πιστοποιητικού. Αυτό το περιεχόμενο προέρχεται από το και παρέχεται από την Google. Σε σίγαση Επιβεβαίωση @@ -216,6 +228,8 @@ Ένας ιστότοπος χρησιμοποιεί την κάμερα και το μικρόφωνό σας. Αυτός ο ιστότοπος εμφανίζει παρεμβατικές ή παραπλανητικές διαφημίσεις Πατήστε για επιστροφή στον ιστότοπο +Αλλαγή +Αναζήτηση προς τα πίσω Ανάκληση άδειας συσκευής Ο ιστότοπος είναι γρήγορος Αποκλεισμός ιστοτόπων από τη σύνδεση σε συσκευές @@ -236,6 +250,7 @@ Αποκλεισμός cookie για έναν συγκεκριμένο ιστοτόπο. Δεν είναι δυνατή η μετάβαση στη διεύθυνση: Διαχειρίζεται από τους γονείς σου +Bluetooth Πρόσβαση στο μικρόφωνό σας Αποκλεισμός εάν ο ιστότοπος εμφανίζει παρεμβατικές ή παραπλανητικές διαφημίσεις (συνιστάται) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_en-GB.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_en-GB.xtb index e96624653f8..a8b9abf20d5 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_en-GB.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_en-GB.xtb @@ -10,6 +10,7 @@ Next Embedded on Stop +To save you data, this page's images have been optimised by Google. Files saved by websites appear here Site added @@ -42,6 +43,7 @@ Ask before allowing sites to create a 3D map of your surroundings or track camera position (recommended) Allow cookies Ask when a site wants to discover nearby Bluetooth devices (recommended) +Client side certificate selection is not supported by the operating system. Continue MB Download complete @@ -61,10 +63,12 @@ Copied Are you sure that you want to reset all site permissions for ? Site +Previous track Ask before allowing sites to play protected content Allow sites to save and read cookie data (recommended) Certificate viewer This will clear all of website storage. +Automatically blocked Show info Blocked on some sites Turn on permissions for Chrome in Android Settings. @@ -83,16 +87,19 @@ Background sync Clear site storage? Back +Seek forward Allow sites to play protected content (recommended) Copy link /? Title Allow cookies for a specific site. +Next track Site settings Notifications may vibrate the device Open settings Navigation is blocked: Allowed +A site is playing media Lite page provided by Google Pop-ups and redirects Allow sites to play protected content @@ -107,6 +114,7 @@ Revoke all permissions for device To let Chrome use AR, also turn on the camera in Android Settings. go forward +Share via Allow Allow sites to access motion sensors (recommended) Usage @@ -163,7 +171,9 @@ Clear Active downloads Move down +Swipe down to close. Prevent third-party websites from saving and reading cookie data +Play Select items This device can't read NFC Deleted @@ -197,6 +207,7 @@ A site is using your camera You'll be signed out of this site. Cookies are files created by websites that you visit. Sites use them to remember your preferences. Third-party cookies are created by other sites. These sites own some of the content, like ads or images, that you see on the web page that you visit. +Playing media Cancel selection You'll be signed out of all sites. Allow sound for a specific site. @@ -204,6 +215,7 @@ Are you sure that you want to clear all local data, including cookies, and reset all permissions for this website? Turned off for this device Download failed +Unable to select certificate. This content is from , delivered by Google. Muted Confirm @@ -216,6 +228,8 @@ A site is using your camera and microphone This site shows intrusive or misleading ads Tap to return to the site +Change +Seek backward Revoke device permission Site is fast Block sites from connecting to devices @@ -236,6 +250,7 @@ Block cookies for a specific site. Navigation is unreachable: Managed by your parents +Bluetooth Access your microphone Block if site shows intrusive or misleading ads (recommended) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es-419.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es-419.xtb index 930bffc3e94..212c7e84c23 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es-419.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es-419.xtb @@ -10,6 +10,7 @@ Siguiente Incorporado en Interrumpir +Para ahorrar datos, Google optimizó las imágenes de esta página. Aquí se mostrarán los archivos que guarden los sitios web : Se agregó el sitio . @@ -42,6 +43,7 @@ Preguntar antes de permitir que los sitios creen un mapa 3D de tu entorno o hagan un seguimiento de la posición de la cámara (recomendado) Permitir cookies Preguntarme cuando un sitio intente conectarse a dispositivos Bluetooth cercanos (recomendado) +El sistema operativo no admite la selección de certificados del lado del cliente. Continuar  MB Se completó la descarga @@ -61,10 +63,12 @@ Copiado ¿Confirmas que quieres restablecer todos los permisos de sitios para ? Sitio +Pista anterior Preguntar antes de permitir que los sitios reproduzcan contenido protegido Permitir que todos los sitios guarden y lean datos de cookies (recomendado) Visualizador de certificados Se borrarán del almacenamiento del sitio web. +Bloqueado de forma automática Mostrar información Bloqueados en algunos sitios Activa los permisos para Chrome en Configuración de Android. @@ -83,16 +87,19 @@ Sincronización en segundo plano ¿Borrar el almacenamiento de sitios? Atrás +Buscar más adelante Permitir que los sitios reproduzcan contenido protegido (recomendado) Copiar vínculo /? Título Permite las cookies para un sitio específico. +Siguiente pista Configuración de sitios Es posible que las notificaciones hagan vibrar el dispositivo Abrir la configuración Navegación bloqueada: Permitido +Un sitio está reproduciendo contenido multimedia Google proporcionó la página básica Ventanas emergentes y redireccionamientos Permitir que los sitios reproduzcan contenido protegido @@ -107,6 +114,7 @@ Revocar todos los permisos del dispositivo Para permitir el uso de la RA en Chrome, también debes activar la cámara en la Configuración de Android. Avanzar +Compartir mediante Permitir Permitir que los sitios accedan a los sensores de movimiento (recomendado) Uso @@ -163,7 +171,9 @@ Borrar Descargas activas Mover hacia abajo +Desliza hacia abajo para cerrar. Evitar que los sitios web de terceros guarden y lean datos de cookies +Reproducir Seleccionar elementos Este dispositivo no puede leer NFC Eliminado @@ -197,6 +207,7 @@ Un sitio está utilizando la cámara Saldrás de este sitio. Las cookies son archivos que crean los sitios web que visitas para recordar tus preferencias. Las cookies de terceros son las que se generan desde otros sitios a los cuales pertenece parte del contenido que ves en la página web que visitas (como anuncios o imágenes). +Reproduciendo contenido multimedia Cancelar la selección Saldrás de todos los sitios. Habilitar el sonido de un sitio específico @@ -204,6 +215,7 @@ ¿Quieres eliminar todos los datos locales de este sitio web, incluidas las cookies, y restablecer todos los permisos? Desactivado para este dispositivo Error en la descarga +No es posible seleccionar un certificado Este contenido es de , publicado por Google. Silenciados Confirmar @@ -216,6 +228,8 @@ Un sitio está utilizando la cámara y el micrófono Este sitio muestra anuncios intrusivos o engañosos Presiona para volver al sitio +Cambiar +Buscar más atrás Revocar permiso para el dispositivo El sitio es rápido Impedir que los sitios se conecten a los dispositivos @@ -236,6 +250,7 @@ Bloquea las cookies en un sitio específico. Navegación inaccesible: Administrado por tus padres +Bluetooth Acceso al micrófono Bloquear si el sitio muestra anuncios intrusivos o engañosos (opción recomendada) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es.xtb index b53e04b414b..0f4f79ac66c 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_es.xtb @@ -10,6 +10,7 @@ Siguiente Insertada en Interrumpir +Para ahorrar datos, Google ha optimizado las imágenes de esta página. Los archivos que guarden los sitios web aparecerán aquí : Se ha añadido el sitio @@ -42,6 +43,7 @@ Preguntar antes de permitir que los sitios web creen un mapa 3D de tu entorno o hagan un seguimiento de la posición de la cámara (recomendado) Permitir cookies Preguntar cuando un sitio web quiera buscar dispositivos Bluetooth cercanos (recomendado) +El sistema operativo no admite la selección de certificados de cliente. Continuar  MB Descarga finalizada @@ -61,10 +63,12 @@ Copiado ¿Seguro que quieres restablecer todos los permisos de los sitios web de ? Sitio +Pista anterior Preguntar antes de permitir que los sitios web reproduzcan contenido protegido Permitir que los sitios guarden y lean datos de cookies (recomendado) Visor de certificados Se borrarán los de almacenamiento del sitio web. +Bloqueado automáticamente Mostrar información Bloqueados en algunos sitios web Activa los permisos para Chrome en los ajustes de Android. @@ -83,16 +87,19 @@ Sincronización en segundo plano ¿Borrar almacenamiento web? Volver +Buscar hacia delante Permitir que los sitios web reproduzcan contenido protegido (recomendado) Copiar enlace /? Título Permitir cookies en un sitio web específico. +Pista siguiente Configuración de sitios web Es posible que las notificaciones hagan que el dispositivo vibre Abrir Configuración Se ha bloqueado la navegación: Permitido +Un sitio web está reproduciendo elementos multimedia Página básica ofrecida por Google Ventanas emergentes y redirecciones Permitir que los sitios web reproduzcan contenido protegido @@ -107,6 +114,7 @@ Revocar todos los permisos de este dispositivo Para que Chrome pueda usar la realidad aumentada, activa la cámara también en los ajustes de Android. Avanzar +Compartir a través de Permitir Permitir que los sitios web accedan a los sensores de movimiento (recomendado) Uso @@ -163,7 +171,9 @@ Eliminar Descargas activas Bajar +Desliza el dedo hacia abajo para cerrarla. Impedir que los sitios web de terceros guarden y consulten datos de cookies +Reproducir Seleccionar elementos Este dispositivo no puede leer NFC Eliminado @@ -197,6 +207,7 @@ Un sitio web está usando tu cámara Se cerrará tu sesión en este sitio web. Las cookies son archivos que crean los sitios web que visitas. Estos las usan para recordar tus preferencias. Las cookies de terceros las crean otros sitios web. Parte del contenido que ves en la página web que visitas, como anuncios o imágenes, pertenece a esos otros sitios web. +Reproduciendo contenido multimedia Cancelar selección Se cerrarán tus sesiones en todos los sitios web. Permite que se reproduzcan sonidos en un sitio web específico. @@ -204,6 +215,7 @@ ¿Seguro que quieres borrar todos los datos locales, incluidas las cookies, y restablecer todos los permisos de este sitio web? Desactivada para este dispositivo No se ha podido descargar el archivo +No se puede seleccionar el certificado. Este contenido procede de , publicado por Google. Silenciados Confirmar @@ -216,6 +228,8 @@ Un sitio web está usando tu cámara y tu micrófono El sitio web muestra anuncios invasivos o engañosos Toca para volver al sitio web +Cambiar +Buscar hacia atrás Revocar permiso de dispositivo El sitio web es rápido No permitir que los sitios web se conecten a dispositivos @@ -236,6 +250,7 @@ Bloquear las cookies de un sitio web específico. No se puede realizar la navegación: Administrado por tus padres +Bluetooth Acceder al micrófono Bloquear si el sitio web muestra anuncios invasivos o engañosos (recomendado) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_et.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_et.xtb index debba371a06..ca9e3851409 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_et.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_et.xtb @@ -10,6 +10,7 @@ Järgmine Manustatud aadressil Peata +Andmemahu säästmiseks optimeerib Google selle lehe kujutisi. Veebisaitide salvestatud failid kuvatakse siin on lisatud @@ -42,6 +43,7 @@ Küsi enne saitidele loa andmist mind ümbritsevast 3D-kaardi loomiseks või kaamera asendi jälgimiseks (soovitatav) Luba küpsisefailid Küsi, kui sait soovib läheduses asuvaid Bluetoothi seadmeid tuvastada (soovitatav) +Operatsioonisüsteem ei toeta kliendipoolset sertifikaadi valimist. Jätka MB Allalaadimine jõudis lõpule: @@ -61,10 +63,12 @@ Kopeeritud Kas soovite objekti jaoks kindlasti lähtestada kõik saidi load? Sait +Eelmine lugu Küsi enne saitidel kaitstud sisu esitamise lubamist Lubab saitidel salvestada küpsiseid ja lugeda küpsiste andmeid (soovitatav) Sertifikaadikuvaja See tühjendab veebisaidi salvestusruumi mahuga . +Automaatselt blokeeritud Kuva teave Blokeeritud teatud saitidel Lülita Android-seadetes sisse load Chrome'ile. @@ -83,16 +87,19 @@ Taustal sünkroonimine Kas tühj. saidi salvestusruum? Tagasi +Keri edasi Luba saitidel esitada kaitstud sisu (soovitatav) Kop. link / ? Pealkiri Lubage konkreetse saidi küpsisefailid. +Järgmine lugu Saidi seaded Seade võib märguannete korral vibreerida Ava seaded Navigeerimine on blokeeritud: Lubatud +Sait esitab meediasisu Lihtsustatud lehte pakub Google Hüpikaknad ja ümbersuunamised Saitidel on lubatud esitada kaitstud sisu @@ -107,6 +114,7 @@ Seadme puhul kõigi lubade tühistamine Selleks et Chrome saaks AR-i kasutada, lülitage Androidi seadetes sisse ka kaamera. Edasiminek +Jagamine: Luba Luba saitide jaoks juurdepääs liikumisanduritele (soovitatav) Kasutus @@ -163,7 +171,9 @@ Tühjenda Aktiivsed allalaadimised Liiguta alla +Sulgemiseks pühkige alla. Keela kolmanda osapoole veebisaitidele küpsisefailide andmete salvestamine ja lugemine +Esita Valige üksused See seade ei saa NFC andmeid lugeda Kustutatud @@ -197,6 +207,7 @@ Sait kasutab teie kaamerat Teid logitakse sellelt saidilt välja. Küpsisefailid on failid, mille on loonud teie külastatud veebisaidid. Saidid kasutavad neid teie eelistuste meeldejätmiseks. Kolmanda osapoole küpsisefailid loovad teised saidid. Need saidid omavad külastatud veebisaidil kuvatud sisu, nt reklaame või pilte. +Meedia esitamine Tühista valik Teid logitakse kõikidelt saitidelt välja. Konkreetse saidi heli lubamine. @@ -204,6 +215,7 @@ Kas soovite kindlasti kustutada kõik kohalikud andmed (sh küpsised) ja lähtestada kõik selle veebisaidi load? Selle seadme puhul on välja lülitatud Allalaadimine ebaõnnestus +Sertifikaati ei saa valida. See sisu pärineb domeenilt ja seda pakub Google. Vaigistatud Kinnita @@ -216,6 +228,8 @@ Sait kasutab teie kaamerat ja mikrofoni Sait kuvab sekkuvaid või eksitavaid reklaame. Puudutage saidile naasmiseks +Muuda +Keri tagasi Seadme loa tühistamine Sait on kiire Blokeeri saitidel seadmetega ühenduse loomine @@ -236,6 +250,7 @@ Blokeerige konkreetse saidi küpsisefailid. Navigeerimine ei ole saadaval: Vanemate hallatud +Bluetooth Juurdepääs mikrofonile Blokeeri, kui sait kuvab sekkuvaid või eksitavaid reklaame (soovitatav) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_eu.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_eu.xtb index be6e665de42..77ac286199d 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_eu.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_eu.xtb @@ -10,6 +10,7 @@ Hurrengoa helbidean kapsulatua Gelditu +Datuak aurrezteko, orri honetako irudiak optimizatu egin ditu Google-k. Webguneek gordetako fitxategiak hemen agertzen dira : webgunea gehitu da @@ -42,6 +43,7 @@ Eskatu zure baimena webguneei inguruaren 3D-ko mapa bat sortu edo kameraren posizioaren jarraipena egiteko baimena eman aurretik (gomendatua) Onartu cookieak Eskatu nire baimena webgune batek inguruko Bluetooth bidezko gailuak bilatu nahi dituenean (gomendatua) +Sistema eragileak ez du onartzen bezeroarentzako ziurtagiria hautatzea. Jarraitu  MB Deskargatu da @@ -61,10 +63,12 @@ Kopiatuta Ziur objektuaren webgune-baimen guztiak berrezarri nahi dituzula? Webgunea +Aurreko pista Webguneei eduki babestua erreproduzitzeko baimena eman aurretik, eskatu onespena Baimendu webguneei cookieen datuak gordetzea eta irakurtzea (gomendatua) Ziurtagiri-ikustailea ezabatuko dira webguneen datuetatik. +Automatikoki blokeatuta Erakutsi informazioa Webgune batzuetan blokeatu dira iragarkiak Aktibatu Chrome-rako baimenak Android-en ezarpenetan. @@ -83,16 +87,19 @@ Atzeko planoko sinkronizazioa Webgune-datuak garbitu? Atzera +Aurreratu Baimendu webguneei eduki babestua erreproduzitzea (gomendatua) Kopiatu esteka / ? Izena Onartu webgune zehatz baten cookieak. +Hurrengo pista Webgunearen ezarpenak Gailua dardararaz dezakete jakinarazpenek Ireki ezarpenak orrira joateko aukera blokeatuta dago Baimenduta +Webgune bat multimedia-edukia erreproduzitzen ari da Google-k eskainitako oinarrizko orria Leiho gainerak. / Birbideratzeak Baimendu webguneei eduki babestua erreproduzitzea @@ -107,6 +114,7 @@ Baliogabetu gailuaren baimen guztiak Chrome-k errealitate areagotua erabil dezan, aktibatu kamera atzitzeko baimena Android-en ezarpenetan. Aurrera +Partekatu honen bidez: Baimendu Eman mugimendu-sentsoreak atzitzeko baimena webguneei (gomendatua) Erabilera @@ -163,7 +171,9 @@ Garbitu Deskarga aktiboak Eraman behera +Pasatu hatza behera ixteko. Eragotzi hirugarrenen webguneek cookieen datuak gordetzea eta irakurtzea +Erreproduzitu Hautatu elementuak Gailu honek ezin du irakurri NFC Ezabatu egin da @@ -197,6 +207,7 @@ Webgune bat kamera erabiltzen ari da Webgune honetako saioa amaitu egingo da. Bisitatzen dituzun webguneek sortutako fitxategiak dira cookieak. Zure hobespenak gogoratzeko erabiltzen dituzte webguneek. Hirugarrenen cookieak beste webgune batzuek sortzen dituzte. Bisitatzen duzun web-orrian ikusten dituzun eduki batzuen jabeak dira webgune horiek; adibidez, iragarkienak edo irudienak. +Multimedia-edukia erreproduzitzen Utzi hautapena bertan behera Webgune guztietan amaituko da saioa. Baimendu webgune jakin baten audioa. @@ -204,6 +215,7 @@ Ziur webgune honek gailuan ezarritako datu guztiak (cookieak barne) ezabatu eta baimen guztiak berrezarri nahi dituzula? Desaktibatu egin da gailu honetan Ezin izan da deskargatu +Ezin da hautatu ziurtagiria. Eduki hau domeinukoa da eta Google-k eskaintzen du. Audioa desaktibatuta Berretsi @@ -216,6 +228,8 @@ Webgune bat kamera eta mikrofonoa erabiltzen ari da Webgune honek iragarki oztopatzaile edo iruzurrezkoak erakusten ditu Sakatu hau webgunera itzultzeko +Aldatu +Atzeratu Kendu gailua atzitzeko baimena Webgunea bizkorra da Ez utzi webguneei gailuekin konektatzen @@ -236,6 +250,7 @@ Blokeatu webgune jakin bateko cookieak. Ezin da joan orrira Gurasoek kudeatuta +Bluetooth-a Atzitu mikrofonoa Webguneak iragarki oztopatzaileak erakusten baditu, blokea itzazu (gomendatua) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb index cf58c1752f7..8c47ba88f81 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb @@ -10,6 +10,7 @@ بعدی جاسازی‌شده در توقف +‏برای صرفه‌جویی درمصرف داده، Google تصاویر این صفحه را بهینه‌سازی کرده است. فایل‌هایی را که وب‌سایت‌ها ذخیره کرده‌اند، در اینجا نمایش داده می‌شود - سایت اضافه شد @@ -42,6 +43,7 @@ قبل از اینکه به سایت‌ها اجازه داده شود نقشه سه‌بعدی از محیط ایجاد کنند یا موقعیت دوربین را ردیابی کنند سؤال شود (توصیه می‌شود) مجاز کردن کوکی ها وقتی سایتی می‌خواهد دستگا‌ه‌های بلوتوث اطراف را پیدا کند، سؤال شود (توصیه می‌شود) +انتخاب گواهی سمت کلاینت توسط سیستم‌عامل پشتیبانی نمی‌شود. ادامه مگابایت بارگیری کامل شد @@ -61,10 +63,12 @@ کپی شد مطمئن‌اید می‌خواهید همه مجوزهای سایت مربوط به را بازنشانی کنید؟ سایت +آهنگ قبلی قبل از اجازه دادن به سایت‌ها برای پخش محتوای محافظت‌شده سؤال شود سایت‌ها مجاز به ذخیره و خواندن داده‌های کوکی باشند (توصیه می‌شود) بیننده گواهی این کار کل فضای ذخیره‌سازی وب‌سایت را پاک می‌کند. +مسدود‌شده به‌طور خودکار نمایش دادن اطلاعات در برخی سایت‌ها مسدود می‌شود ‏مجوزهای Chrome را در تنظیمات Android روشن کنید. @@ -83,16 +87,19 @@ همگام‌سازی پس‌زمینه فضای ذخیره سایت پاک شود؟ بازگشت +جستجو به جلو به سایت‌ها اجازه داده شود محتوای محافظت‌شده پخش کنند (توصیه می‌شود) کپی پیوند از ؟ عنوان کوکی‌ها را برای سایت خاصی مجاز کنید. +آهنگ بعدی تنظیمات سایت اعلان‌ها ممکن است دستگاه را بلرزانند باز کردن تنظیمات پیمایش مسدود شده است: مجاز است +سایتی درحال پخش رسانه است ‏صفحه ساده‌شده را Google ارائه کرده است پنجره‌های بازشو و هدایت‌ها به سایت‌ها اجازه داده شود محتوای محافظت‌شده پخش کنند @@ -107,6 +114,7 @@ لغو همه مجوزها برای دستگاه ‏برای اینکه به Chrome اجازه دهید از AR استفاده کند، دوربین را در تنظیمات Android نیز روشن کنید. جلو رفتن +اشتراک‌گذاری از طریق اجازه دادن مجاز بودن دسترسی سایت‌ها به حسگرهای حرکتی (توصیه می‌شود) کاربر @@ -163,7 +171,9 @@ پاک کردن بارگیری‌های فعال انتقال به پایین +برای بستن، تند به پایین بکشید. جلوگیری از ذخیره کردن و خواندن داده‌های کوکی توسط وب‌سایت‌های شخص ثالث +پخش انتخاب موارد ‏این دستگاه نمی‌تواند NFC را بخواند حذف شد @@ -197,6 +207,7 @@ سایتی درحال استفاده از دوربینتان است از سیستم این سایت خارج خواهید شد. کوکی‌ها فایل‌هایی هستند که توسط وب‌سایت‌هایی که بازدید می‌کنید ایجاد می‌شوند. سایت‌ها برای به‌خاطر سپردن اولویت‌هایتان از آن‌ها استفاده می‌کنند. کوکی‌های شخص ثالث را سایت‌های دیگری ایجاد می‌کنند. برخی از محتواها مثل آگهی‌ها یا تصاویر موجود در صفحه وبی که از آن دیدن می‌کنید، متعلق به این سایت‌ها هستند. +درحال پخش رسانه لغو انتخاب از سیستم همه سایت‌ها خارج خواهید شد. پخش صدا برای سایت خاصی مجاز شود. @@ -204,6 +215,7 @@ مطمئنید که می‌خواهید تمام داده‌های محلی، شامل کوکی‌ها را حذف کنید و تمام مجوزهای این وب‌سایت را بازنشانی کنید؟ برای این دستگاه غیرفعال شد بارگیری نشد +انتخاب گواهی ممکن نیست. ‏این محتوا از است و توسط Google ارائه می‌شود. صامت‌شده تأیید @@ -216,6 +228,8 @@ سایتی درحال استفاده از دوربین و میکروفونتان است این سایتْ آگهی‌های مزاحم یا گمراه‌کننده نشان می‌دهد برای برگشتن به سایت ضربه بزنید +تغییر +جستجو به عقب لغو مجوز دستگاه سایت سریع است مسدود کردن سایت‌ها برای اتصال به دستگاه‌ها @@ -236,6 +250,7 @@ کوکی‌ها را برای سایت خاصی مسدود کنید. پیمایش غیرقابل دسترسی است: مدیریت شده توسط والدین شما +بلوتوث دسترسی به میکروفن اگر سایتْ آگهی‌های مزاحم یا گمراه‌کننده نشان می‌دهد مسدود شود (توصیه می‌شود) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fi.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fi.xtb index 2d4229d3685..855df4d0b47 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fi.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fi.xtb @@ -10,6 +10,7 @@ Seuraava Upotettu sivustolle Pysäytä +Datan säästämiseksi Google on optimoinut tämän sivun kuvat. Sivustojen tallentamat tiedostot näkyvät tässä Sivusto lisättiin. @@ -42,6 +43,7 @@ Kysy, saavatko sivustot luoda 3D-kartan ympäristöstäsi tai seurata kameran asentoa (suositus) Salli evästeet Kysy aina, kun sivusto pyytää lupaa löytää lähellä olevat Bluetooth-laitteet (suositus) +Käyttöjärjestelmä ei tue palvelimen varmennevalintaa. Jatka Mt Lataus valmis @@ -61,10 +63,12 @@ Kopioitu Haluatko varmasti nollata kaikki sivuston käyttöoikeudet ()? Sivusto +Edellinen kappale Kysy, saavatko sivustot toistaa suojattua sisältöä Salli sivustojen tallentaa ja lukea evästetietoja (suositus) Varmennetiedot Tämä tyhjentää yhteensä tallennettuja sivustotietoja. +Estetty automaattisesti Näytä tiedot Estetty tietyillä sivustoilla Ota käyttöoikeudet käyttöön Chromelle Android-asetuksissa. @@ -83,16 +87,19 @@ Taustasynkronointi Poistetaanko tiedot? Takaisin +Kelaa eteenpäin Salli sivustojen toistaa suojattua sisältöä (suositus) Kopioi linkki / ? Nimi Salli evästeet tietyllä sivustolla. +Seuraava kappale Sivustoasetukset Laite voi väristä ilmoitusten yhteydessä. Avaa asetukset Kohde on estetty: Sallittu +Sivusto toistaa mediaa Googlen tarjoama yksinkertaistettu sivu Ponn.ikkunat ja uudelleenohjaus Salli sivustojen toistaa suojattua sisältöä @@ -107,6 +114,7 @@ Peru kaikki laitteen käyttöoikeudet Laita kamera päälle myös Androidin asetuksista, jotta Chrome voi käyttää AR:ää. Siirry eteenpäin +Jaa palvelussa: Salli Anna sivustojen käyttää liiketunnistimien lukemia (suositus) Käyttö @@ -163,7 +171,9 @@ Tyhjennä Aktiiviset lataukset Siirrä alas +Sulje pyyhkäisemällä alas. Estä kolmannen osapuolen verkkosivustoja tallentamasta ja lukemasta evästetietoja +Toista Valitse kohteet Tämä laite ei lue NFC-dataa Poistettu @@ -197,6 +207,7 @@ Sivusto käyttää kameraasi Sinut kirjataan ulos tältä sivustolta. Evästeet ovat avaamiesi verkkosivustojen luomia tiedostoja. Niiden avulla sivustot muistavat valintasi. Kolmannen osapuolen evästeitä luovat muut sivustot. Nämä sivustot omistavat mainoksia, kuvia tai muita sisältöjä, joita käytetään avaamallasi sivulla. +Toistetaan mediaa Peruuta valinta Sinut kirjataan ulos kaikilta sivustoilta. Salli tietyn sivuston äänet. @@ -204,6 +215,7 @@ Haluatko varmasti tyhjentää kaikki paikalliset tiedot, mukaan lukien evästeet, ja nollata kaikki tämän verkkosivuston käyttöluvat? Ei käytössä tällä laitteella Lataus epäonnistui. +Varmenteen valinta epäonnistui Google on toimittanut tämän sisällön osoitteesta . Mykistetty Vahvista @@ -216,6 +228,8 @@ Sivusto käyttää kameraasi ja mikrofoniasi Tällä sivustolla on häiritseviä tai harhaanjohtavia mainoksia Palaa sivustolle napauttamalla +Vaihda +Kelaa taaksepäin Peruuta laitteen käyttöoikeus Sivusto on nopea Estä sivustoja yhdistämästä laitteisiin @@ -236,6 +250,7 @@ Estä evästeet tietyltä sivustolta. Kohde ei ole saatavilla: Vanhempiesi hallinnoima +Bluetooth Mikrofonin käyttöoikeus Estä, jos sivusto näyttää häiritseviä tai harhaanjohtavia mainoksia (suositus) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fil.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fil.xtb index 618e33183a1..8399935a493 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fil.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fil.xtb @@ -10,6 +10,7 @@ Susunod Naka-embed sa Stop +Para makatipid ka ng data, na-optimize ng Google ang mga larawan ng page na ito. Lalabas dito ang mga file na na-save ng mga website - Nadagdag na ang site ng @@ -42,6 +43,7 @@ Magtanong bago payagan ang mga site na gumawa ng 3D na mapa ng iyong kapaligiran o subaybayan ang posisyon ng camera (inirerekomenda) Payagan ang cookies Magtanong kapag gusto ng isang site na tumuklas ng mga Bluetooth device na nasa malapit (inirerekomenda) +Hindi sinusuportahan ng operating system ang pagpipilian ng certificate sa panig ng kliyente. Magpatuloy MB Tapos nang mag-download @@ -61,10 +63,12 @@ Kinopya Sigurado ka bang gusto mong i-reset ang lahat ng pahintulot sa site para sa ? Site +Nakaraang track Magtanong bago payagan ang mga site na mag-play ng pinoprotektahang content Payagan ang mga site na mag-save at magbasa ng data ng cookie (inirerekomenda) Viewer ng certificate Iki-clear nito ang lahat ng ng storage ng website. +Awtomatikong na-block Ipakita ang Impormasyon Naka-block sa ilang site I-on ang mga pahintulot para sa Chrome sa Mga Setting ng Android. @@ -83,16 +87,19 @@ Pag-sync sa background I-clear ang storage ng site? Bumalik +Maghanap nang pasulong Pahintulutan ang mga site na mag-play ng pinoprotektahang content (inirerekomenda) Kopyahin ang link / ? Pamagat Payagan ang cookies para sa isang partikular na site. +Susunod na track Mga setting ng site Maaaring mag-vibrate ang device dahil sa mga notification Buksan ang mga setting Naka-block ang navigation: Pinapayagan +May site na nagpe-play ng media Hatid ng Google ang lite na page Mga pop-up at pag-redirect Pahintulutan ang mga site na mag-play ng pinoprotektahang content @@ -107,6 +114,7 @@ Bawiin ang lahat ng pahintulot para sa device Para payagan ang Chrome na gumamit ng AR, i-on din ang camera sa Mga Setting ng Android. Sumulong +Ibahagi gamit ang Payagan Payagan ang mga site na i-access ang mga sensor ng paggalaw (inirerekomenda) Paggamit @@ -163,7 +171,9 @@ I-clear Mga aktibong pag-download Ibaba +Mag-swipe pababa para isara. Pigilan ang mga third-party na website na mag-save at magbasa ng data ng cookie +I-play Pumili ng mga item Hindi nababasa ng device na ito ang NFC Na-delete @@ -197,6 +207,7 @@ Ginagamit ng isang site ang iyong camera Masa-sign out ka sa site na ito. Ang cookies ay mga file na ginagawa ng mga website na binibisita mo. Ginagamit ng mga site ang mga ito para tandaan ang iyong mga kagustuhan. Ginagawa ng iba pang site ang third-party na cookies. Nagmamay-ari ang mga site na ito ng ilan sa content, gaya ng mga ad o larawan, na nakikita mo sa webpage na iyong binibisita. +Nagpe-play ng media Kanselahin ang pinili Masa-sign out ka sa lahat ng site. Payagan ang tunog para sa isang partikular na site. @@ -204,6 +215,7 @@ Sigurado ka bang gusto mong i-clear ang lahat ng lokal na data, kasama ang cookies, at i-reset ang lahat ng pahintulot para sa website na ito? Naka-off para sa device na ito Hindi na-download +Hindi makapili ng certificate. Ang content na ito ay mula sa , na ipinadala ng Google. Naka-mute Kumpirmahin @@ -216,6 +228,8 @@ Ginagamit ng isang site ang iyong camera at mikropono Nagpapakita ang site na ito ng mga nakakasagabal o nakakapanlinlang na ad Mag-tap para bumalik sa site +Baguhin +Maghanap nang pabalik Bawiin ang pahintulot ng device Mabilis ang site I-block ang mga site sa pagkonekta sa mga device @@ -236,6 +250,7 @@ Mag-block ng cookies para sa isang partikular na site. Hindi gumagana ang navigation: Pinamamahalaan ng iyong mga magulang +Bluetooth I-access ang iyong mikropono I-block kung nagpapakita ang site ng mga nakakasagabal o nakakapanlinlang na ad (inirerekomenda) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr-CA.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr-CA.xtb index 648c5f33af2..0a34e30200e 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr-CA.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr-CA.xtb @@ -10,6 +10,7 @@ Suivant Intégré sur Arrêter +Pour vous faire économiser les données, Google a optimisé les images de cette page. Les fichiers enregistrés par les sites Web s'affichent ici  : Le site  a été ajouté @@ -42,6 +43,7 @@ Demander avant d'autoriser les sites à créer une carte 3D de votre environnement ou à faire le suivi de la position de l'appareil photo (recommandé) Autoriser les témoins Demander quand un site souhaite rechercher les appareils Bluetooth à proximité (recommandé) +La sélection du certificat client SSL n'est pas prise en charge par le système d'exploitation. Continuer  Mo Téléchargement terminé @@ -61,10 +63,12 @@ Copié Voulez-vous vraiment réinitialiser toutes les autorisations de site pour ? Site +Chanson précédente Demander avant d'autoriser les sites à lire du contenu protégé Autoriser les sites à enregistrer et à lire les données des témoins (recommandé) Lecteur de certificats Cette action entraînera la suppression de l'ensemble des d'espace de stockage des sites Web. +Bloquée automatiquement Afficher les renseignements Annonces bloquées sur certains sites Activer les autorisations pour Chrome dans les paramètres Android. @@ -83,16 +87,19 @@ Synchronisation en arrière-plan Suppr. stock. des sites? Retour +Rechercher vers l'avant Autoriser les sites à lire le contenu protégé (recommandé) Copier lien /? Titre Autoriser les témoins pour un site en particulier. +Chanson suivante Paramètres du site Les notifications peuvent faire vibrer l'appareil Ouvrir les paramètres Navigation bloquée : Autorisé +Un site lit du contenu multimédia Page en version simplifiée fournie par Google Fenêt. context. et redirections Autoriser les sites à lire du contenu protégé @@ -107,6 +114,7 @@ Révoquer toutes les autorisations pour l'appareil Pour autoriser Chrome à utiliser la RA, activez aussi l'accès à l'appareil photo dans les paramètres Android. Avancer +Partager par Autoriser Autoriser les sites à accéder à vos capteurs de mouvements (recommandé) Utilisation @@ -163,7 +171,9 @@ Effacer Téléchargements actifs Déplacer vers le bas +Glissez le doigt vers le bas pour fermer. Empêcher les sites Web tiers d'enregistrer et de lire les données des témoins +Jouer Sélectionner des éléments Cet appareil ne prend pas en charge la technologie NFC Supprimé @@ -197,6 +207,7 @@ Un site utilise votre appareil photo Vous serez déconnecté de ce site. Les témoins sont des fichiers créés par les sites Web que vous visitez. Les sites les utilisent pour mémoriser vos préférences. Les témoins tiers sont ceux que créent d'autres sites. Ces sites possèdent certains types de contenu, comme des annonces et des images, que vous voyez sur les pages que vous visitez. +Élém. multim. en cours de lecture Annuler la sélection Vous serez déconnecté de tous les sites. Autorisez le son sur un site précis. @@ -204,6 +215,7 @@ Voulez-vous vraiment effacer toutes les données locales de ce site Web, y compris les témoins, et réinitialiser toutes les autorisations pour ce site Web? Désactivé pour cet appareil Échec du téléchargement +Impossible de sélectionner le certificat. Ce contenu provient de , fourni par Google. Désactivés Confirmer @@ -216,6 +228,8 @@ Un site utilise votre appareil photo et votre microphone Ce site diffuse des annonces intrusives ou trompeuses Touchez pour revenir au site +Modifier +Rechercher vers l'arrière Révoquer l'autorisation de l'appareil Le site est rapide Empêcher les sites de se connecter à des appareils @@ -236,6 +250,7 @@ Bloquer les témoins pour un site en particulier. Navigation inaccessible : Géré par tes parents +Bluetooth Accès à votre microphone Bloquer si le site diffuse des annonces intrusives ou trompeuses (recommandé) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr.xtb index d02c2416b92..fb2b937d601 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_fr.xtb @@ -10,6 +10,7 @@ Suivant Intégration sur Arrêter +Pour sauvegarder vos données, les images de cette page ont été optimisées par Google. Les fichiers enregistrés par des sites Web sont répertoriés ici - Site "" ajouté @@ -42,6 +43,7 @@ Vous demander votre avis avant d'autoriser les sites à créer un plan 3D de votre environnement ou à suivre la position de la caméra (recommandé) Autoriser les cookies Me demander lorsqu'un site souhaite accéder aux appareils Bluetooth se trouvant à proximité (recommandé) +La sélection de certificat côté client n'est pas compatible avec le système d'exploitation. Continuer  Mo Téléchargement terminé @@ -61,10 +63,12 @@ Copié Voulez-vous vraiment réinitialiser toutes les autorisations du site pour  ? Site +Piste précédente Demander avant d'autoriser les sites à lire les contenus protégés Autoriser les sites à enregistrer et à lire les données des cookies (recommandé) Lecteur de certificat Cette action aura pour effet de libérer l'espace de stockage utilisé pour les données de site (). +Bloquée automatiquement Afficher les informations Bloqué sur certains sites Activer les autorisations pour Chrome dans les paramètres Android @@ -83,16 +87,19 @@ Synchronisation en arrière-plan Suppr. données de site ? Retour +Avance rapide Autoriser les sites à lire les contenus protégés (recommandé) Copier lien / ? Titre Autorisez les cookies pour un site spécifique. +Piste suivante Paramètres de site L'appareil vibrera en cas de notifications. Ouvrir les paramètres La navigation sur est bloquée. Autorisé +Un site est en train de lire un contenu multimédia Page simplifiée fournie par Google Pop-up et redirections Autoriser les sites à lire les contenus protégés @@ -107,6 +114,7 @@ Révoquer toutes les autorisations de l'appareil Pour autoriser Chrome à utiliser la RA, activez également l'appareil photo dans les paramètres Android. Avancer +Partager via Autoriser Autoriser les sites à accéder à vos capteurs de mouvement (recommandé) Utilisation @@ -163,7 +171,9 @@ Effacer Téléchargements actifs Descendre +Balayez l'écran vers le bas pour fermer la feuille. Empêcher les sites Web tiers d'enregistrer et de lire les données des cookies +Lire Sélectionner des éléments Lecture NFC impossible sur cet appareil Supprimé @@ -197,6 +207,7 @@ Un site utilise actuellement votre appareil photo Vous allez être déconnecté de ce site. Les cookies sont des fichiers créés par les sites Web que vous consultez. Les sites les utilisent pour mémoriser vos préférences. Les cookies tiers sont créés par d'autres sites, lesquels possèdent une partie du contenu, comme les annonces ou les images, que vous voyez sur la page Web que vous consultez. +Contenu en cours de lecture Annuler la sélection Vous allez être déconnecté de tous les sites. Autorisez le son pour un site spécifique. @@ -204,6 +215,7 @@ Voulez-vous vraiment effacer toutes les données locales, y compris les cookies, et réinitialiser toutes les autorisations pour ce site Web ? Désactivée pour cet appareil Échec du téléchargement +Impossible de sélectionner le certificat Ce contenu est issu de . Il est diffusé par Google. Son coupé Confirmer @@ -216,6 +228,8 @@ Un site utilise votre appareil photo et votre micro Ce site affiche des annonces intrusives ou trompeuses Appuyez pour revenir au site +Modifier +Retour rapide Révoquer l'autorisation d'accès à l'appareil Le site est rapide Interdire à tous les sites de se connecter à des appareils @@ -236,6 +250,7 @@ Bloquez les cookies pour un site spécifique. Impossible d'accéder à . Géré par tes parents +Bluetooth Accéder à votre micro Bloquer si le site affiche des annonces intrusives ou trompeuses (recommandé) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gl.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gl.xtb index ad0656493e1..67a5521ac27 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gl.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gl.xtb @@ -10,6 +10,7 @@ Seguinte Cookie inserida en Deter +Para gardar os teus datos, Google optimizou as imaxes desta páxina. Os ficheiros gardados polos sitios web aparecen aquí : Engadiuse o sitio @@ -42,6 +43,7 @@ Preguntar antes de permitir que os sitios creen un mapa 3D do que te rodea e fagan un seguimento da posición da cámara (recomendado) Permitir uso de cookies Preguntar cando un sitio web queira detectar dispositivos Bluetooth próximos (recomendado) +A selección do certificado do cliente non é compatible co sistema operativo. Continuar  MB Completouse a descarga @@ -61,10 +63,12 @@ Copiada Seguro que queres restablecer todos os permisos do sitio ? Sitio +Pista anterior Preguntar antes de permitir que os sitios reproduzan contido protexido Permitir que os sitios garden e lean datos de cookies (recomendado) Visor de certificados Eliminaranse os de almacenamento do sitio web. +Bloqueado automaticamente Mostrar información Anuncios bloqueados nalgúns sitios Activa os permisos para Chrome na Configuración de Android. @@ -83,16 +87,19 @@ Sincronización en segundo plano Borrar datos do sitio? Atrás +Buscar cara adiante Permitir que os sitios reproduzan contido protexido (recomendado) Copiar ligazón /? Título Permite cookies para un sitio específico. +Pista seguinte Configuración do sitio O dispositivo pode vibrar ao recibir notificacións Abrir configuración A navegación está bloqueada: Permitida +Un sitio está reproducindo contido multimedia Páxina en modo básico ofrecida por Google. Ventás emerxentes e redireccións Permitir que os sitios reproduzan contido protexido @@ -107,6 +114,7 @@ Revogar todos os permisos do dispositivo Para permitir que Chrome utilice a realidade aumentada, tamén debes activar a cámara na sección Configuración de Android. Avanzar +Compartir a través de Permitir Permite aos sitios acceder aos teus sensores de movemento (recomendado) Uso @@ -163,7 +171,9 @@ Borrar Descargas activas Mover cara abaixo +Pasa o dedo cara abaixo para pechar o panel inferior. Evitar que sitios web de terceiros garden e lean datos de cookies +Reproducir Selecciona elementos Este dispositivo non pode ler NFC Eliminado @@ -197,6 +207,7 @@ Un sitio está utilizando a cámara Pecharase a túa sesión neste sitio. As cookies son ficheiros creados polos sitios web que visitas. Os sitios utilízanas para lembrar as túas preferencias. As de terceiros son as que crean outros sitios. Parte do contido que ves na páxina web que visitas, como anuncios ou imaxes, pertence a esoutros sitios. +Reproducindo contido multimedia Cancelar selección Pecharase a túa sesión en todos os sitios. Permite o son dun sitio específico. @@ -204,6 +215,7 @@ Estás seguro de que queres borrar todos os datos locais, incluídas as cookies, e restablecer todos os permisos para este sitio web? Desactivada para este dispositivo Produciuse un erro na descarga +Non se pode seleccionar o certificado. Este contido procede de , ofrecido por Google. Silenciados Confirmar @@ -216,6 +228,8 @@ Un sitio está utilizando a cámara e o micrófono Este sitio mostra anuncios intrusivos ou enganosos Toca para volver ao sitio +Cambiar +Buscar cara atrás Revoga o permiso do dispositivo O sitio é rápido Non permitir que os sitios se conecten aos dispositivos @@ -236,6 +250,7 @@ Bloquea cookies para un sitio específico. A navegación é inaccesible: Xestionado por teus pais +Bluetooth Acceso ao teu micrófono Bloquear se o sitio mostra anuncios enganosos ou intrusivos (recomendado) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gu.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gu.xtb index 0cff9eb110c..a3e8767f884 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gu.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_gu.xtb @@ -10,6 +10,7 @@ આગલું પર શામેલ કર્યું રોકો +તમારો ડેટા બચાવવા માટે, આ પેજની છબીઓને Google દ્વારા ઑપ્ટિમાઇઝ કરવામાં આવી છે. વેબસાઇટ દ્વારા સાચવવામાં આવેલી ફાઇલો અહી દેખાશે - સાઈટ ઉમેરવામાં આવી @@ -42,6 +43,7 @@ કોઈ સાઇટને તમારી આજુબાજુનો 3D નકશો બનાવતા અથવા કૅમેરાની સ્થિતિને ટ્રૅક કરવાની મંજૂરી આપતા પહેલાં પૂછો (સુઝાવ આપીએ છીએ) કુકીને મંજૂરી આપો જ્યારે કોઈ સાઇટ નજીકના બ્લૂટૂથ ડિવાઇસને શોધવા માગે ત્યારે પૂછો (સુઝાવ આપેલ) +ઓપરેટિંગ સિસ્ટમ દ્વારા ક્લાઇન્ટ તરફની પ્રમાણપત્ર પસંદગી સપોર્ટ કરતી નથી. આગળ વધો MB ડાઉનલોડ પૂર્ણ @@ -61,10 +63,12 @@ કૉપિ કર્યું શું તમે ખરેખર ની બધી સાઇટ પસંદગીઓને રીસેટ કરવા માગો છે? સાઇટ +પાછલું ટ્રૅક સાઇટને સંરક્ષિત કન્ટેન્ટ ચલાવવાની મંજૂરી આપતા પહેલાંં પૂછો સાઇટને કૂકી ડેટા સાચવવા અને વાંચવાની મંજૂરી આપો (ભલામણ કરેલ) પ્રમાણપત્ર દર્શક નું બધું વેબસાઇટ સ્ટોરેજ સાફ કરશે. +આપમેળે અવરોધિત માહિતી બતાવો કેટલીક સાઇટ પર બ્લૉક કરેલ Android સેટિંગ્સમાં Chrome માટે પરવાનગીઓ ચાલુ કરો. @@ -83,16 +87,19 @@ પૃષ્ઠભૂમિ સમન્વયન સાઇટ સ્ટોરેજ સાફ કરીએ? પાછળ +આગળ કરો સાઇટને સંરક્ષિત કન્ટેન્ટ ચલાવવાની મંજૂરી આપો (ભલામણ કરેલ) લિંક કૉપિ કરો / ? શીર્ષક કોઈ ચોક્કસ સાઇટ માટે કુકીને મંજૂરી આપો. +આગલો ટ્રૅક સાઇટ સેટિંગ્સ સૂચનાઓ ઉપકરણને વાઇબ્રેટ કરી શકે છે સેટિંગ્સ ખોલો નેવિગેશન અવરોધિત છે: મંજૂર +સાઇટ મીડિયા ચલાવી રહી છે Google દ્વારા પૂરું પાડવામાં આવેલું લાઇટ વર્ઝનનું પેજ પૉપ-અપ અને રીડાયરેક્ટ સાઇટને સંરક્ષિત કન્ટેન્ટ ચલાવવાની મંજૂરી આપો @@ -107,6 +114,7 @@ ડિવાઇસ માટેની બધી પરવાનગીઓને રદબાતલ કરો Chromeને ARનો વપરાશ કરવા દેવા માટે, Android સેટિંગમાંથી કૅમેરા પણ ચાલુ કરો. આગળ જાઓ +આનાથી શેર કરો મંજૂરી આપો સાઇટને મોશન સેન્સરના ઍક્સેસની મંજૂરી આપો (સુઝાવ આપેલ) ઉપયોગ @@ -163,7 +171,9 @@ સાફ કરો ડાઉનલોડ કરવાનું સક્રિય કરો નીચે ખસેડો +બંધ કરવા માટે નીચેની તરફ સ્વાઇપ કરો. તૃતીય-પક્ષ વેબસાઇટને કુકી ડેટા સાચવવા અને વાંચવાથી અટકાવો +ચલાવો આઇટમ પસંદ કરો આ ડિવાઇસ NFC વાંચી શકતું નથી કાઢી નાખ્યું @@ -197,6 +207,7 @@ કોઈ સાઇટ તમારા કૅમેરાનો ઉપયોગ કરી રહી છે આ સાઇટમાંથી તમને સાઇન આઉટ કરવામાં આવશે. કુકી એ તમે મુલાકાત લો છો તે વેબસાઇટ દ્વારા બનાવવામાં આવેલી ફાઇલો છે. સાઇટ તેનો ઉપયોગ તમારી પસંદગીઓને યાદ રાખવા માટે કરે છે. ત્રીજા પક્ષની કુકી અન્ય સાઇટ દ્વારા બનાવવામાં આવે છે. અમુક પ્રકારનું કન્ટેન્ટ આ સાઇટની માલિકીનું હોય છે, જેમ કે તમે મુલાકાત લો છો તે વેેબપેજ પર તમે જોયેલી જાહેરાતો કે છબીઓ. +મીડિયા ચલાવી રહ્યાં છીએ પસંદગી રદ કરો તમે બધી સાઇટમાં સાઇન આઉટ કરવામાં આવશે. કોઈ એક ચોક્કસ સાઇટ માટે અવાજ ચલાવવાની મંજૂરી આપો. @@ -204,6 +215,7 @@ શું તમે ખરેખર કૂકીઝ સહિત આ વેબસાઇટ માટેનો બધો ડેટા સાફ કરી અને આ વેબસાઇટ માટેની બધી પરવાનગીઓ ફરીથી સેટ કરવા માગો છો? આ ઉપકરણ માટે બંધ કર્યું ડાઉનલોડ નિષ્ફળ થયું +પ્રમાણપત્ર પસંદ કરવામાં અસમર્થ. આ કન્ટેન્ટ માંથી, Google દ્વારા વિતરિત કરેલ છે. મ્યૂટ કરેલ પુષ્ટિ કરો @@ -216,6 +228,8 @@ કોઈ સાઇટ તમારા કૅમેરા અને માઇક્રોફોનનો ઉપયોગ કરી રહી છે આ સાઇટ ઘૃણાસ્પદ અથવા ભ્રામક જાહેરાતો બતાવે છે સાઇટ પર પાછા ફરવા માટે, ટૅપ કરો +બદલો +પાછળ કરો ઉપકરણની પરવાનગી રદબાતલ કરો સાઇટ ઝડપી છે સાઇટને ઉપકરણો સાથે કનેક્ટ થવાથી બ્લૉક કરો @@ -236,6 +250,7 @@ કોઈ ચોક્કસ સાઇટ માટે કુકીને બ્લૉક કરો. નેવિગેશન બિનપહોંચ યોગ્ય છે: તમારા માતાપિતા દ્વારા સંચાલિત +બ્લૂટૂથ તમારા માઇક્રોફોનની ઍક્સેસ જો સાઇટ ઘૃણાસ્પદ અથવા ભ્રામક જાહેરાતો બતાવતી હોય, તો બ્લૉક કરો (ભલામણ કરેલ) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hi.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hi.xtb index 27fe6fe8b7e..edc7c1b2cd1 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hi.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hi.xtb @@ -10,6 +10,7 @@ अगला पर एम्बेड किया गया रोकें +आपका डेटा बचाने के लिए, Google ने इस पेज की इमेज ऑप्टमाइज़ कर दी हैं. यहां वे फ़ाइलें दिखाई देती हैं जिन्हें वेबसाइटें सेव करती हैं - साइट जोड़ी गई @@ -42,6 +43,7 @@ किसी साइट को आपके आस-पास की जगह का 3D मैप बनाने या कैमरे की स्थिति ट्रैक करने की अनुमति देने से पहले पूछें (सुझाया गया) कुकी की अनुमति दें जब कोई साइट आस-पास के ब्लूटूथ डिवाइस को खोजना चाहे, तो इसके लिए पूछें (सुझाया गया) +क्लाइंट-साइड प्रमाणपत्र चुनना ऑपरेटिंग सिस्टम से नहीं किया जा सकता है. जारी रखें एमबी डाउनलोड हो गया @@ -61,10 +63,12 @@ कॉपी किया गया क्या आप वाकई की सभी साइट अनुमतियां रीसेट करना चाहते हैं? साइट +पिछला ट्रैक साइटों को सुरक्षित सामग्री चलाने की अनुमति देने से पहले पूछें साइटों को कुकी डेटा सेव करने और पढ़ने की अनुमति दें (सुझाए गए) प्रमाणपत्र व्यूअर इससे वेबसाइट की पूरी मेमोरी साफ़ हो जाएगी. +ऑटोमैटिक रूप से ब्लॉक है जानकारी दिखाएं कुछ साइटों पर ब्लॉक किए गए हैं Android सेटिंग में Chrome के लिए अनुमतियां चालू करें. @@ -83,16 +87,19 @@ बैकग्राउंड सिंक क्या आप साइट मेमोरी खाली करना चाहते हैं? वापस जाएं +आगे जाएं साइटों को सुरक्षित सामग्री चलाने दें (हम इस सेटिंग को चालू रखने का सुझाव देते हैं) लिंक की प्रति बनाएं / ? शीर्षक किसी खास साइट के लिए कुकी की मंज़ूरी दें. +अगला ट्रैक साइट सेटिंग नोटिफ़िकेशन से डिवाइस में कंपन हो सकता है सेटिंग खोलें मार्गदर्शक अवरोधित है: अनुमति है +किसी साइट पर मीडिया चल रहा है लाइट पेज Google ने मुहैया कराया है पॉप-अप और रीडायरेक्ट साइटों को सभी सुरक्षित सामग्री चलाने दें @@ -107,6 +114,7 @@ डिवाइस की सभी अनुमतियां निरस्त करें Chrome को एआर (ऑगमेंटेड रिएलिटी) का इस्तेमाल करने देने के लिए, Android सेटिंग में जाकर कैमरा भी चालू कर दें. आगे जाएं +इससे शेयर करें अनुमति दें साइटों को मोशन सेंसर ऐक्‍सेस करने दें (सुझाया गया) उपयोग @@ -163,7 +171,9 @@ साफ़ करें डाउनलोड हो रहे हैं नीचे ले जाएं +बंद करने के लिए नीचे स्वाइप करें. तृतीय पक्ष वेबसाइट को कुकी डेटा सहेजने और पढ़ने से रोकें +चलाएं आइटम चुनें इस डिवाइस के साथ NFC काम नहीं कर सकता हटाया गया @@ -197,6 +207,7 @@ कोई साइट आपका कैमरा इस्तेमाल कर रही है आप इस साइट से साइन आउट हो जाएंगे. आप जिन वेबसाइटों पर जाते हैं उनकी बनाई फ़ाइलें कुकी कहलाती हैं. आपकी प्राथमिकताओं को याद रखने के लिए, साइटें उनका इस्तेमाल करती हैं. अन्य साइटें, तीसरे पक्ष की कुकी बनाती हैं. इन साइटों पर विज्ञापन या इमेज जैसी कुछ सामग्री होती हैं. ये उस वेबपेज पर दिखती है जिस पर आप जाते हैं. +चलाया जा रहा मीडिया चुना गया हटाएं आप सभी साइटों से साइन आउट हो जाएंगे. किसी खास साइट के लिए आवाज़ की अनुमति दें. @@ -204,6 +215,7 @@ क्‍या आप वाकई कुकी सहित इस वेबसाइट का सभी स्‍थानीय डेटा साफ़ करना और इसकी सभी अनुमतियों को रीसेट करना चाहते हैं? इस डिवाइस के लिए बंद कर दिया गया है डाउनलोड विफल रहा +प्रमाणपत्र चुनने में असमर्थ. यह सामग्री की है जिसे Google के द्वारा वितरित किया गया है. आवाज़ बंद की गई पुष्टि करें @@ -216,6 +228,8 @@ कोई साइट आपके कैमरे और माइक्रोफ़ोन का इस्तेमाल कर रही है इस साइट में तंग करने वाले या गुमराह करने वाले विज्ञापन दिखाई देते हैं साइट पर वापस जाने के लिए टैप करें +बदलें +पीछे जाएं डिवाइस अनुमति निरस्त करें साइट तेज़ी से लोड होती है साइटों को डिवाइस से कनेक्ट होने से रोकें @@ -236,6 +250,7 @@ किसी खास साइट के लिए कुकी ब्लॉक करें. मार्गदर्शक तक नहीं पहुंचा जा सकता: आपके अभिभावकों द्वारा प्रबंधित +ब्लूटूथ अपना माइक्रोफ़ोन एक्सेस करें अगर साइट तंग करने वाले या गुमराह करने वाले विज्ञापन दिखाई देते हैं, तो उन्हें ब्लॉक करें (सुझाव) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hr.xtb index adbb13cf623..86bf11620c0 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hr.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hr.xtb @@ -10,6 +10,7 @@ Dalje Ugrađeno na Zaustavi +Google je optimizirao slike na ovoj stranici radi uštede vaših podataka. Ovdje se prikazuju datoteke koje su spremile web-lokacije Dodana je web-lokacija @@ -42,6 +43,7 @@ Prikaži upit prije omogućivanja web-lokacijama da izrađuju 3D kartu vašeg okruženja i prate položaj kamere (preporučeno) Dopusti kolačiće Prikaži upit kad web-lokacija želi tražiti Bluetooth uređaje u blizini (preporučeno) +Operativni sustav ne podržava odabir klijentskog certifikata. Nastavi MB Preuzimanje je dovršeno: @@ -61,10 +63,12 @@ Kopirano Jeste li sigurni da želite poništiti sva dopuštenja za aplikaciju ? Web lokacija +Prethodna pjesma Web-lokacije moraju tražiti dopuštenje za reprodukciju zaštićenog sadržaja Dopusti web-lokacijama da spremaju i čitaju podatke kolačića (preporučeno) Preglednik certifikata Time će se izbrisati cijela pohrana web-lokacije veličine . +Automatski blokirano Prikaži informacije Blokirano na nekim web-lokacijama Uključite dopuštenja za Chrome u Postavkama Androida. @@ -83,16 +87,19 @@ Sinkronizacija u pozadini Izbrisati pohranu? Natrag +Traži unaprijed Web-lokacije mogu reproducirati zaštićeni sadržaj (preporučeno) Kopiraj vezu /? Naslov Dopusti kolačiće za određenu web-lokaciju. +Sljedeća pjesma Postavke web-lokacije Obavijesti mogu uključiti vibriranje uređaja Otvori postavke Otvaranje je blokirano: Dopušteno +Web-lokacija reproducira medije Jednostavna stranica koju pruža Google Skočni prozori i preusmjeravanja Web-lokacijama je dopuštena reprodukcija zaštićenog sadržaja @@ -107,6 +114,7 @@ Opoziv svih dopuštenja za uređaj Da bi Chrome mogao koristiti proširenu stvarnost, uključite i kameru u Androidovim postavkama. Idi naprijed +Dijeli putem Dopusti Dopuštanje pristupa senzorima kretanja za web-lokacije (preporučeno) Upotreba @@ -163,7 +171,9 @@ Izbriši Aktivna preuzimanja Pomakni dolje +Prijeđite prstom prema dolje da biste zatvorili. Onemogući web-lokacijama treće strane da spremaju i čitaju podatke kolačića. +Reproduciraj Odaberite stavke Ovaj uređaj ne može čitati NFC Izbrisano @@ -197,6 +207,7 @@ Web-lokacija upotrebljava vašu kameru Odjavit ćete se s ove web-lokacije. Kolačići su datoteke web-lokacija koje ste posjetili. Web-lokacije ih koriste kako bi zapamtile vaše postavke. Kolačiće trećih strana izrađuju ostale web-lokacije. Te web-lokacije posjeduju dio sadržaja prikazanog na web-stranici koju ste posjetili, primjerice oglase ili slike. +Reprodukcija medija Poništi odabir Odjavit ćete se sa svih web-lokacija. Dopuštanje zvuka za određenu web-lokaciju. @@ -204,6 +215,7 @@ Jeste li sigurni da želite izbrisati sve lokalne podatke, uključujući kolačiće, i poništiti sva dopuštenja za ovu web-lokaciju? Isključeno za ovaj uređaj Preuzimanje nije uspjelo +Ne može se odabrati certifikat. Sadržaj potječe s domene , a omogućuje ga Google. Bez zvuka Potvrdi @@ -216,6 +228,8 @@ Web-lokacija upotrebljava vašu kameru i mikrofon Ova web-lokacija prikazuje ometajuće ili obmanjujuće oglase Dodirnite da biste se vratili na web-lokaciju +Promijeni +Traži unatrag Opozovi odobrenje uređaja Web-lokacija je brza Blokiraj povezivanje web-lokacija s uređajima @@ -236,6 +250,7 @@ Blokiraj kolačiće za određenu web-lokaciju. Otvaranje je nedostupno: Upravljaju tvoji roditelji +Bluetooth Pristup mikrofonu Blokiraj ako web-lokacija prikazuje ometajuće ili obmanjujuće oglase (preporučeno) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb index 14a8e18489e..6e25bec47d2 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb @@ -10,6 +10,7 @@ Tovább Beágyazva itt: Leállítás +Az adatforgalom csökkentése érdekében a Google optimalizálta az oldalon lévő képeket. A webhelyek által elmentett fájlok itt jelennek meg A(z) webhely hozzáadva @@ -42,6 +43,7 @@ Kérdezzen rá, mielőtt engedélyezi a webhelyek számára 3D-s térkép létrehozását az Ön környezetéről, valamint a kamera pozíciójának követését (ajánlott) Cookie-k engedélyezése Kérdezzen rá, ha valamelyik webhely szeretné felfedezni a közeli Bluetooth-eszközöket (ajánlott) +Az ügyféloldali tanúsítványválasztást az operációs rendszer nem támogatja. Tovább MB Letöltés befejezve @@ -61,10 +63,12 @@ Másolt Biztosan visszaállítja a(z) minden webhelyengedélyét? Webhely +Előző szám Kérdezzen rá, mielőtt engedélyezné a webhelyek számára védett tartalmak lejátszását Cookie-adatok mentésének és olvasásának engedélyezése a webhelyeken (ajánlott) Tanúsítványmegtekintő Ezzel törli a webhely teljes tárhelyét: . +Automatikusan letiltva Információk megjelenítése Letiltva egyes webhelyeken A Chrome-ra vonatkozó engedélyek aktiválása az Android beállításaiban. @@ -83,16 +87,19 @@ Szinkronizálás a háttérben Törli a webhely tárhelyét? Vissza +Ugrás előre Engedélyezi a webhelyek számára a védett tartalmak lejátszását (ajánlott) Link másolása ? / Cím Adott webhely cookie-jainak engedélyezése. +Következő szám Webhelybeállítások Az értesítések miatt rezeghet az eszköz Beállítások megnyitása Le van tiltva az ide vezető navigáció: Engedélyezve +Az egyik webhely médiatartalmat játszik le Egyszerű oldal a Google-tól Előugró ablakok és átirányítások Védett tartalmak lejátszásának engedélyezése a webhelyek számára @@ -107,6 +114,7 @@ Az eszköz összes engedélyének visszavonása Ahhoz, hogy a Chrome használhassa az AR-t, kapcsolja be a kamerát is az Android-beállítások között. Előrelépés +Megosztás itt: Engedélyezés A mozgásérzékelőkhöz való hozzáférés engedélyezése a webhelyek számára (ajánlott) Használat @@ -163,7 +171,9 @@ Törlés Aktív letöltések Mozgatás lefelé +A bezáráshoz csúsztasson lefelé. Megakadályozza, hogy harmadik felek webhelyei mentsék és olvassák a cookie-adatokat. +Lejátszás Válasszon elemeket Ez az eszköz nem képes az NFC-alapú kommunikáció olvasására Törölve @@ -197,6 +207,7 @@ Egy webhely használja az Ön kameráját A rendszer kijelentkezteti majd erről a webhelyről A cookie-k a felkeresett webhelyek által létrehozott fájlok. A webhelyek arra használják őket, hogy megjegyezzék az Ön preferenciáit. A harmadik féltől származó cookie-kat más webhelyek hozzák létre. Ezek a webhelyek birtokolják a meglátogatott weboldalon látható tartalom bizonyos részét (például hirdetéseket vagy képeket). +Médiatartalom lejátszása… Kijelölés törlése Ki fog jelentkezni az összes webhelyről. Egy adott webhely hangjának engedélyezése. @@ -204,6 +215,7 @@ Biztosan törli a webhellyel kapcsolatos összes helyi adatot (a cookie-kkal együtt), és visszaállítja az összes engedélyt? Kikapcsolva ezen az eszközön Nem sikerült a letöltés +A tanúsítvány kiválasztása nem sikerült. Ez a Google által megjelenített tartalom a(z) domainről származik. Némítva Megerősítés @@ -216,6 +228,8 @@ Egy webhely használja az Ön kameráját és mikrofonját Ez a webhely tolakodó vagy félrevezető hirdetéseket jelenít meg Koppintson a webhelyre való visszatéréshez +Módosítás +Ugrás visszafelé Eszközengedély visszavonása A webhely gyors Az eszközökhöz való csatlakozás megtiltása a webhelyeknek @@ -236,6 +250,7 @@ Adott webhely cookie-jainak letiltása. Nem érhető el az ide vezető navigáció: Szülők által kezelt +Bluetooth Hozzáférés a mikrofonhoz Letiltás, ha a webhely tolakodó vagy félrevezető hirdetéseket jelenít meg (ajánlott) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hy.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hy.xtb index ace5b49e9aa..065573fbedc 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hy.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_hy.xtb @@ -10,6 +10,7 @@ Հաջորդը Զետեղված է կայքում Դադարեցնել +Թրաֆիկը խնայելու համար այս էջի պատկերները Google-ի կողմից օպտիմալացվել են։ Կայքերի կողմից պահված ֆայլերը կցուցադրվեն այստեղ կայքն ավելացվել է @@ -42,6 +43,7 @@ Կայքերի համար թույլտվություն խնդրել՝ ստեղծելու շրջակայքի եռաչափ քարտեզն ու հետագծելու ձեր տեսախցիկի դիրքը (խորհուրդ է տրվում) Թույլատրել քուքիները Հարցնել, երբ որևէ կայք ուզում է հայտնաբերել մոտակա Bluetooth սարքերը (խորհուրդ է տրվում) +Տվյալ օպերացիոն համակարգը չի աջակցում սպասառուի կողմից վկայագրի ընտրությունը: Շարունակել ՄԲ Ներբեռնումն ավարտված է՝ @@ -61,10 +63,12 @@ Պատճենվեց Վերակայե՞լ բոլոր կայքերի թույլտվությունները -ի համար: Կայք +Նախորդը Պաշտպանված բովանդակություն նվագարկելու թույլտվություն հարցել Թույլատրել կայքերին պահել և կարդալ քուքիների տվյալները (խորհուրդ է տրվում) Վկայագրերի դիտում Կազատվի կայքի օգտագործած տարածք: +Ավտոմատ արգելափակված է Ցուցադրել տեղեկությունները Արգելափակված է որոշ կայքերում Միացնել Chrome-ի թույլտվությունները Android-ի կարգավորումներում: @@ -83,16 +87,19 @@ Ֆոնային համաժամացում Ջնջե՞լ կայքերի տվյալները: Հետ +Որոնել դեպի առաջ Թույլ տալ կայքերին նվագարկել պաշտպանված բովանդակությունը (խորհուրդ է տրվում) Պատճենել հղումը /? Վերնագիր Թույլատրել քուքիները կոնկրետ կայքի համար: +Հաջորդը Կայքի կարգավորումներ Ծանուցումների ժամանակ սարքը կթրթռա Բացել կարգավորումները Նավարկումն արգելափակված է՝ Թույլատրված է +Կայքում մեդիա ֆայլ է նվագարկվում Էջի Lite տարբերակը տրամադրվել է Google-ի կողմից Ելնող պատուհաններ և վերահղում Թույլատրել կայքերին նվագարկել պաշտպանված բովանդակություն @@ -107,6 +114,7 @@ Չեղարկել սարքի բոլոր թույլտվությունները Chrome-ին AR ռեժիմն օգտագործելու թույլտվություն տալու համար միացրեք նաև տեսախցիկը Android-ի կարգավորումներում։ Առաջ գնալ +Համօգտագործման միջոցը` Թույլ տալ Թույլ տալ կայքերին օգտագործել ձեր տվիչները (խորհուրդ է տրվում) Վիճակագրություն @@ -163,7 +171,9 @@ Մաքրել Ընթացիկ ներբեռնումներ Տեղափոխել վար +Փակելու համար մատը սահեցրեք ներքև։ Թույլ չտալ երրորդ կողմի կայքերին պահել և կարդալ քուքիների տվյալները +Նվագարկել Ընտրեք տարրեր Այս սարքում NFC-ն չի աջակցվում Ջնջված @@ -197,6 +207,7 @@ Կայքն օգտագործում է ձեր տեսախցիկը Դուք դուրս կգաք այս կայքից։ Քուքիները ֆայլեր են, որոնք ստեղծվում են ձեր այցելած կայքերի կողմից։ Կայքերն օգտագործում են դրանք՝ ձեր նախընտրությունները հիշելու համար։ Երրորդ կողմի քուքիները ստեղծվում են այլ կայքերի կողմից։ Այս կայքերն ունեն որոշակի բովանդակություն, օր․՝ գովազդ կամ պատկերներ, որոնք դուք տեսնում եք ձեր այցելած կայքում։ +Մեդիա բովանդակության նվագարկում Չեղարկել ընտրվածքը Դուք դուրս կգաք բոլոր կայքերից։ Թույլատրել ձայնը առանձին կայքերում @@ -204,6 +215,7 @@ Համոզվա՞ծ եք, որ ուզում եք մաքրել բոլոր տեղային տվյալները, այդ թվում նաև քուքիները, և զրոյացնել այս կայքի թույլտվությունները: Անջատված է այս սարքի համար Ներբեռնումը ձախողվեց +Չի հաջողվում ընտրել վկայագիրը: Այս բովանդակությունը -ից է, մատուցվում է Google-ի կողմից: Անջատած ձայնով կայքեր Հաստատել @@ -216,6 +228,8 @@ Կայքն օգտագործում է ձեր տեսախցիկն ու խոսափողը Այս կայքը հոգնեցնող կամ մոլորեցնող գովազդ է ցուցադրում։ Հպեք՝ կայք վերադառնալու համար +Փոխել +Որոնել դեպի հետ Արգելել սարքի օգտագործումը Կայքը բեռնվում է արագ Արգելել կայքերին միանալ սարքերին @@ -236,6 +250,7 @@ Արգելափակել քուքիները կոնկրետ կայքի համար: Նավարկումն անհասանելի է՝ Կառավարվում է ձեր ծնողների կողմից +Bluetooth Խոսափողի օգտագործում Արգելափակել, եթե կայքը հոգնեցնող կամ մոլորեցնող գովազդ է ցուցադրում (խորհուրդ է տրվում) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_id.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_id.xtb index 58175d33ffe..e41a743fec6 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_id.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_id.xtb @@ -10,6 +10,7 @@ Berikutnya Disematkan di Berhenti +Untuk menghemat data Anda, gambar di halaman ini telah dioptimalkan oleh Google. File yang disimpan situs muncul di sini - Situs ditambahkan @@ -42,6 +43,7 @@ Tanyakan sebelum mengizinkan situs membuat peta 3D untuk area di sekeliling Anda atau melacak posisi kamera (direkomendasikan) Izinkan cookie Tanyakan saat situs ingin menemukan perangkat Bluetooth di sekitar (direkomendasikan) +Pilihan sertifikat sisi klien tidak didukung oleh sistem operasi. Lanjutkan MB Download selesai @@ -61,10 +63,12 @@ Disalin Yakin ingin menyetel ulang semua izin situs untuk ? Situs +Lagu sebelumnya Tanyakan sebelum mengizinkan situs untuk memutar konten yang dilindungi Izinkan situs untuk menyimpan dan membaca data cookie (disarankan) Penampil sertifikat Ini akan menghapus seluruh penyimpanan situs web, sebesar . +Diblokir secara otomatis Tampilkan Info Diblokir di beberapa situs Aktifkan izin untuk Chrome di Setelan Android. @@ -83,16 +87,19 @@ Sinkronisasi latar belakang Kosongkan penyimpanan situs? Kembali +Cari maju Izinkan situs memutar konten yang dilindungi (direkomendasikan) Salin link / ? Judul Izinkan cookie untuk situs tertentu. +Lagu berikutnya Setelan situs Notifikasi dapat membuat perangkat bergetar Buka setelan Navigasi diblokir: Diizinkan +Sebuah situs sedang memutar media Halaman ringan yang ditampilkan oleh Google. Pop-up dan pengalihan Izinkan situs memutar konten yang dilindungi @@ -107,6 +114,7 @@ Cabut semua izin untuk perangkat Untuk mengizinkan Chrome menggunakan Augmented Reality (AR), aktifkan juga kamera di Setelan Android. Maju +Bagikan dengan Izinkan Mengizinkan situs mengakses sensor gerakan (disarankan) Penggunaan @@ -163,7 +171,9 @@ Hapus Download aktif Berpindah ke bawah +Geser ke bawah untuk menutup. Cegah situs web pihak ketiga agar tidak menyimpan dan membaca data cookie +Putar Pilih item Perangkat ini tidak dapat membaca NFC Dihapus @@ -197,6 +207,7 @@ Situs menggunakan kamera Anda Anda akan logout dari situs ini. Cookie adalah file yang dibuat oleh situs yang Anda kunjungi. Situs menggunakannya untuk mengingat preferensi Anda. Cookie pihak ketiga dibuat oleh situs lain. Situs seperti ini adalah pemilik beberapa konten, misalnya iklan atau gambar, yang terlihat di halaman yang Anda kunjungi. +Media yang sedang diputar Batalkan pilihan Anda akan logout dari semua situs. Izinkan suara untuk situs tertentu. @@ -204,6 +215,7 @@ Yakin ingin menghapus semua data lokal, termasuk cookie, dan menyetel ulang semua izin untuk situs web ini? Dinonaktifkan untuk perangkat ini Download gagal +Tidak dapat memilih sertifikat. Konten ini dari , dikirimkan oleh Google. Dinonaktifkan Konfirmasi @@ -216,6 +228,8 @@ Situs menggunakan kamera dan mikrofon Anda Situs ini menampilkan iklan yang mengganggu atau menyesatkan Ketuk untuk kembali ke situs +Ubah +Cari mundur Cabut izin perangkat Situs ini cepat Blokir situs agar tidak terhubung ke perangkat @@ -236,6 +250,7 @@ Blokir cookie untuk situs tertentu. Navigasi tidak dapat dijangkau: Dikelola oleh orang tua Anda +Bluetooth Akses mikrofon Anda Blokir jika situs menampilkan iklan yang mengganggu atau menyesatkan (direkomendasikan) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_is.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_is.xtb index e041209ca54..b5a142669c2 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_is.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_is.xtb @@ -10,6 +10,7 @@ Áfram Fellt inn á Stöðva +Þessar myndir voru fínstilltar af Google til að spara gögn. Skrár sem vefsvæði vista birtast hér Vefsvæðinu bætt við @@ -42,6 +43,7 @@ Spyrja áður en vefsvæðum er leyft að búa til þrívíddarkort af umhverfinu eða rekja staðsetningu myndavélarinnar (ráðlagt) Leyfa fótspor Spyrja þegar vefsvæði vill finna nálæg Bluetooth-tæki (ráðlagt) +Stýrikerfið styður ekki vottorðsval hjá biðlara. Halda áfram MB Niðurhali lokið @@ -61,10 +63,12 @@ Afritað Viltu örugglega endurstilla allar heimildir vefsvæða fyrir „“? Vefsvæði +Fyrra lag Spyrja áður en vefsvæðum er veitt heimild til að spila varið efni Leyfa vefsvæðum að vista og lesa fótsporagögn (ráðlagt) Vottorðaskoðari Þetta mun losa alls um af geymslu vefsvæða. +Sjálfkrafa lokað á Sýna upplýsingar Útilokaðar á sumum vefsvæðum Kveiktu á heimildum fyrir Chrome í stillingum Android. @@ -83,16 +87,19 @@ Samstilling í bakgrunni Hreinsa geymslu vefsvæða? Til baka +Leita áfram Leyfa vefsvæðum að spila varið efni (ráðlagt) Afrita tengil /? Heiti Leyfa fótspor fyrir tiltekið vefsvæði. +Næsta lag Svæðisstillingar Tilkynningar gætu látið tækið titra Opna stillingar Lokað er fyrir skoðun: Leyft +Vefsvæði er að spila efni Léttútgáfa síðu frá Google Sprettigluggar og framsendingar Leyfa vefsvæðum að spila varið efni @@ -107,6 +114,7 @@ Afturkalla allar heimildir fyrir þetta tæki Til að leyfa Chrome að nota AR þarftu einnig að kveikja á myndavél í stillingum Android. Áfram +Deila með Leyfa Leyfa vefsvæðum að fá aðgang að hreyfiskynjurunum (ráðlagt) Notkun @@ -163,7 +171,9 @@ Hreinsa Niðurhal í gangi Færa niður +Strjúktu niður til að loka. Koma í veg fyrir að vefsvæði þriðju aðila geti vistað og lesið fótsporagögn +Spila Veldu atriði Þetta tæki getur ekki lesið NFC Eytt @@ -197,6 +207,7 @@ Vefsvæði er að nota myndavélina Þú verður skráð(ur) út af þessu vefsvæði. Fótspor eru skrár sem myndast á vefsvæðum sem þú heimsækir. Vefsvæði geta notað þau til að muna kjörstillingarnar þínar. Fótspor frá þriðja aðila eru búin til af öðrum vefsvæðum. Þessi vefsvæði eiga eitthvað af efninu, eins og auglýsingar eða myndir, sem þú sérð á vefsíðunni sem þú heimsækir. +Spilar efni Hætta við val Þú verður skráð(ur) út af öllum vefsvæðum. Leyfa hljóð á tilteknu vefsvæði. @@ -204,6 +215,7 @@ Ertu viss um að þú viljir hreinsa öll staðbundin gögn fyrir þetta vefsvæði, þar á meðal fótspor, og endurstilla allar heimildir þess? Slökkt fyrir þetta tæki Niðurhal mistókst +Ekki er hægt að velja vottorðið. Þetta efni er frá , birt af Google. Þögguð Staðfesta @@ -216,6 +228,8 @@ Vefsvæði er að nota myndavélina þína og hljóðnemann Þetta vefsvæði sýnir ágengar eða villandi auglýsingar Ýttu til að fara aftur á vefsvæðið +Breyta +Spóla til baka Afturkalla tækjaheimild Vefsvæðið er hratt Ekki leyfa vefsvæðum að tengjast tækjum @@ -236,6 +250,7 @@ Lokaðu á fótspor fyrir tiltekið vefsvæði. Ekki næst samband: Stjórnað af foreldrum þínum +Bluetooth Aðgangur að hljóðnemanum Loka fyrir ef vefsvæði sýnir ágengar eða villandi auglýsingar (ráðlagt) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_it.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_it.xtb index 77ff590249a..977e68105e4 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_it.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_it.xtb @@ -10,6 +10,7 @@ Avanti Incorporato su Interrompi +Per ridurre il consumo di dati, le immagini di questa pagina sono state ottimizzate da Google. I file salvati dai siti web vengono mostrati qui - Sito aggiunto @@ -42,6 +43,7 @@ Chiedi conferma prima di consentire ai siti di creare una mappa 3D dell'ambiente circostante o di monitorare la posizione della fotocamera (opzione consigliata) Consenti cookie Chiedi conferma quando un sito vuole rilevare i dispositivi Bluetooth nelle vicinanze (opzione consigliata) +La selezione del certificato lato client non è supportata dal sistema operativo. Continua MB Download completato: @@ -61,10 +63,12 @@ Copiata Vuoi reimpostare tutte le autorizzazioni del sito per ? Sito +Traccia precedente Chiedi prima di consentire ai siti di riprodurre contenuti protetti Consenti ai siti di salvare e leggere i dati dei cookie (opzione consigliata) Visualizzatore certificati Verranno cancellati tutti i di memoria utilizzata dai siti web. +Bloccata automaticamente Mostra informazioni Bloccati su alcuni siti Abilita le autorizzazioni per Chrome in Impostazioni Android. @@ -83,16 +87,19 @@ Sincronizzazione in background Cancellare i dati dei siti? Indietro +Posiziona avanti Consenti ai siti di riprodurre i contenuti protetti (opzione consigliata) Copia link di ? Titolo Consenti i cookie per un sito specifico. +Traccia successiva Impostazioni sito Le notifiche possono far vibrare il dispositivo Apri le impostazioni Navigazione bloccata: Consentito +Un sito sta riproducendo contenuti multimediali Pagina Lite fornita da Google Popup e reindirizzamenti Consenti ai siti di riprodurre contenuti protetti @@ -107,6 +114,7 @@ Revoca tutte le autorizzazioni per il dispositivo Per consentire a Chrome di utilizzare l'AR, attiva anche la fotocamera nelle Impostazioni Android. Avanti +Condividi tramite Consenti Consenti ai siti di accedere ai sensori di movimento (opzione consigliata) Utilizzo @@ -163,7 +171,9 @@ Cancella Download attivi Sposta giù +Fai scorrere verso il basso per chiudere. Impedisci ai siti web di terze parti di salvare e leggere i dati dei cookie +Play Seleziona elementi Questo dispositivo non può leggere la tecnologia NFC Eliminato @@ -197,6 +207,7 @@ Un sito sta utilizzando la fotocamera Uscirai da questo sito. I cookie sono file creati dai siti web visitati, che li usano per memorizzare le tue preferenze. I cookie di terze parti vengono creati da altri siti, che sono proprietari di alcuni dei contenuti, ad esempio annunci o immagini, che vengono visualizzati nella pagina web visitata. +Riproduzione media Annulla selezione Il tuo account verrà disconnesso da tutti i siti. Consenti l'audio per un sito specifico. @@ -204,6 +215,7 @@ Vuoi cancellare tutti i dati locali, inclusi i cookie, e reimpostare tutte le autorizzazioni relative al sito web? Disattivata per questo dispositivo Download non riuscito +Impossibile selezionare il certificato. Questi contenuti derivano da e sono offerti da Google. Con audio disattivato Conferma @@ -216,6 +228,8 @@ Un sito sta utilizzando la fotocamera e il microfono Questo sito mostra annunci invasivi o fuorvianti Tocca per tornare al sito. +Cambia +Posiziona indietro Revoca autorizzazione dispositivo Il sito è veloce Impedisci ai siti di connettersi ai dispositivi @@ -236,6 +250,7 @@ Blocca i cookie per un sito specifico. Navigazione inaccessibile: Gestito dai genitori +Bluetooth Accesso al microfono Blocca se il sito mostra annunci invasivi o fuorvianti (consigliato) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb index 082b225d2eb..73376a04109 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_iw.xtb @@ -10,6 +10,7 @@ הבא מוטמע ב- הפסק +‏כדי לחסוך לך נתונים, התמונות בדף הזה עברו אופטימיזציה על ידי Google. קבצים שנשמרו על ידי אתרים מופיעים כאן ‏– האתר נוסף @@ -42,6 +43,7 @@ תוצג שאלה לפני מתן הרשאה לאתרים ליצור מפה בתלת ממד של הסביבה שלך או לעקוב אחר מיקום המצלמה (מומלץ) ‏אישור קובצי cookie ‏המערכת מבקשת אישור כשאתר רוצה לאתר התקני Bluetooth קרובים (מומלץ) +מערכת ההפעלה אינה תומכת בבחירת אישור בצד הלקוח. המשך MB ההורדה הושלמה @@ -61,10 +63,12 @@ הועתק לאפס את כל הרשאות האתר בשביל ? אתר +הרצועה הקודמת הצגת שאלה לפני מתן הרשאה לאתרים להפעיל תוכן מוגן ‏אתרים יוכלו לשמור ולקרוא נתונים של קובצי Cookie (מומלץ) מציג האישורים פעולה זו תמחק את כל נתוני האתר המאוחסנים (). +נחסמה אוטומטית הצג פרטים חסומות בחלק מהאתרים ‏הפעל הרשאות בשביל Chrome בהגדרות Android. @@ -83,16 +87,19 @@ סינכרון ברקע למחוק נתוני אתר מהאחסון? חזרה +הרץ קדימה אתרים יוכלו להפעיל תוכן מוגן (מומלץ) העתק קישור / ? כותרת ‏אישור קובצי Cookie של אתר מסוים. +הרצועה הבאה הגדרות לאתרים רטט של המכשיר אפשרי כשמתקבלת הודעה פתח את 'הגדרות' הניווט חסום: מותר +אתר מסוים מפעיל מדיה ‏גרסת Lite של הדף נוצרה על ידי Google חלונות קופצים והפניות אוטומטיות מתן הרשאה לאתרים להציג תוכן מוגן @@ -107,6 +114,7 @@ ביטול כל ההרשאות בשביל המכשיר ‏כדי לאפשר ל-Chrome להשתמש ב-AR, צריך גם להפעיל את המצלמה בהגדרות Android. המשך קדימה +שיתוף באמצעות זה בסדר התרת גישה של אתרים אל חיישני התנועה (מומלץ) שימוש @@ -163,7 +171,9 @@ ניקוי הורדות פעילות הזז למטה +החלקה מטה סוגרת. ‏אתרים של צד שלישי לא יוכלו לשמור ולקרוא נתונים של קובצי cookie +הפעל בחירת פריטים ‏NFC לא נתמך במכשיר הזה נמחק @@ -197,6 +207,7 @@ אתר כלשהו משתמש במצלמה שלך המערכת תוציא אותך מהאתר הזה. ‏קובצי cookie נוצרים על ידי האתרים שביקרת בהם. האתרים משתמשים בהם כדי לזכור את ההעדפות שלך. קובצי cookie של צד שלישי נוצרים על-ידי אתרים אחרים. האתרים האלה הם הבעלים של חלק מהתוכן בדף האינטרנט שביקרת בו, למשל מודעות או תמונות. +מדיה פועלת בטל את הבחירה תתבצע יציאה מכל האתרים. מתן הרשאה להשמעת צלילים באתר ספציפי. @@ -204,6 +215,7 @@ ‏האם אתה בטוח שברצונך לנקות את כל הנתונים המקומיים, כולל קובצי Cookie, ולאפס את כל ההרשאות של האתר הזה? כבוי בשביל המכשיר הזה ההורדה נכשלה +אין אפשרות לבחור אישור. ‏התוכן הזה הוא מ-, ומוגש על-ידי Google. מושתקים אישור @@ -216,6 +228,8 @@ אתר כלשהו משתמש במצלמה ובמיקרופון שלך באתר הזה מוצגות מודעות מפריעות או מטעות כדי לחזור לאתר, יש להקיש כאן +שנה +הרץ לאחור שלול הרשאות מכשיר האתר הזה מהיר חסימת התחברות של אתרים אל התקנים @@ -236,6 +250,7 @@ ‏חסימת קובצי Cookie של אתר מסוים. הניווט לא אפשרי: מנוהל על-ידי ההורים שלך +Bluetooth גישה למיקרופון שלך חסימה אם באתר מוצגות מודעות מפריעות או מטעות (מומלץ) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ja.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ja.xtb index 7283d03500b..64d03bde87f 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ja.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ja.xtb @@ -10,6 +10,7 @@ 次へ に埋め込まれたページ 中止 +データを節約するため、このページの画像は Google によって最適化されています。 ウェブサイトによって保存されたファイルがここに表示されます - サイト を追加しました @@ -42,6 +43,7 @@ サイトに周囲の 3D マップの作成またはカメラ位置の追跡を許可する前に確認します(推奨) Cookie を許可 サイトから近くにある Bluetooth デバイスの検出を求められたときに確認する(推奨) +オペレーティング システムでサポートされていないため、クライアント側で証明書を選択することはできません。 続行 MB ダウンロードが完了しました @@ -61,10 +63,12 @@ コピーしました 」に関するすべてのサイト権限をリセットしてもよろしいですか? サイト +前のトラック 保護されたコンテンツの再生をサイトに許可する前に確認する サイトに Cookie データの保存と読み取りを許可する(推奨) 証明書ビューア ウェブサイトのストレージ のデータをすべて削除します。 +自動ブロックされました 情報を表示 一部のサイトでブロックされています Android の設定で Chrome の権限を有効にしてください。 @@ -83,16 +87,19 @@ バックグラウンド同期 サイトのストレージ データを削除しますか? 戻る +前方にシーク再生 保護されたコンテンツの再生をサイトに許可する(推奨) リンクのコピー / ? タイトル 特定のサイトの Cookie を許可します。 +次のトラック サイトの設定 通知を受け取るとデバイスが振動します 設定を開く へのアクセスがブロックされました 許可 +サイトでメディアが再生されています Google から提供されている軽量版のページ ポップアップとリダイレクト 保護されたコンテンツの再生をサイトに許可する @@ -107,6 +114,7 @@ デバイスのすべての権限を取り消します Chrome に AR の使用を許可するには、Android の設定でもカメラをオンにしてください。 次に進む +共有方法 許可 サイトによるモーション センサーへのアクセスを許可する(推奨) 使用状況 @@ -163,7 +171,9 @@ 削除 アクティブなダウンロード 下に移動 +閉じるには下にスワイプします。 サードパーティのウェブサイトが Cookie データを保存したり読み取ったりできないようにします +再生 アイテムを選択 このデバイスでは NFC を読み込めません 削除済み @@ -197,6 +207,7 @@ サイトでカメラが使用されています このサイトからログアウトします。 Cookie は、アクセスしたサイトによって作成されるファイルです。サイトではこのファイルを使用して各ユーザーが行なった設定が記憶されます。2 つ目の「サードパーティの Cookie」は、他のサイト、つまり現在ウェブページに表示されているコンテンツの一部(広告、画像など)を所有しているサイトによって作成されます。 +再生中のメディア 選択解除 すべてのサイトからログアウトします。 特定のサイトに対して音声の再生を許可します。 @@ -204,6 +215,7 @@ すべてのローカルデータ(Cookie を含む)を削除して、このウェブサイトに指定したすべての権限をリセットしてもよろしいですか? このデバイスに対して無効 ダウンロード エラー +証明書を選択できません のコンテンツを Google から配信しています。 ミュート中 確認 @@ -216,6 +228,8 @@ サイトでカメラとマイクが使用されています このサイトでは煩わしい広告や誤解を招く広告が表示されます タップしてサイトに戻る +変更 +後方にシーク再生 デバイスの許可を取り消します サイトは高速です サイトからデバイスへの接続をブロックする @@ -236,6 +250,7 @@ 特定のサイトの Cookie をブロックします。 にアクセスできません 保護者により管理されています +Bluetooth マイクへのアクセス 煩わしい広告や誤解を招く広告が表示されるサイトの場合にブロック(推奨) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb index 5f457805be7..212372e6c3a 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ka.xtb @@ -10,6 +10,7 @@ შემდეგი -ში ჩაშენებული შეწყვეტა +მონაცემთა მოცულობის დაზოგვის მიზნით, სურათები ოპტიმიზებულია Google-ის მიერ. ვებსაიტების მიერ შენახული ფაილები აქ გამოჩნდება საიტი: დამატებულია @@ -42,6 +43,7 @@ შეკითხვა საიტებისთვის თქვენი გარემოს 3-განზომილებიანი რუკის შექმნის ან კამერის პოზიციისთვის თვალის მიდევნების დაშვებამდე (რეკომენდებული) ქუქი-ჩანაწერების დაშვება საიტების მიერ ახლომდებარე Bluetooth მოწყობილობების აღმოჩენის მოთხოვნა (რეკომენდებული) +კლიენტის სერტიფიკატების არჩევა არ არის მხარდაჭერილი ოპერაციული სისტემის მიერ. გაგრძელება მბაიტი ჩამოტვირთვა დასრულდა: @@ -61,10 +63,12 @@ დაკოპირდა ნამდვილად გსურთ, საიტის ყველა ნებართვის გადაყენება -ისთვის? საიტი +წინა ჩანაწერი შეკითხვა საიტებისთვის დაცული კონტენტის დაკვრის დაშვებამდე საშუალებას აძლევს საიტებს შეინახონ და გაეცნონ ქუქი ფაილებს (რეკომენდირებული) სერტიფიკატთა მაჩვენებელი ეს მოქმედება მთლიანად გაასუფთავებს ვებსაიტების მეხსიერებას (სულ: ). +დაბლოკილია ავტომატურად ინფორმაციის ჩვენება დაბლოკილია ზოგიერთ საიტზე Chrome-ისთვის ნებართვების გააქტიურება Android-ის პარამეტრებში შეგიძლიათ. @@ -83,16 +87,19 @@ ფონური სინქრონიზაცია გასუფთავდეს საიტების მეხსიერება? უკან +წინ ძიება ვებსაიტებისთვის დაცული კონტენტის დაკვრის დაშვება (რეკომენდებული) ბმულის კოპირება / ? სათაური ქუქი-ჩანაწერების დაშვება კონკრეტული საიტისთვის. +შემდეგი ჩანაწერი საიტის პარამეტრები შეტყობინებებმა შეიძლება მოწყობილობის ვიბრაცია გამოიწვიოს პარამეტრების გახსნა ნავიგაცია დაბლოკილია: დაშვებულია +საიტზე გაშვებულია მედია-კონტენტი გვერდის Lite ვერსიას უზრუნველყოფს Google ამომხ. ფანჯრები/გადამისამართება საიტებისთვის დაცული კონტენტის დაკვრის დაშვება @@ -107,6 +114,7 @@ მოწყობილობისთვის ყველა ნებართვის გაუქმება Chrome-მა AR რომ გამოიყენოს, საჭიროა კამერის ჩართვაც Android-ის პარამეტრებიდან. გადასვლა წინ +გაზიარების პროგრამა დაშვება საიტებისთვის მოძრაობის სენსორებზე წვდომის დაშვება (რეკომენდებული) გამოყენება @@ -163,7 +171,9 @@ გასუფთავება მიმდინარე ჩამოტვირთვები გადაადგილება ქვემოთ +დასახურად გადაფურცლეთ ქვემოთ. მესამე მხარის ვებსაიტებისთვის ქუქი-ჩანაწერების მონაცემების შენახვისა და წაკითხვის აკრძალვა +დაკვრა აირჩიეთ ერთეულები NFC მხარდაუჭერელია ამ მოწყობილობის მიერ წაშლილი @@ -197,6 +207,7 @@ ეს საიტი იყენებს თქვენს კამერას თქვენ გამოხვალთ ამ საიტიდან. ქუქი-ჩანაწერები თქვენ მიერ მონახულებული ვებსაიტების მიერ შექმნილი ფაილებია. მათი მეშვეობით საიტები თქვენ მიერ არჩეულ პარამეტრებს იმახსოვრებს. მესამე მხარის ქუქი-ჩანაწერები იქმნება სხვა საიტების მიერ. აღნიშნულ საიტებს ეკუთვნის თქვენ მიერ მონახულებულ ვებგვერდზე თქვენთვის ხილული გარკვეული კონტენტი (როგორიცაა რეკლამა ან სურათები). +გაშვებული მედია შერჩევის გაუქმება თქვენ გამოხვალთ ყველა საიტიდან. ხმის დაშვება კონკრეტული საიტისთვის. @@ -204,6 +215,7 @@ დარწმუნებული ხართ, რომ გსურთ ყველა ადგილობრივი მონაცემების წაშლა, მათ შორის ქუქი ფაილების, და ამ გვერდისათვის ყველა ნებართვების აღდგენა? გამორთულია ამ მოწყობილობისთვის ჩამოტვირთვა ვერ მოხერხდა +სერტიფიკატის არჩევა შეუძლებელია. კონტენტის წყაროა: . მიღებულია Google-ის მეშვეობით. დადუმებული დაადასტურება @@ -216,6 +228,8 @@ ეს საიტი იყენებს თქვენს კამერასა და მიკროფონს ამ საიტზე ნაჩვენებია მომაბეზრებელი ან შეცდომაში შემყვანი რეკლამა შეეხეთ საიტზე დასაბრუნებლად +შეცვლა +უკან ძიება მოწყობილობაზე წვდომის ნებართვის გაუქმება საიტი სწრაფია საიტებისთვის მოწყობილობებთან დაკავშირების აკრძალვა @@ -236,6 +250,7 @@ ქუქი-ჩანაწერების დაბლოკვა კონკრეტული საიტისთვის. ნავიგაცია მიუწვდომელია: იმართება თქვენი მშობლების მიერ +Bluetooth თქვენი მიკროფონი ხელმისაწვდომია დაბლოკვა, თუ საიტი აჩვენებს მომაბეზრებელ ან შეცდომაში შემყვან რეკლამას (რეკომენდებული) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kk.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kk.xtb index 60a0afdba20..7c37fd68b4c 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kk.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kk.xtb @@ -10,6 +10,7 @@ Келесі мекенжайына ендірілген Тоқтату +Деректеріңізді сақтау үшін бұл беттің суреттері Google арқылы оңтайландырылды. Веб-сайттар арқылы сақталған файлдар осында көрсетіледі. сайты қосылды @@ -42,6 +43,7 @@ Cайттарға айналаңыздың 3D картасын жасауға немесе камераңыздың орнын бақылауға рұқсат беру алдында сізден сұраy (ұсынылады) Cookie файлдарына рұқсат беру Сайт маңайдағы Bluetooth құрылғыларын анықтағысы келген кезде, рұқсат сұралсын (ұсынылады). +Операциялық жүйе клиенттік сертификатты таңдауға қолдау көрсетпейді. Жалғастыру МБ жүктеп алынды @@ -61,10 +63,12 @@ Көшірілген үшін барлық сайт рұқсаттары шынымен бастапқы күйіне қайтарылсын ба? Сайт +Алдыңғы аудиотрек Қорғалған мазмұнды ойнатуға рұқсат етпес бұрын сұрау Сайттарға cookie деректерін сақтауға және оқуға рұқсат беру (ұсынылған) Сертификат көру құралы Вебсайттың барлық деректері жойылады (). +Автоматты түрде бөгелген Ақпаратты көрсету Кейбір сайттарда тыйым салынған Android параметрлері ішінен Chrome үшін рұқсаттар қосыңыз. @@ -83,16 +87,19 @@ Фондық синхрондау Сайт деректерін өшіру керек пе? Артқа +Алға Сайттарға қорғалған мазмұнды ойнатуға рұқсат беру (ұсынылады) Сілтемені көшіру /? Тақырып Белгілі бір сайт үшін cookie файлдарына рұқсат беріңіз. +Келесі аудиотрек Сайт параметрлері Хабарландыру келіп түскенде, құрылғы дірілдейді Параметрлерді ашу Навигация тыйылған: Рұқсат етілген +Сайтта мультимедиа ойнатылуда Google бетті Lite нұсқасында ұсынды Қалқымалы терезе және бағыттау Сайттарға қорғалған мазмұнды ойнатуға рұқсат беру @@ -107,6 +114,7 @@ Құрылғыға арналған барлық рұқсаттардың күші жойылсын. Chrome браузеріне AR режимін пайдалануға рұқсат ету үшін Android параметрлерінен камераны да қосыңыз. Алға өту +Бөлісу Рұқсат беру Сайттардың қозғалыс датчиктеріне кіруіне рұқсат ету (ұсынылады) Пайдалану @@ -163,7 +171,9 @@ Тазалау Жүктеп алынып жатқандар Төмен қарай жылжыту +Жабу үшін төмен сырғытыңыз. Үшінші тараптық вебсайттардың cookie деректерін сақтауына және оқуына жол бермеу +Ойнату Элементтерді таңдаңыз Бұл құрылғы NFC сигналын оқи алмайды Жойылды @@ -197,6 +207,7 @@ Сайт камераны пайдаланып жатыр. Сіз бұл сайтта есептік жазбадан шығарыласыз. Cookie файлдары сайттарға кірген кезде жасалады. Сайттар оларды параметрлер есте сақталуы үшін пайдаланады. Үшінші тараптың cookie файлдары басқа сайттарда жасалады. Бұл сайттарда сіз кірген сайттың мазмұны (мысалы, жарнамалар немесе суреттер) болады. +Медиамазмұн ойнату Таңдаудан бас тарту Сіз барлық сайттардағы есептік жазбадан шығарыласыз. Белгілі бір сайтта дыбыстың шығуына рұқсат ету. @@ -204,6 +215,7 @@ Cookie файлдарын қоса, барлық жергілікті деректерді өшіруді және осы веб-сайтқа арналған барлық рұқсаттарды қалпына келтіруді қалайсыз ба? Осы құрылғы үшін өшірілді Жүктеу сәтсіз аяқталды +Сертификатты таңдау мүмкін емес. Бұл мазмұн Google ұсынған доменінен алынған. Дыбысы өшірулі Растау @@ -216,6 +228,8 @@ Сайт камера мен микрофонды пайдаланып жатыр. Бұл сайтта мазалайтын немесе жалған ақпаратты жарнамалар көрсетіледі Сайтқа оралу үшін түртіңіз. +Өзгерту +Артқа Құрылғыға кіру рұқсатының күшін жою Бұл сайт жылдам. Сайттардың құрылғыға жалғануына тыйым салу @@ -236,6 +250,7 @@ Белгілі бір сайт үшін cookie файлдарына тыйым салыңыз. мекенжайына өту мүмкін емес Ата-аналар басқарады +Bluetooth Микрофонды пайдалану мүмкіндігі Сайт мазалайтын немесе жалған ақпаратты жарнамалар көрсеткен жағдайда бөгеу (ұсынылады) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_km.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_km.xtb index da0f98e5ac0..944a179ffa8 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_km.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_km.xtb @@ -10,6 +10,7 @@ បន្ទាប់ បានបង្កប់​នៅលើ ឈប់ +ដើម្បី​សន្សំសំចៃ​ទិន្នន័យ​ឱ្យអ្នក រូបភាព​របស់ទំព័រនេះ​ត្រូវបានបង្កើន​គុណភាព​ដោយ Google។ ឯកសារ​ដែលបានរក្សាទុក​ដោយគេហទំព័រ​បង្ហាញ​នៅទីនេះ - គេហទំព័រ ត្រូវបានបន្ថែម @@ -42,6 +43,7 @@ សួរ​មុនពេល​អនុញ្ញាតឱ្យ​គេហទំព័របង្កើតផែនទី 3D នៃមជ្ឈដ្ឋានជុំវិញរបស់អ្នក ឬតាមដានទីតាំងកាមេរ៉ា (បានណែនាំ) អនុញ្ញាត​ខូគី សួរនៅពេលគេហទំព័រ​ចង់ស្វែងរកឧបករណ៍​ប៊្លូធូសដែលនៅជិត (បានណែនាំ) +ការជ្រើសរើសវិញ្ញាបនបត្រសម្រាប់ម៉ាស៊ីនកូនមិនគាំទ្រដោយប្រព័ន្ធដំណើរការនេះទេ។ បន្ត MB ការទាញយក​បានបញ្ចប់ @@ -61,10 +63,12 @@ បានថតចម្លង តើ​អ្នកប្រាកដថា​ចង់​កំណត់​ការអនុញ្ញាត​គេហទំព័រ​ទាំងអស់​ឡើងវិញ​សម្រាប់ ដែរទេ? គេហទំព័រ +បទ​មុន សួរ​មុនពេល​អនុញ្ញតឱ្យគេហទំព័រ​លេង​ខ្លឹមសារ​ដែលមាន​ការការពារ អនុញ្ញាតឲ្យគេហទំព័ររក្សាទុក និងអានទិន្នន័យខុកឃី (បានណែនាំ) កម្មវិធីមើលវិញ្ញាបនប័ត្រ វានឹងជម្រះ ទាំងស្រុងនៃទំហំផ្ទុកគេហទំព័រ +បាន​ទប់ស្កាត់ដោយ​ស្វ័យ​ប្រវត្តិ បង្ហាញព័ត៌មាន បាន​ទប់ស្កាត់នៅលើគេហទំព័រ​មួយចំនួន បើកសិទ្ធិអនុញ្ញាតសម្រាប់ Chrome នៅក្នុង ការកំណត់ Android @@ -83,16 +87,19 @@ សមកាលកម្មផ្ទៃខាងក្រោយ ជម្រះទំហំផ្ទុកគេហទំព័រឬ? ថយក្រោយ +ស្វែងរក​ទៅ​មុខ អនុញ្ញាត​ឲ្យ​ទំព័រ​ចាក់​មាតិកា​ដែលមាន​ការ​ការពារ (ត្រូវបាន​ណែនាំ) ចម្លងតំណ / ? ចំណងជើង អនុញ្ញាតខូគី​សម្រាប់គេហទំព័រ​ជាក់លាក់។ +បទ​បន្ទាប់ ការកំណត់គេហទំព័រ ការជូនដំណឹងអាចនឹងធ្វើឲ្យឧបករណ៍ញ័រ បើកការកំណត់ ការរុករកត្រូវបានរារាំង៖ បានអនុញ្ញាត +គេហទំព័រ​កំពុង​ចាក់មេឌៀ ទំព័រស្រាល​ដែល​ផ្ដល់​ដោយ Google ផ្ទាំងផុស​ និង​ការ​បញ្ជូន​បន្ត អនុញ្ញាតឱ្យ​គេហទំព័រ​ចាក់ខ្លឹមសារដែលមាន​ការការពារ @@ -107,6 +114,7 @@ ដក​ការអនុញ្ញាត​ទាំងអស់​សម្រាប់​ឧបករណ៍ ដើម្បី​អនុញ្ញាតឱ្យ Chrome ប្រើ AR សូមបើក​កាមេរ៉ានៅក្នុងការកំណត់ Android ផងដែរ។ ទៅមុខ +ចែករំលែកតាមរយៈ អនុញ្ញាត អនុញ្ញាតឱ្យ​គេហទំព័រ​ចូលប្រើ​ឧបករណ៍​ចាប់ចលនា (បាន​ណែនាំ) ការប្រើប្រាស់ @@ -163,7 +171,9 @@ ជម្រះ ការទាញយក​ដែលកំពុង​ដំណើរការ រំកិលចុះ +អូស​ចុះ​ក្រោម​ដើម្បី​បិទ។ ទប់ស្កាត់​គេហទំព័រ​ភាគី​ទីបី​មិនឲ្យ​រក្សាទុក និង​អានទិន្នន័យ​ខូគី +លេង ជ្រើសរើសធាតុ ឧបករណ៍នេះ​មិនអាចអាន NFC បានទេ បានលុប @@ -197,6 +207,7 @@ គេហទំព័រមួយកំពុងប្រើប្រាស់កាមេរ៉ារបស់អ្នក អ្នក​នឹង​ត្រូវនាំចេញ​ពី​គេហទំព័រ​នេះ។ ខូគី​ជាឯកសារ​ដែលបង្កើត​ដោយ​គេហទំព័រ​ដែលអ្នក​ចូលមើល។ គេហទំព័រប្រើ​ខូគីទាំងនេះ ដើម្បីចងចាំចំណូលចិត្តរបស់អ្នក។ ខូគី​ភាគីទីបី​បង្កើត​ដោយ​គេហទំព័រ​ផ្សេងទៀត។ គេហទំព័រ​ទាំងនេះ​គឺជាម្ចាស់​នៃខ្លឹមសារ​មួយចំនួន ​ដូចជា​ការផ្សាយពាណិជ្ជកម្ម ឬរូបភាព ដែលអ្នក​ឃើញ​នៅលើ​ទំព័របណ្ដាញដែលអ្នកចូលមើលជាដើម។ +ការចាក់មេឌៀ បោះបង់ការជ្រើសរើស អ្នក​នឹងត្រូវនាំចេញ​ពី​គេហទំព័រ​ទាំងអស់។ អនុញ្ញាតសំឡេងសម្រាប់ទំព័រជាក់លាក់។ @@ -204,6 +215,7 @@ តើអ្នកប្រាកដទេដែលចង់សម្អាតទិន្នន័យមូលដ្ឋានទាំងអស់រាប់ទាំងខុឃី ហើយកំណត់ការអនុញ្ញាតទាំងឡាយសម្រាប់គេហទំព័រនេះឡើងវិញនោះ? បានបិទសម្រាប់ឧបករណ៍នេះ បានបរាជ័យក្នុងការទាញយក +មិនអាចជ្រើសរើសវិញ្ញាបនបត្រទេ មាតិកានេះបានពី ដែលចែកចាយដោយ Google។ បានបិទសំឡេង អះអាង @@ -216,6 +228,8 @@ គេហទំព័រមួយ​កំពុង​ប្រើប្រាស់កាមេរ៉ា និងមីក្រូហ្វូនរបស់អ្នក គេហទំព័រ​នេះបង្ហាញ​ការផ្សាយពាណិជ្ជកម្ម​ដែលនាំឱ្យយល់ច្រឡំ ឬរំខាន ចុចដើម្បីត្រឡប់ទៅគេហទំព័រវិញ +ប្តូរ +ស្វែងរក​ថយក្រោយ ដកហូតសិទ្ធិអនុញ្ញាតឧបករណ៍ គេហទំព័រ​ដំណើរការ​លឿន ទប់​ស្កាត់​ទំព័រ​មិន​ឱ្យ​ភ្ជាប់​ជាមួយ​ឧបករណ៍ @@ -236,6 +250,7 @@ ទប់ស្កាត់​ខូគីសម្រាប់​គេហទំព័រ​ជាក់លាក់។ ការុករកមិនអាចភ្ជាប់បានទេ៖ គ្រប់គ្រងដោយឪពុកម្តាយរបស់អ្នក +ប៊្លូធូស ចូលប្រើម៉ៃក្រូហ្វូនរបស់អ្នក ទប់ស្កាត់ ​ប្រសិនបើ​គេហទំព័របង្ហាញ​ការផ្សាយពាណិជ្ជកម្ម​ដែលនាំឱ្យយល់ច្រឡំ ឬរំខាន (បានណែនាំ) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kn.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kn.xtb index 4cd93ff8d7e..dc020e32ff3 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kn.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_kn.xtb @@ -10,6 +10,7 @@ ಮುಂದೆ ನಲ್ಲಿ ಎಂಬೆಡ್ ಮಾಡಲಾಗಿದೆ ನಿಲ್ಲಿಸಿ +ನಿಮ್ಮ ಡೇಟಾವನ್ನು ಉಳಿಸಲು, ಈ ಪುಟದ ಚಿತ್ರಗಳನ್ನು Google ಮೂಲಕ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ. ವೆಬ್‌ಸೈಟ್‌ಗಳ ಮೂಲಕ ಉಳಿಸಲಾಗಿರುವ ಫೈಲ್‌ಗಳು ಇಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ - ಸೈಟ್ ಸೇರಿಸಲಾಗಿದೆ @@ -42,6 +43,7 @@ ನಿಮ್ಮ ಸುತ್ತಮುತ್ತಲಿನ 3D ನಕ್ಷೆಗಳನ್ನು ರಚಿಸಲು ಅಥವಾ ಕ್ಯಾಮರಾ ಸ್ಥಿತಿಯನ್ನು ಟ್ರ್ಯಾಕ್ ಮಾಡಲು ಸೈಟ್‌ಗೆ ಅನುಮತಿಸುವ ಮೊದಲು ಕೇಳಿ (ಶಿಫಾರಸು ಮಾಡಲಾಗಿರುವುದು) ಕುಕೀಗಳನ್ನು ಅನುಮತಿಸಿ ಸೈಟ್ ಯಾವಾಗ ಸಮೀಪದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ಅನ್ವೇಷಿಸಲು ಬಯಸುತ್ತದೆಯೋ ಆಗ ಕೇಳಿ (ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ) +ಕ್ಲೈಂಟ್‌ನ ಪ್ರಮಾಣಪತ್ರ ಆಯ್ಕೆಯನ್ನು ಆಪರೇಟಿಂಗ್ ಸಿಸ್ಟಂನಿಂದ ಬೆಂಬಲಿಸಲಾಗಿಲ್ಲ. ಮುಂದುವರೆಸಿ MB ಡೌನ್‌ಲೋಡ್‌‌ ಪೂರ್ಣಗೊಂಡಿದೆ @@ -61,10 +63,12 @@ ನಕಲಿಸಲಾಗಿದೆ ನೀವು ಗಾಗಿ ಎಲ್ಲಾ ಸೈಟ್ ಅನುಮತಿಗಳನ್ನು ಖಚಿತವಾಗಿಯೂ ಮರುಹೊಂದಿಸಲು ಬಯಸುತ್ತೀರಾ? ಸೈಟ್ +ಹಿಂದಿನ ಟ್ರ್ಯಾಕ್ ಸಂರಕ್ಷಿತ ವಿಷಯವನ್ನು ಪ್ಲೇ ಮಾಡಲು ಸೈಟ್‌ಗಳಿಗೆ ಅನುಮತಿಸುವ ಮೊದಲು ಕೇಳಿ ಕುಕೀ ಡೇಟಾವನ್ನು ಉಳಿಸಲು ಮತ್ತು ರೀಡ್ ಮಾಡಲು ಸೈಟ್‌ಗಳನ್ನು ಅನುಮತಿಸಿ (ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ) ಪ್ರಮಾಣಪತ್ರ ವೀಕ್ಷಕ ಇದು ವೆಬ್‌ಸೈಟ್ ಸಂಗ್ರಹಣೆಯ ಎಲ್ಲಾ ಅನ್ನು ತೆರವುಗೊಳಿಸುತ್ತದೆ. +ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ ಮಾಹಿತಿಯನ್ನು ತೋರಿಸಿ ಕೆಲವು ಸೈಟ್‌ಗಳಲ್ಲಿ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ Android ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ Chrome ಗೆ ಅನುಮತಿಗಳನ್ನು ಆನ್ ಮಾಡಿ. @@ -83,16 +87,19 @@ ಹಿನ್ನೆಲೆ ಸಿಂಕ್ ಸೈಟ್ ಸಂಗ್ರಹಣೆ ತೆರವುಗೊಳಿಸುವುದೇ? ಹಿಂದೆ +ಮುಂದಕ್ಕೆ ಸೀಕ್ ಮಾಡಿ ಸಂರಕ್ಷಿಸಲಾದ ವಿಷಯವನ್ನು ಪ್ಲೇ ಮಾಡಲು ಸೈಟ್‌ಗಳಿಗೆ ಅನುಮತಿಸಿ (ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ) ಲಿಂಕ್ ನಕಲಿಸಿ / ? ಶೀರ್ಷಿಕೆ ನಿರ್ದಿಷ್ಟ ಸೈಟ್‌ ಒಂದಕ್ಕೆ ಕುಕೀಗಳನ್ನು ಅನುಮತಿಸಿ. +ಮುಂದಿನ ಟ್ರ್ಯಾಕ್ ಸೈಟ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಪ್ರಕಟಣೆಗಳು ಸಾಧನವನ್ನು ವೈಬ್ರೇಟ್ ಮಾಡಬಹುದು ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆ ನ್ಯಾವಿಗೇಶನ್‌ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ: ಅನುಮತಿಸಲಾಗಿದೆ +ಒಂದು ಸೈಟ್, ಮಾಧ್ಯಮವನ್ನು ಪ್ಲೇ ಮಾಡುತ್ತಿದೆ Lite ಪುಟವನ್ನು Google ಒದಗಿಸಿದೆ ಪಾಪ್-ಅಪ್‌ಗಳು ಹಾಗೂ ಮರುನಿರ್ದೇಶನಗಳು ಸಂರಕ್ಷಿತ ವಿಷಯವನ್ನು ಪ್ಲೇ ಮಾಡಲು ಸೈಟ್‌ಗಳಿಗೆ ಅನುಮತಿ ನೀಡಿ @@ -107,6 +114,7 @@ ಸಾಧನಕ್ಕೆ ನೀಡಿರುವ ಎಲ್ಲಾ ಅನುಮತಿಗಳನ್ನು ಹಿಂಪಡೆಯಿರಿ AR ಬಳಸಲು Chrome ಗೆ ಅನುಮತಿ ನೀಡುವುದಕ್ಕಾಗಿ, Android ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಕ್ಯಾಮರಾವನ್ನು ಆನ್ ಮಾಡಿ. ಮುಂದಕ್ಕೆ ಹೋಗು +ಇದರ ಮೂಲಕ ಹಂಚಿ ಅನುಮತಿಸಿ ಚಲನಾ ಸೆನ್ಸರ್‌ಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಸೈಟ್‌ಗಳಿಗೆ ಅನುಮತಿ ನೀಡಿ (ಶಿಫಾರಸು ಮಾಡಿರುವುದು) ಬಳಕೆ @@ -163,8 +171,10 @@ ತೆರವುಗೊಳಿಸಿ ಸಕ್ರಿಯ ಡೌನ್‌ಲೋಡ್‌ಗಳು ಕೆಳಗೆ ಸರಿಸು +ಮುಚ್ಚಲು ಕೆಳಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ. ಕುಕೀ ಡೇಟಾವನ್ನು ಮೂರನೇ ವ್ಯಕ್ತಿ ವೆಬ್‌ಸೈಟ್‌ಗಳು ಉಳಿಸದಂತೆ ಮತ್ತು ಓದದಂತೆ ತಡೆಯಿರಿ. +ಪ್ಲೇ ಮಾಡು ಐಟಂಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ ಈ ಸಾಧನವು NFC ಯನ್ನು ಓದಲು ಸಾಧ್ಯವಿಲ್ಲ ಅಳಿಸಲಾಗಿದೆ @@ -198,6 +208,7 @@ ಒಂದು ಸೈಟ್ ನಿಮ್ಮ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸುತ್ತಿದೆ ನಿಮ್ಮನ್ನು ಈ ಸೈಟ್‌ನಿಂದ ಸೈನ್ ಔಟ್ ಮಾಡಲಾಗುತ್ತದೆ. ಕುಕೀಗಳು ಎಂದರೆ ನೀವು ಭೇಟಿ ನೀಡುವ ವೆಬ್‌ಸೈಟ್‌ಗಳು ರಚಿಸಿದ ಫೈಲ್‌ಗಳು. ನಿಮ್ಮ ಆದ್ಯತೆಗಳನ್ನು ನೆನಪಿನಲ್ಲಿಡಲು, ಸೈಟ್‌ಗಳು ಕುಕೀಗಳನ್ನು ಬಳಸಿಕೊಳ್ಳುತ್ತವೆ. ಥರ್ಡ್-ಪಾರ್ಟಿ ಕುಕೀಗಳನ್ನು ಇತರ ಸೈಟ್‌ಗಳಿಂದ ರಚಿಸಲಾಗಿದೆ. ನೀವು ಭೇಟಿ ನೀಡುವ ವೆಬ್‌ಪುಟ ನೀವು ನೋಡುವ ಜಾಹೀರಾತುಗಳು ಅಥವಾ ಚಿತ್ರಗಳಂತಹ ಕೆಲವು ರೀತಿಯ ವಿಷಯವನ್ನು, ಈ ಸೈಟ್‌ಗಳು ಮಾಲೀಕತ್ವವನ್ನು ಹೊಂದಿವೆ. +ಮೀಡಿಯಾ ಪ್ಲೇ ಆಗುತ್ತಿದೆ ಆಯ್ಕೆ ರದ್ದುಮಾಡಿ ಎಲ್ಲಾ ಸೈಟ್‌ಗಳಿಂದ ನಿಮ್ಮನ್ನು ಸೈನ್ ಔಟ್ ಮಾಡಲಾಗುತ್ತದೆ. ನಿರ್ದಿಷ್ಟ ಸೈಟ್‌ನ ಧ್ವನಿಯನ್ನು ಅನುಮತಿಸಿ. @@ -205,6 +216,7 @@ ಕುಕೀಗಳು ಸೇರಿದಂತೆ, ಸ್ಥಳೀಯ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಅಳಿಸಲು ಮತ್ತು ಈ ವೆಬ್‌ಸೈಟ್‌ಗೆ ಎಲ್ಲಾ ಅನುಮತಿಗಳನ್ನು ಮರುಹೊಂದಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ? ಈ ಸಾಧನಕ್ಕೆ ಆಫ್ ಮಾಡಲಾಗಿದೆ ಡೌನ್‌ಲೋಡ್‌ ವಿಫಲಗೊಂಡಿದೆ +ಪ್ರಮಾಣಪತ್ರವನ್ನು ಆಯ್ಕೆಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ಡೊಮೇನ್‌‌ನ ಈ ವಿಷಯವನ್ನು Google ನಿಂದ ವಿತರಿಸಲಾಗಿದೆ. ಮ್ಯೂಟ್‌ ಆಗಿರುವುದು ದೃಢೀಕರಿಸು @@ -217,6 +229,8 @@ ಒಂದು ಸೈಟ್ ನಿಮ್ಮ ಕ್ಯಾಮರಾ ಹಾಗೂ ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು ಬಳಸುತ್ತಿದೆ ಅತಿಕ್ರಮಣಕಾರಿಯಾಗಿರುವ ಅಥವಾ ತಪ್ಪುದಾರಿಗೆಳೆಯುವ ಜಾಹೀರಾತುಗಳನ್ನು ಈ ಸೈಟ್ ತೋರಿಸುತ್ತದೆ ಸೈಟ್‌ಗೆ ಮರಳಲು ಟ್ಯಾಪ್ ಮಾಡಿ +ಬದಲಿಸಿ +ಹಿಂದಕ್ಕೆ ಸೀಕ್ ಮಾಡಿ ಸಾಧನ ಅನುಮತಿಯನ್ನು ಹಿಂತೆಗೆದುಕೊಳ್ಳಿ ಸೈಟ್ ವೇಗವಾಗಿದೆ ಸಾಧನಗಳಿಗೆ ಸಂಪರ್ಕಿಸದಂತೆ, ಸೈಟ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ @@ -237,6 +251,7 @@ ನಿರ್ದಿಷ್ಟ ಸೈಟ್‌ ಒಂದಕ್ಕೆ ಕುಕೀಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ. ನ್ಯಾವಿಗೇಶನ್‌ ತಲುಪಲಾಗುವುದಿಲ್ಲ: ನಿಮ್ಮ ಪೋಷಕರು ನಿರ್ವಹಿಸುತ್ತಿದ್ದಾರೆ +ಬ್ಲೂಟೂತ್‌ ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಪ್ರವೇಶಿಸಿ ಅತಿಕ್ರಮಣಕಾರಿಯಾಗಿರುವ ಅಥವಾ ತಪ್ಪುದಾರಿಗೆಳೆಯುವ ಜಾಹೀರಾತುಗಳನ್ನು ಸೈಟ್ ತೋರಿಸಿದರೆ ಅದನ್ನು ನಿರ್ಬಂಧಿಸಿ (ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ko.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ko.xtb index 741be12d77b..403f48f88cc 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ko.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ko.xtb @@ -10,6 +10,7 @@ 다음 에 삽입됨 중지 +데이터를 저장하기 위해 Google에서 이 페이지의 이미지를 최적화했습니다. 웹사이트에서 저장한 파일이 여기에 표시됩니다. - 사이트 추가됨 @@ -42,6 +43,7 @@ 사이트에서 주변 환경의 3D 지도를 생성하거나 카메라 위치를 추적하도록 허용하기 전에 확인(권장) 쿠키 허용 사이트가 주변 블루투스 기기를 조회하려고 할 때 확인(권장) +클라이언트측 인증서 선택이 운영체제에서 지원되지 않습니다. 계속 MB 다운로드 완료: @@ -61,10 +63,12 @@ 복사됨 의 모든 사이트 권한을 재설정하시겠습니까? 사이트 +이전 트랙 사이트에서 보호된 콘텐츠를 재생하도록 허용하기 전에 확인 사이트에서 쿠키 데이터를 저장하고 읽도록 허용(권장) 인증서 뷰어 웹사이트 저장공간 가 모두 삭제됩니다. +자동으로 차단됨 정보 표시 일부 사이트에서 차단됨 Android 설정에서 Chrome 관련 권한을 설정합니다. @@ -83,16 +87,19 @@ 백그라운드 동기화 사이트 저장공간을 삭제하시겠습니까? 뒤로 +앞으로 탐색 사이트에서 보호된 콘텐츠를 재생하도록 허용(권장) 링크 복사 /? 제목 특정 사이트의 쿠키를 허용합니다. +다음 트랙 사이트 설정 알림이 있으면 진동이 울릴 수도 있습니다. 설정 열기 탐색이 차단됨: 허용 +사이트에서 미디어 재생 중 Google에서 제공하는 라이트 페이지 팝업 및 리디렉션 사이트에서 보호된 콘텐츠를 재생하도록 허용 @@ -107,6 +114,7 @@ 기기에서 모든 권한을 취소합니다. Chrome에서 AR을 사용하려면 Android 설정에서도 카메라를 사용 설정합니다. 앞으로 이동 +공유 방법 허용 사이트의 움직임 감지 센서 액세스 허용(권장) 사용 @@ -163,7 +171,9 @@ 삭제 진행 중인 다운로드 아래로 이동 +닫으려면 아래로 스와이프하세요. 타사 웹사이트에서 쿠키 데이터를 저장하거나 읽을 수 없도록 방지 +재생 항목 선택 기기에서 NFC를 읽을 수 없음 삭제됨 @@ -197,6 +207,7 @@ 사이트에서 카메라 사용 중 이 사이트에서 로그아웃됩니다. 쿠키는 방문한 웹사이트에서 생성된 파일로, 사이트에서 사용자의 설정을 기억하기 위해 사용합니다. 타사 쿠키는 다른 사이트, 즉 광고나 이미지 등 방문하는 웹페이지에 표시되는 일부 콘텐츠를 소유하는 사이트에 의해 생성됩니다. +재생 중인 미디어 선택 취소 모든 사이트에서 로그아웃됩니다. 특정 사이트에서 소리를 재생하도록 허용합니다. @@ -204,6 +215,7 @@ 쿠키를 포함하여 이 웹사이트의 모든 로컬 데이터를 삭제하고 모든 권한을 재설정하시겠습니까? 기기에 대해 사용 중지됨 다운로드 실패 +인증서를 선택할 수 없습니다. 이 콘텐츠의 출처는 Google에서 제공하는 입니다. 음소거됨 확인 @@ -216,6 +228,8 @@ 사이트에서 카메라와 마이크를 사용 중입니다. 이 사이트에서는 방해가 되거나 사용자를 현혹하는 광고를 표시합니다. 탭하여 사이트로 돌아가기 +변경 +뒤로 탐색 기기 액세스 권한 취소 사이트 속도 빠름 사이트에서 기기에 연결하지 못하도록 차단 @@ -236,6 +250,7 @@ 특정 사이트의 쿠키를 차단합니다. 탐색할 수 없음: 부모님이 관리합니다. +블루투스 마이크에 액세스 사이트에서 방해가 되거나 사용자를 현혹하는 광고를 표시하는 경우 광고 차단(권장) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ky.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ky.xtb index 51ca8fd3abc..48481112d39 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ky.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ky.xtb @@ -10,6 +10,7 @@ Кийинки кыналган Токтотуу +Интернет трафигин үнөмдөө үчүн Google бул барактагы сүрөттөрдү ыңгайлаштырды. Вебсайттар сактаган файлдар ушул жерде көрүнөт , сайты кошулду @@ -42,6 +43,7 @@ Сайттар айланаңыздын 3D картасын түзгөнү же камераңыздын абалын көргөнү жатканда уруксат суралсын (сунушталат) Cookies файлдарына уруксат берүү Сайт жакын жердеги Bluetooth түзмөктөрүн колдонгону жатканда уруксат суралсын (сунушталат) +Кардар тандаган тастыктама операциялык тутумда колдоого алынбайт. Улантуу Мб Жүктөлүп алынды: @@ -61,10 +63,12 @@ Көчүрүлдү үчүн сайттагы бардык уруксаттар баштапкы абалга келтирилсинби? Сайт +Мурунку трек Сайттар корголгон мазмунду ойнотуудан мурда уруксат суралсын Сайттарга куки дайындарын сактоого жана окууга уруксат берүү (сунушталат) Тастыктаманы көрүүчү Ушуну менен сайттардын дайындарынын баары тазаланат. +Автоматтык түрдө бөгөттөлдү Маалыматты көрсөтүү Айрым сайттарда бөгөттөлдү Chrome үчүн уруксаттарды Android Жөндөөлөрүнөн күйгүзүңүз. @@ -83,16 +87,19 @@ Фонду шайкештирүү Сайттардын дайындары тазалансынбы? Артка +Алдыга түрдүрүү Сайттарга корголгон мазмунду ойнотууга уруксат берүү (сунушталат) Шилтм көчр / ? Аталышы Белгилүү бир сайттын cookie файлдарына уруксат берүү. +Кийинки трек Сайт жөндөөлөрү Билдирмелер кегенде түзмөк дирилдейт Жөндөөлөрдү ачуу Чабыттоо бөгөттөлгөн: Уруксат берилген +Сайтта медиа файл ойнотулууда Барактын lite-версиясы Google аркылуу алынган. Калкыма терезелер жана багыттоолор Сайттарга корголгон мазмунду ойнотууга уруксат берүү @@ -107,6 +114,7 @@ Түзмөктөгү бардык уруксаттар жоюлсун Chrome'го AR'ди колдоонуусуна уруксат берүү үчүн камераны Android жөндөөлөрүнөн күйгүзүңүз. Алга +Төмөнкү аркылуу бөлүшүү Уруксат берүү Сайттарга сенсорлорду колдонууга уруксат берүү (сунушталат) Колдонулушу @@ -163,7 +171,9 @@ Тазалоо Жүктөлүп алынган файлдар Төмөн жылдыруу +Жабуу үчүн ылдый сүрүңүз. Үчүнчү тараптардын вебсайттарына cookie файлдарын окуп, сактаганга тыюу салынат +Ойнотуу Элементтерди тандоо Бул түзмөк NFC'ни окуй албайт Жок кылынды @@ -197,6 +207,7 @@ Сайт камераңызды колдонуп жатат Бул сайттан чыгасыз. Cookie файлдары сиз баш баккан вебсайттар түзгөн файлдар. Сайттар аларды жеке параметрлериңизди эстеп калуу үчүн колдонушат. Үчүнчү тараптын cookie файлдарын башка сайттар түзөт. Ал сайттарга сиз баш баккан веб-баракчада көрсөтүлгөн жарнамалар жана сүрөттөр сыяктуу айрым мазмун таандык. +Медиа ойнотулууда Тандоону жокко чыгаруу Бардык сайттардан чыгарыласыз. Белгилүү бир сайтка добуш чыгарууга уруксат берүү. @@ -204,6 +215,7 @@ Чын эле жергиликтүү дайындарын баарын тазалап, анын ичинде кукилерди, жана бул вебсайттын уруксаттарынын баарын баштапкы абалга келтиргиңиз келеби? Бул түзмөк үчүн өчүрүлгөн Жүктөлүп алынбай калды +Тастыктаманы тандоо мүмкүн эмес. Бул мазмун доменинен алынып, Google аркылуу жеткирилген. Үнсүз кылынган Ырастоо @@ -216,6 +228,8 @@ Сайт камераңызды жана микрофонуңузду колдонуп жатат Бул сайт тажатма же адаштыруучу жарнамаларды көрсөтүп жатат Сайтка кайтуу үчүн таптаңыз +Өзгөртүү +Артка түрдүрүү Түзмөккө болгон уруксатты жоюу Сайт тез жүктөлөт Сайттардын түзмөктөргө туташуусун бөгөттөө @@ -236,6 +250,7 @@ Белгилүү бир сайттын cookie файлдарын бөгөттөө. Чабыттоо жеткиликсиз: Ата-энеңиз башкарат +Bluetooth Микрофонуңузга мүмкүнчүлүк алуу Эгер сайт тажатма же адаштыруучу жарнамаларды көрсөтүп баштаса, бөгөттөлсүн (сунушталат) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lo.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lo.xtb index 4cefef70cf4..b20ba9b4013 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lo.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lo.xtb @@ -10,6 +10,7 @@ ຕໍ່ໄປ ຝັງຕິດໃນ ຢຸດ +ເພື່ອຊ່ວຍທ່ານປະຢັດອິນເຕີເນັດ, Google ໄດ້ປັບຮູບຂອງໜ້ານີ້ໃຫ້ເໝາະສົມແລ້ວ. ໄຟລ໌ທີ່ບັນທຶກໄວ້ໂດຍເວັບໄຊປາກົດຢູ່ບ່ອນນີ້ - ເພີ່ມ ເວັບ​ໄຊ​ທ໌​ແລ້ວ @@ -42,6 +43,7 @@ ຖາມກ່ອນທີ່ຈະອະນຸຍາດໃຫ້ເວັບໄຊສ້າງແຜນທີ່ 3 ມິຕິຂອງສິ່ງທີ່ຢູ່ອ້ອມຂ້າງຕົວທ່ານ ຫຼື ຕາມຕຳແໜ່ງກ້ອງ (ແນະນຳ) ອະນຸຍາດຄຸກກີ້ ຖາມເມື່ອເວັບໄຊຕ້ອງການກວດຫາອຸປະກອນ Bluetooth ທີ່ຢູ່ໃກ້ຄຽງ (ແນະນຳ) +ການ​ເລືອກ​ໃບ​ຢັ້ງ​ຢືນເບື້ອງ​ລູກ​ຂ່າຍ​ບໍ່​ຖືກ​ຮອງ​ຮັບ​ໂດຍ​ລະ​ບົບ​ປະຕິບັດການ. ສືບຕໍ່ MB ການດາວໂຫຼດສຳເລັດແລ້ວ @@ -61,10 +63,12 @@ ອັດ​ສຳ​ເນົາ​ແລ້ວ ທ່ານແນ່ໃຈບໍ່ວ່າທ່ານຕ້ອງການຣີເຊັດການອະນຸຍາດເວັບໄຊທັງໝົດສຳລັບ ? ເວັບ​ໄຊທ໌ +ເພງກ່ອນນີ້ ຖາມກ່ອນທີ່ຈະອະນຸຍາດໃຫ້ເວັບໄຊຫຼິ້ນເນື້ອຫາທີ່ມີການປ້ອງກັນໄວ້ ອະ​ນຸ​ຍາດ​ໃຫ້​ເວັບ​ໄຊ​ທ໌​ບັນ​ທຶກ ແລະ​ອ່ານ​ຂໍ້​ມູນ​ຄຸກ​ກີ້ (ແນະ​ນຳ​ໃຫ້) ຕົວເບິ່ງໃບຢັ້ງຢືນ ນີ້ຈະລຶບລ້າງທັງໝົດ ຂອງບ່ອນເກັບຂໍ້ມູນເວັບໄຊ. +ບລັອກອັດຕະໂນມັດແລ້ວ ສະແດງຂໍ້ມູນ ບລັອກໃນບາງເວັບໄຊ ເປີດ​ການ​ອະ​ນຸ​ຍາດ​ສຳ​ລັບ Chrome ຢູ່​ໃນ ການ​ຕັ້ງ​ຄ່າ Android. @@ -83,16 +87,19 @@ ການຊິ້ງຂໍ້ມູນໃນພື້ນຫຼັງ ລຶບລ້າງບ່ອນເກັບຂໍ້ມູນເວັບໄຊບໍ? ກັບ​ຄືນ​ +ເລື່ອນໄປໜ້າ ອະນຸຍາດໃຫ້ເວັບໄຊຕ່າງໆຫຼິ້ນເນື້ອຫາທີ່ໄດ້ຮັບການປົກປ້ອງ (ແນະນຳ) ອັດ​ສຳ​ເນົາລິ້ງເຊື່ອມໂຍງ / ? ຫົວຂໍ້ ອະນຸຍາດຄຸກກີ້ສຳລັບເວັບໄຊສະເພາະ. +ເພງຕໍ່ໄປ ການຕັ້ງຄ່າເວັບໄຊທ໌ ການແຈ້ງເຕືອນອາດຈະເຮັດໃຫ້ອຸປະກອນສັ່ນ ເປີດ​ການ​ຕັ້ງ​ຄ່າ ການນໍາທາງຖືກບລັອກໄວ້: ອະນຸຍາດແລ້ວ +ເວັບໄຊກຳລັງຫຼິ້ນສື່ຢູ່ ໜ້າ Lite ທີ່ Google ສະໜອງໃຫ້ ປັອບອັບ ແລະ ການປ່ຽນເສັ້ນທາງ ອະນຸຍາດໃຫ້ເວັບໄຊຫຼິ້ນເນື້ອຫາທີ່ໄດ້ຮັບການປົກປ້ອງ @@ -107,6 +114,7 @@ ຖອນການອະນຸຍາດທັງໝົດສຳລັບອຸປະກອນ ເພື່ອອະນຸຍາດໃຫ້ Chrome ໃຊ້ AR, ກະລຸນາເປີດກ້ອງໃນ ການຕັ້ງຄ່າ Android ນຳ. ໄປຂ້າງ​ຫນ້າ +ແຊຣ໌ຜ່ານ ອະ​ນຸ​ຍາດ​ ອະນຸຍາດໃຫ້ເວັບໄຊເຂົ້າເຖິງເຊັນເຊີກວດຈັບການເຄື່ອນໄຫວ (ແນະນຳ) ການນໍາໃຊ້ @@ -163,7 +171,9 @@ ລຶບ ການດາວໂຫຼດທີ່ດຳເນີນຢູ່ ຍ້າຍ​ລົງ +ປັດລົງເພື່ອປິດ. ປ້ອງກັນບໍ່ໃຫ້ເວັບໄຊພາກສ່ວນທີສາມບັນທຶກ ແລະ ອ່ານຂໍ້ມູນຄຸກກີ້ +​ຫຼິ້ນ ເລືອກລາຍການ ອຸປະກອນນີ້ບໍ່ສາມາດອ່ານ NFC ໄດ້ ລຶບແລ້ວ @@ -197,6 +207,7 @@ ເວັບໄຊກຳລັງໃຊ້ກ້ອງຂອງທ່ານຢູ່ ທ່ານຈະຖືກນຳອອກຈາກລະບົບເວັບໄຊນີ້. ຄຸກກີ້ແມ່ນໄຟລ໌ທີ່ສ້າງຂຶ້ນໂດຍເວັບໄຊທີ່ທ່ານເຂົ້າເບິ່ງ. ເວັບໄຊໃຊ້ພວກມັນເພື່ອຈື່ການຕັ້ງຄ່າຂອງທ່ານ. ຄຸກກີ້ພາກສ່ວນທີສາມຖືກສ້າງຂຶ້ນໂດຍເວັບໄຊອື່ນ. ເວັບໄຊເຫຼົ່ານີ້ເປັນເຈົ້າຂອງບາງເນື້ອຫາ ເຊັ່ນ: ໂຄສະນາ ຫຼື ຮູບພາບ ທີ່ທ່ານເຫັນໃນໜ້າເວັບທີ່ທ່ານເຂົ້າເບິ່ງ. +ກຳລັງຫຼິ້ນສື່ ຍົກ​ເລີກ​ການ​ເລືອກ ທ່ານຈະຖືກນຳອອກຈາກລະບົບເວັບໄຊທັງໝົດ. ອະນຸຍາດສຽງສຳລັບເວັບໄຊສະເພາະໃດໜຶ່ງ. @@ -204,6 +215,7 @@ ທ່ານ​ແນ່​ໃຈ​ບໍ​ວ່າ ທ່ານ​ຕ້ອງ​ການ​ລຶບ​ຂໍ້​ມູນໃນ​ເຄື່ອງ​ທັງ​ໝົດ, ລວມ​ທັງ​ຄຸກ​ກີ້, ແລະ​ຕັ້ງ​ຄ່າ​ການ​ອະ​ນຸ​ຍາດ​ທັງ​ໝົດສຳ​ລັບ​ເວັບ​ໄຊ​ທ໌​ນີ້? ປິດ​ສຳ​ລັບ​ອຸ​ປະ​ກອນ​ນີ້ ການດາວໂຫຼດບໍ່ສຳເລັດ +ບໍ່​ສາ​ມາດ​ເລືອກ​ໃບ​ຢັ້ງ​ຢືນ​ໄດ້. ເນື້ອຫານີ້ມາຈາກ , ນຳສົ່ງໂດຍ Google. ປິດສຽງແລ້ວ ຢືນ​ຢັນ @@ -216,6 +228,8 @@ ເວັບໄຊກຳລັງໃຊ້ກ້ອງ ແລະ ໄມໂຄຣໂຟນຂອງທ່ານຢູ່ ເວັບໄຊນີ້ສະແດງໂຄສະນາທີ່ລົບກວນ ຫຼື ຫຼອກລວງ ແຕະເພື່ອກັບຄືນຫາເວັບໄຊ +ປ່ຽນ​ແປງ +ເລື່ອນກັບຫຼັງ ຖອນການອະນຸຍາດອຸປະກອນ ເວັບໄຊໄວດີ ບລັອກບໍ່ໃຫ້ເວັບໄຊເຊື່ອມຕໍ່ກັບອຸປະກອນ @@ -236,6 +250,7 @@ ບລັອກຄຸກກີ້ສຳລັບເວັບໄຊສະເພາະ. ການນຳທາງແມ່ນບໍ່ສາມາດເຂົ້າເຖິງໄດ້: ຈັດ​ການ​ໂດຍ​ຜູ້​ປົກ​ຄອງ​ຂອງ​ທ່ານ +Bluetooth ເຂົ້າ​ຫາ​ໄມ​ໂຄ​ຣ​ໂຟນ​ຂອງ​ທ່ານ ບລັອກຖ້າເວັບໄຊສະແດງໂຄສະນາທີ່ລົບກວນ ຫຼື ຫຼອກລວງ (ແນະນຳ) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lt.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lt.xtb index 369ae5fb324..f4caf4227a8 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lt.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lt.xtb @@ -10,6 +10,7 @@ Kitas Įterpta Sustabdyti +Siekdama apsaugoti jūsų duomenis, „Google“ optimizavo šio puslapio vaizdus. Svetainių išsaugoti failai rodomi čia “: Pridėta svetainė @@ -42,6 +43,7 @@ Klausti, ar svetainėms leidžiama kurti jūsų aplinkos 3D žemėlapį ir stebėti kameros padėtį (rekomenduojama) Leisti slapukus Paklausti, kai svetainė nori atrasti netoliese esančius „Bluetooth“ įrenginius (rekomenduojama) +Kliento pasirinkto sertifikato nepalaiko operacinė sistema. Tęskite MB Atsisiųsta: @@ -61,10 +63,12 @@ Nukopij. Ar tikrai norite iš naujo nustatyti visus „“ svetainės leidimus? Svetainė +Ankstesnis takelis Paklausti prieš leidžiant svetainėms leisti saugomą turinį Leisti svetainėms išsaugoti ir nuskaityti slapukų duomenis (rekomenduojama) Sertifikato peržiūros priemonė Bus išvalyta visa svetainės saugykla. +Automatiškai užblokuota Rodyti informaciją Užblokuota kai kuriose svetainėse Įjunkite „Chrome“ leidimus „Android“ nustatymuose. @@ -83,16 +87,19 @@ Fono sinchronizavimas Išvalyti svet. saugyklą? Atgal +Eiti pirmyn Leisti svetainėms paleisti apsaugotą turinį (rekomenduojama) Kop. nuor. iš ? Pavadinimas Leisti konkrečios svetainės slapukus. +Kitas takelis Svetainės nustatymai Gavus pranešimą įrenginys gali vibruoti Atidaryti nustatymus Naršymas užblokuotas: Leidžiama +Svetainėje leidžiama medija Supaprastintasis puslapis, kurį teikia „Google“ Iššok. langai ir peradresavimai Leisti svetainėms leisti saugomą turinį @@ -107,6 +114,7 @@ Anuliuoti visus įrenginio leidimus Norėdami leisti „Chrome“ naudoti AR, taip pat įjunkite fotoaparatą „Android“ nustatymuose. Eiti pirmyn +Bendrinti per Leisti Leidžiama svetainėms pasiekti judesio jutiklius (rekomenduojama) Naudojimas @@ -163,7 +171,9 @@ Išvalyti Aktyvūs atsisiuntimai Perkelti žemyn +Perbraukite žemyn, kad uždarytumėte. Neleisti trečiosios šalies svetainėms išsaugoti ir skaityti slapukų duomenų +Žaisti Pasirinkite elementus Šis įrenginys negali nuskaityti ALR Ištrintas @@ -197,6 +207,7 @@ Svetainėje naudojamas jūsų fotoaparatas Būsite atjungti nuo šios svetainės. Slapukai yra svetainių, kuriose lankėtės, sukurti failai. Svetainėse jie naudojami jūsų nuostatoms įsiminti. Trečiųjų šalių slapukus kuria kitos svetainės. Šioms svetainėms priklauso tam tikras tinklalapyje, kuriame lankotės, rodomas turinys, pvz., skelbimai ar vaizdai. +Leidžiama medija Atšaukti pasirinkimą Būsite atjungti nuo visų svetainių. Konkrečios svetainės garso leidimas. @@ -204,6 +215,7 @@ Ar tikrai norite išvalyti visus šios svetainės vietinius duomenis, įskaitant slapukus, ir iš naujo nustatyti visus leidimus? Išjungta šiame įrenginyje Įvyko atsisiuntimo klaida +Nepavyko pasirinkti sertifikato. Šis turinys yra iš domeno , kurį teikia „Google“. Išjungta Patvirtinti @@ -216,6 +228,8 @@ Svetainėje naudojamas jūsų fotoaparatas ir mikrofonas Šioje svetainėje rodomi nepageidaujami arba klaidinantys skelbimai Norėdami grįžti į svetainę, palieskite +Keisti +Ieškoti einant atgal Anuliuoti įrenginio leidimą Svetainė veikia sparčiai Blokuoti svetaines, kad nebūtų galima prisijungti prie įrenginių @@ -236,6 +250,7 @@ Blokuoti konkrečios svetainės slapukus. Naršymas nepasiekiamas: Tvarko jūsų tėvai +Bluetooth Prieiga prie mikrofono Blokuoti, jei svetainėje rodomi nepageidaujami arba klaidinantys skelbimai (rekomenduojama) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lv.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lv.xtb index 7808128cb97..e3b52607c46 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lv.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_lv.xtb @@ -10,6 +10,7 @@ Tālāk Iegults vietnē Apturēt +Lai palīdzētu jums samazināt datu lietojumu, Google optimizēja šajā lapā iekļautos attēlus. Šeit tiek rādīti tīmekļa vietņu saglabātie faili  — Tika pievienota vietne @@ -42,6 +43,7 @@ Vaicāt, pirms ļaut vietnēm izveidot jūsu apkārtnes 3D karti vai izsekot kameras pozīciju (ieteicams) Atļaut sīkfailus Vaicāt, ja vietne vēlas redzēt tuvumā esošās Bluetooth ierīces (ieteicams) +Operētājsistēma neatbalsta klienta puses sertifikāta atlasi. Turpināt  MB Lejupielāde pabeigta:   @@ -61,10 +63,12 @@ Nokopēts Vai tiešām vēlaties atiestatīt visas vietņu atļaujas, kas piešķirtas šim objektam: ? Vietne +Iepriekšējais ieraksts Jautāt, pirms atļaut vietnēm atskaņot aizsargātu saturu Atļaut vietnēm saglabāt un lasīt sīkfailu datus (ieteicams) Sertifikātu skatītājs Tādējādi tiks notīrīti visi vietnes krātuves dati (). +Automātiski bloķēta Rādīt informāciju Bloķētas dažās vietnēs Android iestatījumos ieslēdziet atļaujas pārlūkam Chrome. @@ -83,16 +87,19 @@ Sinhronizācija fonā Vai notīrīt vietnes krātuvi? Atpakaļ +Pārtīt uz priekšu Atļaut vietnēm atskaņot aizsargātu saturu (ieteicams) Saites kopēšana no ? Nosaukums Atļaut sīkfailus konkrētai vietnei. +Nākamais ieraksts Vietnes iestatījumi Saņemot paziņojumu, ierīce var vibrēt. Atvērt iestatījumus Navigācija ir bloķēta: Atļauts +Vietne atskaņo multivides saturu Vienkāršota lapa, ko nodrošina Google Uznirstošie elem. un novirzīšana Ļaut vietnēm atskaņot aizsargātu saturu @@ -107,6 +114,7 @@ Atsaukt visas atļaujas, kas piešķirtas ierīcei Lai pārlūkā Chrome varētu izmantot papildināto realitāti, ieslēdziet arī kameras atļauju Android iestatījumos. Doties uz priekšu +Kopīgošanas veids: Atļaut Atļaut vietnēm piekļūt kustību sensoriem (ieteicams) Lietojums @@ -163,7 +171,9 @@ Notīrīt Aktīvas lejupielādes Pārvietot uz leju +Velciet uz leju, lai aizvērtu. Neļaut trešo pušu vietnēm saglabāt un nolasīt sīkfailu datus +Atskaņot Vienumu atlase Šī ierīce nevar lasīt NFC Dzēsts @@ -197,6 +207,7 @@ Kāda vietne izmanto jūsu kameru Jūs tiksiet izrakstīts no šīs vietnes. Sīkfaili ir faili, ko izveido jūsu apmeklētās vietnes. Vietnes izmanto šos sīkfailus, lai atcerētos jūsu preferences. Trešo pušu sīkfailus izveido citas vietnes. Jūsu apmeklētajā tīmekļa lapā var būt ietverts citu vietņu saturs, piemēram, reklāmas vai attēli. +Multivides atskaņošana Atcelt atlasi Jūs tiksiet izrakstīts no visām vietnēm. Atļaut skaņu konkrētai vietnei @@ -204,6 +215,7 @@ Vai tiešām vēlaties notīrīt visus ar šo vietni saistītos lokālos datus, tostarp sīkfailus, un atiestatīt visas atļaujas? Izslēgts šai ierīcei Lejupielāde neizdevās +Nevar atlasīt sertifikātu. Šis saturs ir no vietnes , ko nodrošina Google. Izslēgta skaņa Apstiprināt @@ -216,6 +228,8 @@ Kāda vietne izmanto jūsu kameru un mikrofonu Šajā vietnē tiek rādītas traucējošas vai maldinošas reklāmas Lai atgrieztos vietnē, pieskarieties šeit +Mainīt +Pārtīt atpakaļ Atsaukt ierīces atļauju Ātra vietnes darbība Neļaut vietnēm izveidot savienojumu ar ierīci @@ -236,6 +250,7 @@ Bloķēt sīkfailus konkrētai vietnei. Navigācija nav sasniedzama: Pārvalda jūsu vecāki +Bluetooth Piekļuve mikrofonam Bloķēt, ja vietnē tiek rādītas traucējošas vai maldinošas reklāmas (ieteicams) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mk.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mk.xtb index 17bf88a0b67..8ba453167da 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mk.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mk.xtb @@ -10,6 +10,7 @@ Следно Вметнато во Запри +Google ги оптимизираше сликите на страницава за да ви заштеди интернет. Датотеките зачувани од веб-сајтовите се појавуваат тука - Локацијата е додадена @@ -42,6 +43,7 @@ Прашувај пред да им дозволиш на сајтовите да создадат 3D-карта на опкружувањето или да ја следат позицијата на камерата (препорачано) Дозволи колачиња Прашај кога некој сајт сака да открива уреди со Bluetooth во близина (се препорачува) +Оперативниот систем не поддржува избор на сертификат од страна на клиентот. Продолжи MB Преземањето е завршено @@ -61,10 +63,12 @@ Копирано Сигурно ли сакате да ги ресетирате сите дозволи на сајтот за ? Локација +Претходна песна Прашај пред да дозволиш сајтовите да пуштаат заштитени содржини Дозволете локациите да зачувуваат и читаат податоци за колачињата (препорачано) Прикажувач на сертификат Ова ќе ги избрише сите од просторот на веб-сајтот. +Автоматски блокирана Прикажи ги информациите Блокирано на некои сајтови Вклучете ги дозволите за Chrome во Поставки на Android. @@ -83,16 +87,19 @@ Синхронизација во заднина Испразни го просторот? Назад +Барај понапред Дозволете сајтовите да пуштаат заштитени содржини (се препорачува) Копирај линк /? Наслов Дозволете колачиња за конкретен сајт. +Следна песна Поставки на локација Известувањата може да предизвикаат вибрации на уредот Отвори ги поставките Навигацијата е блокирана: Дозволено +Сајтот репродуцира аудиовизуелни содржини Lite-страница овозможена од Google Скокачки прозорци/пренасочувања Дозволи сајтовите да пуштаат заштитени содржини @@ -107,6 +114,7 @@ Повлечи ги сите дозволи за уредот За да дозволите Chrome да користи AR, вклучете ја и камерата во Поставките за Android. Оди напред +Сподели преку Дозволи Дозволете сајтовите да пристапуваат до сензорите за движење (препорачано) Користење @@ -163,7 +171,9 @@ Исчисти Активни преземања Премести долу +Повлечете надолу за да затворите. Спречи ги веб-сајтовите на трети лица да зачувуваат и читаат податоци за колачињата +Репродуцирај Изберете ставки Уредов не може да чита NFC Избришано @@ -197,6 +207,7 @@ Сајтот ја користи камерата Ќе ве одјавиме од сајтов. Колачињата се датотеки создадени од веб-сајтовите што ги посетувате. Сајтовите ги користат за да ги запомнат вашите поставки. Колачињата од трети страни се создадени од други сајтови. Овие сајтови се сопственици на дел од содржините, како рекламите или сликите што ги гледате на веб-страницата што ја посетувате. +Пуштање аудиовизуелни содржини Откажи избор Ќе ве одјавиме од сите сајтови. Дозволете го звукот за одреден сајт. @@ -204,6 +215,7 @@ Дали сте сигурни дека сакате да ги исчистите сите локални податоци, вклучувајќи ги колачињата, и да ги ресетирате сите дозволи за веб-локацијата? Исклучена за овој уред Неуспешно преземање +Сертификатот не може да се избере. Оваа содржина е од , овозможена од Google. Со исклучен звук Потврди @@ -216,6 +228,8 @@ Сајтот ги користи камерата и микрофонот Сајтов прикажува нападни или лажни реклами Допрете за да се вратите на сајтот +Промени +Барај поназад Отповикај ја дозволата за пристап до уредот Сајтот е брз Блокирај ги сајтовите од поврзување со уреди @@ -236,6 +250,7 @@ Блокирајте колачиња за одреден сајт. Навигацијата е недостапна: Управувано од вашите родители +Bluetooth Пристапи до микрофонот Блокирај ако сајтот прикажува нападни или лажни реклами (препорачано) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ml.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ml.xtb index 43f847cd546..9caf00796e0 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ml.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ml.xtb @@ -10,6 +10,7 @@ അടുത്തത് -ൽ ഉൾച്ചേർത്തത് നിര്‍ത്തുക +നിങ്ങളുടെ ഡാറ്റ സംരക്ഷിക്കുന്നതിന്, ഈ പേജിലെ ചിത്രങ്ങൾ Google ഒപ്റ്റിമൈസ് ചെയ്തിരിക്കുന്നു. വെബ്സൈറ്റുകൾ സംരക്ഷിച്ചിട്ടുള്ള ഫയലുകള്‍ ഇവിടെ ദൃശ്യമാവും - എന്ന സൈറ്റ് ചേർത്തൂ @@ -42,6 +43,7 @@ നിങ്ങളുടെ ചുറ്റുപാടുകളുടെ 3D മാപ്പ് സൃഷ്ടിക്കുന്നതിനോ ക്യാമറയുടെ സ്ഥാനം ട്രാക്ക് ചെയ്യുന്നതിനോ സൈറ്റുകളെ അനുവദിക്കുന്നതിന് മുമ്പ് ചോദിക്കുക (ശുപാർശ ചെയ്യുന്നത്) കുക്കികൾ അനുവദിക്കുക സമീപത്തുള്ള Bluetooth ഉപകരണങ്ങൾ കണ്ടെത്താൻ ഒരു സൈറ്റ് താൽപ്പര്യപ്പെടുമ്പോൾ ചോദിക്കുക (ശുപാർശചെയ്‌തത്) +ഓപ്പറേറ്റിംഗ് സിസ്റ്റം ക്ലയന്റിന്റെ സർട്ടിഫിക്കറ്റ് തിരഞ്ഞെടുക്കലിനെ പിന്തുണയ്‌ക്കുന്നില്ല. തുടരുക MB ഡൗൺലോഡ് പൂർത്തിയായി @@ -61,10 +63,12 @@ പകർത്തി എന്നതിനുള്ള എല്ലാ സൈറ്റ് അനുമതികളും പുനഃസജ്ജീകരിക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? സൈറ്റ് +മുമ്പത്തെ ട്രാക്ക് പരിരക്ഷിത ഉള്ളടക്കം പ്ലേ ചെയ്യാൻ സൈറ്റുകളെ അനുവദിക്കുന്നതിന് മുമ്പ് ചോദിക്കുക കുക്കി ഡാറ്റ സംരക്ഷിക്കുന്നതിനും വായിക്കുന്നതിനും സൈറ്റുകളെ അനുവദിക്കുക (ശുപാർശചെയ്‌തത്) സർട്ടിഫിക്കറ്റ് വ്യൂവർ ഇത് വെബ്‌സൈറ്റ് സ്‌റ്റോറേജിലെ പൂർണ്ണമായും മായ്‌ക്കും. +സ്വയമേവ ബ്ലോക്കുചെയ്‌തു വിവരങ്ങൾ കാണിക്കുക ചില സൈറ്റുകളിൽ ബ്ലോക്ക് ചെയ്‌തിരിക്കുന്നു Android ക്രമീകരണത്തിൽ Chrome-നായി അനുമതികൾ ഓണാക്കുക. @@ -83,16 +87,19 @@ പശ്ചാത്തല സമന്വയിപ്പിക്കൽ സൈറ്റ് സ്‌റ്റോറേജ് മായ്‌ക്കണോ? പിന്നോട്ട് +മുന്നോട്ട് നീക്കുക പരിരക്ഷിത ഉള്ളടക്കം പ്ലേ ചെയ്യാൻ സൈറ്റുകളെ അനുവദിക്കുക (ശുപാർശ ചെയ്‌തിരിക്കുന്നു) ലിങ്ക് പകർത്തുക / ? ശീർഷകം ഒരു പ്രത്യേക സൈറ്റിനായി കുക്കികൾ അനുവദിക്കുക. +അടുത്ത ട്രാക്ക് സൈറ്റ് ക്രമീകരണങ്ങൾ അറിയിപ്പുകൾ ലഭിക്കുമ്പോൾ ഉപകരണം വൈബ്രേറ്റ് ചെയ്‌തേക്കാം ക്രമീകരണം തുറക്കുക നാവിഗേഷൻ തടഞ്ഞിരിക്കുന്നു: അനുവദനീയം +സൈറ്റ്, മീഡിയ പ്ലേ ചെയ്യുന്നു Google നല്‍കുന്ന ലൈറ്റ് പേജ് പോപ്-അപ്പുകളും റീഡയറക്‌റ്റുകളും പരിരക്ഷിത ഉള്ളടക്കം പ്ലേ ചെയ്യാൻ സൈറ്റുകളെ അനുവദിക്കുക @@ -107,6 +114,7 @@ ഉപകരണത്തിനുള്ള എല്ലാ അനുമതികളും അസാധുവാക്കുക Chrome-നെ AR ഉപയോഗിക്കാൻ അനുവദിക്കുന്നതിന് Android ക്രമീകരണത്തിൽ ക്യാമറയും ഓണാക്കുക. മുന്നോട്ട് പോകുക +ഇതുവഴി പങ്കിടുക അനുവദിക്കൂ നിങ്ങളുടെ ചലന സെൻസറുകൾ ആക്‌സസ് ചെയ്യാൻ സൈറ്റുകളെ അനുവദിക്കുക (ശുപാർശ ചെയ്യുന്നത്) ഉപയോഗം @@ -163,7 +171,9 @@ മായ്‌ക്കുക സജീവ ഡൗൺലോഡുകൾ താഴേക്ക് നീക്കുക +അവസാനിപ്പിക്കാൻ താഴേക്ക് സ്വൈപ്പ് ചെയ്യുക. കുക്കി വിവരം സംരക്ഷിക്കുകയും വായിക്കുകയും ചെയ്യുന്നതിൽ നിന്ന് മൂന്നാംകക്ഷി വെബ്‌സൈറ്റുകളെ തടയുക +പ്ലേചെയ്യുക ഇനങ്ങൾ തിരഞ്ഞെടുക്കുക ഈ ഉപകരണത്തിന് NFC റീഡ് ചെയ്യാനാവില്ല ഇല്ലാതാക്കി @@ -197,6 +207,7 @@ ഒരു സൈറ്റ് നിങ്ങളുടെ ക്യാമറ ഉപയോഗിക്കുന്നു നിങ്ങൾ ഈ സൈറ്റിൽ നിന്ന് സൈൻ ഔട്ടാകും. നിങ്ങൾ സന്ദർശിക്കുന്ന വെബ്‌സൈറ്റുകൾ സൃഷ്ടിക്കുന്ന ഫയലുകളാണ് കുക്കികൾ. നിങ്ങളുടെ മുൻ‌ഗണനകൾ ഓർമ്മിക്കാൻ സൈറ്റുകൾ അവ ഉപയോഗിക്കുന്നു. മൂന്നാം കക്ഷി കുക്കികളെ സൃഷ്ടിക്കുന്നത് മറ്റ് സൈറ്റുകളാണ്. നിങ്ങൾ സന്ദർശിക്കുന്ന വെബ്‌പേജിൽ കാണുന്ന പരസ്യങ്ങളോ ചിത്രങ്ങളോ പോലുള്ള ചില ഉള്ളടക്കം ഈ സൈറ്റുകളുടെ ഉടമസ്ഥതയിലാണ്. +മീഡിയ പ്ലേ ചെയ്യുന്നു തിരഞ്ഞെടുത്തത് റദ്ദാക്കുക എല്ലാ സൈറ്റുകളിൽ നിന്നും നിങ്ങൾ സൈൻ ഔട്ട് ചെയ്യപ്പെടും. ഒരു പ്രത്യേക സൈറ്റിനായി ശബ്‌ദം അനുവദിക്കുക. @@ -204,6 +215,7 @@ കുക്കികൾ ഉൾപ്പെടെ എല്ലാ പ്രാദേശിക വിവരവും മായ്‌ച്ച് ഈ വെബ്‌സൈറ്റിനായുള്ള എല്ലാ അനുമതികളും റീസെറ്റ് ചെയ്യാൻ താൽപ്പര്യമുണ്ടോ? ഈ ഉപകരണത്തിനായി ഓഫാക്കി ഡൗൺലോഡ് പരാജയപ്പെട്ടു +സർട്ടിഫിക്കറ്റ് തിരഞ്ഞെടുക്കാനാവുന്നില്ല. ഈ ഉള്ളടക്കം Google-ൽ നിന്നുള്ള ഡൊമെയ്‌നിൽ നിന്നുള്ളതാണ്. മ്യൂട്ടുചെയ്‌തു സ്ഥിരീകരിക്കുക @@ -216,6 +228,8 @@ ഒരു സൈറ്റ് നിങ്ങളുടെ ക്യാമറയും മൈക്രോഫോണും ഉപയോഗിക്കുന്നു ഈ സൈറ്റ്, അനാവശ്യമോ തെറ്റിദ്ധരിപ്പിക്കുന്നതോ ആയ പരസ്യങ്ങള്‍ കാണിക്കുന്നു സൈറ്റിലേക്ക് മടങ്ങാൻ ടാപ്പ് ചെയ്യുക +മാറ്റുക +പുറകിലേക്ക് സീക്കുചെയ്യുക ഉപകരണ അനുമതി റദ്ദാക്കുക വളരെ വേഗതയുള്ള സൈറ്റ് ഉപകരണങ്ങളിലേക്ക് കണക്‌റ്റ് ചെയ്യുന്നതിൽ നിന്ന് സൈറ്റുകളെ ബ്ലോക്ക് ചെയ്യുക @@ -236,6 +250,7 @@ ഒരു പ്രത്യേക സൈറ്റിനായി കുക്കികൾ ബ്ലോക്ക് ചെയ്യുക. നാവിഗേഷൻ ലഭ്യമല്ല: നിങ്ങളുടെ രക്ഷിതാക്കൾ നിയന്ത്രിക്കുന്നു +Bluetooth നിങ്ങളുടെ മൈക്രോഫോൺ ആക്‌സസ് ചെയ്യുക സൈറ്റ്, അനാവശ്യമോ തെറ്റിദ്ധരിപ്പിക്കുന്നതോ ആയ പരസ്യങ്ങള്‍ കാണിക്കുന്നുണ്ടെങ്കില്‍ ബ്ലോക്ക് ചെയ്യുക (ശുപാര്‍ശ ചെയ്യുന്നു) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb index be39f27ded0..513b6707fa6 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mn.xtb @@ -10,6 +10,7 @@ Дараагийнх -д оруулсан Зогс +Таны өгөгдлийг хадгалахын тулд Google энэ хуудасны зургуудыг оновчилсон байна. Вэб сайтын хадгалсан файлыг энд харуулна - Сайт нэмсэн @@ -42,6 +43,7 @@ Сайтуудад таны эргэн тойрны 3D газрын зургийг үүсгэх эсвэл камерын хөдөлгөөнийг хянахыг зөвшөөрөхийн өмнө асуух (санал болгосон) Күүкиг зөвшөөрөх Сайт ойролцоох Bluetooth төхөөрөмжийг илрүүлэх хүсэлтэй үед асуух (санал болгосон) +Үйлчлүүлэгч талын сертификатын сонголтыг үйлдлийн системээс дэмжээгүй. Цааш мегабайт Татаж дууссан @@ -61,10 +63,12 @@ Хуулсан Та -н бүх сайтын зөвшөөрлийг шинэчлэх үү? Ажлын талбар +Өмнөх бичлэг Сайтад хамгаалалттай контент тоглуулахыг зөвшөөрөхөөс өмнө асуух Cookie data-г хадгалах мөн унших боломжийг сайтуудад олгох (зөвлөж байна) Сертификат харагч Энэ нь вэб хуудасны сангийн бүх -г устгах болно. +Автоматаар блок хийсэн Мэдээллийг харуулах Зарим сайт дээр блоклосон Chrome-ын зөвшөөрлийг Android-ын Тохиргоо-д асаагаарай. @@ -83,16 +87,19 @@ Дэвсгэрт синк хийх Сайтын санг устгах уу? Буцах +Урагш хайх Сайтад хамгаалсан агуулгыг тоглохыг зөвшөөрөх (санал болгосон) Холбоосыг хуулах / ? Гарчиг Күүкиг тодорхой сайтад зөвшөөрнө үү. +Дараагийн бичлэг Сайтын тохиргоо Мэдэгдэл ирэхэд төхөөрөмж чичрэнэ Нээлттэй тохиргоо Навигацыг хориглосон байна: Зөвшөөрөгдсөн +Сайт медиа тоглуулж байна Google-с өгч буй энгийн хуудас Попап болон дахин чиглүүлэлт Сайтуудад хамгаалагдсан контент тоглуулахыг зөвшөөрөх @@ -107,6 +114,7 @@ Төхөөрөмжийн бүх зөвшөөрлийг цуцлах AR ашиглах боломжийг Chrome-д олгохын тулд камерыг Android тохиргоо хэсэгт мөн асаана уу. Цаашаа явах +-аар хуваалцах Зөвшөөрөх Сайтад хөдөлгөөн мэдрэгчид хандахыг зөвшөөрөх (санал болгосон) Ашиглалт @@ -163,7 +171,9 @@ Цэвэрлэх Идэвхтэй татаж авсан файл Доош зөөх +Хаахын тулд доош шударна уу. Күүки өгөгдлийг гуравдагч талын вэб хуудсанд хадгалж, уншихаас сэргийлэх +Тоглуулах Зүйл сонгох Энэ төхөөрөмж NFC-г унших боломжгүй байна Устгасан @@ -197,6 +207,7 @@ Сайт таны камерыг ашиглаж байна Та энэ сайтаас гарна. Күүки нь таны зочилдог вэб сайтуудын үүсгэдэг файл юм. Сайтууд нь таны сонголтыг санахын тулд түүнийг ашигладаг. Гуравдагч талын күүкиг бусад сайт үүсгэдэг. Эдгээр сайт нь таны зочилдог вэб хуудсан дээрээ хардаг зар эсвэл зураг зэрэг контентын заримыг нь эзэмшдэг. +Медиаг тоглуулж байна Сонголтыг цуцлах Та бүх сайтаас гарах болно. Тодорхой сайтад дуу тоглуулахыг зөвшөөрнө үү. @@ -204,6 +215,7 @@ Та cookies гэх мэт энэ вэбсайтны бүх мэдээллийг устгах мөн бүх зөвшөөрлийг дахин тохируулахдаа итгэлтэй байна уу? Энэ төхөөрөмж дээр унтраастай байна Татаж чадсангүй +Сертификат сонгох боломжгүй. -н энэ агуулгыг Google танд хүргэж байна. Дууг хаасан Батлах @@ -216,6 +228,8 @@ Сайт таны камер болон микрофоныг ашиглаж байна Энэ сайт төвөгтэй эсвэл хуурамч зар харуулдаг Сайт руу буцахын тулд товшино уу +Өөрчлөлт +Буцаж хайх Төхөөрөмжийн зөвшөөрлийг хүчингүй болгох Сайт хурдан Сайтуудыг төхөөрөмжүүдэд холбогдохыг нь блоклох @@ -236,6 +250,7 @@ Күүкиг тодорхой сайтад хориглоно уу. Навигацтай холбогдох боломжгүй байна: Эцэг, эх нь хариуцан удирдаж байна +Bluetooth Микрофонд хандах Сайт төвөгтэй эсвэл хуурамч зар харуулсан тохиолдолд блоклох (санал болгосон) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mr.xtb index ce9bf9403a6..fabc78405eb 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mr.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_mr.xtb @@ -10,6 +10,7 @@ पुढील वर एम्बेड केले थांबा +तुमचा डेटा वाचवण्यासाठी, या पेजच्या इमेज Google ने ऑप्टिमाइझ केल्या आहेत. वेबसाइटद्वारे सेव्ह केलेल्या फाइल येथे दिसतील - साइट जोडली @@ -42,6 +43,7 @@ साइटना तुमच्या आसपासच्या परिसराचा 3D नकाशा तयार करू किंवा कॅमेर्‍याचे स्थान ट्रॅक करू देण्याआधी विचारा (शिफारस केलेले) कुकींना अनुमती द्या साइटला केव्हा जवळपासचे ब्लूटूथ डिव्हाइस शोधायचे आहे हे विचारा (शिफारस केलेले) +क्लायंट साइड प्रमाणपत्र निवडीला ऑपरेटिंग सिस्टमचा सपोर्ट नाही. सुरू ठेवा MB डाउनलोड पूर्ण झाले @@ -61,10 +63,12 @@ कॉपी केले तुम्हाला नक्की साठी सर्व साइट परवानग्या रीसेट करायच्या आहेत का? साइट +मागील ट्रॅक सायटींना संरक्षित आशय प्ले करू देण्याआधी विचारा कुकी डेटा सेव्ह करणे आणि वाचण्यासाठी साइटना अनुमती द्या (शिफारस केलेले) सर्टिफिकेट दर्शक हे सर्व वेबसाइट स्टोरेज साफ करेल. +आपोआप ब्लॉक केलेले माहिती दाखवा काही साइटवर ब्लॉक केले आहे Chrome साठी परवानग्या Android सेटिंग्ज मध्‍ये सुरू करा. @@ -83,16 +87,19 @@ पार्श्वभूमी संकालन साइट स्टोरेज साफ करायचे? मागील +पुढे शोधा संरक्षित आशय प्ले करण्यासाठी साइटना अनुमती द्या (शिफारस केलेले) लिंक कॉपी करा / ? शीर्षक विशिष्ट साइटसाठी कुकीना अनुमती द्या. +पुढील ट्रॅक साइट सेटिंग्ज सूचनांमुळे डिव्हाइसचे कंपन होऊ शकते सेटिंग्ज उघडा नेव्हिगेशन अवरोधित केले आहे: अनुमत +साइट मीडिया प्ले करत आहे Google द्वारे पुरवलेले लाइट पेज पॉप-अप आणि रीडिरेक्ट साइटना संरक्षित आशय प्ले करू द्या @@ -107,6 +114,7 @@ डिव्हाइससाठीच्या सर्व परवानग्या रद्द करा Chrome ला AR वापरू देण्यासाठी, Android सेटिंग्जमध्ये कॅमेरादेखील सुरू करा. पुढे जा +याद्वारे शेअर करा परवानगी द्या साइटना मोशन सेन्सर ॲक्सेस करण्याची अनुमती द्या (शिफारस केलेले) वापर @@ -163,7 +171,9 @@ साफ करा सुरू असलेली डाउनलोड खाली हलवा +बंद करण्यासाठी खाली स्वाइप करा. तृतीय-पक्ष वेबसाइटना कुकी डेटा सेव्ह करण्यास आणि वाचण्यास प्रतिबंधित करा +प्ले करा आयटम निवडा हे डिव्हाइस NFC रीड करू शकत नाही हटवला @@ -197,6 +207,7 @@ साइट तुमचा कॅमेरा वापरत आहे तुम्हाला या साइटमधून साइन आउट केले जाईल. कुकी म्हणजे तुम्ही भेट देता त्या वेबसाइटनी तयार केलेल्या फाइल. तुमची प्राधान्ये लक्षात ठेवण्यासाठी साइट त्यांचा वापर करतात. तृतीय पक्ष कुकी इतर साइटकडून तयार केल्या जातात. या साइटमध्ये जाहिराती किंवा इमेज यांसारखा काही आशय असतो, जो तुम्ही भेट देत असलेल्या वेबपेजवर दिसतो. +मीडिया प्ले करत आहे निवड रद्द करा तुम्हाला सर्व साइटमधून साइन आउट केले जाईल. विशिष्ट साइटसाठी ध्वनीची परवानगी द्या. @@ -204,6 +215,7 @@ तुम्ही कुकीजसह, सर्व स्थानिक डेटा साफ करू इच्छिता आणि या वेबसाइटसाठी सर्व परवानग्या रीसेट करू इच्छिता? या डिव्हाइससाठी बंद करा डाउनलोड अयशस्वी झाले +सर्टिफिकेट निवडण्यास अक्षम. Google ने वितरित केलेली ही आशय, मधील आहे. म्यूट केले पुष्टी करा @@ -216,6 +228,8 @@ साइट तुमचा कॅमेरा आणि मायक्रोफोन वापरत आहे ही साइट अनाहूत किंवा दिशाभूल करणाऱ्या जाहिराती दाखवते साइटवर परत जाण्यासाठी टॅप करा +बदल करा +मागे शोधा डिव्हाइस परवानगी रद्द करा साइट जलद आहे डिव्हाइसेसशी कनेक्ट करण्यापासून साइटना ब्लॉक करा @@ -236,6 +250,7 @@ विशिष्ट साइटसाठी कुकी ब्लॉक करा. नेव्हिगेशन आवाक्याबाहेर आहे: आपल्या पालकांद्वारे व्यवस्थापित करण्यात आले +ब्लूटूथ तुमचा मायक्रोफोन ॲक्सेस करा साइट अनाहूत किंवा दिशाभूल करणाऱ्या जाहिराती दाखवत असल्यास ब्लॉक करा (शिफारस केलेले) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ms.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ms.xtb index 3d15ce530de..8ffe59154a3 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ms.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ms.xtb @@ -10,6 +10,7 @@ Seterusnya Dibenamkan pada Berhenti +Untuk menjimatkan data anda, imej halaman ini telah dioptimumkan oleh Google. Fail yang disimpan oleh tapak web akan dipaparkan di sini - Tapak telah ditambahkan @@ -42,6 +43,7 @@ Tanya sebelum membenarkan tapak membuat peta 3D bagi persekitaran anda atau menjejaki kedudukan kamera (disyorkan) Benarkan kuki Tanya apabila tapak mahu mencari peranti Bluetooth berdekatan (disyorkan) +Pemilihan sijil pihak pelanggan tidak disokong oleh sistem pengendalian ini. Teruskan MB Muat turun selesai: @@ -61,10 +63,12 @@ Disalin Adakah anda pasti mahu menetapkan semula semua kebenaran tapak untuk ? Tapak +Lagu sebelumnya Tanya sebelum membenarkan tapak memainkan kandungan yang dilindungi Benarkan tapak untuk menyimpan dan membaca data kuki (disyorkan) Pemapar sijil Tindakan ini akan menghapuskan semua daripada storan tapak web. +Disekat secara automatik Tunjukkan Maklumat Disekat di sesetengah tapak Hidupkan kebenaran untuk Chrome dalam Tetapan Android. @@ -83,16 +87,19 @@ Penyegerakan latar belakang Hapuskan storan tapak? Kembali +Cari ke hadapan Benarkan tapak untuk memainkan kandungan yang dilindungi (disyorkan) Salin pautan / ? Tajuk Benarkan kuki untuk tapak tertentu. +Lagu seterusnya Tetapan tapak Pemberitahuan boleh menggetarkan peranti Buka tetapan Navigasi disekat: Dibenarkan +Tapak sedang memainkan media Halaman Lite disediakan oleh Google Tetingkap timbul dan ubah hala Benarkan tapak memainkan kandungan yang dilindungi @@ -107,6 +114,7 @@ Batalkan semua kebenaran untuk peranti Untuk membolehkan Chrome menggunakan AR, hidupkan juga kamera dalam Tetapan Android. Ke hadapan +Kongsi melalui Benarkan Benarkan tapak mengakses penderia gerakan (disyorkan) Penggunaan @@ -163,7 +171,9 @@ Kosongkan Muat turun aktif Alihkan ke bawah +Leret ke bawah untuk tutup. Halang tapak web pihak ketiga daripada menyimpan dan membaca data kuki +Mainkan Pilih item Peranti ini tidak dapat membaca NFC Dipadamkan @@ -197,6 +207,7 @@ Suatu tapak sedang menggunakan kamera anda Anda akan dilog keluar daripada tapak ini. Kuki ialah fail yang dibuat oleh tapak web yang anda lawati. Tapak menggunakan kuki untuk mengingat pilihan anda. Kuki pihak ketiga dibuat oleh tapak lain. Tapak ini memiliki sebahagian daripada kandungan, seperti iklan atau imej, yang anda lihat dalam halaman web yang anda lawati. +Memainkan media Batalkan pilihan Anda akan dilog keluar daripada semua tapak. Benarkan bunyi untuk tapak tertentu. @@ -204,6 +215,7 @@ Anda pasti ingin mengosongkan semua data setempat, termasuk kuki dan menetapkan semula semua kebenaran untuk tapak web ini? Dimatikan untuk peranti ini Muat turun gagal +Tidak dapat memilih sijil. Kandungan ini adalah daripada , disampaikan oleh Google. Diredam Sahkan @@ -216,6 +228,8 @@ Suatu tapak sedang menggunakan kamera dan mikrofon anda Tapak ini menyiarkan iklan yang mengganggu atau mengelirukan Ketik untuk kembali ke tapak +Tukar +Cari ke belakang Batalkan kebenaran peranti Tapak laju Sekat tapak daripada menyambung ke peranti @@ -236,6 +250,7 @@ Sekat kuki untuk tapak tertentu. Tidak dapat mencapai navigasi: Diurus oleh ibu bapa anda +Bluetooth Akses mikrofon anda Sekat jika tapak menyiarkan iklan yang mengganggu atau mengelirukan (disyorkan) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_my.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_my.xtb index 7352ea3ef7c..51c599bcb11 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_my.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_my.xtb @@ -10,6 +10,7 @@ ရှေ့သို့ တွင် မြှုပ်သွင်းထားသည် ရပ်ရန် +ဒေတာချွေတာရန် စာမျက်နှာပါပုံများကို Google က အကောင်းဆုံးပြုပြင်ထားသည်။ ဝဘ်ဆိုက်ကသိမ်းထားသော ဖိုင်များကို ဤနေရာတွင် ပြပါသည် - ဆိုက် အား ထည့်ခဲ့၏ @@ -42,6 +43,7 @@ သင့်ပတ်ဝန်းကျင်၏ 3D မြေပုံဆွဲခြင်း သို့မဟုတ် ကင်မရာအနေအထား ခြေရာခံခြင်းတို့အတွက် ဝဘ်ဆိုက်များကို ခွင့်မပြုမီ မေးမြန်းရန် (အကြံပြုထားသည်) ကွတ်ကီးများ ခွင့်ပြုရန် ဝဘ်ဆိုက်က အနီးအနားရှိ ဘလူးတုသ်ကိရိယာများ ရှာဖွေလိုသည့်အခါ ခွင့်တောင်းရန် (အကြံပြုထားသည်) +ကလိုင်းယင့် ဘက်မှ လက်မှတ်ရွေးမှုကို လည်ပတ်သည့်စနစ်မှ မပံ့ပိုးပါ။ ဆက်လုပ်ရန် မီဂါဘိုက် ဒေါင်းလုဒ်လုပ်ပြီးပါပြီ @@ -61,10 +63,12 @@ ကူးယူပြီးပါပြီ အတွက် ဝဘ်ဆိုက်ခွင့်ပြုချက်အားလုံးကို ပြင်ဆင်သတ်မှတ်လိုသည်မှာ သေချာသလား။ ဆိုဒ် +ယခင်တစ်ပုဒ် ဝဘ်ဆိုက်များအား ကာကွယ်ထားသည့် အကြောင်းအရာများကို ပြသခွင့် မပြုမီ ဦးစွာ မေးပါ ဆိုဒ်များကို ကွက်ကီးများ ဖတ်ရန်နှင့် သိမ်းရန် ခွင့်ပြုသည် (အကြံပြုထား) လက်မှတ်ကို ကြည့်ရှုသူ ၎င်းသည် ဝဘ်ဆိုက်သိုလှောင်ခန်း၏ ကို ရှင်းလင်းလိုက်ပါမည်။ +အလိုအလျောက် ပိတ်ထားသည် အချက်အလက်များကို ပြသရန် အချို့ဝဘ်ဆိုက်များတွင် ပိတ်ထားသည် Chrome အတွက် ခွင့်ပြုချက်များကို Android ချိန်ညှိချက်များ တွင် ဖွင့်ပါ။ @@ -83,16 +87,19 @@ နောက်ခံတွင် စင့်ခ်လုပ်ခြင်း ဆိုက်နေရာကိုရှင်းမလား။ နောက်သို့ +အရှေ့သို့ ရစ်ရန် ကာကွယ်ထားသည့် အကြောင်းအရာများကို ဆိုက်များအား ဖွင့်ခွင့်ပြုပါ (အကြံပြုထားသည်) လင့်ခ်ကူးယူမည် / ? ခေါင်းစဉ် အချို့ဝဘ်ဆိုက်များအတွက် ကွတ်ကီးများ ခွင့်ပြုသည်။ +နောက်တစ်ပုဒ် ဝက်ဆိုက် ဆက်တင်များ အကြောင်းကြားချက်သည် စက်ပစ္စည်းကို တုန်ခါစေပါမည် ဆက်တင်များကို ဖွင့်ရန် သွားလာမှုပြလမ်းညွှန်အား ပိတ်ထား၏- ခွင့်ပြုထား +ဝဘ်ဆိုက်တစ်ခုက မီဒီယာကို ဖွင့်နေသည် Google ၏ အပေါ့စား စာမျက်နှာ ပေါ့ပ်အပ်နှင့် တစ်ဆင့်ညွှန်ချက် ကာကွယ်ထားသော အကြောင်းအရာများ ဖွင့်ရန် ဝဘ်ဆိုက်များကို� ခွင့်ပြုသည် @@ -107,6 +114,7 @@ စက်ပစ္စည်းအတွက် ခွင့်ပြုချက်အားလုံး ရုပ်သိမ်းသည် Chrome ကို AR အသုံးပြုခွင့်ပေးရန် Android ဆက်တင်များ တွင်လည်း ကင်မရာဖွင့်ပါ။ ရှေ့သို့သွား +မှတစ်ဆင့် မျှဝေရန် ခွင့်ပြုရန် အာရုံခံစနစ်များ အသုံးပြုရန် ဤဝဘ်ဆိုက်များကို ခွင့်ပြုသည် (အကြံပြုထားသည်) အသုံးပြုပုံ @@ -163,7 +171,9 @@ ရှင်းရန် လက်ရှိ ဒေါင်းလုဒ်များ အောက်သို့ရွှေ့ပါ +ပိတ်ရန် အောက်သို့ ပွတ်ဆွဲပါ။ ကွတ်ကီးဒေတာများကို ပြင်ပကုမ္ပဏီဝဘ်ဆိုက်များက သိမ်းဆည်းခြင်းနှင့် ဖတ်ခြင်းများ ပြုလုပ်၍မရနိုင်အောင် တားဆီးပါ +ဖွင့်ရန် စာရင်းမှ ရွေးရန် ဤစက်က NFC ဖတ်၍မရပါ ဖျက်ပြီး @@ -197,6 +207,7 @@ ဝဘ်ဆိုက်တစ်ခုက သင့်ကင်မရာကို အသုံးပြုနေသည် ဤဝဘ်ဆိုက်မှ သင် ထွက်သွားပါမည်။ ကွတ်ကီးများဟူသည်မှာ သင်ဝင်ကြည့်သည့်ဝဘ်ဆိုက်များက ပြုလုပ်ထားသော ဖိုင်များဖြစ်သည်။ ဝဘ်ဆိုက်များက ၎င်းတို့ကို အသုံးပြုပြီး သင့်ရွေးချယ်မှုများကို မှတ်သားထားသည်။ ပြင်ပကွတ်ကီးများကို အခြားဝဘ်ဆိုက်များက ပြုလုပ်ခြင်းဖြစ်သည်။ သင်ဝင်ကြည့်သည့်ဝဘ်စာမျက်နှာပေါ်တွင် တွေ့ရသည့်ကြော်ငြာ သို့မဟုတ် ပုံများကဲ့သို့သော အကြောင်းအရာအချို့ကို ဤဝဘ်ဆိုက်များက ပိုင်ဆိုင်သည်။ +မီဒီယာ ဖွင့်နေသည် ရွေးချယ်ထားသောဖိုင်များကို ပြန်ဖျက်ပါ ဝဘ်ဆိုက်အားလုံးမှ သင် ထွက်သွားပါမည်။ ဝဘ်ဆိုက်တစ်ခုအတွက် အသံဖွင့်ခွင့်ပြုသည်။ @@ -204,6 +215,7 @@ ကွက်ကီးများအပါအဝင်၊ အတွင်းရှိ ဒေတာများအားလုံး ရှင်းလင်းကာ၊ ဤဝဘ်ဆိုက်အတွက် ခွင့်ပြုချက်များအားလုံးအား ပြန်လည်သတ်မှတ်လိုသည်မှာ သေချာပါသလား? ဤစက်ပစ္စည်းအတွက် ပိတ်ထားသည် ဒေါင်းလုဒ်လုပ်မှု မအောင်မြင်ပါ +လက်မှတ်ကို မရွေးနိုင်ပါ။ ဤအကြောင်းအရာသည် Google မှနေ၍ပံ့ပိုးပေးထားသည့် မှဖြစ်သည်။ ဖျောက်ထားသည် အတည်ပြုရန် @@ -216,6 +228,8 @@ ဝဘ်ဆိုက်တစ်ခုက သင့်ကင်မရာနှင့် မိုက်ကရိုဖုန်းကို အသုံးပြုနေသည် ဤဝဘ်ဆိုက်က စိတ်အနှောင့်အယှက်ဖြစ်စေသော (သို့) အထင်အမြင်မှားစေသော ကြော်ငြာများကို ပြသည် ဝဘ်ဆိုက်သို့ ပြန်သွားရန် တို့ပါ +ပြောင်းရန် +နောက်ပြန်ရစ်ရန် စက်ပစ္စည်းခွင့်ပြုချက်ကို ရုပ်သိမ်းပါ ဝဘ်ဆိုက်က မြန်သည် စက်သို့ ဝဘ်ဆိုက်များ ချိတ်ဆက်ခြင်းကို ပိတ်ထားသည် @@ -236,6 +250,7 @@ အချို့ဝဘ်ဆိုက်များအတွက် ကွတ်ကီးများကို ပိတ်ဆို့သည်။ သွားလာမှုပြလမ်းညွှန်ဆီသို့ မရောက်နိုင်ပါ- သင့်မိဘများမှ စီမံသည် +ဘလူးတုသ် သင့်မိုက်အား အသုံးပြုမည် စိတ်အနှောင့်အယှက်ဖြစ်စေသော (သို့) အထင်အမြင်မှားစေသော ကြော်ငြာများ ပြလျှင် ဝဘ်ဆိုက်ကို ပိတ်သည် (အကြံပြုထားသည်) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ne.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ne.xtb index d35588602e3..dd6d6ab2a4d 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ne.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ne.xtb @@ -10,6 +10,7 @@ अर्को मा इम्बेड गरिएको रोक्नुहोस् +तपाईंको डेटाको खपत कम गर्न Google ले यो पृष्ठका फोटोहरू अप्टिमाइज गरेको छ। वेबसाइटहरूले सुरक्षित गरेका फाइल यहाँ देखिने छन् - साइट थपियो @@ -42,6 +43,7 @@ साइटहरूलाई आफू वरपरको ठाउँको 3D नक्सा बनाउन वा क्यामेराको अवस्था पत्ता लगाउन दिनुअघि मलाई सोधियोस् (सिफारिस गरिएको) कुकीहरूलाई अनुमति दिनुहोस् कुनै साइटले छेउछाउका ब्लुटुथ यन्त्रहरू पत्ता लगाउन खोज्दा सोध्नुहोस् (सिफारिस गरिएको) +अपरेटिङ सिस्टमले क्लाइन्ट साइड प्रमाणपत्रको चयनलाई समर्थन गर्दैन। जारी राख्नुहोस् एम.बि. डाउनलोड सम्पन्न भयो @@ -61,10 +63,12 @@ प्रतिलिपि गरियो तपाईं साँच्चिकै का साइटसम्बन्धी सम्पूर्ण अनुमतिहरू रिसेट गर्न चाहनुहुन्छ? साइट +अघिल्लो ट्र्याक साइटहरूलाई संरक्षित सामग्री प्ले गर्ने अनुमति दिनुअघि सोध्नुहोस् साइटहरूलाई कुकी डेटा सुरक्षित गर्ने र पढ्ने अनुमति दिनुहोस् (सिफारिस गरिएको) प्रमाणपत्र दर्शक यसले वेबसाइटको भण्डारणको सबै खाली गर्ने छ। +स्वतः रोक लगाइयो जानकारी देखाउनुहोस् केही साइटका हकमा रोक लगाइएको छ Android सेटिङहरू मा Chrome का लागि अनुमतिहरू सक्रिय गर्नुहोस् । @@ -83,16 +87,19 @@ पृष्ठभूमिमा सिंक गर्ने सुविधा साइटको भण्डारणलई खाली गर्ने हो? पछाडि जानुहोस् +अगाडि खोज्नुहोस् साइटहरूलाई सुरक्षित सामग्री प्ले गर्न अनुमति दिनुहोस् (सिफारिस गरिएको) लिंक प्रतिलिपि गर्नुहोस् / ? शीर्षक कुनै खास साइटमा कुकीहरूलाई अनुमति दिनुहोस्। +अर्को ट्र्याक साइट सेटिङहरू सूचनाहरूले गर्दा यन्त्र कम्पन गर्न सक्छ सेटिङहरू खोल्नुहोस् नेभिगेशन रोकियो: अनुमति प्राप्त +कुनै साइटले मिडियो प्ले गरिरहेको छ Google ले प्रदान गरेको मूल पृष्ठको लाइट संस्करण पपअप तथा रिडिरेक्टहरू साइटहरूलाई संरक्षित सामग्री प्ले गर्ने अनुमति दिनुहोस् @@ -107,6 +114,7 @@ यन्त्रका सबै अनुमतिहरू फिर्ता लिनुहोस् Chrome लाई AR प्रयोग गर्न दिन Android का सेटिङमा गई क्यामेरा पनि सुचारु गर्नुहोस्। अगाडि जानुहोस् +मार्फत साझा गर्नुहोस् अनुमति दिनुहोस् साइटहरूलाई चालसम्बन्धी सेन्सरहरूमाथि पहुँच राख्ने अनुमति दिनुहोस्‌ (सिफारिस गरिएको) उपयोग @@ -163,7 +171,9 @@ खालि गर्नुहोस् जारी डाउनलोडहरू तल सार्नुहोस् +बन्द गर्न तलतिर स्वाइप गर्नुहोस्। तेस्रो पक्षीय वेबसाइटहरूलाई कुकी सम्बन्धी डेटा सुरक्षित गर्न र पढ्न नदिनुहोस् +प्ले गर्नुहोस् वस्तुहरू छनाैट गर्नुहोस् यस यन्त्रले NFC पढ्न सक्दैन मेटाइएको @@ -197,6 +207,7 @@ कुनै साइटले तपाईंको क्यामेरा प्रयोग गर्दै छ तपाईं यो साइटबाट साइन आउट हुनु हुने छ। कुकीहरू भनेका तपाईं जाने वेबसाइटहरूले सिर्जना गर्ने फाइल हुन्। तपाईंका प्राथमिकताहरू याद राख्न वेबसाइटहरूले ती कुकीहरूको प्रयोग गर्छन्। अन्य वेबसाइटहरूले तेस्रो पक्षीय कुकीहरू सिर्जना गर्छन्। तपाईं जाने वेबपृष्ठमा तपाईंले देख्ने विज्ञापन वा छविहरू जस्ता केही सामग्री यी वेबसाइटहरूको स्वामित्वमा हुन्छन्। +प्ले भइरहेको मिडिया चयन रद्द गर्नुहोस् तपाईं सबै साइटहरूबाट साइन आउट हुनु हुने छ। निश्चित साइटको ध्वनिलाई अनुमति दिनुहोस्‌। @@ -204,6 +215,7 @@ के तपाईं कुकीहरू सहित सबै स्थानीय डेटा खाली गर्न चाहनुहुन्छ, र यो वेबसाइटको लागि सबै अनुमतिहरू रिसेट गर्न चाहनुहुन्छ? यस यन्त्रका लागि बन्द गरियो। डाउनलोड गर्न सकिएन +प्रमाणपत्र चयन गर्न असमर्थ। यो Google द्वारा डेलिभर गरिएको को सामग्री हो। म्युट गरियो निश्चित @@ -216,6 +228,8 @@ कुनै साइटले तपाईंको क्यामेरा र माइक्रोफोन प्रयोग गर्दै छ यो साइटले हस्तक्षेपकारी वा भ्रामक विज्ञापनहरू देखाउँछ साइटमा फर्कन ट्याप गर्नुहोस् +परिवर्तन गर्नुहोस् +पछाडि खोज्नुहोस् यन्त्र सम्बन्धी अनुमतिलाई उल्टाउनुहोस् साइट द्रूत छ साइटहरूलाई यन्त्रमा जडान गर्नबाट रोक लगाउनुहोस् @@ -236,6 +250,7 @@ कुनै खास साइटमा कुकीहरूलाई रोक लगाउनुहोस्। नेभिगेशन पहुँचयोग्य छैन: तपाईँको अविभावक द्वारा व्यवस्थित +ब्लुटुथ आफ्नो माइक्रोफोन पहुँच गर्नुहोस् साइटले हस्तक्षेपकारी वा भ्रामक विज्ञापनहरू देखाएमा त्यसलाई रोक लगाउनुहोस् (सिफारिस गरिएको) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_nl.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_nl.xtb index d80b5d40411..6efd0126edc 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_nl.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_nl.xtb @@ -10,6 +10,7 @@ Volgende Ingesloten in Stop +De afbeeldingen op deze pagina zijn door Google geoptimaliseerd om data te besparen. Bestanden die worden opgeslagen door websites, worden hier weergegeven - Site toegevoegd @@ -42,6 +43,7 @@ Vragen voordat sites een 3D-kaart van je omgeving mogen maken of de camerapositie mogen volgen (aanbevolen) Cookies toestaan Vragen wanneer een site Bluetooth-apparaten in de buurt wilt detecteren (aanbevolen) +Certificaatselectie aan clientzijde wordt niet ondersteund door het besturingssysteem. Doorgaan MB Download voltooid @@ -61,10 +63,12 @@ Gekopieerd Weet je zeker dat je alle siterechten voor wilt resetten? Site +Vorig nummer Vragen voordat sites beschermde content mogen afspelen Sites toestaan cookiegegevens op te slaan en te lezen (aanbevolen) Certificaatviewer Hiermee wordt de volledige aan site-opslag gewist. +Automatisch geblokkeerd Informatie weergeven Geblokkeerd op bepaalde sites Schakel rechten in voor Chrome via de Android-instellingen. @@ -83,16 +87,19 @@ Synchronisatie op de achtergrond Site-opslag wissen? Terug +Vooruit zoeken Toestaan dat sites beschermde content afspelen (aanbevolen) Link kop. van ? Titel Cookies voor een specifieke site toestaan. +Volgend nummer Site-instellingen Het apparaat kan trillen bij meldingen Instellingen openen Navigatie is geblokkeerd: Toegestaan +Een site speelt media af Lite-pagina geleverd door Google Pop-ups en omleidingen Sites toestaan om beschermde content af te spelen @@ -107,6 +114,7 @@ Alle rechten voor apparaat intrekken Schakel ook de camera in via de Android-instellingen als je Chrome AR wilt laten gebruiken. Naar voren gaan +Delen via Toestaan Sites toegang geven tot bewegingssensoren (aanbevolen) Gebruik @@ -163,7 +171,9 @@ Wissen Actieve downloads Omlaag +Swipe omlaag om te sluiten. Voorkomen dat websites van derden cookiegegevens opslaan en lezen +Spelen Items selecteren Dit apparaat kan NFC niet lezen Verwijderd @@ -197,6 +207,7 @@ Een site gebruikt de camera Je wordt uitgelogd van deze site. Cookies zijn bestanden die worden gemaakt door websites die je bezoekt. Sites gebruiken ze om je voorkeuren te onthouden. Cookies van derden worden gemaakt door andere sites. Deze sites zijn eigenaar van een deel van de content (zoals advertenties of afbeeldingen) die je ziet op de webpagina die je bezoekt. +Media afspelen Selectie opheffen Je wordt uitgelogd van alle sites. Geluid toestaan voor een specifieke site. @@ -204,6 +215,7 @@ Weet je zeker dat je alle gegevens, inclusief cookies, voor deze website wilt wissen en alle rechten opnieuw wilt instellen? Uitgeschakeld voor dit apparaat Downloaden mislukt +Kan certificaat niet selecteren. Deze content is afkomstig van , geleverd door Google. Gedempt Bevestigen @@ -216,6 +228,8 @@ Een site gebruikt de camera en microfoon Deze site geeft opdringerige of misleidende advertenties weer Tik hier om terug te gaan naar de site +Wijzigen +Achteruit zoeken Rechten voor apparaat intrekken Site is snel Voorkomen dat sites verbinding maken met apparaten @@ -236,6 +250,7 @@ Cookies voor een specifieke site blokkeren. Navigatie is onbereikbaar: Beheerd door je ouders +Bluetooth Toegang tot je microfoon Blokkeren als site opdringerige of misleidende advertenties weergeeft (aanbevolen) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_no.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_no.xtb index 6522c94b138..bc3e7e58b35 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_no.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_no.xtb @@ -10,6 +10,7 @@ Neste Innebygd på Stopp +For å spare data er bildene på denne siden optimalisert av Google. Filer som nettsteder har lagret, vises her Nettstedet er lagt til @@ -42,6 +43,7 @@ Spør før nettsteder får lage 3D-kart av omgivelsene dine eller spore kameraposisjonen (anbefales) Tillat informasjonskapsler Spør når nettsteder vil oppdage Bluetooth-enheter i nærheten (anbefales) +Operativsystemet har ikke støtte for sertifikatvalg på klientsiden. Fortsett MB Nedlasting fullført @@ -61,10 +63,12 @@ Kopiert Er du sikker på at du vil tilbakestille alle nettstedstillatelser for ? Nettsted +Forrige spor Spør før nettsteder kan spille av beskyttet innhold Tillat at nettsteder lagrer og leser data i informasjonskapsler (anbefales) Visningsprogram for sertifikater Dette sletter alle dataene () fra nettstedslagringen. +Automatisk blokkert Vis informasjon Blokkert på enkelte nettsteder Slå på tillatelsene for Chrome i Android-innstillingene. @@ -83,16 +87,19 @@ Bakgrunnssynkronisering Slette nettstedslagring? Tilbake +Spol fremover Gi nettsteder tillatelse til å spille av beskyttet innhold (anbefales) Kopiér link / ? Tittel Tillat informasjonskapsler for et spesifikt nettsted. +Neste spor Nettstedsinnstillinger Varsler kan gjøre at enheten vibrerer Åpne innstillingene Nettadressen er blokkert: Tillatt +Et nettsted spiller av medier Forenklet versjon av siden levert av Google Forgrunnsvinduer/viderekoblinger Tillat at nettsteder kan spille beskyttet innhold @@ -107,6 +114,7 @@ Opphev alle tillatelser for enheten For å la Chrome bruke AR, slå på kameraet i Android-innstillingene også. Gå til neste +Del via Tillat Gi nettsteder tilgang til bevegelsessensorer (anbefalt) Bruk @@ -163,7 +171,9 @@ Tøm Aktive nedlastinger Flytt ned +Sveip ned for å lukke. Forhindrer at nettsteder fra tredjeparter lagrer og leser data i informasjonskapsler +Spill av Velg elementer Denne enheten kan ikke lese NFC Slettet @@ -197,6 +207,7 @@ Et nettsted bruker kameraet ditt Du blir logget av dette nettstedet. Informasjonskapsler er filer som opprettes av nettsteder du besøker. Nettsteder bruker dem til å huske preferansene dine. Informasjonskapsler fra tredjeparter opprettes av andre nettsteder. Disse nettstedene eier noe av innholdet, for eksempel annonser eller bilder, som du ser på nettsiden du besøker. +Spiller av medier Opphev valget Du blir logget av alle nettsteder. Tillat lyd for et bestemt nettsted. @@ -204,6 +215,7 @@ Er du sikker på at du vil slette alle lokale data, deriblant informasjonskapsler, og tilbakestille alle tillatelser for dette nettstedet? Slått av for denne enheten Nedlastingen mislyktes +Kan ikke velge sertifikat. Dette innholdet er fra og er levert av Google. Kuttet lyd Bekreft @@ -216,6 +228,8 @@ Et nettsted bruker kameraet ditt og mikrofonen din Dette nettstedet viser forstyrrende eller villedende annonser Trykk for å gå tilbake til nettstedet +Endre +Spol bakover Opphev tillatelsen til enhetstilgang Nettstedet er raskt Blokkér at nettsteder kobler til enheter @@ -236,6 +250,7 @@ Blokkér informasjonskapsler for et spesifikt nettsted. Nettadressen er utilgjengelig: Administrert av foreldrene dine +Bluetooth Tilgang til mikrofonen din Blokkér hvis nettstedet viser forstyrrende eller villedende annonser (anbefalt) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_or.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_or.xtb index 3b6ae16fef4..00797b8282c 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_or.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_or.xtb @@ -10,6 +10,7 @@ ପରବର୍ତ୍ତୀ ରେ ଏମ୍ବେଡ୍ କରାଯାଇଛି ବନ୍ଦ କରନ୍ତୁ +ଆପଣଙ୍କ ଡାଟା ସେଭ୍ କରିବା ପାଇଁ, Google ଏହି ପୃଷ୍ଠାର ଛବିଗୁଡ଼ିକୁ ଅପ୍ଟିମାଇଜ୍ କରିଛି। ୱେବ୍‍ସାଇଟ୍‍ଗୁଡ଼ିକ ଦ୍ୱାରା ସେଭ୍ କରାଯାଇଥିବା ଫାଇଲ୍‍ଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯାଏ - ସାଇଟ୍ ଯୋଗ କରାଗଲା @@ -42,6 +43,7 @@ ଆପଣଙ୍କ ପରିପାର୍ଶ୍ୱର ଏକ 3D ମ୍ୟାପ୍ ତିଆରି କରିବା ଏବଂ କ୍ୟାମେରା ସ୍ଥିତି ଟ୍ରାକ୍ କରିବାକୁ ସାଇଟଗୁଡ଼ିକୁ ଅନୁମତି ଦେବା ପୂର୍ବରୁ ପଚାରନ୍ତୁ (ସୁପାରିଶ କରାଯାଇଛି) କୁକୀଗୁଡ଼ିକୁ ଅନୁମତି ଦିଅନ୍ତୁ ଯେତେବେଳେ ଏକ ସାଇଟ୍ ଆଖପାଖର ବ୍ଲୁଟୂଥ୍ ଡିଭାଇସ୍‍ଗୁଡ଼ିକୁ ଖୋଜିବାକୁ ଚାହୁଁଛି, ସେତେବେଳେ ପଚାରନ୍ତୁ (ସୁପାରିଶ କରାଯାଇଛି) +କ୍ଲାଏଣ୍ଟ ସାଇଡ୍ ସାର୍ଟିଫିକେଟ୍ ଚୟନ ଅପରେଟିଂ ସିଷ୍ଟମ୍‍ ଦ୍ବାରା ସପୋର୍ଟ କରୁନାହିଁ। ଜାରି ରଖନ୍ତୁ MB ଡାଉନ୍‌ଲୋଡ୍ ଶେଷ ହେଲା @@ -61,10 +63,12 @@ କପି କରାଯାଇଛି ଆପଣ କ’ଣ ନିଶ୍ଚିତ ଯେ ଆପଣ ପାଇଁ ସମସ୍ତ ସାଇଟ୍ ଅନୁମତିଗୁଡ଼ିକ ଚାହୁଁଛନ୍ତି? ସାଇଟ୍ +ପୂର୍ବବର୍ତ୍ତୀ ଟ୍ରାକ୍‌ ସାଇଟ୍‌ଗୁଡ଼ିକୁ ସୁରକ୍ଷିତ ବିଷୟବସ୍ତୁ ଚଲାଇବା ପାଇଁ ଅନୁମତି ଦେବା ପୂର୍ବରୁ, ପଚାରନ୍ତୁ କୁକୀ ଡାଟାଗୁଡ଼ିକୁ ସେଭ୍ କରିବାକୁ ଏବଂ ପଢ଼ିବା ପାଇଁ ସାଇଟ୍‌ଗୁଡ଼ିକୁ ଅନୁମତି ଦିଅନ୍ତୁ (ସୁପାରିଶ୍ କରାଯାଇଛି) ସାର୍ଟିଫିକେଟ୍‌ ଭ୍ୟୁଅର୍‌ ଏହା ୱେବ୍‌ସାଇଟ୍‌ ଷ୍ଟୋରେଜ୍‌ର ସମସ୍ତ ଡାଟା ଖାଲି କରିବ। +ସ୍ୱଚାଳିତ ଭାବେ ବ୍ଲକ୍ କରାଗଲା ସୂଚନା ଦେଖାନ୍ତୁ କିଛି ସାଇଟ୍‍ଗୁଡ଼ିକରେ ଅବରୋଧ କରାଯାଇଛି Android Settingsରେ Chrome ପାଇଁ ଅନୁମତି ଚାଲୁ କରନ୍ତୁ। @@ -83,16 +87,19 @@ ପୃଷ୍ଠପଟ ସିଙ୍କ୍‌ ସାଇଟ୍ ଷ୍ଟୋରେଜ୍ ଖାଲି କରିବେ? ପଛକୁ +ଆଗକୁ ବଢ଼ାନ୍ତୁ ସୁରକ୍ଷିତ ବିଷୟବସ୍ତୁକୁ ଚଲାଇବା ପାଇଁ ସାଇଟ୍‍ଗୁଡ଼ିକୁ ଅନୁମତି ଦିଆଯାଇଛି।(ସୁପାରିଶ୍ କରାଯାଇଛି) ଲିଙ୍କ୍ କପି କରନ୍ତୁ / ? ଆଖ୍ୟା ଏକ ନିର୍ଦ୍ଦିଷ୍ଟ ସାଇଟ୍ ପାଇଁ କୁକୀଗୁଡ଼ିକୁ ଅନୁମତି ଦିଏ। +ପରବର୍ତ୍ତୀ ଟ୍ରାକ୍ ସାଇଟ୍‍ ସେଟିଂସ୍‌ ବିଜ୍ଞପ୍ତି ଡିଭାଇସ୍‍କୁ ଭାଇବ୍ରେଟ୍ କରିପାରେ ସେଟିଂସ୍‌ ଖୋଲନ୍ତୁ ନାଭିଗେସନ୍ ଅବରୋଧ କରାଯାଇଛି: ଅନୁମୋଦିତ +କୌଣସି ସାଇଟ୍ ମିଡିଆ ଚଲାଉଛି Google ଦ୍ୱାରା ପ୍ରଦତ୍ତ ଲାଇଟ୍ ପୃଷ୍ଠା ପପ୍-ଅପ୍ ଏବଂ ରିଡାଇରେକ୍‌ଟ ସାଇଟ୍‍ଗୁଡ଼ିକୁ ସୁରକ୍ଷିତ ବିଷୟବସ୍ତୁ ଚଳାଇବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ @@ -107,6 +114,7 @@ ଡିଭାଇସ୍‌ପାଇଁ ଥିବା ସମସ୍ତ ଅନୁମତିଗୁଡ଼ିକ ପ୍ରତ୍ୟାହାର କରନ୍ତୁ Chromeକୁ AR ବ୍ୟବହାର କରିବାକୁ ଦେବା ନିମନ୍ତେ, Android ସେଟିଂସରେ କ୍ୟାମେରା ମଧ୍ୟ ଚାଲୁ କରନ୍ତୁ। ଆଗକୁ ଯାଆନ୍ତୁ +ଏହା ମାଧ୍ୟମରେ ସେୟାର୍ କରନ୍ତୁ ଅନୁମତି ମୋସନ୍ ସେନ୍‌ସର୍ ଆକ୍ସେସ୍ କରିବାକୁ ସାଇଟ୍‌ଗୁଡ଼ିକୁ ଅନୁମତି ଦିଅନ୍ତୁ (ସୁପାରିଶ କରାଯାଇଛି) ବ୍ୟବହାର @@ -163,7 +171,9 @@ ଖାଲି କରନ୍ତୁ ସକ୍ରିୟ ଡାଉନଲୋଡଗୁଡ଼ିକ ତଳକୁ ଚାଳନ କରନ୍ତୁ +ବନ୍ଦ କରିବା ପାଇଁ ତଳକୁ ସ୍ଵାଇପ୍ କରନ୍ତୁ ଡାଟା ସେଭ୍‍ କରିବା ଏବଂ ପଢ଼ିବାରୁ ତୃତୀୟ ପକ୍ଷ ୱେବ୍‌ସାଇଟ୍‍ଗୁଡ଼ିକୁ ନିବୃତ୍ତ କରେ +ଚଲାନ୍ତୁ ଆଇଟମ୍ ଚୟନ କରନ୍ତୁ ଏହି ଡିଭାଇସ୍ NFC ପଢ଼ିପାରିବ ନାହିଁ ଡିଲିଟ୍ କରାଗଲା @@ -197,6 +207,7 @@ କୌଣସି ସାଇଟ୍ ଆପଣଙ୍କ କ୍ୟାମେରା ବ୍ୟବହାର କରୁଛି ଏହି ସାଇଟରୁ ଆପଣଙ୍କୁ ସାଇନ୍ ଆଉଟ୍ କରିଦିଆଯିବ। କୁକୀଗୁଡ଼ିକ ହେଉଛି ଆପଣ ୱେବସାଇଟଗୁଡ଼ିକୁ ଭିଜିଟ୍ କଲେ ତିଆରି ହେଉଥିବା ଫାଇଲଗୁଡ଼ିକ। ଆପଣଙ୍କ ପସନ୍ଦଗୁଡ଼ିକ ମନେରଖିବାକୁ ସାଇଟଗୁଡ଼ିକ ସେଗୁଡ଼ିକୁ ବ୍ୟବହାର କରେ। ଅନ୍ୟ ସାଇଟଗୁଡ଼ିକ ଦ୍ୱାରା ତୃତୀୟ-ପକ୍ଷ କୁକୀଗୁଡ଼ିକ ତିଆରି କରାଯାଇଛି। ବିଜ୍ଞାପନ କିମ୍ବା ଛବିଗୁଡ଼ିକ ପରି କିଛି ବିଷୟବସ୍ତୁ ଉପରେ ଏହି ସାଇଟଗୁଡ଼ିକର ମାଲିକାନା ଅଛି, ଯାହାକୁ ଆପଣ ଭିଜିଟ୍ କରୁଥିବା ୱେବପୃଷ୍ଠାରେ ଦେଖନ୍ତି। +ମିଡିଆ ପ୍ଲେ ହେଉଛି ଚୟନିତ ଆଇଟମ୍ ବାତିଲ୍ କରନ୍ତୁ ଆପଣ ସମସ୍ତ ସାଇଟରୁ ସାଇନ୍ ଆଉଟ୍ ହୋଇଯିବେ। ଏକ ନିର୍ଦ୍ଧିଷ୍ଟ ସାଇଟ୍‌ରେ ଧ୍ୱନୀ ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ। @@ -204,6 +215,7 @@ ଆପଣ କ’ଣ ସୁନିଶ୍ଚିତ ଯେ ଆପଣ କୁକୀ ସହିତ ଏହି ୱେବ୍‌ସାଇଟ୍‌ର ସମସ୍ତ ସ୍ଥାନୀୟ ଡାଟା ଖାଲି କରିବା ଏବଂ ଏହାର ସମସ୍ତ ଅନୁମତି ରିସେଟ୍‍ କରିବା ପାଇଁ ଚାହୁଁଛନ୍ତି? ଏହି ଡିଭାଇସ୍‌ ପାଇଁ ବନ୍ଦ କରନ୍ତୁ ଡାଉନ୍‌ଲୋଡ୍ ବିଫଳ ହୋଇଛି +ସାର୍ଟିଫିକେଟ୍ ଚୟନ କରିବାକୁ ଅକ୍ଷମ। ଏହି ବିଷୟବସ୍ତୁ Google ଦ୍ଵାରା ବିତରଣ କରାଯାଇଥିବା ରୁ ଆସିଛି। ମ୍ୟୁଟ୍‍ କରାଗଲା ନିଶ୍ଚିତ କରନ୍ତୁ @@ -216,6 +228,8 @@ କୌଣସି ସାଇଟ୍ ଆପଣଙ୍କ କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନକୁ ବ୍ୟବହାର କରୁଛି ଏହି ସାଇଟ୍, ଅନଧିକାର ପ୍ରବେଶ କରିଥିବା କିମ୍ବା ବିଭ୍ରାନ୍ତିକର ବିଜ୍ଞାପନ ଦେଖାଉଛି ସାଇଟକୁ ଫେରିବାକୁ ଟାପ୍ କରନ୍ତୁ +ପରିବର୍ତ୍ତନ କରନ୍ତୁ +ପଛକୁ ଫେରନ୍ତୁ ଡିଭାଇସ୍ ଅନୁମତି କାଢ଼ି ଦିଅନ୍ତୁ ସାଇଟ୍ ଶୀଘ୍ର ଲୋଡ୍ ହୁଏ ସାଇଟ୍‌ଗୁଡ଼ିକୁ ଡିଭାଇସ୍‌ଗୁଡ଼ିକ ସହିତ ସଂଯୋଗ ହେବାରୁ ବ୍ଲକ୍ କରନ୍ତୁ @@ -236,6 +250,7 @@ ଏକ ନିର୍ଦ୍ଦିଷ୍ଟ ସାଇଟ୍ ପାଇଁ କୁକୀଗୁଡ଼ିକୁ ଅବରୋଧ କରନ୍ତୁ। ନାଭିଗେସନ୍‌ରେ ପହଞ୍ଚି ହେଉନାହିଁ: ଆପଣଙ୍କ ମାତା-ପିତାଙ୍କ ଦ୍ୱାରା ପରିଚାଳିତ କରାଯାଉଛି +ବ୍ଲୁଟୁଥ୍ ଆପଣଙ୍କର ମାଇକ୍ରୋଫୋନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ ଯଦି ସାଇଟ୍ ଅନଧିକାର ପ୍ରବେଶ କରିଥିବା କିମ୍ବା ବିଭ୍ରାନ୍ତିକର ବିଜ୍ଞାପନ ଦେଖାଉଛି, ତେବେ ବ୍ଲକ୍ କରନ୍ତୁ(ସୁପାରିଶ୍ କରାଯାଇଛି) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pa.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pa.xtb index 13557e0bf1c..3b94551f7f4 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pa.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pa.xtb @@ -10,6 +10,7 @@ ਅੱਗੇ 'ਤੇ ਪਰੋਇਆ ਗਿਆ ਰੋਕੋ +ਤੁਹਾਡਾ ਡਾਟਾ ਰੱਖਿਅਤ ਕਰਨ ਲਈ, Google ਨੇ ਇਸ ਪੰਨੇ ਦੇ ਚਿੱਤਰਾਂ ਨੂੰ ਸੁਯੋਗ ਬਣਾਇਆ ਹੈ। ਵੈੱਬਸਾਈਟਾਂ ਵੱਲੋਂ ਰੱਖਿਅਤ ਕੀਤੀਆਂ ਫ਼ਾਈਲਾਂ ਇੱਥੇ ਦਿਸਣਗੀਆਂ - ਸਾਈਟ ਜੋੜੀ ਗਈ @@ -42,6 +43,7 @@ ਸਾਈਟਾਂ ਵੱਲੋਂ ਤੁਹਾਡੇ ਆਲੇ-ਦੁਆਲੇ ਦਾ 3D ਨਕਸ਼ਾ ਬਣਾਉਣ ਜਾਂ ਕੈਮਰਾ ਸਥਿਤੀ ਨੂੰ ਟਰੈਕ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣ ਤੋਂ ਪਹਿਲਾਂ ਪੁੱਛੋ (ਸਿਫ਼ਾਰਸ਼ੀ) ਕੁਕੀਜ਼ ਨੂੰ ਆਗਿਆ ਦਿਓ ਕਿਸੇ ਸਾਈਟ ਵੱਲੋਂ ਨਜ਼ਦੀਕੀ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਦਾ ਪਤਾ ਲਗਾਉਣ ਵੇਲੇ ਪੁੱਛੋ (ਸਿਫ਼ਾਰਸ਼ੀ) +ਕਲਾਈਂਟ ਵੱਲੋਂ ਕੀਤੀ ਗਈ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੀ ਚੋਣ ਓਪਰੇਟਿੰਗ ਸਿਸਟਮ ਵੱਲੋਂ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ। ਜਾਰੀ ਰੱਖੋ MB ਡਾਊਨਲੋਡ ਮੁਕੰਮਲ ਹੋਇਆ @@ -61,10 +63,12 @@ ਕਾਪੀ ਕੀਤਾ ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਲਈ ਸਾਰੀਆਂ ਸਾਈਟ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਸਾਈਟ +ਪਿਛਲਾ ਟਰੈਕ ਸਾਈਟਾਂ ਨੂੰ ਸੁਰੱਖਿਅਤ ਸਮੱਗਰੀ ਚਲਾਉਣ ਦੇਣ ਤੋਂ ਪਹਿਲਾਂ ਪੁੱਛੋ ਸਾਈਟਾਂ ਨੂੰ ਕੁਕੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕਰਨ ਅਤੇ ਪੜ੍ਹਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ (ਸਿਫ਼ਾਰਸ਼ੀ) ਪ੍ਰਮਾਣ-ਪੱਤਰ ਵਿਊਅਰ ਇਸ ਨਾਲ ਵੈੱਬਸਾਈਟ ਸਟੋਰੇਜ ਦਾ ਸਾਰਾ ਡਾਟਾ ਕਲੀਅਰ ਹੋ ਜਾਵੇਗਾ। +ਸਵੈਚਲਿਤ ਤੌਰ 'ਤੇ ਬਲੌਕ ਕੀਤੀ ਗਈ ਜਾਣਕਾਰੀ ਦਿਖਾਓ ਕੁਝ ਸਾਈਟਾਂ 'ਤੇ ਬਲਾਕ ਕੀਤੇ ਗਏ Chrome ਲਈ Android ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਇਜਾਜ਼ਤਾਂ ਚਾਲੂ ਕਰੋ। @@ -83,16 +87,19 @@ ਬੈਕਗ੍ਰਾਊਂਡ ਸਮਕਾਲੀਕਰਨ ਕੀ ਸਾਈਟ ਸਟੋਰੇਜ ਸਾਫ਼ ਕਰਨੀ ਹੈ? ਪਿੱਛੇ +ਅੱਗੇ ਵੱਲ ਨੂੰ ਲੈ ਜਾਓ ਸਾਈਟਾਂ ਨੂੰ ਸੁਰੱਖਿਅਤ ਸਮੱਗਰੀ ਚਲਾਉਣ ਦਿਓ (ਸਿਫ਼ਾਰਸ਼ੀ) ਲਿੰਕ ਕਾਪੀ ਕਰੋ / ? ਸਿਰਲੇਖ ਕਿਸੇ ਖਾਸ ਸਾਈਟ ਨੂੰ ਕੁਕੀਜ਼ ਨੂੰ ਵਰਤਣ ਦਿਓ। +ਅਗਲਾ ਟਰੈਕ ਸਾਈਟ ਸੈਟਿੰਗਾਂ ਸੂਚਨਾਵਾਂ ਡੀਵਾਈਸ ਨੂੰ ਥਰਥਰਾਹਟ ਕਰ ਸਕਦੀਆਂ ਹਨ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ ਨੈਵੀਗੇਸ਼ਨ ਬਲੌਕ ਕੀਤੀ ਹੈ: ਆਗਿਆ ਦਿੱਤੀ +ਕੋਈ ਸਾਈਟ ਮੀਡੀਆ ਫ਼ਾਈਲ ਨੂੰ ਚਲਾ ਰਹੀ ਹੈ Google ਵੱਲੋਂ ਮੁਹੱਈਆ ਕਰਵਾਏ ਗਏ ਤੇਜ਼ੀ ਨਾਲ ਖੁੱਲ੍ਹਣ ਵਾਲੇ ਪੰਨੇ ਪੌਪ-ਅੱਪ ਅਤੇ ਰੀਡਾਇਰੈਕਟ ਸਾਈਟਾਂ ਨੂੰ ਸੁਰੱਖਿਅਤ ਕੀਤੀ ਸਮੱਗਰੀ ਚਲਾਉਣ ਦਿਓ @@ -107,6 +114,7 @@ ਡੀਵਾਈਸ ਲਈ ਸਾਰੀਆਂ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਰੱਦ ਕਰੋ Chrome ਨੂੰ AR ਵਰਤਣ ਦੇਣ ਲਈ Android ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਕੈਮਰਾ ਵੀ ਚਾਲੂ ਕਰੋ। ਅੱਗੇ ਵੱਲ ਜਾਓ +ਰਾਹੀਂ ਸ਼ੇਅਰ ਕਰੋ ਆਗਿਆ ਦਿਓ ਸਾਈਟਾਂ ਨੂੰ ਗਤੀਸ਼ੀਲਤਾ ਸੈਂਸਰਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦਿਓ (ਸਿਫ਼ਾਰਸ਼ੀ) ਵਰਤੋਂ @@ -163,7 +171,9 @@ ਹਟਾਓ ਕਿਰਿਆਸ਼ੀਲ ਡਾਊਨਲੋਡ ਹੇਠਾਂ ਜਾਓ +ਬੰਦ ਕਰਨ ਲਈ ਹੇਠਾਂ ਵੱਲ ਸਵਾਈਪ ਕਰੋ। ਤੀਜੀ-ਧਿਰ ਵਾਲੀਆਂ ਵੈੱਬਸਾਈਟਾਂ ਨੂੰ ਕੁਕੀ ਡਾਟੇ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਅਤੇ ਪੜ੍ਹਨ ਤੋਂ ਰੋਕੋ +ਪਲੇ ਕਰੋ ਆਈਟਮਾਂ ਚੁਣੋ ਇਹ ਡੀਵਾਈਸ NFC ਨੂੰ ਨਹੀਂ ਪੜ੍ਹ ਸਕਦਾ ਮਿਟਾਇਆ ਗਿਆ @@ -197,6 +207,7 @@ ਕੋਈ ਸਾਈਟ ਤੁਹਾਡਾ ਕੈਮਰਾ ਵਰਤ ਰਹੀ ਹੈ ਤੁਹਾਨੂੰ ਇਸ ਸਾਈਟ ਤੋਂ ਸਾਈਨ-ਆਊਟ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਕੁਕੀਜ਼ ਉਹ ਫ਼ਾਈਲਾਂ ਹਨ ਜੋ ਤੁਹਾਡੇ ਵੱਲੋਂ ਦੇਖੀਆਂ ਵੈੱਬਸਾਈਟਾਂ ਦੁਆਰਾ ਬਣਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਸਾਈਟਾਂ ਉਹਨਾਂ ਨੂੰ ਤੁਹਾਡੀਆਂ ਤਰਜੀਹਾਂ ਯਾਦ ਰੱਖਣ ਲਈ ਵਰਤਦੀਆਂ ਹਨ। ਤੀਜੀ-ਧਿਰ ਦੀਆਂ ਕੁਕੀਜ਼ ਹੋਰ ਸਾਈਟਾਂ ਵੱਲੋਂ ਬਣਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਇਹ ਸਾਈਟਾਂ ਵਿਗਿਆਪਨਾਂ ਜਾਂ ਚਿੱਤਰਾਂ ਵਰਗੀ ਕੁਝ ਸਮੱਗਰੀ ਦੀਆਂ ਮਾਲਕ ਹੁੰਦੀਆਂ ਹਨ, ਜੋ ਤੁਸੀਂ ਵੈੱਬਸਾਈਟ 'ਤੇ ਦੇਖਦੇ ਹੋ। +ਮੀਡੀਆ ਚਲਾਇਆ ਜਾ ਰਿਹਾ ਹੈ ਚੋਣ ਰੱਦ ਕਰੋ ਤੁਹਾਨੂੰ ਸਾਰੀਆਂ ਸਾਈਟਾਂ ਤੋਂ ਸਾਈਨ-ਆਊਟ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਕਿਸੇ ਖ਼ਾਸ ਸਾਈਟ ਲਈ ਧੁਨੀ ਵਜਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ। @@ -204,6 +215,7 @@ ਕੀ ਤੁਸੀਂ ਪੱਕਾ ਕੁਕੀਜ਼ ਸਮੇਤ ਸਾਰਾ ਸਥਾਨਕ ਡਾਟਾ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ ਅਤੇ ਇਸ ਵੈੱਬਸਾਈਟ ਲਈ ਸਾਰੀਆਂ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਇਸ ਡਿਵਾਈਸ ਲਈ ਬੰਦ ਕੀਤਾ ਗਿਆ ਡਾਊਨਲੋਡ ਅਸਫਲ ਰਿਹਾ +ਪ੍ਰਮਾਣ-ਪੱਤਰ ਚੁਣਨ ਵਿੱਚ ਅਸਮਰੱਥ। ਇਸ ਸਮੱਗਰੀ ਵੱਲੋਂ ਹੈ, ਜਿਸਨੂੰ Google ਡਿਲੀਵਰ ਕਰਦਾ ਹੈ। ਮਿਊਟ ਕੀਤਾ ਗਿਆ ਪੁਸ਼ਟੀ ਕਰੋ @@ -216,6 +228,8 @@ ਕੋਈ ਸਾਈਟ ਤੁਹਾਡਾ ਕੈਮਰਾ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਵਰਤ ਰਹੀ ਹੈ ਇਹ ਸਾਈਟ ਦਖਲਅੰਦਾਜ਼ੀ ਜਾਂ ਗੁਮਰਾਹ ਕਰਨ ਵਾਲੇ ਵਿਗਿਆਪਨ ਦਿਖਾਉਂਦੀ ਹੈ ਸਾਈਟ 'ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ ਟੈਪ ਕਰੋ +ਬਦਲੋ +ਪਿੱਛੇ ਵੱਲ ਨੂੰ ਲੈ ਜਾਓ ਡੀਵਾਈਸ 'ਤੇ ਪਹੁੰਚ ਦੀ ਇਜਾਜ਼ਤ ਰੱਦ ਕਰੋ ਸਾਈਟ ਤੇਜ਼ ਹੈ ਸਾਈਟਾਂ ਨੂੰ ਡੀਵਾਈਸਾਂ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਤੋਂ ਬਲਾਕ ਕਰੋ @@ -236,6 +250,7 @@ ਕਿਸੇ ਖਾਸ ਸਾਈਟ ਲਈ ਕੁਕੀਜ਼ ਬਲਾਕ ਕਰੋ। ਨੈਵੀਗੇਸ਼ਨ ਨਾਪਹੁੰਚਯੋਗ ਹੈ: ਤੁਹਾਡੇ ਮਾਪਿਆਂ ਵੱਲੋਂ ਵਿਵਸਥਿਤ +ਬਲੂਟੁੱਥ ਆਪਣੇ ਮਾਈਕ੍ਰੋਫੋਨ ਤੱਕ ਪਹੁੰਚੋ ਜੇਕਰ ਸਾਈਟ ਦਖਲਅੰਦਾਜ਼ੀ ਅਤੇ ਗੁਮਰਾਹ ਕਰਨ ਵਾਲੇ ਵਿਗਿਆਪਨ ਦਿਖਾਉਂਦੀ ਹੈ, ਤਾਂ ਇਸਨੂੰ ਬਲਾਕ ਕਰੋ (ਸਿਫ਼ਾਰਸ਼ੀ) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pl.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pl.xtb index dbc0c87b911..9668cbb1f84 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pl.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pl.xtb @@ -10,6 +10,7 @@ Dalej Umieszczono na Zatrzymaj +Aby zaoszczędzić dane, obrazy z tej strony zostały zoptymalizowane przez Google. W tym miejscu pojawiają się pliki zapisane przez witryny Strona została dodana @@ -42,6 +43,7 @@ Pytaj, zanim zezwolisz stronom na tworzenie mapy 3D Twojego otoczenia lub śledzenie pozycji kamery (zalecane) Zezwalaj na pliki cookie Pytaj, gdy strona chce wykryć urządzenia Bluetooth w pobliżu (zalecane) +Wybieranie certyfikatu klienta nie jest obsługiwane przez ten system operacyjny. Dalej  MB Ukończono pobieranie @@ -61,10 +63,12 @@ Skopiowane Czy na pewno chcesz zresetować wszystkie uprawnienia witryny dla obiektu ? Witryna +Poprzedni utwór Pytaj, zanim zezwolisz stronom na odtwarzanie treści chronionej Zezwalaj witrynom na zapisywanie danych w plikach cookie i ich odczytywanie (zalecane) Przeglądarka certyfikatów Spowoduje to usunięcie danych witryn. +Automatycznie zablokowane Pokaż informacje Blokowane na niektórych stronach Przyznaj Chrome uprawnienia w ustawieniach Androida. @@ -83,16 +87,19 @@ Synchronizacja w tle Wyczyścić dane witryn? Wstecz +Przewiń do przodu Zezwalaj witrynom na odtwarzanie treści chronionej (zalecane) Kopiuj link / ? Tytuł Zezwalaj na pliki cookie z określonej strony internetowej. +Następny utwór Ustawienia witryn Powiadomienia będą sygnalizowane wibracjami Otwórz ustawienia Adres zablokowany: Dopuszczone +Strona odtwarza multimedia Lżejsza wersja strony dostarczona przez Google Pop-upy i przekierowania Zezwalaj stronom na odtwarzanie treści chronionych @@ -107,6 +114,7 @@ Anuluj wszystkie uprawnienia urządzenia Aby w Chrome można było korzystać z AR, włącz też kamerę w ustawieniach Androida. Dalej +Udostępnij przez Zezwalaj Zezwalaj stronom na dostęp do czujników ruchu (zalecane) Wykorzystanie @@ -163,7 +171,9 @@ Wyczyść W trakcie pobierania W dół +Przesuń w dół, by zamknąć. Nie pozwalaj witrynom innych firm zapisywać ani odczytywać danych z plików cookie +Odtwórz Wybierz elementy To urządzenie nie obsługuje NFC Usunięto @@ -197,6 +207,7 @@ Strona używa kamery Wylogujemy Cię z tej strony. Pliki cookie są tworzone przez odwiedzane witryny. Witryny zapisują w nich Twoje ustawienia. Pliki cookie innych firm są tworzone przez inne witryny. W tych witrynach znajdują się niektóre treści, np. reklamy i obrazy, które wyświetlają się na odwiedzanej stronie. +Odtwarzanie multimediów Anuluj wybór Wylogujemy Cię ze wszystkich stron. Zezwalaj na dźwięk na określonej stronie. @@ -204,6 +215,7 @@ Na pewno chcesz usunąć wszystkie dane lokalne (w tym pliki cookie) i zresetować uprawnienia tej witryny? Wyłączona na tym urządzeniu Nie udało się pobrać +Nie można wybrać certyfikatu. Treść z dostarczana przez Google. Wyciszone Potwierdź @@ -216,6 +228,8 @@ Strona używa kamery i mikrofonu Na tej stronie wyświetlają się uciążliwe lub wprowadzające w błąd reklamy Kliknij, by wrócić na tę stronę +Zmień +Przewiń do tyłu Cofnij zgodę na dostęp do urządzenia Strona jest szybka Nie zezwalaj stronom na łączenie się z urządzeniami @@ -236,6 +250,7 @@ Blokuj pliki cookie z określonej strony internetowej. Adres nieosiągalny: Zarządzany przez Twoich rodziców +Bluetooth Dostęp do mikrofonu Zablokuj, jeśli na stronie wyświetlają się uciążliwe lub wprowadzające w błąd reklamy (zalecane) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-BR.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-BR.xtb index f0b57a98669..db38c113d5a 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-BR.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-BR.xtb @@ -10,6 +10,7 @@ Próxima Incorporado em Parar +Para que você economize dados, as imagens desta página foram otimizadas pelo Google. Os arquivos salvos por sites são exibidos aqui : Site adicionado @@ -42,6 +43,7 @@ Perguntar antes de permitir que sites criem um mapa 3D dos seus arredores ou acompanhem a posição da câmera (recomendado) Permitir cookies Perguntar quando um site quer descobrir dispositivos Bluetooth nas proximidades (recomendado) +A seleção de certificado do cliente não é compatível com o sistema operacional. Continuar MB Download concluído @@ -61,10 +63,12 @@ Copiado Tem certeza de que quer redefinir todas as permissões de site para ? Site +Faixa anterior Perguntar antes de permitir que sites reproduzam conteúdo protegido Permitir que os sites salvem e leiam os dados de arquivos "cookies" - que armazenam temporariamente o que você visitou na rede. (Recomendado) Leitor de certificados Essa ação limpará tudo, de dados de armazenamento de sites. +Bloqueada automaticamente Mostrar informações Bloqueados em alguns sites Ative as permissões para o Chrome nas configurações do Android. @@ -83,16 +87,19 @@ Sincronização em segundo plano Limpar armazenamento de sites? Voltar +Avançar Permitir que os sites reproduzam conteúdo protegido (recomendado) Copiar link / ? Título Permita cookies para um site específico. +Próxima faixa Configurações do site É possível que as notificações façam o dispositivo vibrar Abrir configurações. A Navegação GPS está bloqueada: Permitido +Um site está com mídia aberta Página Lite exibida pelo Google Pop-ups e redirecionamentos Permitir que sites reproduzam conteúdo protegido @@ -107,6 +114,7 @@ Revogar todas as permissões para o dispositivo Para permitir que o Chrome use RA, ative também a câmera em configurações do Android. Avançar +Compartilhar via Permitir Permitir o acesso de sites aos seus sensores de movimento (recomendado) Uso @@ -163,7 +171,9 @@ Limpar Downloads ativos Mover para baixo +Deslize para baixo para fechar. Impedir sites de terceiros de salvar e ler dados de cookies +Reproduzir Selecionar itens O dispositivo não consegue ler a NFC Excluído @@ -197,6 +207,7 @@ Um site está usando sua câmera Sua conta será desconectada desse site. Os cookies são arquivos criados pelos sites que você visita, usados por esses sites para lembrar suas preferências. Os cookies de terceiros são criados por outros sites que possuem uma parte do conteúdo, como anúncios ou imagens, que você vê na página da Web acessada. +Tocando mídia Cancelar seleção Sua conta será desconectada de todos os sites. Permitir o som de um site específico. @@ -204,6 +215,7 @@ Você quer mesmo apagar todos os dados locais, inclusive os cookies, e redefinir todas as permissões para este website? Desativado neste dispositivo Falha no download +Não foi possível selecionar certificado. Este conteúdo é de , veiculado pelo Google. Com som desativado Confirmar @@ -216,6 +228,8 @@ Um site está usando sua câmera e seu microfone Neste site, há exibição de anúncios invasivos ou enganosos Toque para voltar ao site +Alterar +Retroceder Revogar permissão do dispositivo O site é rápido Impedir a conexão de sites a dispositivos @@ -236,6 +250,7 @@ Bloqueie cookies de um site específico. A Navegação GPS está inacessível: Gerenciado pelos seus pais +Bluetooth Acessar seu microfone Bloquear se o site mostrar anúncios invasivos ou enganosos (recomendado) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-PT.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-PT.xtb index 10bb2eadcdf..703c0c7c8a7 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-PT.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_pt-PT.xtb @@ -10,6 +10,7 @@ Seguinte Incorporada em Parar +Para guardar os seus dados, as imagens desta página foram otimizadas pela Google. Os ficheiros guardados por Websites são apresentados aqui. Site adicionado @@ -42,6 +43,7 @@ Perguntar antes de permitir que os sites criem um mapa 3D do ambiente à sua volta ou monitorizem a posição da câmara (recomendado) Permitir cookies Perguntar quando um site pretende detetar dispositivos Bluetooth próximos (recomendado) +O sistema operativo não suporta a seleção do certificado do lado do cliente. Continuar MB Transferência concluída: @@ -61,10 +63,12 @@ Copiado Tem a certeza de que pretende repor todas as autorizações do site para ? Site +Faixa anterior Perguntar antes de permitir que os sites reproduzam conteúdos protegidos Permitir que os sites guardem e leiam dados de cookies (recomendado) Visualizador de certificados Esta ação elimina os de armazenamento do Website. +Bloqueada automaticamente Mostrar informações Bloqueado em alguns sites. Ative as autorizações para o Chrome nas Definições do Android. @@ -83,16 +87,19 @@ Sincronização em segundo plano Limpar armazenamento do site? Anterior +Procurar para a frente Permitir que os sites reproduzam conteúdo protegido (recomendado) Cop. link /? Título Permita cookies para um site específico. +Faixa seguinte Definições de sites As notificações podem fazer com que o dispositivo vibre Abrir definições A navegação está bloqueada: Permitido +Um site está a reproduzir multimédia. Página em modo lite fornecida pela Google Pop-ups e redirecionamentos Permitir que os sites reproduzam conteúdos protegidos @@ -107,6 +114,7 @@ Revogar todas as autorizações do dispositivo Para permitir que o Chrome utilize a realidade aumentada, ative também a câmara nas Definições do Android. Avançar +Partilhar através de Permitir Permitir que os sites acedam aos sensores de movimentos (recomendado) Utilização @@ -163,7 +171,9 @@ Limpar Transferências ativas Mover para baixo +Deslize rapidamente para baixo para fechar. Impedir que os Sites de terceiros guardem e leiam dados de cookies +Reproduzir Selecionar itens Este dispositivo não suporta a tecnologia NFC. Eliminado @@ -197,6 +207,7 @@ Um site está a utilizar a sua câmara. A sua sessão será terminada neste site. Os cookies são ficheiros criados pelos Websites que visitou. Os sites utilizam os cookies para memorizar as suas preferências. Os cookies de terceiros são criados por outros sites. Estes sites possuem algum do conteúdo, tal como anúncios ou imagens, que vê na página Web que visita. +Multimédia em reprodução Cancelar seleção A sua sessão terminará em todos os sites. Permita a reprodução de som de um site específico. @@ -204,6 +215,7 @@ Tem a certeza de que pretende limpar todos os dados locais, incluindo cookies, e repor todas as autorizações para este Website? Desativada para este dispositivo Falha ao transferir +Não é possível selecionar o certificado. Este conteúdo é proveniente de , fornecido pela Google. Com som desativado Confirmar @@ -216,6 +228,8 @@ Um site está a utilizar a sua câmara e microfone. Este site apresenta anúncios intrusivos ou enganadores. Toque para regressar ao site. +Alterar +Procurar para trás Revogar autorização do dispositivo O site é rápido Impedir a ligação de sites a dispositivos @@ -236,6 +250,7 @@ Bloqueie cookies para um site específico. A navegação está inacessível: Gerido pelos teus pais +Bluetooth Aceder ao microfone Bloquear se o site apresentar anúncios intrusivos ou enganadores (recomendado) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ro.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ro.xtb index 446d5deec4c..a31bf2c924f 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ro.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ro.xtb @@ -10,6 +10,7 @@ Înainte Încorporat în Oprește +Pentru a-ți salva datele, Google a optimizat imaginile din această pagină. Fișierele salvate de site-uri apar aici Site-ul a fost adăugat @@ -42,6 +43,7 @@ Întreabă înainte de a permite site-urilor să creeze o hartă 3D a lucrurilor din jur sau să urmărească poziția camerei video (recomandat) Permite cookie-urile Întreabă-mă când un site dorește să descopere dispozitive Bluetooth din apropiere (recomandat) +Selectarea certificatelor pe partea de client nu este acceptată de sistemul de operare. Continuă MB Descărcare finalizată @@ -61,10 +63,12 @@ Copiat Sigur dorești să resetezi toate permisiunile la nivel de site pentru ? Site +Melodia anterioară Întreabă înainte de a permite site-urilor să redea conținut protejat Permite site-urilor să salveze și să citească datele asociate cookie-urilor (recomandat) Vizualizator de certificate Astfel, se vor șterge din stocarea site-urilor. +Blocată automat Afișează informațiile Blocate pe anumite site-uri Activează permisiunile pentru Chrome din Setări Android. @@ -83,16 +87,19 @@ Sincronizare în fundal Ștergi stocarea site-urilor? Înapoi +Derulează înainte Permite site-urilor să redea conținutul protejat (recomandat) Copiază linkul / ? Titlu Permite cookie-uri pentru un anumit site. +Melodia următoare Setări pentru site-uri Notificările pot face dispozitivul să vibreze Deschide setările Navigarea este blocată: Se permite +Un site redă fișiere media Pagină Lite oferită de Google Ferestre pop-up și redirecționări Permite site-urilor să redea conținut protejat @@ -107,6 +114,7 @@ Revocă toate permisiunile pentru dispozitiv Pentru a permite ca Chrome să folosească RA, activează camera foto în Setările Android. Înainte +Trimite prin Permite Permite accesul site-urilor la senzorii de mișcare (recomandat) Utilizare @@ -163,7 +171,9 @@ Șterge Descărcări active Mutați în jos +Glisează în jos pentru a închide. Împiedică site-urile terță parte să salveze și să citească datele asociate cookie-urilor +Redă Selectează elementele Dispozitivul nu poate citi comunicările NFC Șters @@ -197,6 +207,7 @@ Un site folosește camera foto Te vei deconecta de la acest site. Cookie-urile sunt fișiere create de site-urile pe care le accesezi. Site-urile le folosesc pentru a-ți reține preferințele. Cookie-urile terță parte sunt create de alte site-uri. Aceste site-uri dețin o parte din conținut, cum ar fi anunțuri sau imagini, pe care le vezi pe pagina web pe care o accesezi. +Se redă conținutul media Anulează selecția Te vei deconecta de la toate site-urile. Permite sunetul pentru un anumit site. @@ -204,6 +215,7 @@ Sigur dorești să ștergi toate datele locale, inclusiv cookie-urile pentru acest site și să îi resetezi permisiunile? Dezactivată pentru acest dispozitiv Descărcarea nu a reușit +Certificatul nu poate fi selectat. Acest conținut provine de pe , oferit de Google. Cu sunetul dezactivat Confirmați @@ -216,6 +228,8 @@ Un site folosește camera foto și microfonul Acest site afișează anunțuri deranjante sau înșelătoare Atinge pentru a reveni la site +Modificați +Derulează înapoi Revocă permisiunea de accesare a dispozitivului Site-ul este rapid Blochează conectarea site-urilor la dispozitive @@ -236,6 +250,7 @@ Blochează cookie-urile pentru un anumit site. Navigarea nu este accesibilă: Gestionat de părinții tăi +Bluetooth Accesează microfonul Blochează dacă site-ul afișează anunțuri deranjante sau înșelătoare (recomandat) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ru.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ru.xtb index eca2f47c92a..bd6a7aa595f 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ru.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ru.xtb @@ -10,6 +10,7 @@ Далее Встроено на сайте Остановить +В целях экономии трафика изображения на странице были оптимизированы. Здесь появятся файлы, сохраненные сайтами. : "". Добавлен сайт @@ -42,6 +43,7 @@ Запрашивать для сайтов разрешение на создание 3D-карты места, в котором вы находитесь, и отслеживание положения камеры (рекомендуется) Разрешить файлы cookie Запрашивать для сайтов разрешение на поиск Bluetooth-устройств поблизости (рекомендуется) +Сертификат, выбранный клиентом, не поддерживается операционной системой. Продолжить  МБ Скачивание завершено @@ -61,10 +63,12 @@ Скопировано Сбросить разрешения для сайта ? Сайт +Предыдущий трек Запрашивать разрешение на воспроизведение защищенного контента Разрешить сайтам сохранять и читать файлы cookie (рекомендуется) Просмотр сертификатов Будут удалены все данные сайтов (). +Заблокировано автоматически Показать информацию Заблокировано на некоторых сайтах Разрешения для Chrome можно предоставить в настройках Android. @@ -83,16 +87,19 @@ Фоновая синхронизация Удалить данные сайтов? Назад +Перемотать вперед Разрешить сайтам воспроизводить защищенный контент (рекомендуется) Копировать ссылку из ? Название Разрешить определенному сайту сохранять файлы cookie. +Следующий трек Настройки сайтов Вибрация при получении уведомлений Открыть настройки Навигация заблокирована: Разрешено +На сайте воспроизводятся медиафайлы Lite-версия страницы получена с помощью Google Всплывающие окна и переадресация Разрешить сайтам воспроизводить защищенный контент @@ -107,6 +114,7 @@ Отозвать все разрешения для устройства Чтобы браузер Chrome мог использовать дополненную реальность, также предоставьте доступ к камере в настройках Android. Вперед +Способ отправки Разрешить Предоставить сайтам доступ к датчикам движения (рекомендуется) Использование @@ -163,7 +171,9 @@ Удалить Текущие скачивания Переместить вниз +Проведите вниз, чтобы закрыть панель. Запретить сторонним веб-сайтам сохранять и просматривать файлы cookie +Воспроизвести Выберите объекты Это устройство не поддерживает NFC. Удалено @@ -197,6 +207,7 @@ Сайт использует вашу камеру Вы выйдете из аккаунта на этом сайте. Когда вы посещаете сайты, они создают файлы cookie для сохранения настроек и других пользовательских данных. Сторонние файлы cookie создаются другими сайтами, которые размещают свой контент (например, объявления или изображения) на просматриваемых вами веб-страницах. +Воспроизведение медиаконтента Отменить выбор Вы выйдете из аккаунта на всех сайтах. Включить звуки на определенном сайте. @@ -204,6 +215,7 @@ Вы уверены, что хотите удалить все данные этого веб-сайта, включая файлы cookie, и сбросить заданные разрешения? Отключено для этого устройства Ошибка скачивания +Невозможно выбрать сертификат. Контент с сайта . Получен с помощью Google. Сайты с отключенным звуком Подтвердить @@ -216,6 +228,8 @@ Сайт использует вашу камеру и микрофон Этот сайт показывает навязчивую или вводящую в заблуждение рекламу Нажмите, чтобы вернуться на сайт. +Изменить +Перемотать назад Отключить доступ к устройству Сайт работает быстро Не разрешать сайтам подключаться к устройствам @@ -236,6 +250,7 @@ Блокировать файлы cookie для определенного сайта. Страница не найдена: Управляется вашими родителями +Bluetooth Доступ к микрофону Блокировать, если сайт показывает навязчивую или вводящую в заблуждение рекламу (рекомендуется) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_si.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_si.xtb index d1171b274f0..861d7e8878b 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_si.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_si.xtb @@ -10,6 +10,7 @@ ඊළඟ මත කාවද්දන්න නවතන්න +ඔබේ දත්ත සුරැකීමට, මෙම පිටුවේ රූප Google මගින් ප්‍රශස්ත කර ඇත. වෙබ් අඩවිවලින් සුරකින ලද ගොනු මෙහි දිස් වෙති , අඩවි එක් කරන ලදී @@ -42,6 +43,7 @@ වෙබ් අඩවිවලට ඔබේ වටපිටාවේ ත්‍රිමාන සිතියමක් සෑදීමට හෝ කැමරා ස්ථානය හඹා යෑමට ඉඩ දීමට පෙර අසන්න (නිර්දේශිතයි) කුකීවලට ඉඩ දෙන්න වෙබ් අඩවියකට අවට බ්ලූටූත් උපාංග සොයා ගැනීමට අවශ්‍ය වූ විට අසන්න (නිර්දේශිතයි) +මෙහෙයුම් පද්ධතිය සේවාලාභී පැත්තේ සහතික තේරීමට සහාය නොදක්වයි. කරගෙන යන්න MB බාගැනීම සම්පූර්ණයි @@ -61,10 +63,12 @@ පිටපත් කරන ලදි ඔබට සඳහා සියලුම වෙබ් අඩවි අවසර යළි සැකසිය යුතු බව ඔබට තහවුරුද? අඩවිය +පෙර ඛණ්ඩය ආරක්ෂිත අන්තර්ගත වාදනය කිරීමට ඉඩ දීමට පෙර විමසන්න (නිර්දේශිතයි) අඩවි වලට කුකී දත්ත සුරැකීමට සහ කුකී දත්ත කියවන්න (නිර්දේශිතයි) සහතික දක්වනය මෙය වෙබ් අඩවි ආචයනයේ සියලු හිස් කරනු ඇත. +ස්වයංක්‍රියව අවහිර කර ඇත තතු පෙන්වන්න සමහර වෙබ් අඩවිවල අවහිර කෙරේ Chrome සඳහා අවසර ප්‍රවේශය Android සැකසීම් තුළ ක්‍රියාත්මක කරන්න. @@ -83,16 +87,19 @@ පසුබිමෙහි සමමුහුර්තකරණය අඩවි ආචයනය හිස් කරනවා ද? ආපසු +ඉදිරියට සොයන්න අඩවිවලට ආරක්ෂිත අන්තර්ගතය වාදනය කිරීමට ඉඩ දෙන්න (නිර්දේශිතයි) සබැඳිය පිටපත් කරන්න / ? සිරස්තලය නිශ්චිත අඩවියක් සඳහා කුකීවලට ඉඩ දෙන්න. +ඊළඟ ඛණ්ඩය අඩවි සැකසුම් දැනුම්දීම් උපාංගය කම්පනය කළ හැක සැකසීම් විවෘත කරන්න සංචාලනය අවහිර කර ඇත: ඉඩ දුන් +වෙබ් අඩවියක් මාධ්‍ය ධාවන කරයි Google විසින් දෙනු ලබන සැහැල්ලු පිටුව උත්පතන සහ හරවා යැවීම් මෙම වෙබ් අඩවියට ආරක්‍ෂිත අන්තර්ගතය ධාවන කිරීමට ඉඩ දෙන්න. @@ -107,6 +114,7 @@ උපාංගය සඳහා සියලුම අවසර අහෝසි කරන්න Chrome හට AR භාවිත කිරීමට ඉඩ දීමට, Android සැකසීම් තුළ කැමරාව ද සක්‍රීය කරන්න. ඉදිරියට යන්න +හරහා බෙදාගන්න ඉඩදෙන්න වෙබ් අඩවිවලට චලන සංවේදකවලට ප්‍රවේශ වීමට ඉඩ දෙන්න (නිර්දේශිතයි) භාවිතය @@ -163,7 +171,9 @@ මකන්න ක්‍රියාත්මක බාගැනීම් පහළට ගෙන යන්න +වැසීමට පහළට ස්වයිප් කරන්න. තෙවන-පාර්ශ්ව වෙබ් අඩවි කුකී දත්ත සුරැකීම සහ කියවීම වළක්වන්න +වාදනය කරන්න අයිතම තෝරන්න මෙම උපාංගය NFC කියවිය නොහැක මකාදැමූ @@ -197,6 +207,7 @@ වෙබ් අඩවියක් ඔබේ කැමරාව භාවිත කරයි ඔබ මෙම වෙබ් අඩවියන් වරනු ලැබේ. කුකීස් යනු ඔබ පැමිණෙන වෙබ් අඩවිවලින් නිර්මාණ කෙරෙන ගොනු වේ. වෙබ් අඩවි ඔබේ මනාප මතක තබා ගැනීමට ඒවා භාවිත කරයි. තෙවන පාර්ශ්ව කුකී වෙනත් වෙබ් අඩවිවලින් නිර්මාණ කෙරේ. මෙම වෙබ් පිටුවලට ඔබ පැමිණෙන වෙබ් අඩවිය මත ඔබ දකින රූප හෝ දැන්වීම් වැනි සමහර අන්තර්ගත හිමි වේ. +මාධ්‍ය ධාවනය තේරීම අවලංගු කරන්න ඔබ සියලුම වෙබ් අඩවිවලින් වරනු ලැබේ. නිශ්චිත අඩවියක් සඳහා ශබ්දයට ඉඩ දෙන්න. @@ -204,6 +215,7 @@ කුකීස් ඇතුළු සියලු පෙදෙසි දත්ත හිස් කිරීමට අවශ්‍ය යැයි සහ මෙම වෙබ් අඩවිය සඳහා සියලු අවසර නැවත සැකසිය යුතු යැයි ඔබට විශ්වාස ද? මෙම උපාංගය සඳහා ක්‍රියාවිරහිත කරන ලදී. බාගැනීම අසමත් විය +සහතිකය තේරීමට නොහැකිය. මෙම අන්තර්ගතය Google විසින් බෙදාහරින, වෙතින් වේ. නිහඬයි තහවුරු කරන්න @@ -216,6 +228,8 @@ වෙබ් අඩවියක් ඔබේ කැමරාව සහ මයික්‍රොෆෝනය භාවිත කරයි මෙම වෙබ් අඩවිය ආක්‍රමණික හෝ නොමඟ යවන දැන්වීම් පෙන්වයි වෙබ් අඩවියට ආපසු යාමට තට්ටු කරන්න +වෙනස් කරන්න +පසුපසට සොයන්න උපාංගයේ අවසර අහෝසි කරන්න අඩවිය වේගවත් ය අඩවි උපාංගවලට සම්බන්ධ වීම අවහිර කරන්න @@ -236,6 +250,7 @@ නිශ්චිත අඩවියක් සඳහා කුකී අවහිර කරන්න. සංචාලනය වෙත ළඟා විය නොහැකිය: ඔබේ දෙමව්පියන් පාලනය කරයි +Bluetooth ඔබේ මයික්‍රෆෝනය වෙත ප්‍රවේශ වන්න වෙබ් අඩවිය ආක්‍රමණශීලී හෝ නොමඟ යවන දැන්වීම් පෙන්වන්නේ නම් අවහිර කරන්න (නිර්දේශිතයි) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sk.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sk.xtb index 8dd182b734b..43f8280592a 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sk.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sk.xtb @@ -10,6 +10,7 @@ Ďalej Vložené na webe Zastaviť +Google optimalizoval obrázky tejto stránky, aby vám ušetril dáta. Tu sa zobrazia súbory uložené webmi  –  Boli pridané stránky @@ -42,6 +43,7 @@ Opýtať sa, či chcete povoliť webom vytvoriť 3D mapu vášho okolia alebo sledovať umiestnenie kamier (odporúčané) Povoliť súbory cookie Opýtať sa, keď chce web objavovať zariadenia Bluetooth v okolí (odporúčané) +Operačný systém nepodporuje výber certifikátu na strane klienta. Pokračovať  MB Sťahovanie bolo dokončené @@ -61,10 +63,12 @@ Skopírované Naozaj chcete resetovať všetky povolenia webu pre ? Web +Predchádzajúca skladba Pýtať sa, či chcete povoliť webu prehrávať chránený obsah Povoliť webom ukladať a čítať súbory cookie (odporučané) Zobrazovač certifikátov Vymažete celé úložisko webu (). +Automaticky blokované Zobraziť informácie Blokované na niektorých weboch Zapnite povolenia pre Chrome v nastaveniach Androidu. @@ -83,16 +87,19 @@ Synchronizácia na pozadí Vymazať úložisko webov? Späť +Pretočiť dopredu Povoliť webom prehrávať chránený obsah (odporúčané) Kopírovať odkaz  / ? Názov Povolenie súborov cookie konkrétneho webu +Ďalšia skladba Nastavenia webu Upozornenia môžu pri prijatí na zariadení spustiť vibrovanie Otvoriť nastavenia Navigácia je zablokovaná: Povolené +Web prehráva médiá Zjednodušenú verziu stránky poskytol Google Vyskakovacie okná a presmerovania Povoliť webom prehrávať chránený obsah @@ -107,6 +114,7 @@ Odvolať všetky povolenia pre zariadenie Fotoaparát zapnite aj v Nastaveniach Androidu, aby mohol Chrome používať RR. Ďalej +Zdieľať Povoliť Povoliť webom prístup k senzorom pohybu (odporúčané) Použitie @@ -163,7 +171,9 @@ Vymazať Práve sťahované položky Presunúť nadol +Zatvorte potiahnutím dole. Brániť webom tretích strán ukladať a čítať súbory cookie +Prehrať Vyberte položky Toto zariadenie nedokáže čítať NFC Odstránené @@ -197,6 +207,7 @@ Váš fotoaparát používa nejaký web Systém vás z tohto webu odhlási. Súbory cookie sú vytvárané webmi, ktoré navštívite. Pamätajú si pomocou nich vaše predvoľby. Súbory cookie tretích strán sú vytvárané ďalšími webmi. Tieto weby sú vlastníkmi prvkov obsahu, ktorý sa zobrazuje na navštívenej webovej stránke (napríklad reklamy alebo obrázky). +Prehrávanie médií Zrušiť výber Systém vás odhlási zo všetkých webov. Povoliť zvuk pre konkrétny web. @@ -204,6 +215,7 @@ Naozaj chcete vymazať všetky miestne dáta tohto webu, vrátane súborov cookie, a resetovať všetky jeho povolenia? Vypnuté v tomto zariadení Stiahnutie zlyhalo +Nedá sa vybrať certifikát Tento obsah pochádza z domény a bol doručený Googlom. Zvuk bol vypnutý Potvrdiť @@ -216,6 +228,8 @@ Váš fotoaparát a mikrofón používa nejaký web Tento web zobrazuje obťažujúce alebo zavádzajúce reklamy Klepnutím sa vrátite na web +Zmeniť +Pretočiť dozadu Odvolať povolenie pre zariadenie Web je rýchly Zakázať webom pripájať sa k zariadeniam @@ -236,6 +250,7 @@ Blokovanie súborov cookie konkrétneho webu Navigácia je nedostupná: Spravované vašimi rodičmi +Bluetooth Prístup k mikrofónu Blokovať, ak web zobrazuje obťažujúce alebo zavádzajúce reklamy (odporúčané) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sl.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sl.xtb index 389a519e7a9..91cccd984de 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sl.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sl.xtb @@ -10,6 +10,7 @@ Naprej Vdelano v Ustavi +Google optimizira slike na tej strani, da prihranite pri prenosu podatkov. Datoteke, ki jih shranijo spletna mesta, so prikazane tukaj Spletno mesto je bilo dodano @@ -42,6 +43,7 @@ Vprašaj, preden se spletnim mestom dovoli ustvarjanje 3D-zemljevida vaše okolice ali spremljanje položaja kamere (priporočljivo) Dovoli piškotke Vprašaj, ko želi spletno mesto odkrivati naprave Bluetooth v bližini (priporočljivo) +Operacijski sistem ne podpira izbire potrdila pri odjemalcu. Naprej MB Prenos je končan @@ -61,10 +63,12 @@ Kopirano Ali ste prepričani, da želite ponastaviti vsa dovoljenja za spletna mesta za ? Spletno mesto +Prejšnja skladba Prikaži poziv, preden se spletnim mestom dovoli predvajanje zaščitene vsebine Dovoli spletnim mestom shranjevanje in branje podatkov piškotkov (priporočljivo) Pregledovalnik potrdil S tem bo izbrisanih vseh shranjenih podatkov spletnega mesta. +Samodejno blokirano Pokaži informacije Blokirano na nekaterih spletnih mestih V nastavitvah za Android vklopite dovoljenja za Chrome. @@ -83,16 +87,19 @@ Sinhroniziranje v ozadju Izbris shrambe mesta? Nazaj +Išči naprej Dovoli spletnim mestom predvajanje zaščitene vsebine (priporočeno) Kopiranje povezave /? Naslov Omogočanje piškotkov za določeno spletno mesto. +Naslednja skladba Nastavitve spletnega mesta Ob prejemanju obvestil naprava morda vibrira Odpri nastavitve Krmarjenje je blokirano: Dovoljeno +Spletno mesto predvaja predstavnost Stran v osnovnem načinu, ki jo je prikazal Google Pojavna okna in preusmeritve Spletnim mestom dovoli predvajanje zaščitene vsebine @@ -107,6 +114,7 @@ Umik vseh dovoljenj za napravo Če želite Chromu dovoliti uporabo razširjene resničnosti, prav tako vklopite fotoaparat v nastavitvah za Android. Pojdi naprej +Skupna raba prek Dovoli Spletnim mestom dovoli dostop do tipal gibanja (priporočeno) Uporaba @@ -163,7 +171,9 @@ Izbriši Aktivni prenosi Premakni dol +Povlecite navzdol, da zaprete. Drugim spletnim mestom prepreči shranjevanje in branje podatkov piškotkov +Predvajanje Izberite elemente Ta naprava ne podpira NFC-ja Izbrisano @@ -197,6 +207,7 @@ Spletno mesto uporablja kamero Odjavljeni boste s tega spletnega mesta. Piškotki so datoteke, ki jih ustvarijo spletna mesta, ki jih obiščete. Spletna mesta jih uporabljajo, da si zapomnijo vaše nastavitve. Piškotke drugih spletnih mest ustvarijo druga spletna mesta. Ta spletna mesta imajo v lasti nekatero vsebino, na primer oglase ali slike, ki jo vidite na spletni strani, ki jo obiščete. +Predvajanje predstavnosti Prekliči izbor Odjavljeni boste z vseh spletnih mest. Dovolitev zvoka za določeno spletno mesto. @@ -204,6 +215,7 @@ Ali želite res izbrisati vse lokalne podatke, vključno s piškotki, in ponastaviti vsa dovoljenja za to spletno mesto? Izklopljeno za to napravo Prenos ni uspel +Izbira potrdila ni mogoča Ta vsebina je iz domene in jo prikazuje Google. Prezrto Potrdi @@ -216,6 +228,8 @@ Spletno mesto uporablja kamero in mikrofon To spletno mesto prikazuje vsiljive ali zavajajoče oglase Dotaknite se, če se želite vrniti na spletno mesto +Spremeni +Išči nazaj Umik dovoljenja za dostop do naprave Spletno mesto je hitro Spletnim mestom prepreči povezovanje z napravami @@ -236,6 +250,7 @@ Blokiranje piškotkov za določeno spletno mesto. Krmarjenje ni dosegljivo: Upravljajo starši +Bluetooth Dostop do mikrofona Blokiraj, če spletno mesto prikazuje vsiljive ali zavajajoče oglase (priporočljivo) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sq.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sq.xtb index c827338a314..01c6aadbb76 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sq.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sq.xtb @@ -10,6 +10,7 @@ Tjetra Integruar në Ndalo +Për të ruajtur të dhënat, imazhet e kësaj faqeje janë optimizuar nga Google. Skedarët e ruajtur nga sajtet e uebit shfaqen këtu - Sajti u shtua @@ -42,6 +43,7 @@ Pyet përpara se të lejosh krijimin nga sajtet të një harte 3D të ambientit tënd rrethues ose gjurmimin prej tyre të pozicionit të kamerës (rekomandohet) Lejo kukit Pyet kur një sajt dëshiron të zbulojë pajisjet Bluetooth në afërsi (rekomandohet) +Përzgjedhja e certifikatës nga ana e klientit nuk mbështetet nga sistemi operativ. Vazhdo MB Shkarkimi përfundoi @@ -61,10 +63,12 @@ Kopjuar Je i sigurt që dëshiron t'i rivendosësh të gjitha lejet e sajtit për ? Sajti +Kënga e mëparshme Pyet përpara se sajtet të lejohen të luajnë përmbajtje të mbrojtura Lejo faqet të ruajnë dhe lexojnë të dhënat e kukive (rekomandohet) Shikuesi i certifikatave Kjo do të pastrojë të gjitha të hapësirës ruajtëse të sajtit të uebit. +Bllokuar automatikisht Shfaq informacionin Bllokuar në disa sajte Aktivizo lejimet për Chrome te "Cilësimet" e Android. @@ -83,16 +87,19 @@ Sinkronizimi në sfond Të pastrohet hapësira ruajtëse e sajtit? Prapa +Kërko përpara Lejo që sajtet të luajnë përmbajtje të mbrojtur (rekomandohet) Kopjo lidhjen / ? Titulli Lejo kukit për një sajt specifik. +Kënga tjetër Cilësimet e sajtit Njoftimet mund të bëjnë që pajisja të dridhet Hap cilësimet Lundrimi është bllokuar: Të lejuara +Një sajt po luan media Faqja e lehtë ofrohet nga Google. Dritaret kërcyese dhe ridrejtimet Lejo sajtet të luajnë përmbajtje të mbrojtura @@ -107,6 +114,7 @@ Anulo të gjitha lejet për pajisjen Për të lejuar që Chrome të përdorë AR, aktivizo gjithashtu kamerën në Cilësimet e Android. Vazhdo përpara +Ndaje nëpërmjet Lejo Lejoju faqeve të të hapin sensorët e lëvizjes (rekomandohet) Përdorimi @@ -163,7 +171,9 @@ Pastro Shkarkimet aktive Lëviz poshtë +Rrëshqit shpejt poshtë për ta mbyllur. Parandalo sajtet e uebit të palëve të treta që të ruajnë dhe lexojnë të dhënat e kukive +Luaj Zgjidh artikujt Kjo pajisje nuk mund të lexojë NFC-në Fshirë @@ -197,6 +207,7 @@ Një sajt po përdor kamerën tënde Do të dalësh nga ky sajt. Kukit janë skedarë të krijuar nga sajtet e uebit që viziton ti. Përdori ato për të kujtuar preferencat e tua. Kukit e palëve të treta krijohen nga sajte të tjera. Këto sajte zotërojnë disa nga përmbajtjet, si p.sh. reklamat ose imazhet që ti shikon në faqen e uebit që viziton. +Po luan media Anulo zgjedhjen Do të dalësh nga të gjitha sajtet. Lejo tingullin për një sajt specifik. @@ -204,6 +215,7 @@ Je i sigurt që dëshiron t'i pastrosh të gjitha të dhënat lokale, duke përfshirë kukit, dhe të rivendosësh të gjitha lejet për këtë sajt uebi? E çaktivizuar për këtë pajisje Shkarkimi dështoi +Nuk mund të zgjidhet certifikata. Kjo përmbajtje është nga , dërguar nga Google. Zëri i çaktivizuar Konfirmo @@ -216,6 +228,8 @@ Një sajt po përdor kamerën dhe mikrofonin tënd Ky sajt shfaq reklama ndërhyrëse ose mashtruese Trokit për t'u kthyer në sajt +Ndrysho +Kërko prapa Revoko lejen e pajisjes Sajti është i shpejtë Blloko lidhjen e sajteve me pajisjet @@ -236,6 +250,7 @@ Blloko kukit për një sajt specifik. Lundrimi është i paarritshëm: Menaxhuar nga prindërit e tu +Bluetooth-i Qasu te mikrofoni Blloko nëse sajti shfaq reklama ndërhyrëse ose mashtruese (rekomandohet) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr-Latn.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr-Latn.xtb index 75825ffd0bc..9477e75bb85 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr-Latn.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr-Latn.xtb @@ -10,6 +10,7 @@ Dalje Ugrađeno u sajt Zaustavi +Da bi se smanjilo korišćenje podataka, Google je optimizovao slike na ovoj stranici. Datoteke koje su sačuvali veb-sajtovi prikazuju se ovde Sajt je dodat @@ -42,6 +43,7 @@ Pre nego što dozvolite sajtovima da prave 3D mapu okruženja ili prate položaj kamere prikazuje se upit (preporučeno) Dozvoli kolačiće Pitaj kada sajt želi da otkrije Bluetooth uređaje u blizini (preporučeno) +Operativni sistem ne podržava izbor sertifikata za klijenta. Nastavite MB Preuzimanje je završeno @@ -61,10 +63,12 @@ Kopirano Želite li stvarno da resetujete sve dozvole za sajt za ? Sajt +Prethodna pesma Pitaj pre nego što dozvoliš sajtovima da puštaju zaštićeni sadržaj Dozvoli sajtovima da čuvaju i čitaju podatke kolačića (preporučuje se) Prikazivač sertifikata Ovim ćete obrisati ceo memorijski prostor veb-sajta od . +Automatski je blokirano Prikaži informacije Blokirano na nekim sajtovima Uključite dozvole za Chrome u Android podešavanjima. @@ -83,16 +87,19 @@ Sinhronizacija u pozadini Brišete memoriju sajta? Nazad +Premotaj unapred Dozvoli sajtovima da puštaju zaštićeni sadržaj (preporučeno) Kopiraj link /? Naslov Omogućava kolačiće za određeni sajt. +Sledeća pesma Podešavanja sajta Uređaj će vibrirati kada primate obaveštenja Otvori podešavanja Navigacija je blokirana: Dozvoljeno +Sajt pušta medijski sadržaj Pojednostavljenu stranicu pruža Google Iskačući prozori i preusmeravanja Dozvolite sajtovima da puštaju zaštićeni sadržaj @@ -107,6 +114,7 @@ Opozovite sve dozvole za uređaj Da biste dozvolili da Chrome koristi PR, uključite i kameru u Android podešavanjima. Kretanje unapred +Deljenje preko Dozvoli Dozvoli sajtovima da pristupaju senzorima za pokret (preporučeno) Korišćenje @@ -163,7 +171,9 @@ Obriši Aktivna preuzimanja Premesti nadole +Prevucite nadole da biste zatvorili. Spreči veb-sajtove treće strane da čuvaju i čitaju podatke kolačića +Pusti Izaberite stavke Ovaj uređaj ne može da čita NFC Izbrisano @@ -197,6 +207,7 @@ Sajt koristi kameru Odjavićete se sa ovog sajta. Kolačići su datoteke koje prave veb-sajtovi koje posećujete. Sajtovi ih koriste da bi zapamtili vaša podešavanja. Kolačiće treće strane prave drugi sajtovi. Ti sajtovi su vlasnici delova sadržaja koji vidite na posećenoj veb-stranici, poput oglasa ili slika. +Reprodukcija medija Otkaži izbor Odjavićete se sa svih sajtova. Dozvoli zvuk za određeni sajt. @@ -204,6 +215,7 @@ Želite li stvarno da obrišete sve lokalne podatke, uključujući kolačiće, i resetujete sve dozvole za ovaj veb-sajt? Isključeno je za ovaj uređaj Preuzimanje nije uspelo +Nije moguće izabrati sertifikat. Ovaj sadržaj je sa , prikazuje Google. Zvuk je isključen Potvrdi @@ -216,6 +228,8 @@ Sajt koristi kameru i mikrofon Ovaj sajt prikazuje oglase koji ometaju aktivnosti ili obmanjujuće oglase Dodirnite da biste se vratili na sajt +Promeni +Premotaj unazad Opozovi dozvolu za uređaj Sajt je brz Onemogući sajtovima da se povezuju sa uređajima @@ -236,6 +250,7 @@ Blokirajte kolačiće za određeni sajt. Navigacija je nedostupna: Ovim upravljaju tvoji roditelji +Bluetooth Pristup mikrofonu Blokiraj ako sajt prikazuje oglase koji ometaju aktivnosti ili obmanjujuće oglase (preporučeno) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr.xtb index c1c0fbad00e..1ecb4df00f0 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sr.xtb @@ -10,6 +10,7 @@ Даље Уграђено у сајт Заустави +Да би се смањило коришћење података, Google је оптимизовао слике на овој страници. Датотеке које су сачували веб-сајтови приказују се овде Сајт је додат @@ -42,6 +43,7 @@ Пре него што дозволите сајтовима да праве 3D мапу окружења или прате положај камере приказује се упит (препоручено) Дозволи колачиће Питај када сајт жели да открије Bluetooth уређаје у близини (препоручено) +Оперативни систем не подржава избор сертификата за клијента. Наставите MB Преузимање је завршено @@ -61,10 +63,12 @@ Копирано Желите ли стварно да ресетујете све дозволе за сајт за ? Сајт +Претходна песма Питај пре него што дозволиш сајтовима да пуштају заштићени садржај Дозволи сајтовима да чувају и читају податке колачића (препоручује се) Приказивач сертификата Овим ћете обрисати цео меморијски простор веб-сајта од . +Аутоматски је блокирано Прикажи информације Блокирано на неким сајтовима Укључите дозволе за Chrome у Android подешавањима. @@ -83,16 +87,19 @@ Синхронизација у позадини Бришете меморију сајта? Назад +Премотај унапред Дозволи сајтовима да пуштају заштићени садржај (препоручено) Копирај линк /? Наслов Омогућава колачиће за одређени сајт. +Следећа песма Подешавања сајта Уређај ће вибрирати када примате обавештења Отвори подешавања Навигација је блокирана: Дозвољено +Сајт пушта медијски садржај Поједностављену страницу пружа Google Искачући прозори и преусмеравања Дозволите сајтовима да пуштају заштићени садржај @@ -107,6 +114,7 @@ Опозовите све дозволе за уређај Да бисте дозволили да Chrome користи ПР, укључите и камеру у Android подешавањима. Кретање унапред +Дељење преко Дозволи Дозволи сајтовима да приступају сензорима за покрет (препоручено) Коришћење @@ -163,7 +171,9 @@ Обриши Активна преузимања Премести надоле +Превуците надоле да бисте затворили. Спречи веб-сајтове треће стране да чувају и читају податке колачића +Пусти Изаберите ставке Овај уређај не може да чита NFC Избрисано @@ -197,6 +207,7 @@ Сајт користи камеру Одјавићете се са овог сајта. Колачићи су датотеке које праве веб-сајтови које посећујете. Сајтови их користе да би запамтили ваша подешавања. Колачиће треће стране праве други сајтови. Ти сајтови су власници делова садржаја који видите на посећеној веб-страници, попут огласа или слика. +Репродукција медија Откажи избор Одјавићете се са свих сајтова. Дозволи звук за одређени сајт. @@ -204,6 +215,7 @@ Желите ли стварно да обришете све локалне податке, укључујући колачиће, и ресетујете све дозволе за овај веб-сајт? Искључено је за овај уређај Преузимање није успело +Није могуће изабрати сертификат. Овај садржај је са , приказује Google. Звук је искључен Потврди @@ -216,6 +228,8 @@ Сајт користи камеру и микрофон Овај сајт приказује огласе који ометају активности или обмањујуће огласе Додирните да бисте се вратили на сајт +Промени +Премотај уназад Опозови дозволу за уређај Сајт је брз Онемогући сајтовима да се повезују са уређајима @@ -236,6 +250,7 @@ Блокирајте колачиће за одређени сајт. Навигација је недоступна: Овим управљају твоји родитељи +Bluetooth Приступ микрофону Блокирај ако сајт приказује огласе који ометају активности или обмањујуће огласе (препоручено) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sv.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sv.xtb index 3fee51d6e98..9a7b370a96f 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sv.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sv.xtb @@ -10,6 +10,7 @@ Nästa Inbäddad på Stopp +Bilderna på den här sidan har optimerats av Google så att du sparar data. Filer som webbplatser har sparat visas här Webbplatsen har lagts till @@ -42,6 +43,7 @@ Fråga innan webbplatser tillåts att skapa en 3D-karta över dina omgivningar eller registrera kamerans position (rekommenderas) Tillåt cookies Fråga när en webbplats försöker söka efter Bluetooth-enheter i närheten (rekommenderas) +Val av certifikat på klienten stöds inte av operativsystemet. Fortsätt MB Nedladdningen är klar @@ -61,10 +63,12 @@ Kopierat Vill du återställa alla webbplatsbehörigheter för ? Webbplats +Föregående spår Fråga innan webbplatser tillåts att spela upp skyddat innehåll Tillåt att webbplatser sparar och läser cookiedata (rekommenderas) Certifikatvisare Det här alternativet tar bort alla webbplatslagring. +Blockerades automatiskt Visa info Blockeras på vissa webbplatser Aktivera behörigheter för Chrome i Android-inställningar. @@ -83,16 +87,19 @@ Synkronisera i bakgrunden Rensa webbplatslagring? Föregående +Sök framåt Tillåt att skyddat innehåll spelas upp på webbplatser (rekommenderas) Kopiera länk /? Titel Tillåt cookies för en enskild webbplats. +Nästa spår Platsinställningar Aviseringar kan göra att enheten vibrerar Öppna Inställningar Webbadressen har blockerats: Tillåts +Media spelas upp på en webbplats Lite-sida tillhandahållen av Google Popup och omdirigeringar Tillåt att webbplatser spelar upp skyddat innehåll @@ -107,6 +114,7 @@ Återkalla alla behörigheter för enheten Om du vill att Chrome ska använda AR måste du även aktivera kameran i inställningarna för Android. Fortsätt +Dela via Tillåt Tillåt webbplatser att använda enhetens rörelsesensorer (rekommenderas) Användning @@ -163,7 +171,9 @@ Rensa Pågående nedladdningar Flytta ned +Svep nedåt när du vill stänga. Hindra att webbplatser från tredje part sparar och läser cookiedata +Spela Välj objekt Enheten kan inte läsa NFC Borttagen @@ -197,6 +207,7 @@ En webbplats använder kameran Du loggas ut från webbplatsen. Cookies är filer som har skapats av webbplatser du besöker. De används för att spara inställningar på en webbplats. Cookies från tredje part skapas av andra webbplatser. Dessa webbplatser äger en del av innehållet som visas på webbplatsen du besöker, till exempel annonser eller bilder. +Spelar upp media Rensa val Du loggas ut från alla webbplatser. Tillåt ljud för en webbplats. @@ -204,6 +215,7 @@ Vill du ta bort all lokal data för webbplatsen, inklusive cookies, och återställa alla behörigheter för den? Inaktiverad för den här enheten Nedladdningen misslyckades +Det går inte att välja certifikat. Innehållet kommer från via Google. Ljudet avstängt Bekräfta @@ -216,6 +228,8 @@ En webbplats använder kameran och mikrofonen Påträngande eller vilseledande annonser visas på den här webbplatsen Återgå till webbplatsen genom att trycka här +Ändra +Sök bakåt Återkalla enhetsbehörighet Webbplatsen är snabb Förhindra att webbplatser ansluter till enheter @@ -236,6 +250,7 @@ Blockera cookies för en enskild webbplats. Det går inte att nå webbadressen: Hanteras av dina föräldrar +Bluetooth Tillgång till din mikrofon Blockera om påträngande eller vilseledande annonser visas på webbplatsen (rekommenderas) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sw.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sw.xtb index e177f012180..6e395feecee 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sw.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_sw.xtb @@ -10,6 +10,7 @@ Endelea Imepachikwa kwenye Simamisha +Ili uhifadhi data yako, picha kwenye ukurasa huu zimeboreshwa na Google. Faili zinazohifadhiwa na tovuti huonekana hapa , Tovuti imeongezwa @@ -42,6 +43,7 @@ Uliza kabla ya kuruhusu tovuti zibuni ramani ya 3D ya mazingira yako au kufuatilia mkao wa kamera (inapendekezwa) Ruhusu vidakuzi Niulize wakati tovuti inataka kugundua vifaa vya Bluetooth vilivyo karibu (inapendekezwa) +Uchaguzi wa cheti cha sehemu ya seva teja hautumiwi na mfumo wa uendeshaji. Endelea MB Upakuaji umekamilika @@ -61,10 +63,12 @@ Imenakiliwa Una uhakika ungependa kubadilisha ruhusa zote za tovuti ya ? Tovuti +Wimbo uliotangulia Iulize kabla ya kuruhusu tovuti kucheza maudhui yanayolindwa Ruhusu tovuti zihifadhi na kusoma data ya vidakuzi (imependekezwa) Kitazamaji vyeti Hatua hii itafuta yote ya hifadhi ya tovuti. +Imezuiwa kiotomatiki Onyesha Maelezo Yamezuiwa kwenye baadhi ya tovuti Washa ruhusa za Chrome katika Mipangilio ya Android. @@ -83,16 +87,19 @@ Usawazishaji wa chini chini Ungependa kufuta hifadhi ya tovuti? Rudi nyuma +Peleka mbele Ruhusu tovuti zicheze maudhui yanayolindwa (inapendekezwa) Nakili kiungo / ? Kichwa Ruhusu vidakuzi katika tovuti maalum. +Wimbo unaofuata Mipangilio ya tovuti Arifa huenda zitatetemesha kifaa Fungua mipangilio Kudurusu kumezuiwa: Imeruhusiwa +Tovuti inacheza maudhui Ukurasa mwepesi umetolewa na Google Madirisha ibukizi/kuelekeza kwingine Ruhusu tovuti icheze maudhui yanayolindwa @@ -107,6 +114,7 @@ Batilisha ruhusa zote za kifaa Ili uruhusu Chome itumie Uhalisia Ulioboreshwa, washa pia kamera katika Mipangilio ya Android. Nenda mbele +Shiriki kupitia Ruhusu Ruhusu tovuti zifikie vitambuzi vyako vya mwendo (inapendekezwa) Matumizi @@ -163,7 +171,9 @@ Futa Faili zinazopakuliwa Songa chini +Telezesha chini ili ufunge. Zuia tovuti nyingine kuhifadhi na kusoma data ya vidakuzi +Cheza Chagua vipengee Kifaa hiki kimeshindwa kusoma NFC Imeondolewa @@ -197,6 +207,7 @@ Tovuti inatumia kamera yako Utaondolewa kwenye akaunti ya tovuti hii. Vidakuzi ni faili zinazoundwa na tovuti unazotembelea. Tovuti huvitumia kukumbuka mapendeleo yako. Vidakuzi vya washirika wengine hutengenezwa na tovuti nyingine. Tovuti hizo humiliki baadhi ya maudhui kama vile matangazo au picha ambazo unaona kwenye kurasa za wavuti unazotembelea. +Kucheza maudhui Ghairi uchaguzi Utaondolewa kwenye akaunti za tovuti zote. Ruhusu sauti katika tovuti mahususi. @@ -204,6 +215,7 @@ Una uhakika unataka kufuta data zote za ndani, ikiwemo vidakuzi, na kuweka upya ruhusa za tovuti hii? Imezimwa kwa kifaa hiki Haikuweza kupakua +Imeshindwa kuchagua cheti. Maudhui haya yanatoka , yamewasilishwa na Google. Imezimwa Thibitisha @@ -216,6 +228,8 @@ Tovuti inatumia kamera na maikrofoni yako Tovuti hii inaonyesha matangazo yanayopotosha au yanayokatiza huduma Gusa ili urudi kwenye tovuti +Badilisha +Peleka nyuma Batilisha ruhusa ya kifaa Tovuti inapakia haraka Zuia tovuti zisiunganishe kwenye vifaa @@ -236,6 +250,7 @@ Zuia vidakuzi katika tovuti maalum. Kudurusu hakufikiki: Inadhibitiwa na wazazi wako +Bluetooth Kufikia maikrofoni yako Zuia ikiwa tovuti inaonyesha matangazo yanayopotosha au yanayokatiza huduma (inapendekezwa) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ta.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ta.xtb index be81d24e2c7..eec69344951 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ta.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ta.xtb @@ -10,6 +10,7 @@ அடுத்து இல் உட்பொதியப்பட்டது நிறுத்து +உங்கள் டேட்டாவைச் சேமிப்பதற்காக, இந்தப் பக்கத்தின் படங்கள் Googleளால் சுருக்கப்பட்டுள்ளன. இணையதளங்கள் சேமித்த கோப்புகள் இங்கே தோன்றும் - தளம் சேர்க்கப்பட்டது @@ -42,6 +43,7 @@ உங்களைச் சுற்றியுள்ள இடங்களின் 3D மேப்பை உருவாக்கவோ கேமரா நிலையை டிராக் செய்யவோ தளங்களை அனுமதிப்பதற்கு முன்பாக அனுமதி கேட்கும் (பரிந்துரைக்கப்படுகிறது) குக்கீகளை அனுமதி அருகிலுள்ள புளூடூத் சாதனங்களை வலைதளம் கண்டறிய முயலும்போது கேள் (பரிந்துரைக்கப்படுகிறது) +கிளையன்ட் சார்பாக சான்றிதழ் தேர்ந்தெடுப்பை ஆப்ரேட்டிங் சிஸ்டம் ஆதரிக்கவில்லை. தொடர்க மெ.பை. பதிவிறக்கம் முடிந்தது @@ -61,10 +63,12 @@ நகலெடுக்கப்பட்டது க்கான தள அனுமதிகள் அனைத்தையும் நிச்சயமாக மீட்டமைக்க விரும்புகிறீர்களா? தளம் +முந்தைய டிராக் பாதுகாக்கப்பட்ட உள்ளடக்கத்தை இயக்குவதற்குத் தளங்களை அனுமதிக்கும் முன்பு அனுமதி கோரும் குக்கீத் தரவை, தளங்கள் சேமிக்கவும் படிக்கவும் அனுமதி (பரிந்துரைக்கப்பட்டது) சான்றிதழ் வியூவர் இணையதளச் சேமிப்பகத்தில் உள்ள தரவையும் இது அழிக்கும். +தானாகத் தடுக்கப்பட்டது தகவலைக் காட்டு சில தளங்களில் தடுக்கப்பட்டுள்ளன Android அமைப்புகளில் Chromeக்கான அனுமதிகளை இயக்கவும். @@ -83,16 +87,19 @@ பின்புல ஒத்திசைவு தளச் சேமிப்பகத்தை அழிக்கவா? திரும்பு +முன்செல் பாதுகாக்கப்பட்ட உள்ளடக்கத்தை இயக்க, தளங்களை அனுமதி (பரிந்துரைக்கப்படுகிறது) இணைப்பை நகலெடு / ? தலைப்பு குறிப்பிட்ட தளத்திற்கு, குக்கீகளை அனுமதிக்கும். +அடுத்த டிராக் தள அமைப்புகள் அறிவிப்புகள் வரும் போது சாதனம் அதிர்வுறக்கூடும் அமைப்புகளைத் திற செல்வது தடுக்கப்பட்டது: அனுமதிக்கப்பட்டது +ஒரு தளம் மீடியாவை இயக்குகிறது Google வழங்கும் Lite பக்கம் பாப்-அப்கள் & திசைதிருப்புதல்கள் பாதுகாக்கப்பட்ட உள்ளடக்கத்தை இயக்குவதற்குத் தளங்களை அனுமதிக்கும் @@ -107,6 +114,7 @@ சாதனத்திற்கான அனைத்து அனுமதிகளையும் ரத்துசெய்யும் ARரைப் பயன்படுத்த Chromeமை அனுமதிப்பதற்கு, Android அமைப்புகளில் கேமராவையும் இயக்கவும். முன் செல்க +இதன்வழியாக பகிர் அனுமதி மோஷன் சென்சார்களை அணுக தளங்களை அனுமதிக்கும் (பரிந்துரைக்கப்படுகிறது) பயன்பாடு @@ -163,7 +171,9 @@ அழி செயலிலுள்ள பதிவிறக்கங்கள் கீழே நகர்த்து +மூடுவதற்கு, கீழே ஸ்வைப் செய்யவும். குக்கீத் தரவைச் சேமிப்பதிலிருந்தும் படிப்பதிலிருந்தும் மூன்றாம் தரப்பு இணையதளங்களைத் தடு +இயக்கு பட்டியலிலிருந்து தேர்ந்தெடுக்கவும் இந்தச் சாதனத்தில் NFC ஆதரிக்கப்படவில்லை நீக்கப்பட்டது @@ -197,6 +207,7 @@ ஒரு தளம் உங்கள் கேமராவைப் பயன்படுத்துகிறது இந்தத் தளத்தில் இருந்து வெளியேற்றப்படுவீர்கள். நீங்கள் பார்வையிடும் தளங்களால் உருவாக்கப்பட்ட கோப்புகளே குக்கீகள். உங்கள் விருப்பங்களை நினைவில் வைத்துக்கொள்ள இணையதளங்கள் இவற்றைப் பயன்படுத்துகின்றன. பிற தளங்களால் மூன்றாம் தரப்புக் குக்கீகள் உருவாக்கப்படுகின்றன. நீங்கள் பார்வையிடும் இணையப் பக்கத்தில் தோன்றக்கூடிய விளம்பரங்கள், படங்கள் போன்ற சில உள்ளடக்கங்கள் இந்தத் தளங்களுடையவையாக இருக்கும். +மீடியாவைப் பிளே செய்கிறது தேர்வை ரத்துசெய் அனைத்துத் தளங்களிலிருந்தும் வெளியேற்றப்படுவீர்கள். குறிப்பிட்ட தளத்திற்கு ஒலியை அனுமதி @@ -204,6 +215,7 @@ இந்த இணையதளத்தின் குக்கீகள் உட்பட எல்லா அகத் தரவையும் அழித்து, அதன் எல்லா அனுமதிகளையும் மீட்டமைக்கவா? இந்தச் சாதனத்திற்கு முடக்கப்பட்டுள்ளது பதிவிறக்க முடியவில்லை +சான்றிதழைத் தேர்ந்தெடுக்க முடியவில்லை. இந்த உள்ளடக்கம் (Google ஆல் வழங்கப்படுவது) இலிருந்து கிடைக்கிறது. ஒலியடக்கியவை உறுதிப்படுத்து @@ -216,6 +228,8 @@ ஒரு தளம் உங்கள் மைக்ரோஃபோனையும் கேமராவையும் பயன்படுத்துகிறது குறுக்கிடும் அல்லது தவறாக வழிநடத்தும் விளம்பரங்களை இந்தத் தளம் காண்பிக்கிறது தளத்திற்குச் செல்ல, தட்டவும் +மாற்று +பின்செல் சாதன அனுமதியை ரத்துசெய்யும் தளம் வேகமாக இருக்கிறது சாதனங்களை இணைப்பதிலிருந்து தளங்களைத் தடுக்கும் @@ -236,6 +250,7 @@ குறிப்பிட்ட தளத்திற்கான குக்கீகளைத் தடுக்கும். செல்ல முடியவில்லை: உங்கள் பெற்றோரால் நிர்வகிக்கப்படுகிறது +புளூடூத் உங்கள் மைக்ரோஃபோனை அணுகலாம் குறுக்கிடும் அல்லது தவறாக வழிநடத்தும் விளம்பரங்களை தளம் காண்பித்தால், அதைத் தடு (பரிந்துரைக்கப்படுவது) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb index 6db82f2c272..8b6e2e82461 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb @@ -10,6 +10,7 @@ తరువాత లో పొందుపరచబడింది ఆపు +మీ కోసం డేటాను సేవ్ చేయడానికి, ఈ పేజీ ఇమేజ్‌లు Google ద్వారా ఆప్టిమైజ్ చేయబడ్డాయి. వెబ్‌సైట్‌లు సేవ్ చేసిన ఫైల్‌లు ఇక్కడ కనిపిస్తాయి - సైట్ జోడించబడింది @@ -42,6 +43,7 @@ మీ పరిసరాల 3D మ్యాప్‌ను రూపొందించడానికి లేదా కెమెరా పొజిషన్‌ను ట్రాక్ చేయడానికి సైట్‌లను అనుమతించే ముందు అడగాలి (సిఫార్సు చేయడమైనది) కుక్కీలను అనుమతించు ఏదైనా ఒక సైట్ సమీపంలోని బ్లూటూత్ పరికరాలను కనుగొనాలనుకున్నప్పుడు అనుమతి అడుగుతుంది (సిఫార్సు చేయడమైనది) +క్లయింట్ తరపు స‌ర్టిఫికెట్‌ ఎంపికకు ఆపరేటింగ్ సిస్టమ్ మద్దతు లేదు. కొనసాగించు MB డౌన్‌లోడ్ పూర్తయింది @@ -61,10 +63,12 @@ కాపీ చేయబడింది మీరు కోసం అన్ని సైట్ అనుమతులను ఖచ్చితంగా రీసెట్ చేయాలనుకుంటున్నారా ? సైట్ +మునుపటి ట్రాక్ సైట్‌లు రక్షిత కంటెంట్‌ను ప్లే చేయడానికి ముందు అనుమతి కోసం అడుగుతాయి కుక్కీ డేటాను సేవ్ చేయడానికి, చదవడానికి సైట్‌లను అనుమతిస్తుంది (సిఫార్సు చేయబడింది) ప్రమాణపత్రం వ్యూయర్ ఇది వెబ్‌సైట్ నిల్వలోని మొత్తం ను తీసివేస్తుంది. +స్వయంచాలకంగా బ్లాక్ చేయబడింది సమాచారాన్ని చూపు కొన్ని సైట్‌లలో బ్లాక్ చేయబడింది Android సెట్టింగ్‌లులో Chrome కోసం అనుమతులను ఆన్ చేయండి. @@ -83,16 +87,19 @@ బ్యాక్‌గ్రౌండ్ సింక్ సైట్ నిల్వను తీసివేయాలా? వెనుకకు +ముందుకు జరుపు రక్షిత కంటెంట్‌ను ప్లే చేయడానికి సైట్‌లను అనుమతిస్తుంది (సిఫార్సు చేయబడింది) లింక్‌ను కాపీ చేయి / ? శీర్షిక నిర్దిష్ట సైట్ కోసం కుక్కీలను అనుమతించండి. +తరువాత ట్రాక్ సైట్ సెట్టింగ్‌లు నోటిఫికేషన్‌లు పరికరాన్ని వైబ్రేట్ చేయవచ్చు సెట్టింగ్‌లను తెరువు నావిగేషన్ బ్లాక్ చేయబడింది: అనుమతించబడింది +ఒక సైట్‌లో మీడియా ప్లే చేయబడుతోంది Google అందించిన లైట్ పేజీ పాప్-అప్‌లు మరియు మళ్లింపులు రక్షిత కంటెంట్‌ను ప్లే చేయడానికి సైట్‌లను అనుమతిస్తుంది @@ -107,6 +114,7 @@ పరికరానికి అన్ని అనుమతులను ఉపసంహరించు ARను Chrome ఉపయోగించడానికి వీలుగా, Android సెట్టింగ్‌లలో కూడా కెమెరాను ఆన్ చేయండి. ముందుకు వెళ్ళు +దీని ద్వారా భాగస్వామ్యం చే. అనుమతించు మోషన్ సెన్సార్‌లను యాక్సెస్ చేయడానికి సైట్‌లను అనుమతించండి (సిఫార్సు చేస్తున్నాము) నిల్వ వినియోగం @@ -163,7 +171,9 @@ క్లియర్ చేయి యాక్టివ్‌గా ఉన్న డౌన్‌లోడ్‌లు క్రిందికి తరలించు +మూసివేయడానికి దిగువకు స్వైప్ చేయండి. మూడవ పక్షం వెబ్‌సైట్‌లను కుక్కీ డేటా సేవ్ చేయనీయకుండా, చదవనీయకుండా నిరోధించు +ప్లే చేయి అంశాలను ఎంచుకోండి ఈ పరికరం NFCని రీడ్ చేయదు తొలగించబడింది @@ -197,6 +207,7 @@ ఒక సైట్ మీ కెమెరాను ఉపయోగిస్తోంది మీరు ఈ సైట్ నుండి సైన్ అవుట్ చేయబడతారు. మీరు సందర్శించే వెబ్‌సైట్‌లు రూపొందించిన ఫైల్‌లను కుక్కీలు అంటారు. మీ ప్రాధాన్యతలను గుర్తుంచుకోవడానికి సైట్‌లు వాటిని ఉపయోగిస్తాయి. ఇతర సైట్‌లు రూపొందించిన కుక్కీలను థర్డ్-పార్టీ కుక్కీలు అంటారు. మీరు సందర్శించే వెబ్ పేజీలో మీరు చూసే యాడ్‌లు లేదా ఇమేజ్‌ల లాంటి, కొంత కంటెంట్‌ను ఈ సైట్‌లు స్వంతంగా కలిగి ఉంటాయి. +మీడియా ప్లే అవుతోంది ఎంపికను రద్దు చేయి మీరు అన్ని సైట్‌ల నుండి సైన్ అవుట్ చేయబడతారు. నిర్దిష్ట సైట్ కోసం ధ్వనిని అనుమతించండి. @@ -204,6 +215,7 @@ కుక్కీలతో సహా, మొత్తం స్థానిక డేటాను తీసివేసి, ఈ వెబ్‌సైట్‌కు ఇచ్చిన అన్ని అనుమతులను రీసెట్ చేయాలని మీరు ఖచ్చితంగా కోరుకుంటున్నారా? ఈ పరికరం కోసం ఆఫ్ చేయబడింది డౌన్‌లోడ్ విఫలమైంది +ప్రమాణపత్రం ఎంపిక చేయడం సాధ్యపడలేదు. ఈ కంటెంట్ Google ద్వారా డెలివర్ చేయబడిన లోనిది. మ్యూట్ చేసినవి నిర్ధారించు @@ -216,6 +228,8 @@ ఒక సైట్ మీ కెమెరాను, మైక్రోఫోన్‌ను ఉపయోగిస్తోంది ఈ సైట్ అనుచితమైన లేదా తప్పుదారి పట్టించే ప్రకటనలను చూపుతుంది సైట్‌కు తిరిగి వెళ్లడానికి ట్యాప్ చేయండి +మార్చు +వెనుకకు జరుపు పరికర అనుమతిని ఉపసంహరిస్తుంది సైట్ వేగంగా లోడ్ అవుతుంది పరికరాలకు కనెక్ట్ కాకుండా సైట్‌లను బ్లాక్ చేస్తుంది @@ -236,6 +250,7 @@ ఏదైనా ఒక నిర్దిష్ట సైట్‌లో కుక్కీలను బ్లాక్ చేయండి. దీనికి నావిగేట్ చేయడం సాధ్యపడదు: మీ తల్లిదండ్రుల ద్వారా నిర్వహించబడుతోంది +బ్లూటూత్ మీ మైక్రోఫోన్ యాక్సెస్ అనుమతి సైట్ అనుచితమైన లేదా తప్పుదారి పట్టించే ప్రకటనలను చూపించినప్పుడు బ్లాక్ చేయి (సిఫార్సు చేయబడింది) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_th.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_th.xtb index 49e039dc3a9..357292959a9 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_th.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_th.xtb @@ -10,6 +10,7 @@ ถัดไป ฝังอยู่ใน หยุด +Google ได้เพิ่มประสิทธิภาพให้รูปภาพในหน้านี้เพื่อประหยัดอินเทอร์เน็ตมือถือ ไฟล์ที่เว็บไซต์บันทึกไว้จะปรากฏที่นี่ - เพิ่มเว็บไซต์ แล้ว @@ -42,6 +43,7 @@ ถามก่อนที่จะอนุญาตให้เว็บไซต์สร้างแผนที่ 3 มิติของสิ่งที่อยู่รอบตัวคุณหรือติดตามตำแหน่งของกล้อง (แนะนำ) อนุญาตคุกกี้ ถามเมื่อเว็บไซต์ต้องการค้นหาอุปกรณ์บลูทูธใกล้เคียง (แนะนำ) +ระบบปฏิบัติการไม่สนับสนุนการเลือกใบรับรองฝั่งลูกค้า ต่อไป MB ดาวน์โหลดเสร็จสมบูรณ์ @@ -61,10 +63,12 @@ คัดลอกแล้ว แน่ใจไหมว่าต้องการรีเซ็ตสิทธิ์ทั้งหมดของเว็บไซต์สำหรับ เว็บไซต์ +แทร็กก่อนหน้า ถามก่อนอนุญาตให้เว็บไซต์เล่นเนื้อหาที่ได้รับความคุ้มครอง อนุญาตให้เว็บไซต์บันทึกและอ่านข้อมูลคุกกี้ (แนะนำ) เครื่องมือดูใบรับรอง การดำเนินการนี้จะล้างพื้นที่เก็บข้อมูลเว็บไซต์ทั้ง +ถูกบล็อกโดยอัตโนมัติ แสดงข้อมูล บล็อกในบางเว็บไซต์ เปิดการใช้สิทธิ์สำหรับ Chrome ในการตั้งค่า Android @@ -83,16 +87,19 @@ ซิงค์ในแบ็กกราวด์ ล้างพื้นที่เก็บข้อมูลเว็บไซต์ไหม กลับ +ไปข้างหน้า อนุญาตให้เว็บไซต์เล่นเนื้อหาที่ได้รับความคุ้มครอง (แนะนำ) คัดลอกลิงก์ /? ชื่อ อนุญาตคุกกี้ของเว็บไซต์ที่เจาะจง +แทร็กถัดไป การตั้งค่าเว็บไซต์ การแจ้งเตือนอาจทำให้อุปกรณ์สั่น เปิดการตั้งค่า มีการบล็อกการนำทาง: อนุญาตแล้ว +เว็บไซต์กำลังเล่นสื่อ หน้าเวอร์ชัน Lite ให้บริการโดย Google ป๊อปอัปและการเปลี่ยนเส้นทาง อนุญาตให้เว็บไซต์เล่นเนื้อหาที่ได้รับความคุ้มครอง @@ -107,6 +114,7 @@ เพิกถอนสิทธิ์ทั้งหมดสำหรับอุปกรณ์ หากต้องการให้ Chrome ใช้ AR ให้เปิดกล้องในการตั้งค่า Android ด้วย ไปข้างหน้า +แชร์ผ่าน อนุญาต อนุญาตให้เว็บไซต์เข้าถึงเซ็นเซอร์ตรวจจับความเคลื่อนไหว (แนะนำ) การใช้ @@ -163,7 +171,9 @@ ล้าง การดาวน์โหลดที่ทำงานอยู่ เลื่อนลง +เลื่อนลงเพื่อปิด ป้องกันไม่ให้เว็บไซต์ของบุคคลที่สามบันทึกและอ่านข้อมูลคุกกี้ +เล่น เลือกรายการ อุปกรณ์นี้อ่าน NFC ไม่ได้ ลบแล้ว @@ -197,6 +207,7 @@ มีเว็บไซต์กำลังใช้กล้อง คุณจะออกจากระบบของเว็บไซต์นี้ คุกกี้คือไฟล์ที่เว็บไซต์ต่างๆ ที่คุณเข้าชมสร้างขึ้น เว็บไซต์ใช้คุกกี้เพื่อจดจำข้อมูลการท่องเว็บของคุณ คุกกี้ของบุคคลที่สามซึ่งเว็บไซต์อื่นๆ เป็นผู้สร้างขึ้น เว็บไซต์เหล่านี้เป็นเจ้าของเนื้อหาบางอย่าง เช่น โฆษณาหรือรูปภาพที่คุณเห็นในหน้าเว็บที่เข้าชม +กำลังเล่นสื่อ ยกเลิกการเลือก คุณจะออกจากระบบของทุกเว็บไซต์ อนุญาตให้เว็บไซต์ที่ต้องการเล่นเสียงได้ @@ -204,6 +215,7 @@ คุณแน่ใจไหมว่าต้องการล้างข้อมูลในเครื่องทั้งหมด รวมถึงคุกกี้ และรีเซ็ตสิทธิ์ทั้งหมดสำหรับเว็บไซต์นี้ ปิดสำหรับอุปกรณ์นี้ การดาวน์โหลดล้มเหลว +เลือกใบรับรองไม่ได้ เนื้อหานี้มาจาก และนำส่งโดย Google ปิดเสียง ยืนยัน @@ -216,6 +228,8 @@ มีเว็บไซต์กำลังใช้กล้องถ่ายรูปและไมโครโฟน เว็บไซต์นี้แสดงโฆษณาที่แทรกหรือทำให้เข้าใจผิด แตะเพื่อกลับไปที่เว็บไซต์ +เปลี่ยน +ย้อนกลับ เพิกถอนสิทธิ์ของอุปกรณ์ เว็บไซต์โหลดเร็ว บล็อกเว็บไซต์ไม่ให้เชื่อมต่อกับอุปกรณ์ @@ -236,6 +250,7 @@ บล็อกคุกกี้ของเว็บไซต์ที่เจาะจง ไม่สามารถเข้าถึงการนำทางได้: มีการจัดการโดยผู้ปกครอง +บลูทูธ เข้าถึงไมโครโฟน บล็อกหากเว็บไซต์แสดงโฆษณาที่แทรกหรือทำให้เข้าใจผิด (แนะนำ) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_tr.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_tr.xtb index cc5d9333a0c..f38b63a362f 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_tr.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_tr.xtb @@ -10,6 +10,7 @@ İleri adresinde yerleşik Durdur +Veri tasarrufu amacıyla bu sayfadaki görseller Google tarafından optimize edilmiştir. Web siteleri tarafından kaydedilen dosyalar burada görünür - sitesi eklendi @@ -42,6 +43,7 @@ Sitelerin çevremin 3D haritasını oluşturmasına veya kamera konumunu takip etmesine izin vermeden önce sor (önerilir) Çerezlere izin ver Bir site yakındaki Bluetooth cihazları bulmak istediğinde sor (önerilir) +İstemci tarafı sertifika seçimi, işletim sistemi tarafından desteklenmiyor. Devam et MB İndirme işlemi tamamlandı @@ -61,10 +63,12 @@ Kopyalandı için tüm site izinlerini sıfırlamak istediğinizden emin misiniz? Site +Önceki parça Sitelerin korumalı içeriği oynatmasına izin verilmeden önce size sorulur Sitelerin, çerez verilerini kaydetmelerine ve okumalarına izin ver (önerilir) Sertifika görüntüleyici Bu işlem olan web sitesi depolama alanının tamamını temizleyecek. +Otomatik olarak engellendi Bilgileri Göster Bazı sitelerde engellendi Android Ayarları'nda Chrome için izinleri açın. @@ -83,16 +87,19 @@ Arka plan senkronizasyonu Site depolama silinsin mi? Geri +İleriye doğru git Sitelerin korumalı içeriği oynatmasına izin ver (önerilir) Bağlantıyı kopyala / ? Başlık Belirli bir site için çerezlere izin verin. +Sonraki parça Site ayarları Bildirimler cihazı titretebilir Ayarları aç Gezinme engellendi: İzin verilen +Bir site medya oynatıyor Google tarafından sağlanan basit sayfa Pop-up'lar ve yönlendirmeler Sitelerin korumalı içeriği oynatmasına izin ver @@ -107,6 +114,7 @@ Cihaz için tüm izinleri iptal eder Chrome'un artırılmış gerçekliği (AR) kullanması için Android Ayarlarında kamerayı da açın. İlerle +Paylaşım yöntemi: İzin ver Sitelerin hareket sensörlerine erişmesine izin ver (önerilen) Kullanım @@ -163,7 +171,9 @@ Temizle Etkin indirme işlemleri Aşağı taşı +Kapatmak için aşağı kaydırın. Üçüncü taraf web sitelerinin çerez verilerini kaydetmesini ve okumasını engelle +Oynat Öğe seçin Bu cihaz NFC'yi okuyamıyor Silindi @@ -197,6 +207,7 @@ Bir site kameranızı kullanıyor Bu sitedeki oturumunuz kapatılacak. Çerezler, ziyaret ettiğiniz web siteleri tarafından oluşturulan dosyalardır. Siteler tercihlerinizi hatırlamak için çerezleri kullanır. Üçüncü taraf çerezleri diğer siteler tarafından oluşturulur. Bu siteler, ziyaret ettiğiniz web sayfasında gördüğünüz reklam veya resim gibi içeriğin bir kısmına sahiptir. +Medya oynatılıyor/çalınıyor Seçimi iptal et Tüm sitelerdeki oturumunuz kapatılacak. Belirli bir site için sese izin verin. @@ -204,6 +215,7 @@ Bu web sitesine ilişkin çerezler dahil tüm yerel verileri temizlemek ve bu sitenin tüm izinlerini sıfırlamak istediğinizden emin misiniz? Bu cihaz için kapatıldı İndirilemedi +Sertifika seçilemiyor. Bu içerik Google tarafından adresinden sağlanmaktadır. Ses kapatıldı Onayla @@ -216,6 +228,8 @@ Bir site kameranızı ve mikrofonunuzu kullanıyor Bu site, araya giren veya yanıltıcı reklamlar gösteriyor Siteye dönmek için dokunun +Değiştir +Geriye doğru git Cihaz iznini iptal et Site hızlı yükleniyor Sitelerin cihazlara bağlanmasını engelle @@ -236,6 +250,7 @@ Belirli bir site için çerezleri engelleyin. Gezinme işlevine ulaşılamıyor: Ebeveynleriniz tarafından yönetiliyor +Bluetooth Mikrofonunuza erişim Site, araya giren veya yanıltıcı reklamlar gösteriyorsa engelle (önerilen) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uk.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uk.xtb index fdfb5512501..d449f6d6177 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uk.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uk.xtb @@ -10,6 +10,7 @@ Далі Вбудовано на сайті Зупинити +Щоб заощадити трафік, ми оптимізували зображення на цій сторінці. Файли, збережені веб-сайтами, відображаються тут Сайт додано @@ -42,6 +43,7 @@ Запитувати, перш ніж дозволяти сайтам створювати 3D-карту вашого оточення або відстежувати положення камери (рекомендовано) Дозволити файли cookie Запитувати, коли сайт хоче шукати пристрої Bluetooth поблизу (рекомендовано) +Операційна система не підтримує сертифікат, вибраний на стороні клієнта. Продовжити МБ Завантажено @@ -61,10 +63,12 @@ Скопійов. Скинути всі дозволи сайту ? Сайт +Попередня композиція Запитувати, перш ніж дозволяти сайтам відтворювати захищений вміст Дозволити сайтам зберігати та розпізнавати дані файлів cookie (рекомендується) Перегляд сертифікатів Буде видалено всі дані сайтів (). +Блокується автоматично Показати інформацію Заблоковано на деяких сайтах Увімкніть дозволи для Chrome у налаштуваннях Android. @@ -83,16 +87,19 @@ Фонова синхронізація Видалити дані сайтів? Назад +Далі Дозволити сайтам відтворювати захищений вміст (рекомендується) Копіювати /? Назва Дозволити файли cookie для конкретного сайту. +Наступна композиція Налаштування сайту Коли надходитимуть сповіщення, пристрій може вібрувати Відкрити налаштування Веб-сторінку заблоковано Дозволено +Сайт відтворює медіа-вміст Спрощену сторінку надає Google Спливаючі вікна й переадресація Дозволити сайтам відтворювати захищений вміст @@ -107,6 +114,7 @@ Скасувати всі дозволи для пристрою Щоб дозволити Chrome використовувати доповнену реальність, увімкніть камеру в налаштуваннях Android. Перейти вперед +Надіслати Дозволити Надати сайтам доступ до датчиків руху (рекомендовано) Використання @@ -163,7 +171,9 @@ Очистити Активні завантаження Вниз +Проведіть пальцем униз, щоб закрити. Забороняти стороннім веб-сайтам зберігати та переглядати дані файлів cookie +Відтворити Виберіть елементи Цей пристрій не підтримує NFC Видалено @@ -197,6 +207,7 @@ Сайт використовує камеру Ви вийдете з облікового запису на цьому сайті. Файли cookie – це файли, створені веб-сайтами, які ви відвідали. Сайти використовують їх, щоб запам'ятовувати ваші налаштування. Сторонні файли cookie створюються іншими сайтами. Ці сайти показують власний контент, як-от оголошення чи зображення, на веб-сторінці, яку ви відвідуєте. +Відтворення медіафайлу Скасувати вибір Ви вийдете з облікового запису на всіх сайтах. Увімкнути звук для певного сайту. @@ -204,6 +215,7 @@ Видалити всі локальні дані, зокрема файли cookie, і скинути всі дозволи для цього веб-сайту? Вимкнено для цього пристрою Не вдалося завантажити +Не вдалося вибрати сертифікат. Це вміст із сайту , який доставляє Google. Звук вимкнено Підтвердити @@ -216,6 +228,8 @@ Сайт використовує камеру та мікрофон Цей сайт показує нав’язливі чи оманливі оголошення Натисніть, щоб повернутися на сайт +Змінити +Назад Скасувати доступ до пристрою Сайт працює швидко Заборонити сайтам підключатися до пристроїв @@ -236,6 +250,7 @@ Блокувати файли cookie з конкретного сайту. Веб-сторінка недоступна Керується батьками +Bluetooth Доступ до мікрофона Блокувати, якщо сайт показує нав’язливі чи оманливі оголошення (рекомендовано) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ur.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ur.xtb index afe83ed8f6f..564ff83471d 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ur.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_ur.xtb @@ -10,6 +10,7 @@ اگلا پر سرایت کردہ روکیں +‏آپ کے ڈیٹا کو محفوظ کرنے کیلئے، اس صفحہ کی تصاویر Google کے ذریعے بہتر بنائی گئی ہیں۔ ویب سائٹس کے ذریعے محفوظ کردہ فائلز یہاں ظاہر ہوں گی - سائٹ شامل کی گئی @@ -42,6 +43,7 @@ ‏سائٹس کو اپنے اطراف کا 3D نقشہ تخلیق یا کیمرے کی پوزیشن ٹریک کرنے کی اجازت دینے سے پہلے پوچھیں (تجویز کردہ) کوکیز کی اجازت دیں اس وقت پوچھیں جب کسی سائٹ میں قریبی بلوٹوتھ آلات کو دریافت کرنا مقصود ہو (تجویز کردہ) +کلائنٹ سائڈ سرٹیفکیٹ کا انتخاب آپریٹنگ سسٹم کے ذریعہ تعاون یافتہ نہیں ہے۔ جاری رکھیں MB ڈاؤن لوڈ مکمل ہو گیا @@ -61,10 +63,12 @@ کاپی کیا کیا آپ واقعی کے لیے سائٹ کی سبھی اجازتوں کو دوبارہ ترتیب دینا چاہتے ہیں؟ سائٹ +پچھلا ٹریک سائٹس کو محفوظ مواد چلانے کی اجازت دینے سے پہلے پوچھیں سائٹس کو کوکی ڈیٹا کو محفوظ کرنے اور پڑھنے کی اجازت دیں (تجویز کردہ) سرٹیفیکیٹ ناظر اس سے ویب سائٹ اسٹوریج کا کُل صاف ہو جائے گا۔ +خود کار طور پر مسدود ہو گيا معلومات دکھائیں بعض سائٹس پر مسدود ہے Android ترتیبات میں Chrome کیلئے اجازتیں آن کریں۔ @@ -83,16 +87,19 @@ پس منظر کی مطابقت پذیری سائٹ کا اسٹوریج صاف کریں؟ پیچھے +آگے لے جائیں سائٹس کو محفوظ کردہ مواد چلانے کی اجازت دیں (تجویز کردہ) لنک کاپی کریں / ? عنوان مخصوص سائٹ کے لیے کوکیز کی اجازت دیں۔ +اگلا ٹریک سائٹ کی ترتیبات اطلاعات آلہ کو وائبریٹ کر سکتی ہیں ترتیبات کھولیں نیویگیشن مسدود ہے: اجازت یافتہ +سائٹ پر میڈیا چل رہا ہے ‏Google کی طرف سے فراہم کردہ لائٹ صفحہ پوپ اپس اور ری ڈائریکٹس سائٹس کو محفوظ مواد چلانے کی اجازت دیں @@ -107,6 +114,7 @@ آلہ کے لیے سبھی اجازتوں کو کالعدم کریں ‏Chrome کو AR کا استعمال کرنے دینے کے لیے، Android ترتیبات میں بھی کیمرا آن کریں۔ آگے جائیں +اشتراک کریں بذریعہ اجازت دیں سائٹس کو موشن سینسرز تک رسائی حاصل کرنے کی اجازت دیں (تجویز کردہ) استعمال @@ -163,7 +171,9 @@ صاف کریں فعال ڈاؤن لوڈز نیچے منتقل کریں +بند کرنے کے لئے نیچے سوائپ کریں۔ فریق ثالث ویب سائٹس کو کوکی ڈیٹا محفوظ کرنے اور پڑھنے سے روکیں +چلائیں آئٹمز منتخب کریں ‏یہ آلہ NFC کو نہیں پڑھ سکتا حذف کردہ @@ -197,6 +207,7 @@ ایک سائٹ آپ کا کیمرا استعمال کر رہی ہے ایک آپ اس سائٹ سے سائن آؤٹ ہو جائیں گے۔ کوکیز ایسی فائلز ہوتی ہیں جنہیں آپ کی ملاحظہ کی جانے والی ویب سائٹس تخلیق کرتی ہیں۔ سائٹس آپ کی ترجیحات کو یاد رکھنے کیلئے ان کا استعمال کرتی ہیں۔ فریق ثالث کوکیز دیگر سائٹس کے ذریعے تخلیق کی جاتی ہیں۔ یہ سائٹس کچھ مواد کی مالک ہوتی ہیں، جیسے اشتہارات یا تصاویر جنہیں آپ ملاحظہ کی جانے والی ویب صفحہ پر دیکھتے ہیں۔ +میڈیا کو چلایا جا رہا ہے انتخاب منسوخ کریں آپ سبھی سائٹس سے سائن آؤٹ ہو جائیں گے۔ کسی مخصوص سائٹ کیلئے آواز کی اجازت دیں۔ @@ -204,6 +215,7 @@ کیا آپ واقعی کوکیز سمیت سبھی مقامی ڈيٹا صاف کرنا اور اس ویب سائٹ کیلئے سبھی اجازتیں دوبارہ ترتیب دینا چاہتے ہیں؟ اس آلے کیلئے آف کر دیا گیا ڈاؤن لوڈ ناکام ہوگیا +سرٹیفکیٹ منتخب کرنے سے قاصر ہے۔ ‏یہ مواد کی جانب سے ہے، جسے Google نے ڈیلیور کیا ہے۔ ‏خاموش کردہ تصدیق کریں @@ -216,6 +228,8 @@ ایک سائٹ آپ کا کیمرا اور مائیکروفون استعمال کر رہی ہے یہ سائٹ دخل انداز یا گمراہ کن اشتہارات دکھاتی ہے سائٹ پر واپس جانے کے لیے تھپتھپائیں +تبدیل کریں +پیچھے لے جائیں آلہ کی اجازت کالعدم کریں سائٹ تیز ہے سائٹس کو آلات سے منسلک ہونے سے مسدود کریں @@ -236,6 +250,7 @@ کسی مخصوص سائٹ کے لیے کوکیز مسدود کریں۔ نیویگیشن ناقابل رسائی ہے: آپ کے والدین کے زیر انتظام +بلوٹوتھ اپنے مائیکروفون تک رسائی حاصل کریں اگر سائٹ دخل انداز یا گمراہ کن اشتہارات دکھاتی ہے تو مسدود کریں (تجویز کردہ) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uz.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uz.xtb index 1bcb22ecb1e..989f9e8f569 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uz.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_uz.xtb @@ -10,6 +10,7 @@ Keyingisi saytiga joylangan To‘xtatish +Trafikni tejash maqsadida sahifadagi rasmlar Google tomonidan takomillashtirildi. Saytlardan saqlangan fayllar shu yerda chiqadi - sayti qo‘shildi @@ -42,6 +43,7 @@ Saytlar atrofingiz 3D xaritasini yaratish yoki kamera holatini kuzatish oldin ruxsat olsin (tavsiya etiladi) Cookie fayllariga ruxsat berish Sayt atrofdagi Bluetooth qurilmalarni tekshirmoqchi boʻlsa, xabar berilsin (tavsiya etiladi) +Tanlangan mijoz sertifikati eskirgan va operatsion tizim tomonidan qo‘llab-quvvatlanmaydi. Davom etish MB Yuklab olindi: @@ -61,10 +63,12 @@ Nusxa olindi uchun barcha sayt ruxsatnomalari ilk holatga qaytarilsinmi? Sayt +Avvalgi musiqa Himoyalangan kontentni ijro etishdan avval ruxsat so‘ralsin Saytlarga cookie-fayllarini saqlash va o’qishga ruxsat berish (tavsiya etiladi) Sertifikatlarni ko‘rish vositasi Veb-sayt xotirasidan tozalanadi. +Avtomatik bloklangan Ma’lumotlarni ko‘rsatish Ayrim saytlarda bloklangan Android sozlamalaridan Chrome uchun ruxsatnomalarni yoqing. @@ -83,16 +87,19 @@ Orqa fonda sinxronlash Sayt xotirasi tozalansinmi? Orqaga +Oldinga surish Saytlarga himoyalangan kontentni ijro qilish uchun ruxsat berish (tavsiya etiladi) Nusxalash / ? Nomi Muayyan saytlar uchun cookie-fayllarga ruxsat berish +Keyingi musiqa Sayt sozlamalari Bildirishnoma kelganida qurilma tebranishi mumkin Sozlamalarni ochish Sahifa bloklandi: Berilgan ruxsatnomalar +Saytda media-fayl ijro etilmoqda Sahifaning Lite versiyasi Google tomonidan taqdim etildi Qalquvchi oyna va yo‘naltirishlar Saytlarga himoyalangan kontentni ijro qilish uchun ruxsat berish @@ -107,6 +114,7 @@ Qurilma uchun barcha ruxsatnomalarni bekor qilish Chrome AR ishlatishi uchun Android Sozlamalar orqali kamerani yoqing. Oldinga +Yuborish usuli Ruxsat berish Saytlarga harakat sensorlaridan foydalanish uchun ruxsat berish (tavsiya etiladi) Sarfi @@ -163,7 +171,9 @@ Tozalash Faol yuklanmalar Pastga tushish +Yopish uchun pastga suring. Tashqi saytlarga cookie ma’lumotlarini saqlash va o‘qishni taqiqlash. +Ijro etish Fayllarni tanlang Bu qurilma NFC teglarni oʻqiy olmaydi O‘chirib tashlandi @@ -197,6 +207,7 @@ Sayt kameradan foydalanmoqda Bu saytdagi hisobingizdan chiqib ketasiz. Cookie fayllar saytlarga tashrif buyurilganda yaratiladigan fayllardir. Saytlar ulardan axborotingizni eslab qolishda foydalanadi. Tashqi cookie fayllar boshqa saytlar tomonidan yaratiladi. Bunday saytlarga ochilgan sahifadagi kontent, reklama yoki rasmlar tegishli boʻlishi mumkin. +Mediani ijro etish Belgilashni bekor qilish Barcha saytdagi hisobingizdan chiqib ketasiz. Muayyan saytga ovoz ijro etish uchun ruxsat berish. @@ -204,6 +215,7 @@ Siz haqiqatdan ham bu saytning cookie fayllari kabi barcha ma’lumotlarni o‘chirmoqchimisiz (barcha ruxsatnomalar ilk holatga qaytariladi)? Ushbu qurilma uchun o‘chirilgan Yuklab olib bo‘lmadi +Sertifikatni tanlab bo‘lmadi. sayti kontenti, Google tomonidan taqdim etildi. Ovozsiz Tasdiqlash @@ -216,6 +228,8 @@ Sayt kamera va mikrofondan foydalanmoqda Bu saytda yoqimsiz yoki befoyda reklamalar chiqadi Saytga qaytish uchun bosing +O‘zgartirish +Orqaga qaytarish Qurilma ruxsatini bekor qilish Bu sayt tez Saytlarga qurilmalarga ulanishni taqiqlash @@ -236,6 +250,7 @@ Muayyan saytlar uchun cookie-fayllarni bloklang. Sahifa topilmadi: Ota-onangiz tomonidan boshqariladi +Bluetooth Mikrofonga ruxsat Saytlarda chiquvchi yoqimsiz yoki befoyda reklamalar bloklansin (tavsiya etiladi) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb index ddd01c91d7c..959084613b6 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_vi.xtb @@ -10,6 +10,7 @@ Tiếp theo Được nhúng trên Dừng +Google đã tối ưu hóa các hình ảnh trong trang này để tiết kiệm dữ liệu cho bạn. Những tệp mà trang web lưu lại sẽ xuất hiện ở đây Đã thêm trang web @@ -42,6 +43,7 @@ Hỏi trước khi cho phép các trang web tạo bản đồ 3D về các khu vực xung quanh bạn hoặc theo dõi thông tin vị trí của máy ảnh (khuyên dùng) Cho phép cookie Hỏi khi một trang web muốn tìm các thiết bị Bluetooth ở gần (khuyên dùng) +Lựa chọn chứng chỉ phía ứng dụng khách không được hệ điều hành hỗ trợ. Tiếp tục MB Quá trình tải xuống hoàn tất @@ -61,10 +63,12 @@ Đã sao chép Bạn có chắc muốn đặt lại tất cả các quyền của trang web cho không? Trang web +Bản nhạc trước Hỏi trước khi cho phép trang web phát nội dung được bảo vệ Cho phép trang web lưu và đọc dữ liệu cookie (được đề xuất) Trình xem chứng chỉ Thao tác này sẽ xóa tất cả bộ nhớ trang web. +Tự động bị chặn Hiển thị thông tin Đã chặn trên một số trang web Bật quyền cho Chrome trong Cài đặt Android. @@ -83,16 +87,19 @@ Đồng bộ hóa dưới nền Xóa dữ liệu trang trong bộ nhớ? Quay lại +Tìm kiếm tiến Cho phép trang web phát nội dung được bảo vệ (được đề xuất) Sao chép đường liên kết /? Tiêu đề Cho phép cookie của một trang web cụ thể. +Bản nhạc tiếp theo Cài đặt trang web Thông báo có thể làm rung thiết bị Mở phần cài đặt Điều hướng bị chặn: Được cho phép +Một trang web đang phát nội dung đa phương tiện Trang phiên bản rút gọn do Google cung cấp Cửa sổ bật lên và liên kết chuyển hướng Cho phép trang web phát nội dung được bảo vệ @@ -107,6 +114,7 @@ Thu hồi tất cả các quyền đối với thiết bị Để cho phép Chrome sử dụng công nghệ thực tế tăng cường (AR), hãy bật cả máy ảnh trong phần Cài đặt của Android. Đi về phía trước +Chia sẻ qua Cho phép Cho phép trang web sử dụng cảm biến chuyển động (nên dùng) Mức sử dụng @@ -163,7 +171,9 @@ Xóa Hiện đang tải xuống Di chuyển xuống +Vuốt xuống để đóng. Ngăn các trang web của bên thứ ba lưu và đọc dữ liệu cookie +Phát Chọn mục Thiết bị này không hỗ trợ công nghệ Giao tiếp phạm vi gần (NFC) Đã xóa @@ -197,6 +207,7 @@ Một trang web đang sử dụng máy ảnh của bạn Bạn sẽ bị đăng xuất khỏi trang web này. Cookie là các tệp do các trang web bạn truy cập tạo ra. Các trang web dùng cookie để ghi nhớ lựa chọn của bạn. Cookie bên thứ ba do các trang web khác tạo ra. Các trang web này sở hữu một số nội dung như quảng cáo hoặc hình ảnh mà bạn thấy trên trang web mình truy cập. +Đang phát nội dung nghe nhìn Hủy chọn Bạn sẽ bị đăng xuất khỏi tất cả các trang web. Cho phép phát âm thanh trên một trang web cụ thể. @@ -204,6 +215,7 @@ Bạn có chắc chắn muốn xóa tất cả dữ liệu cục bộ, bao gồm cookie và đặt lại tất cả các quyền cho trang web này không? Tắt cho thiết bị này Tải xuống không thành công +Không thể chọn chứng chỉ. Nội dung này đến từ , do Google phân phối. Đã ẩn Xác nhận @@ -216,6 +228,8 @@ Một trang web đang sử dụng máy ảnh và micrô của bạn Trang web này hiển thị quảng cáo xâm nhập hoặc quảng cáo gây hiểu nhầm Nhấn để quay lại trang web +Thay đổi +Tìm kiếm lùi Thu hồi quyền truy cập thiết bị Trang web tải nhanh Chặn các trang web kết nối với thiết bị @@ -236,6 +250,7 @@ Chặn cookie của một trang web cụ thể. Không thể tiếp cận điều hướng: Do cha mẹ của bạn quản lý +Bluetooth Truy cập micrô của bạn Chặn nếu trang web hiển thị quảng cáo xâm nhập hoặc quảng cáo gây hiểu nhầm (khuyên dùng) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-CN.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-CN.xtb index e3010ddcff0..5bba417ee1b 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-CN.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-CN.xtb @@ -10,6 +10,7 @@ 下一步 嵌入来源: 停止 +为节省您的流量,Google 已优化了此网页中的图片。 网站保存的文件会显示在这里 - 网站 已添加 @@ -42,6 +43,7 @@ 在允许网站为您的周边环境创建 3D 地图或跟踪摄像头位置之前询问您(推荐) 允许使用 Cookie 在网站想发现附近的蓝牙设备时询问您(推荐) +操作系统不支持选择客户端证书。 继续 MB 下载完成 @@ -61,10 +63,12 @@ 已复制 确定要重置的所有网站权限吗? 网站 +上一首 网站需先询问并得到许可才能播放受保护内容 允许网站保存和读取 Cookie 数据(推荐) 证书查看器 这会清除全部的网站存储数据 ()。 +已被自动禁止 显示信息 已禁止部分网站显示广告 Android 设置中为 Chrome 启用这些权限。 @@ -83,16 +87,19 @@ 后台同步 要清除网站存储数据吗? 返回 +前进 允许网站播放受保护的内容(推荐) 复制链接 已下载 ,总大小不明 标题 允许特定网站使用 Cookie。 +下一曲 网站设置 收到通知时设备会振动 打开“设置” 已屏蔽 允许 +某个网站正在播放媒体内容 由 Google 提供的精简版网页 弹出式窗口和重定向 允许网站播放受保护内容。 @@ -107,6 +114,7 @@ 撤消设备的所有权限 若要允许 Chrome 使用 AR 功能,您还需在 Android 设置中开启摄像头。 前进 +分享方式 允许 允许网站使用动态传感器(推荐) 使用情况 @@ -163,7 +171,9 @@ 清除 正在下载内容 下移 +向下滑动即可关闭。 阻止第三方网站保存和读取 Cookie 数据 +播放 选择内容 此设备无法读取 NFC 数据 已删除 @@ -197,6 +207,7 @@ 有一个网站正在使用您的摄像头 您将会退出此网站。 Cookie 是由您访问过的网站创建的文件,供网站用来记住您的偏好;第三方 Cookie 是由其他网站创建而成,这些网站是您在所访问网页上看到的部分内容(比如广告或图片)的所有方。 +正在播放媒体 取消选择 您将会自动退出所有网站。 允许特定网站播放声音。 @@ -204,6 +215,7 @@ 确定要清除该网站的所有本地数据(包括 Cookie)并重置该网站的所有权限吗? 已对此设备停用 下载失败 +无法选择证书。 此内容来自 (由 Google 提供)。 已静音的网站 确认 @@ -216,6 +228,8 @@ 有一个网站正在使用您的摄像头和麦克风 此网站会展示侵扰性或误导性广告 点按即可返回到网站 +更改 +后退 撤消设备权限 网站加载速度很快 禁止网站连接到设备 @@ -236,6 +250,7 @@ 阻止特定网站使用 Cookie。 无法访问 由您父母管理 +蓝牙 使用您的麦克风 屏蔽会展示侵扰性或误导性广告的网站(推荐) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb index 45c6f7b1c81..03756536183 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-HK.xtb @@ -10,6 +10,7 @@ 下一個 嵌入至 停止 +Google 已優化此網頁的圖片,以節省數據用量。 網站儲存的檔案會在這裡顯示 - 已新增網站 @@ -42,6 +43,7 @@ 在允許網站建立您身處環境的 3D 地圖或追蹤攝錄機位置前先詢問您 (建議) 允許 Cookie 在網站要探索附近的藍牙裝置時詢問您 (建議) +作業系統不支援由客戶端選取憑證。 繼續 MB 下載完成 @@ -61,10 +63,12 @@ 已複製 確定要重設「」的所有網站權限嗎? 網站 +上一首曲目 網站播放受保護內容前先詢問您 允許網站儲存及讀取 Cookie 資料 (建議) 憑證檢視者 這會將網站儲存空間的 資料全部清除。 +已自動封鎖 顯示資料 在部分網站上設定封鎖 已在「Android 設定」中為 Chrome 開啟權限。 @@ -83,16 +87,19 @@ 背景同步處理 要清除網站儲存空間的資料嗎? 返回 +向前快轉 允許網站播放受保護的內容 (建議) 複製連結 已下載:,總大小:不明 標題 允許特定網站存取 Cookie。 +下一首曲目 網站設定 裝置會在收到通知時震動 開啟設定 瀏覽網址被封鎖: 已允許 +網站正在播放媒體 此簡易版網頁由 Google 提供 彈出式視窗和重新導向 允許網站播放受保護的內容 @@ -107,6 +114,7 @@ 撤銷裝置所有的權限 如要讓 Chrome 使用 AR,請在「Android 設定」中開啟相機。 往前 +共用方式 允許 允許網站存取動作感應器 (建議) 用量 @@ -163,7 +171,9 @@ 清除 正在下載的項目 下移 +向下掃就可以閂咗佢。 禁止第三方網站儲存及讀取 Cookie 資料 +播放 選取項目 此裝置無法讀取 NFC 已刪除 @@ -197,6 +207,7 @@ 有網站正在使用您的相機 您將會從這個網站登出。 Cookie 是您瀏覽的網站所建立的檔案,網站會使用這些檔案記住您的偏好設定,第三方 Cookie 則由其他網站建立。這些網站擁有您所瀏覽網頁的部分內容,例如廣告和圖片。 +正在播放媒體 取消選取 您將會從所有網站登出。 允許特定網站播放音效。 @@ -204,6 +215,7 @@ 您確定要清除這個網站的所有本機資料 (包括 Cookie),然後重設其所有權限嗎? 已為這部裝置關閉 下載失敗 +無法選擇憑證。 此內容來自 ,由 Google 提供。 已設為靜音的網站 確定 @@ -216,6 +228,8 @@ 有網站正在使用您的相機和麥克風 此網站顯示滋擾性或誤導廣告 輕按即可返回網站 +變更 +向後回轉 撤消裝置權限 網站快速 禁止網站連接裝置 @@ -236,6 +250,7 @@ 禁止特定網站存取 Cookie。 無法存取瀏覽網址: 由您的家長管理 +藍牙 存取您的麥克風 封鎖顯示滋擾性或誤導廣告網站上的廣告 (建議) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-TW.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-TW.xtb index 53d66504c81..ca1de8d8405 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-TW.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zh-TW.xtb @@ -10,6 +10,7 @@ 繼續 嵌入至 停止 +Google 已將這個頁面的圖片進行最佳化處理,為你節省資料用量。 網站儲存的檔案會顯示在這裡 - 已新增 網站 @@ -42,6 +43,7 @@ 網站必須先詢問你,才能根據你的周遭環境建立 3D 地圖或追蹤攝影機位置 (建議) 允許 Cookie 當網站要搜尋附近的藍牙裝置時,必須先詢問你 (建議) +作業系統不允許您在用戶端選取憑證。 繼續 MB 下載完成 @@ -61,10 +63,12 @@ 已複製 確定要重設「」的所有網站權限嗎? 網站 +上一首曲目 允許網站播放受保護的內容前,必須先詢問你 允許網站儲存及讀取 Cookie 資料 (建議) 憑證檢視器 這會將網站儲存的資料全部清除 (共 )。 +已自動封鎖 顯示資訊 在某些網站上設定封鎖 請在 Android 設定中為 Chrome 啟用這些權限。 @@ -83,16 +87,19 @@ 背景同步處理 要清除網站儲存的資料嗎? 返回 +快轉到特定的播放時間點 允許網站播放受保護的內容 (建議) 複製連結 已下載:,總大小:不明 標題 允許特定網站的 Cookie。 +下一首曲目 網站設定 收到通知時裝置會震動 開啟設定 瀏覽的網址已封鎖: 允許 +一個網站正在播放媒體內容 由 Google 提供的精簡版網頁 彈出式視窗與重新導向 允許網站播放受保護的內容 @@ -107,6 +114,7 @@ 撤銷裝置的所有權限 如要允許 Chrome 使用 AR,請一併開啟 Android 設定中的攝影機。 往前 +分享方式: 允許 允許網站存取動作感應器 (建議) 用量 @@ -163,7 +171,9 @@ 清除 正在下載的項目 下移 +向下滑動即可關閉。 禁止第三方網站儲存及讀取 Cookie 資料 +播放 選取項目 這部裝置無法讀取 NFC 已刪除 @@ -197,6 +207,7 @@ 有網站正在使用你的攝影機 系統會將你登出這個網站。 Cookie 是你造訪過的網站所建立的檔案,網站會使用這些檔案記住你的偏好設定。第三方 Cookie 由其他網站所建立。這類網站通常是在你造訪的網站上提供部分內容 (例如廣告或圖片) 的其他網站。 +正在播放媒體 全部取消選取 系統會將你登出所有網站。 允許特定網站播放音訊。 @@ -204,6 +215,7 @@ 確定要刪除這個網站儲存的所有本機資料 (包括 Cookie 在內),並重設這個網站的所有權限嗎? 已在這個裝置上關閉 下載失敗 +無法選取憑證。 這個內容來自 ,由 Google 所提供。 已設為靜音 確認 @@ -216,6 +228,8 @@ 有網站正在使用你的攝影機和麥克風 這個網站會顯示干擾性或誤導性的廣告 輕觸即可返回網站 +變更 +倒轉到特定的播放時間點 撤銷裝置權限 這個網站的載入速度很快 禁止網站連線至裝置 @@ -236,6 +250,7 @@ 封鎖特定網站的 Cookie。 瀏覽的網址無法存取: 你的家長已停用這項功能 +藍牙 存取您的麥克風 封鎖干擾性或誤導性的網站廣告 (建議) \ No newline at end of file diff --git a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zu.xtb b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zu.xtb index a7627d00562..9f089ae8998 100644 --- a/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zu.xtb +++ b/chromium/components/browser_ui/strings/android/translations/browser_ui_strings_zu.xtb @@ -10,6 +10,7 @@ Okulandelayo Ishumekwe ku- Misa +Ukuze ulondoloze idatha, izithombe zaleli khasi zizolungiselelwa yi-Google. Amafayela ngamawebhusayithi abonakala lapha , Isayithi le- lingeziwe @@ -42,6 +43,7 @@ Buza ngaphambi kokuvumela amasayithi ukudala imephu ye-3D yendawo ekuzungezile noma ukulandelela indawo yekhamera (kuyanconywa) Vumela amakhukhi Buza uma isayithi lifuna ukuthola amadivayisi aseduze e-Bluetooth (kunconyiwe) +Ukukhethwa kwesitifiketi sohlangothi lweklayenti akusekelwe yisistimu yokusebenza. Qhubeka MB Ukulanda kuqedile @@ -61,10 +63,12 @@ Kukopishiwe Ingabe uqinisekile ukuthi ufuna ukusetha kabusha zonke izimvume zesayithi le-? Isayithi +Ithrekhi yangaphambilini Buza ngaphambi kokuvumela amasayithi ukudlala okuqukethwe okuvikelwe Vumela amasayithi ukulondoloza nokufunda idatha yamakhukhi (kunconyiwe) Isibukeli sesitifiketi Lokhu kuzosula yonke i- yesitoreji sewebhusayithi. +Kuvinjelwe ngokuzenzakalelayo Khombisa ulwazi Kuvinjelwe kwamanye amasayithi Vula izimvume ze-Chrome kuzilungiselelo ze-Android. @@ -83,16 +87,19 @@ Ukuvumelanisa ngemuva Sula isitoreji sesayithi? Emuva +Funela phambili Vumela amasayithi ukudlala okuqukethwe okuvikelwe (kunconyiwe) Kopisha isixhumanisi / ? Isihloko Vumela amakhukhi esayithi elithile. +Ithrekhi elandelayo Izilungiselelo zesayithi Izaziso zingadlidliza idivayisi Vula izilungiselelo Ukuzulazula kuvinjiwe: Kuvunyelwe +Isayithi lidlala imidiya Ikhasi elilula linikezwe i-Google Okwesikhashana nokuqondiswa kabusha Vumela amasayithi ukudlala okuqukethwe okuvikelekile @@ -107,6 +114,7 @@ Buyisa zonke izimvume zedivayisi Vumela i-Chrome ukusebenzisa i-AR, futhi vula ikhamera kokuthi Izilungiselelo ze-Android. Iya phambili +Yabelana nge- Vumela Vumela amasayithi ukuthi afinyelele kuzinzwa zokunyakaza (kunconyiwe) Ukusetshenziswa @@ -163,7 +171,9 @@ Sula Ukulanda okusebenzayo Hambisa phansi +Swayiphela phansi ukuze uvale. Vimbela amawebhusayithi enkampani yangaphandle kusukela ekulondolozeni nasekufundeni idatha yamakhukhi +Dlala Khetha izinto Le divayisi ayikwazi ukufunda i-NFC Kususiwe @@ -197,6 +207,7 @@ Isayithi lisebenzisa ikhamera yakho Uzokhishwa ngemvume kule sayithi. Amakhukhi amafayela adalwe amawebhusayithi owavakashelayo. Amasayithi ayawasebenzisa ukukhumbula okuncamelayo. Amakhukhi wenkampani yangaphandle adalwa ngamanye amasayithi. Lamasayithi aphethe okunye okuqukethwe, njengezikhangiso noma izithombe, ozibona ekhasini lewebhu olivakashelayo. +Ukudlala imidiya Khansela ukukhetha Uzokhishwa ngemvume kuwo wonke amasayithi Vumela umsindo wesayithi elithile. @@ -204,6 +215,7 @@ Ingabe uqinisekile ukuthi ufuna ukususa yonke idatha yasendaweni, efaka amakhukhi, uphinde usethe kabusha zonke izimvume zale webhusayithi? Kuvaliwe kule divayisi Ukulanda kwehlulekile +Ayikwazi ukukhetha isitifiketi. Lokhu okuqukethwe kubuya ku-, kulethwa i-Google. Kuthulisiwe Qinisekisa @@ -216,6 +228,8 @@ Isayithi lisebenzisa ikhamera nemakrofoni yakho Leli sayithi libonisa izikhangiso ezingathandeki noma ezidukisayo Thepha ukubuyela kusayithi +Guqula +Funela emuva Buyisa imvume yedivayisi Isayithi liyashesha Vimbela amasayithi kusukela ekuxhumekeni kumadivayisi @@ -236,6 +250,7 @@ Vimbela amakhukhi kusayithi elithile. Ukuzulazula akufinyeleleki: Kuphethwa abazali bakho +I-Bluetooth Finyelela imakrofoni yakho Vimba uma isayithi libonisa izikhangiso ezingathandeki noma ezidukisayo (kunconyiwe) \ No newline at end of file diff --git a/chromium/components/browser_ui/styles/android/BUILD.gn b/chromium/components/browser_ui/styles/android/BUILD.gn index df181094a3d..f8d1a9e3184 100644 --- a/chromium/components/browser_ui/styles/android/BUILD.gn +++ b/chromium/components/browser_ui/styles/android/BUILD.gn @@ -11,6 +11,7 @@ android_library("java") { ":java_resources", "//base:base_java", "//third_party/android_deps:android_support_v7_appcompat_java", + "//third_party/android_deps:androidx_appcompat_appcompat_resources_java", ] } @@ -25,45 +26,78 @@ android_resources("java_resources") { "java/res/color/default_icon_color_tint_list.xml", "java/res/drawable-hdpi/ic_delete_white_24dp.png", "java/res/drawable-hdpi/ic_folder_blue_24dp.png", + "java/res/drawable-hdpi/ic_pause_white_24dp.png", + "java/res/drawable-hdpi/ic_pause_white_36dp.png", + "java/res/drawable-hdpi/ic_play_arrow_white_24dp.png", + "java/res/drawable-hdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-hdpi/ic_stop_white_36dp.png", "java/res/drawable-hdpi/ic_videocam_white_24dp.png", "java/res/drawable-hdpi/infobar_downloading.png", "java/res/drawable-hdpi/modern_toolbar_shadow.png", "java/res/drawable-hdpi/plus.png", "java/res/drawable-hdpi/settings_all_sites.png", + "java/res/drawable-hdpi/top_round.9.png", "java/res/drawable-mdpi/ic_delete_white_24dp.png", "java/res/drawable-mdpi/ic_folder_blue_24dp.png", + "java/res/drawable-mdpi/ic_pause_white_24dp.png", + "java/res/drawable-mdpi/ic_pause_white_36dp.png", + "java/res/drawable-mdpi/ic_play_arrow_white_24dp.png", + "java/res/drawable-mdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-mdpi/ic_stop_white_36dp.png", "java/res/drawable-mdpi/ic_videocam_white_24dp.png", "java/res/drawable-mdpi/infobar_downloading.png", "java/res/drawable-mdpi/modern_toolbar_shadow.png", "java/res/drawable-mdpi/plus.png", "java/res/drawable-mdpi/settings_all_sites.png", + "java/res/drawable-mdpi/top_round.9.png", + "java/res/drawable-night-hdpi/top_round.9.png", + "java/res/drawable-night-mdpi/top_round.9.png", + "java/res/drawable-night-xhdpi/top_round.9.png", + "java/res/drawable-night-xxhdpi/top_round.9.png", + "java/res/drawable-night-xxxhdpi/top_round.9.png", "java/res/drawable-xhdpi/ic_delete_white_24dp.png", "java/res/drawable-xhdpi/ic_folder_blue_24dp.png", + "java/res/drawable-xhdpi/ic_pause_white_24dp.png", + "java/res/drawable-xhdpi/ic_pause_white_36dp.png", + "java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png", + "java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-xhdpi/ic_stop_white_36dp.png", "java/res/drawable-xhdpi/ic_videocam_white_24dp.png", "java/res/drawable-xhdpi/infobar_downloading.png", "java/res/drawable-xhdpi/modern_toolbar_shadow.png", "java/res/drawable-xhdpi/plus.png", "java/res/drawable-xhdpi/settings_all_sites.png", + "java/res/drawable-xhdpi/top_round.9.png", "java/res/drawable-xxhdpi/ic_delete_white_24dp.png", "java/res/drawable-xxhdpi/ic_folder_blue_24dp.png", + "java/res/drawable-xxhdpi/ic_pause_white_24dp.png", + "java/res/drawable-xxhdpi/ic_pause_white_36dp.png", + "java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png", + "java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-xxhdpi/ic_stop_white_36dp.png", "java/res/drawable-xxhdpi/ic_videocam_white_24dp.png", "java/res/drawable-xxhdpi/infobar_downloading.png", "java/res/drawable-xxhdpi/modern_toolbar_shadow.png", "java/res/drawable-xxhdpi/plus.png", "java/res/drawable-xxhdpi/settings_all_sites.png", + "java/res/drawable-xxhdpi/top_round.9.png", "java/res/drawable-xxxhdpi/ic_delete_white_24dp.png", "java/res/drawable-xxxhdpi/ic_folder_blue_24dp.png", + "java/res/drawable-xxxhdpi/ic_pause_white_24dp.png", + "java/res/drawable-xxxhdpi/ic_pause_white_36dp.png", + "java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png", + "java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-xxxhdpi/ic_stop_white_36dp.png", "java/res/drawable-xxxhdpi/ic_videocam_white_24dp.png", "java/res/drawable-xxxhdpi/infobar_downloading.png", "java/res/drawable-xxxhdpi/plus.png", "java/res/drawable-xxxhdpi/settings_all_sites.png", + "java/res/drawable-xxxhdpi/top_round.9.png", "java/res/drawable/ic_help_and_feedback.xml", "java/res/drawable/ic_offline_pin_24dp_on_light_bg.xml", + "java/res/drawable/ic_security_grey.xml", + "java/res/drawable/ic_update_grey.xml", + "java/res/drawable/ic_vpn_key_grey.xml", "java/res/drawable/permission_location.xml", "java/res/values-night/colors.xml", "java/res/values/colors.xml", diff --git a/chromium/components/browser_ui/styles/android/java/res/color/default_icon_color_light_tint_list.xml b/chromium/components/browser_ui/styles/android/java/res/color/default_icon_color_light_tint_list.xml index 31e780762db..545e91cb0da 100644 --- a/chromium/components/browser_ui/styles/android/java/res/color/default_icon_color_light_tint_list.xml +++ b/chromium/components/browser_ui/styles/android/java/res/color/default_icon_color_light_tint_list.xml @@ -8,15 +8,11 @@ - - - - + + + + diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_24dp.png new file mode 100644 index 00000000000..1701f34b01c Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_36dp.png new file mode 100644 index 00000000000..caa2c77768c Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png new file mode 100644 index 00000000000..35f2e7f6df5 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 00000000000..29adeed052e Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/top_round.9.png new file mode 100644 index 00000000000..0b1d52afa0b Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-hdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_24dp.png new file mode 100644 index 00000000000..e1169e56ff6 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_36dp.png new file mode 100644 index 00000000000..1701f34b01c Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png new file mode 100644 index 00000000000..cc060f1a080 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 00000000000..35f2e7f6df5 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/top_round.9.png new file mode 100644 index 00000000000..20dfd1d6fbf Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-mdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-night-hdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-night-hdpi/top_round.9.png new file mode 100644 index 00000000000..25891a03726 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-night-hdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-night-mdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-night-mdpi/top_round.9.png new file mode 100644 index 00000000000..dfee2710b30 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-night-mdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-night-xhdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-night-xhdpi/top_round.9.png new file mode 100644 index 00000000000..aaad5198012 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-night-xhdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-night-xxhdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-night-xxhdpi/top_round.9.png new file mode 100644 index 00000000000..e167a10f58d Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-night-xxhdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-night-xxxhdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-night-xxxhdpi/top_round.9.png new file mode 100644 index 00000000000..f1d02de72ee Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-night-xxxhdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png new file mode 100644 index 00000000000..f49aed75711 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_36dp.png new file mode 100644 index 00000000000..7192ad487ea Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png new file mode 100644 index 00000000000..a3c80e73daa Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 00000000000..547ef30aacd Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/top_round.9.png new file mode 100644 index 00000000000..35ce64c4dda Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xhdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png new file mode 100644 index 00000000000..7192ad487ea Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_36dp.png new file mode 100644 index 00000000000..a03bad27edd Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png new file mode 100644 index 00000000000..547ef30aacd Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 00000000000..23bb1ba9f68 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/top_round.9.png new file mode 100644 index 00000000000..e88e33970cd Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxhdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png new file mode 100644 index 00000000000..660ac658589 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_36dp.png new file mode 100644 index 00000000000..3ea7e03e5dc Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png new file mode 100644 index 00000000000..be5c062b5fe Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 00000000000..2745c3ab929 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/top_round.9.png b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/top_round.9.png new file mode 100644 index 00000000000..3df273c1425 Binary files /dev/null and b/chromium/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/top_round.9.png differ diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable/ic_security_grey.xml b/chromium/components/browser_ui/styles/android/java/res/drawable/ic_security_grey.xml new file mode 100644 index 00000000000..3d022d7b088 --- /dev/null +++ b/chromium/components/browser_ui/styles/android/java/res/drawable/ic_security_grey.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable/ic_update_grey.xml b/chromium/components/browser_ui/styles/android/java/res/drawable/ic_update_grey.xml new file mode 100644 index 00000000000..84c22bde410 --- /dev/null +++ b/chromium/components/browser_ui/styles/android/java/res/drawable/ic_update_grey.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/chromium/components/browser_ui/styles/android/java/res/drawable/ic_vpn_key_grey.xml b/chromium/components/browser_ui/styles/android/java/res/drawable/ic_vpn_key_grey.xml new file mode 100644 index 00000000000..58bf849f60a --- /dev/null +++ b/chromium/components/browser_ui/styles/android/java/res/drawable/ic_vpn_key_grey.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/chromium/components/browser_ui/util/android/BUILD.gn b/chromium/components/browser_ui/util/android/BUILD.gn index 5b7a7d3e0a4..3e582066cba 100644 --- a/chromium/components/browser_ui/util/android/BUILD.gn +++ b/chromium/components/browser_ui/util/android/BUILD.gn @@ -10,6 +10,9 @@ android_library("java") { "java/src/org/chromium/components/browser_ui/util/ComposedBrowserControlsVisibilityDelegate.java", "java/src/org/chromium/components/browser_ui/util/ConversionUtils.java", "java/src/org/chromium/components/browser_ui/util/DownloadUtils.java", + "java/src/org/chromium/components/browser_ui/util/date/CalendarFactory.java", + "java/src/org/chromium/components/browser_ui/util/date/CalendarUtils.java", + "java/src/org/chromium/components/browser_ui/util/date/StringUtils.java", ] deps = [ @@ -17,6 +20,7 @@ android_library("java") { "//base:base_java", "//components/embedder_support/android:util_java", "//content/public/android:content_java", + "//third_party/android_deps:androidx_core_core_java", ] } @@ -44,5 +48,9 @@ java_library("junit") { "//base:base_java_test_support", "//base:base_junit_test_support", "//base/test:test_support_java", + "//content/public/android:content_java", + "//third_party/android_deps:robolectric_all_java", + "//third_party/junit", + "//third_party/mockito:mockito_java", ] } diff --git a/chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarFactory.java b/chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarFactory.java new file mode 100644 index 00000000000..af194842f39 --- /dev/null +++ b/chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarFactory.java @@ -0,0 +1,44 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.util.date; + +import org.chromium.base.task.AsyncTask; +import org.chromium.base.task.BackgroundOnlyAsyncTask; + +import java.util.Calendar; +import java.util.concurrent.ExecutionException; + +/** Helper class to simplify querying for a {@link Calendar} instance. */ +public final class CalendarFactory { + private static final AsyncTask sCalendarBuilder = + new CalendarBuilder().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + private CalendarFactory() {} + + /** + * + * @return A unique {@link Calendar} instance. This version will (1) not be handed out to any + * other caller and (2) will be completely reset. + */ + public static Calendar get() { + Calendar calendar = null; + try { + calendar = (Calendar) sCalendarBuilder.get().clone(); + } catch (InterruptedException | ExecutionException e) { + // We've tried our best. If AsyncTask really does not work, we give up. :( + calendar = Calendar.getInstance(); + } + + calendar.clear(); + return calendar; + } + + private static class CalendarBuilder extends BackgroundOnlyAsyncTask { + @Override + protected Calendar doInBackground() { + return Calendar.getInstance(); + } + } +} diff --git a/chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarUtils.java b/chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarUtils.java new file mode 100644 index 00000000000..8f22e69d090 --- /dev/null +++ b/chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/CalendarUtils.java @@ -0,0 +1,57 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.util.date; + +import java.util.Calendar; + +/** A set of utility methods meant to make interacting with a {@link Calendar} instance easier. */ +public final class CalendarUtils { + private static final class LazyHolder { + private static Calendar sCalendar1 = CalendarFactory.get(); + private static Calendar sCalendar2 = CalendarFactory.get(); + } + + private CalendarUtils() {} + + /** + * Helper method to return the start of the day according to the localized {@link Calendar}. + * This instance will have no hour, minute, second, etc. associated with it. Only year, month, + * and day of month. + * @param t The timestamp (in milliseconds) since epoch. + * @return A {@link Calendar} instance representing the beginning of the day denoted by + * {@code t}. + */ + public static Calendar getStartOfDay(long t) { + LazyHolder.sCalendar1.setTimeInMillis(t); + + int year = LazyHolder.sCalendar1.get(Calendar.YEAR); + int month = LazyHolder.sCalendar1.get(Calendar.MONTH); + int day = LazyHolder.sCalendar1.get(Calendar.DATE); + LazyHolder.sCalendar1.clear(); + LazyHolder.sCalendar1.set(year, month, day, 0, 0, 0); + + return LazyHolder.sCalendar1; + } + + /** + * Helper method determining whether or not two timestamps occur on the same localized calendar + * day. + * @param t1 A timestamp (in milliseconds) since epoch. + * @param t2 A timestamp (in milliseconds) since epoch. + * @return Whether or not {@code t1} and {@code t2} represent times on the same localized + * calendar day. + */ + public static boolean isSameDay(long t1, long t2) { + LazyHolder.sCalendar1.setTimeInMillis(t1); + LazyHolder.sCalendar2.setTimeInMillis(t2); + return isSameDay(LazyHolder.sCalendar1, LazyHolder.sCalendar2); + } + + /** @return Whether {@code cal1} and {@code cal2} have the same localized calendar day. */ + public static boolean isSameDay(Calendar cal1, Calendar cal2) { + return cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) + && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR); + } +} \ No newline at end of file diff --git a/chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/StringUtils.java b/chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/StringUtils.java new file mode 100644 index 00000000000..510d03d0fdc --- /dev/null +++ b/chromium/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/date/StringUtils.java @@ -0,0 +1,52 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.util.date; + +import android.content.Context; +import android.text.format.DateUtils; + +import org.chromium.base.ContextUtils; +import org.chromium.components.browser_ui.util.R; + +import java.util.Calendar; +import java.util.Date; + +/** Helper methods to deal with converting dates to various strings. */ +public class StringUtils { + private static final class LazyHolder { + private static Calendar sCalendar1 = CalendarFactory.get(); + private static Calendar sCalendar2 = CalendarFactory.get(); + } + + private StringUtils() {} + + /** + * Converts {@code date} into a string meant to be used as a list header. + * @param date The {@link Date} to convert. + * @return The {@link CharSequence} representing the header. + */ + public static CharSequence dateToHeaderString(Date date) { + Context context = ContextUtils.getApplicationContext(); + + LazyHolder.sCalendar1.setTimeInMillis(System.currentTimeMillis()); + LazyHolder.sCalendar2.setTime(date); + + StringBuilder builder = new StringBuilder(); + if (CalendarUtils.isSameDay(LazyHolder.sCalendar1, LazyHolder.sCalendar2)) { + builder.append(context.getString(R.string.today)).append(" - "); + } else { + LazyHolder.sCalendar1.add(Calendar.DATE, -1); + if (CalendarUtils.isSameDay(LazyHolder.sCalendar1, LazyHolder.sCalendar2)) { + builder.append(context.getString(R.string.yesterday)).append(" - "); + } + } + + builder.append(DateUtils.formatDateTime(context, date.getTime(), + DateUtils.FORMAT_ABBREV_WEEKDAY | DateUtils.FORMAT_ABBREV_MONTH + | DateUtils.FORMAT_SHOW_YEAR)); + + return builder; + } +} \ No newline at end of file diff --git a/chromium/components/browser_ui/webshare/OWNERS b/chromium/components/browser_ui/webshare/OWNERS new file mode 100644 index 00000000000..c880f89d823 --- /dev/null +++ b/chromium/components/browser_ui/webshare/OWNERS @@ -0,0 +1,5 @@ +ericwilligers@chromium.org +raymes@chromium.org + +# COMPONENT: Blink>WebShare +# OS: Android diff --git a/chromium/components/browser_ui/webshare/android/BUILD.gn b/chromium/components/browser_ui/webshare/android/BUILD.gn new file mode 100644 index 00000000000..05149697937 --- /dev/null +++ b/chromium/components/browser_ui/webshare/android/BUILD.gn @@ -0,0 +1,42 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_library("java") { + sources = [ + "java/src/org/chromium/components/browser_ui/webshare/BlobReceiver.java", + "java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java", + "java/src/org/chromium/components/browser_ui/webshare/SharedFileCollator.java", + ] + deps = [ + "//base:base_java", + "//components/browser_ui/share/android:java", + "//content/public/android:content_java", + "//mojo/public/java:system_java", + "//mojo/public/java/system:system_impl_java", + "//third_party/blink/public/mojom:android_mojo_bindings_java", + "//ui/android:ui_java", + "//url/mojom:url_mojom_gurl_java", + ] +} + +java_library("junit") { + # Skip platform checks since Robolectric depends on requires_android targets. + bypass_platform_checks = true + testonly = true + sources = [ + "java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java", + "java/src/org/chromium/components/browser_ui/webshare/SharedFileCollatorTest.java", + ] + deps = [ + ":java", + "//base:base_java", + "//base:base_java_test_support", + "//base:base_junit_test_support", + "//base/test:test_support_java", + "//third_party/blink/public/mojom:android_mojo_bindings_java", + "//third_party/junit", + ] +} diff --git a/chromium/components/browser_ui/webshare/android/DEPS b/chromium/components/browser_ui/webshare/android/DEPS new file mode 100644 index 00000000000..bb311370365 --- /dev/null +++ b/chromium/components/browser_ui/webshare/android/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+mojo/public/java/system", + "+content/public/android/java", + "+ui/android/java", +] diff --git a/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/BlobReceiver.java b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/BlobReceiver.java new file mode 100644 index 00000000000..e1008c3c68f --- /dev/null +++ b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/BlobReceiver.java @@ -0,0 +1,177 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.webshare; + +import org.chromium.base.Callback; +import org.chromium.base.Log; +import org.chromium.base.StreamUtil; +import org.chromium.blink.mojom.Blob; +import org.chromium.blink.mojom.BlobReaderClient; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.DataPipe; +import org.chromium.mojo.system.MojoException; +import org.chromium.mojo.system.MojoResult; +import org.chromium.mojo.system.Pair; +import org.chromium.mojo.system.ResultAnd; +import org.chromium.mojo.system.Watcher; +import org.chromium.mojo.system.impl.CoreImpl; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +/** + * Receives a blob over mojom and writes it to the stream. + */ +public class BlobReceiver implements BlobReaderClient { + private static final String TAG = "share"; + private static final int CHUNK_SIZE = 64 * 1024; + private static final int PIPE_CAPACITY = 2 * CHUNK_SIZE; + + private final ByteBuffer mBuffer; + private final OutputStream mOutputStream; + private long mMaximumContentSize; + private long mExpectedContentSize; + private long mReceivedContentSize; + private DataPipe.ConsumerHandle mConsumerHandle; + private Callback mCallback; + + /** + * Constructs a BlobReceiver. + * + * @param outputStream the destination for the blob contents. + * @param maximumContentSize the maximum permitted length of the blob. + */ + public BlobReceiver(OutputStream outputStream, long maximumContentSize) { + mBuffer = ByteBuffer.allocateDirect(CHUNK_SIZE); + mOutputStream = outputStream; + mMaximumContentSize = maximumContentSize; + } + + /** + * Initiates reading of the blob contents. + * + * @param blob the blob to read. + * @param callback the callback to call when reading is complete. + */ + public void start(Blob blob, Callback callback) { + mCallback = callback; + DataPipe.CreateOptions options = new DataPipe.CreateOptions(); + options.setElementNumBytes(1); + options.setCapacityNumBytes(PIPE_CAPACITY); + + Pair pipe = + CoreImpl.getInstance().createDataPipe(options); + mConsumerHandle = pipe.second; + blob.readAll(pipe.first, this); + } + + // Interface + @Override + public void close() {} + + // ConnectionErrorHandler + @Override + public void onConnectionError(MojoException e) { + if (mCallback == null) return; + reportError(e.getMojoResult(), "Connection error detected."); + } + + // BlobReaderClient + @Override + public void onCalculatedSize(long totalSize, long expectedContentSize) { + if (mCallback == null) return; + if (expectedContentSize > mMaximumContentSize) { + reportError(MojoResult.RESOURCE_EXHAUSTED, "Stream exceeds permitted size"); + return; + } + mExpectedContentSize = expectedContentSize; + if (mReceivedContentSize >= mExpectedContentSize) { + complete(); + return; + } + + Watcher watcher = CoreImpl.getInstance().getWatcher(); + watcher.start(mConsumerHandle, Core.HandleSignals.READABLE, new Watcher.Callback() { + @Override + public void onResult(int result) { + if (mCallback == null) return; + if (result == MojoResult.OK) { + read(); + } else { + reportError(result, "Watcher reported error."); + } + } + }); + } + + // BlobReaderClient + @Override + public void onComplete(int status, long dataLength) { + if (mCallback == null) return; + read(); + } + + private void read() { + try { + while (true) { + ResultAnd result = + mConsumerHandle.readData(mBuffer, DataPipe.ReadFlags.NONE); + + if (result.getMojoResult() == MojoResult.SHOULD_WAIT) return; + + if (result.getMojoResult() != MojoResult.OK) { + reportError(result.getMojoResult(), "Failed to read from blob."); + return; + } + + Integer bytesRead = result.getValue(); + if (bytesRead <= 0) { + reportError(MojoResult.SHOULD_WAIT, "No data available"); + return; + } + try { + mOutputStream.write(mBuffer.array(), mBuffer.arrayOffset(), bytesRead); + } catch (IOException e) { + reportError(MojoResult.DATA_LOSS, "Failed to write to stream."); + return; + } + mReceivedContentSize += bytesRead; + if (mReceivedContentSize >= mExpectedContentSize) { + if (mReceivedContentSize == mExpectedContentSize) { + complete(); + } else { + reportError( + MojoResult.OUT_OF_RANGE, "Received more bytes than expected size."); + } + return; + } + } + } catch (MojoException e) { + reportError(e.getMojoResult(), "Failed to receive blob."); + } + } + + private void complete() { + try { + mOutputStream.close(); + } catch (IOException e) { + reportError(MojoResult.CANCELLED, "Failed to close stream."); + return; + } + mCallback.onResult(MojoResult.OK); + mCallback = null; + } + + private void reportError(int result, String message) { + if (result == MojoResult.OK) { + result = MojoResult.INVALID_ARGUMENT; + } + Log.w(TAG, message); + StreamUtil.closeQuietly(mOutputStream); + mCallback.onResult(result); + mCallback = null; + } +} diff --git a/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java new file mode 100644 index 00000000000..dcdc8ddc798 --- /dev/null +++ b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImpl.java @@ -0,0 +1,290 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.webshare; + +import android.app.Activity; +import android.content.ComponentName; +import android.net.Uri; + +import androidx.annotation.Nullable; + +import org.chromium.base.CollectionUtil; +import org.chromium.base.ContentUriUtils; +import org.chromium.base.FileUtils; +import org.chromium.base.Log; +import org.chromium.base.metrics.RecordHistogram; +import org.chromium.base.task.AsyncTask; +import org.chromium.base.task.PostTask; +import org.chromium.base.task.TaskRunner; +import org.chromium.base.task.TaskTraits; +import org.chromium.components.browser_ui.share.ShareImageFileUtils; +import org.chromium.components.browser_ui.share.ShareParams; +import org.chromium.content_public.browser.WebContents; +import org.chromium.mojo.system.MojoException; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.url.mojom.Url; +import org.chromium.webshare.mojom.ShareError; +import org.chromium.webshare.mojom.ShareService; +import org.chromium.webshare.mojom.SharedFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Set; + +/** + * Android implementation of the ShareService service defined in + * third_party/blink/public/mojom/webshare/webshare.mojom. + */ +public class ShareServiceImpl implements ShareService { + private final WindowAndroid mWindow; + private final WebShareDelegate mDelegate; + + private static final String TAG = "share"; + + // These numbers are written to histograms. Keep in sync with WebShareMethod enum in + // histograms.xml, and don't reuse or renumber entries (except for the _COUNT entry). + private static final int WEBSHARE_METHOD_SHARE = 0; + // Count is technically 1, but recordEnumeratedHistogram requires a boundary of at least 2 + // (https://crbug.com/645032). + private static final int WEBSHARE_METHOD_COUNT = 2; + + // These numbers are written to histograms. Keep in sync with WebShareOutcome enum in + // histograms.xml, and don't reuse or renumber entries (except for the _COUNT entry). + private static final int WEBSHARE_OUTCOME_SUCCESS = 0; + private static final int WEBSHARE_OUTCOME_UNKNOWN_FAILURE = 1; + private static final int WEBSHARE_OUTCOME_CANCELED = 2; + private static final int WEBSHARE_OUTCOME_COUNT = 3; + + // These protect us if the renderer is compromised. + private static final int MAX_SHARED_FILE_COUNT = 10; + private static final int MAX_SHARED_FILE_BYTES = 50 * 1024 * 1024; + + // clang-format off + private static final Set PERMITTED_EXTENSIONS = + Collections.unmodifiableSet(CollectionUtil.newHashSet( + "bmp", // image/bmp / image/x-ms-bmp + "css", // text/css + "csv", // text/csv / text/comma-separated-values + "ehtml", // text/html + "flac", // audio/flac + "gif", // image/gif + "htm", // text/html + "html", // text/html + "ico", // image/x-icon + "jfif", // image/jpeg + "jpeg", // image/jpeg + "jpg", // image/jpeg + "m4a", // audio/x-m4a + "m4v", // video/mp4 + "mp3", // audio/mp3 + "mp4", // video/mp4 + "mpeg", // video/mpeg + "mpg", // video/mpeg + "oga", // audio/ogg + "ogg", // audio/ogg + "ogm", // video/ogg + "ogv", // video/ogg + "opus", // audio/ogg + "pjp", // image/jpeg + "pjpeg", // image/jpeg + "png", // image/png + "shtm", // text/html + "shtml", // text/html + "svg", // image/svg+xml + "svgz", // image/svg+xml + "text", // text/plain + "tif", // image/tiff + "tiff", // image/tiff + "txt", // text/plain + "wav", // audio/wav + "weba", // audio/webm + "webm", // video/webm + "webp", // image/webp + "xbm" // image/x-xbitmap + )); + + private static final Set PERMITTED_MIME_TYPES = + Collections.unmodifiableSet(CollectionUtil.newHashSet( + "audio/flac", + "audio/mp3", + "audio/ogg", + "audio/wav", + "audio/webm", + "audio/x-m4a", + "image/bmp", + "image/gif", + "image/jpeg", + "image/png", + "image/svg+xml", + "image/tiff", + "image/webp", + "image/x-icon", + "image/x-ms-bmp", + "image/x-xbitmap", + "text/comma-separated-values", + "text/css", + "text/csv", + "text/html", + "text/plain", + "video/mp4", + "video/mpeg", + "video/ogg", + "video/webm" + )); + // clang-format on + + private static final TaskRunner TASK_RUNNER = + PostTask.createSequencedTaskRunner(TaskTraits.USER_BLOCKING); + + /** Delegate class that provides embedder-specific functionality. */ + public interface WebShareDelegate { + /** + * @return true if sharing is currently possible. + */ + public boolean canShare(); + + /** + * Overridden by the embedder to execute the share. + * @param params the share data. + */ + public void share(ShareParams params); + } + + public ShareServiceImpl(@Nullable WebContents webContents, WebShareDelegate delegate) { + mWindow = webContents.getTopLevelNativeWindow(); + mDelegate = delegate; + } + + @Override + public void close() {} + + @Override + public void onConnectionError(MojoException e) {} + + @Override + public void share(String title, String text, Url url, final SharedFile[] files, + final ShareResponse callback) { + RecordHistogram.recordEnumeratedHistogram( + "WebShare.ApiCount", WEBSHARE_METHOD_SHARE, WEBSHARE_METHOD_COUNT); + + if (!mDelegate.canShare()) { + RecordHistogram.recordEnumeratedHistogram("WebShare.ShareOutcome", + WEBSHARE_OUTCOME_UNKNOWN_FAILURE, WEBSHARE_OUTCOME_COUNT); + callback.call(ShareError.INTERNAL_ERROR); + return; + } + + ShareParams.TargetChosenCallback innerCallback = new ShareParams.TargetChosenCallback() { + @Override + public void onTargetChosen(ComponentName chosenComponent) { + RecordHistogram.recordEnumeratedHistogram( + "WebShare.ShareOutcome", WEBSHARE_OUTCOME_SUCCESS, WEBSHARE_OUTCOME_COUNT); + callback.call(ShareError.OK); + } + + @Override + public void onCancel() { + RecordHistogram.recordEnumeratedHistogram( + "WebShare.ShareOutcome", WEBSHARE_OUTCOME_CANCELED, WEBSHARE_OUTCOME_COUNT); + callback.call(ShareError.CANCELED); + } + }; + + final ShareParams.Builder paramsBuilder = new ShareParams.Builder(mWindow, title, url.url) + .setText(text) + .setCallback(innerCallback); + if (files == null || files.length == 0) { + mDelegate.share(paramsBuilder.build()); + return; + } + + if (files.length > MAX_SHARED_FILE_COUNT) { + callback.call(ShareError.PERMISSION_DENIED); + return; + } + + for (SharedFile file : files) { + if (isDangerousFilename(file.name) || isDangerousMimeType(file.blob.contentType)) { + Log.i(TAG, + "Cannot share potentially dangerous \"" + file.blob.contentType + + "\" file \"" + file.name + "\"."); + callback.call(ShareError.PERMISSION_DENIED); + return; + } + } + + new AsyncTask() { + @Override + protected void onPostExecute(Boolean result) { + if (result.equals(Boolean.FALSE)) { + callback.call(ShareError.INTERNAL_ERROR); + } + } + + @Override + protected Boolean doInBackground() { + ArrayList fileUris = new ArrayList<>(files.length); + ArrayList blobReceivers = new ArrayList<>(files.length); + try { + File sharePath = ShareImageFileUtils.getSharedFilesDirectory(); + + if (!sharePath.exists() && !sharePath.mkdir()) { + throw new IOException("Failed to create directory for shared file."); + } + + for (int index = 0; index < files.length; ++index) { + File tempFile = File.createTempFile("share", + "." + FileUtils.getExtension(files[index].name), sharePath); + fileUris.add(ContentUriUtils.getContentUriFromFile(tempFile)); + blobReceivers.add(new BlobReceiver( + new FileOutputStream(tempFile), MAX_SHARED_FILE_BYTES)); + } + + } catch (IOException ie) { + Log.w(TAG, "Error creating shared file", ie); + return false; + } + + paramsBuilder.setFileContentType(SharedFileCollator.commonMimeType(files)); + paramsBuilder.setFileUris(fileUris); + SharedFileCollator collator = new SharedFileCollator(files.length, success -> { + if (success) { + mDelegate.share(paramsBuilder.build()); + } else { + callback.call(ShareError.INTERNAL_ERROR); + } + }); + + for (int index = 0; index < files.length; ++index) { + blobReceivers.get(index).start(files[index].blob.blob, collator); + } + return true; + } + }.executeOnTaskRunner(TASK_RUNNER); + } + + static boolean isDangerousFilename(String name) { + // Reject filenames without a permitted extension. + return name.indexOf('.') <= 0 + || !PERMITTED_EXTENSIONS.contains(FileUtils.getExtension(name)); + } + + static boolean isDangerousMimeType(String contentType) { + return !PERMITTED_MIME_TYPES.contains(contentType); + } + + @Nullable + private static Activity activityFromWebContents(@Nullable WebContents webContents) { + if (webContents == null) return null; + + WindowAndroid window = webContents.getTopLevelNativeWindow(); + if (window == null) return null; + + return window.getActivity().get(); + } +} diff --git a/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java new file mode 100644 index 00000000000..4c9d3359870 --- /dev/null +++ b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/ShareServiceImplTest.java @@ -0,0 +1,84 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.webshare; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; + +/** + * Unit tests for {@link ShareServiceImpl}. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class ShareServiceImplTest { + @Test + @SmallTest + public void testExtensionFormatting() { + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("foo/bar.txt")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("foo\\bar\u03C0.txt")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo\\bar.tx\u03C0t")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("https://example.com/a/b.html")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foo/bar.txt/")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("foobar.tx\\t")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("hello")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("hellotxt")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename(".txt")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("https://example.com/a/.txt")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("/.txt")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("..")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename(".hello.txt")); + } + + @Test + @SmallTest + public void testExecutable() { + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("application.apk")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("application.dex")); + Assert.assertTrue(ShareServiceImpl.isDangerousFilename("application.sh")); + } + + @Test + @SmallTest + public void testContent() { + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("diagram.svg")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("greeting.txt")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("movie.mpeg")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("photo.jpeg")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("recording.wav")); + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("statistics.csv")); + } + + @Test + @SmallTest + public void testCompound() { + Assert.assertFalse(ShareServiceImpl.isDangerousFilename("powerless.sh.txt")); + } + + @Test + @SmallTest + public void testUnsupportedMime() { + Assert.assertTrue(ShareServiceImpl.isDangerousMimeType("application/x-shockwave-flash")); + Assert.assertTrue(ShareServiceImpl.isDangerousMimeType("image/wmf")); + Assert.assertTrue(ShareServiceImpl.isDangerousMimeType("text/calendar")); + Assert.assertTrue(ShareServiceImpl.isDangerousMimeType("video/H264")); + } + + @Test + @SmallTest + public void testSupportedMime() { + Assert.assertFalse(ShareServiceImpl.isDangerousMimeType("audio/wav")); + Assert.assertFalse(ShareServiceImpl.isDangerousMimeType("image/jpeg")); + Assert.assertFalse(ShareServiceImpl.isDangerousMimeType("image/svg+xml")); + Assert.assertFalse(ShareServiceImpl.isDangerousMimeType("text/csv")); + Assert.assertFalse(ShareServiceImpl.isDangerousMimeType("text/plain")); + Assert.assertFalse(ShareServiceImpl.isDangerousMimeType("video/mpeg")); + } +} diff --git a/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollator.java b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollator.java new file mode 100644 index 00000000000..268903ba59e --- /dev/null +++ b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollator.java @@ -0,0 +1,74 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.webshare; + +import org.chromium.base.Callback; +import org.chromium.base.task.PostTask; +import org.chromium.content_public.browser.UiThreadTaskTraits; +import org.chromium.mojo.system.MojoResult; +import org.chromium.webshare.mojom.SharedFile; + +/** + * Initiates the share dialog when all files have been received. + */ +public class SharedFileCollator implements Callback { + private static final String WILDCARD = "*/*"; + + private int mPending; + private Callback mCallback; + + /** + * Constructs a SharedFileCollator. + * + * @param params the share request to issue if blobs are successfully received. + * @param callback the callback to call if any blob is not successfully received. + */ + public SharedFileCollator(int pendingFileCount, Callback callback) { + mPending = pendingFileCount; + mCallback = callback; + + assert mPending > 0; + } + + /** + * Call with a MojoResult each time a blob has been received. + * + * @param result a MojoResult indicating if a blob was successfully received. + */ + @Override + public void onResult(final Integer result) { + if (mCallback == null) return; + + if (result == MojoResult.OK && --mPending > 0) return; + + final Callback callback = mCallback; + mCallback = null; + + PostTask.postTask( + UiThreadTaskTraits.DEFAULT, () -> { callback.onResult(result == MojoResult.OK); }); + } + + /** + * If the files have a common type and subtype, returns type / subtype + * Otherwise if the files have a common type, returns type / * + * Otherwise returns * / * + * + * @param files an array of files being shared. + */ + public static String commonMimeType(SharedFile[] files) { + if (files == null || files.length == 0) return WILDCARD; + String[] common = files[0].blob.contentType.split("/"); + if (common.length != 2) return WILDCARD; + for (int index = 1; index < files.length; ++index) { + String[] current = files[index].blob.contentType.split("/"); + if (current.length != 2) return WILDCARD; + if (!current[0].equals(common[0])) return WILDCARD; + if (!current[1].equals(common[1])) { + common[1] = "*"; + } + } + return common[0] + "/" + common[1]; + } +} diff --git a/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollatorTest.java b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollatorTest.java new file mode 100644 index 00000000000..3d1e9a2ebf1 --- /dev/null +++ b/chromium/components/browser_ui/webshare/android/java/src/org/chromium/components/browser_ui/webshare/SharedFileCollatorTest.java @@ -0,0 +1,108 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.browser_ui.webshare; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.blink.mojom.SerializedBlob; +import org.chromium.webshare.mojom.SharedFile; + +/** + * Unit tests for {@link SharedFileCollator}. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class SharedFileCollatorTest { + @Test + @SmallTest + public void testDissimilar() { + Assert.assertEquals("*/*", SharedFileCollator.commonMimeType(new SharedFile[0])); + Assert.assertEquals( + "*/*", SharedFileCollator.commonMimeType(createFiles("text/plain", "image/jpeg"))); + Assert.assertEquals("*/*", + SharedFileCollator.commonMimeType( + createFiles("video/mpeg", "video/ogg", "text/html"))); + } + + @Test + @SmallTest + public void testMalformed() { + Assert.assertEquals("*/*", SharedFileCollator.commonMimeType(createFiles("invalid"))); + Assert.assertEquals( + "*/*", SharedFileCollator.commonMimeType(createFiles("text/xml/svg", "text/xml"))); + Assert.assertEquals("*/*", + SharedFileCollator.commonMimeType(createFiles("image/webp", "image/webp/jpeg"))); + } + + @Test + @SmallTest + public void testApplication() { + Assert.assertEquals("application/*", + SharedFileCollator.commonMimeType( + createFiles("application/rtf", "application/x-bzip2"))); + } + + @Test + @SmallTest + public void testAudio() { + Assert.assertEquals("audio/*", + SharedFileCollator.commonMimeType(createFiles("audio/mp3", "audio/wav"))); + } + + @Test + @SmallTest + public void testImage() { + Assert.assertEquals( + "image/jpeg", SharedFileCollator.commonMimeType(createFiles("image/jpeg"))); + Assert.assertEquals("image/gif", + SharedFileCollator.commonMimeType( + createFiles("image/gif", "image/gif", "image/gif"))); + Assert.assertEquals("image/*", + SharedFileCollator.commonMimeType(createFiles("image/gif", "image/jpeg"))); + Assert.assertEquals("image/*", + SharedFileCollator.commonMimeType( + createFiles("image/gif", "image/gif", "image/jpeg"))); + } + + @Test + @SmallTest + public void testText() { + Assert.assertEquals("text/css", SharedFileCollator.commonMimeType(createFiles("text/css"))); + Assert.assertEquals( + "text/csv", SharedFileCollator.commonMimeType(createFiles("text/csv", "text/csv"))); + Assert.assertEquals("text/*", + SharedFileCollator.commonMimeType( + createFiles("text/csv", "text/html", "text/csv"))); + } + + @Test + @SmallTest + public void testVideo() { + Assert.assertEquals( + "video/webm", SharedFileCollator.commonMimeType(createFiles("video/webm"))); + Assert.assertEquals("video/*", + SharedFileCollator.commonMimeType(createFiles("video/mpeg", "video/webm"))); + Assert.assertEquals("video/*", + SharedFileCollator.commonMimeType( + createFiles("video/mpeg", "video/webm", "video/webm"))); + } + + private static SharedFile[] createFiles(String... mimeTypeList) { + SharedFile[] result = new SharedFile[mimeTypeList.length]; + for (int i = 0; i < mimeTypeList.length; ++i) { + SerializedBlob blob = new SerializedBlob(); + blob.contentType = mimeTypeList[i]; + result[i] = new SharedFile(); + result[i].blob = blob; + } + return result; + } +} diff --git a/chromium/components/browser_ui/widget/android/BUILD.gn b/chromium/components/browser_ui/widget/android/BUILD.gn index 2eaf6be1599..0d95a17fd7f 100644 --- a/chromium/components/browser_ui/widget/android/BUILD.gn +++ b/chromium/components/browser_ui/widget/android/BUILD.gn @@ -101,6 +101,7 @@ android_library("java") { "//components/embedder_support/android:util_java", "//third_party/android_deps:android_support_v4_java", "//third_party/android_deps:android_support_v7_appcompat_java", + "//third_party/android_deps:androidx_appcompat_appcompat_resources_java", "//third_party/android_deps:androidx_interpolator_interpolator_java", "//third_party/android_deps:androidx_recyclerview_recyclerview_java", "//ui/android:ui_java", @@ -235,9 +236,11 @@ android_library("javatests") { "//base:base_java_test_support", "//content/public/test/android:content_java_test_support", "//third_party/android_deps:androidx_appcompat_appcompat_java", + "//third_party/android_deps:androidx_appcompat_appcompat_resources_java", "//third_party/android_deps:androidx_core_core_java", "//third_party/android_support_test_runner:rules_java", "//third_party/android_support_test_runner:runner_java", + "//third_party/hamcrest:hamcrest_java", "//third_party/junit", "//ui/android:ui_java", "//ui/android:ui_java_test_support", @@ -284,5 +287,8 @@ java_library("junit") { "//base:base_java_test_support", "//base:base_junit_test_support", "//base/test:test_support_java", + "//third_party/android_deps:robolectric_all_java", + "//third_party/junit", + "//third_party/mockito:mockito_java", ] } diff --git a/chromium/components/browser_ui/widget/android/java/res/layout/promo_dialog_layout.xml b/chromium/components/browser_ui/widget/android/java/res/layout/promo_dialog_layout.xml index 7008991f559..1597e8a35b6 100644 --- a/chromium/components/browser_ui/widget/android/java/res/layout/promo_dialog_layout.xml +++ b/chromium/components/browser_ui/widget/android/java/res/layout/promo_dialog_layout.xml @@ -63,6 +63,7 @@ android:id="@+id/header" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:textDirection="locale" android:layout_marginTop="@dimen/dialog_header_margin" android:layout_marginBottom="@dimen/dialog_header_margin" android:textAppearance="@style/TextAppearance.Headline.Primary" /> @@ -71,6 +72,7 @@ android:id="@+id/subheader" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:textDirection="locale" app:leading="@dimen/text_size_medium_leading" style="@style/TextAppearance.TextMedium.Secondary" /> diff --git a/chromium/components/browser_ui/widget/android/java/res/values/dimens.xml b/chromium/components/browser_ui/widget/android/java/res/values/dimens.xml index bb87b51078a..6b5699af985 100644 --- a/chromium/components/browser_ui/widget/android/java/res/values/dimens.xml +++ b/chromium/components/browser_ui/widget/android/java/res/values/dimens.xml @@ -16,7 +16,7 @@ 600dp 14dp - 24dp + 8dp 150dp 16dp 16dp diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/DualControlLayoutTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/DualControlLayoutTest.java index 81d29b61223..3fb1559fe9e 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/DualControlLayoutTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/DualControlLayoutTest.java @@ -7,7 +7,6 @@ package org.chromium.components.browser_ui.widget; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.SmallTest; import android.support.test.rule.UiThreadTestRule; import android.view.LayoutInflater; import android.view.View; @@ -17,6 +16,8 @@ import android.widget.Button; import android.widget.FrameLayout; import android.widget.Space; +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Before; import org.junit.Rule; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/InsetObserverViewTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/InsetObserverViewTest.java index eec6e7e9303..f432e57c7fb 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/InsetObserverViewTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/InsetObserverViewTest.java @@ -12,10 +12,11 @@ import static org.mockito.Mockito.verify; import android.app.Activity; import android.content.Context; import android.graphics.Rect; -import android.support.test.filters.SmallTest; import android.view.WindowInsets; import android.widget.LinearLayout; +import androidx.test.filters.SmallTest; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java index 150fe66426f..80e15e276f3 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java @@ -8,13 +8,14 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.app.Activity; -import android.support.test.filters.MediumTest; -import android.support.test.filters.SmallTest; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.TextView; +import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialog.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialog.java index c7eb7c979bb..56cf042fdd9 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialog.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialog.java @@ -49,6 +49,12 @@ public abstract class PromoDialog extends AlwaysDismissedDialog /** Resource ID of the String to show as the promo title. */ public int headerStringResource; + /** + * Optional: CharSequence to show as promo title. + * This parameter takes precedence over {@link #headerStringResoruce} + */ + public CharSequence headerCharSequence; + /** * Optional: CharSequence to show as descriptive text. * This parameter takes precedence over {@link #subheaderStringResoruce} @@ -67,6 +73,12 @@ public abstract class PromoDialog extends AlwaysDismissedDialog /** Optional: Resource ID of the String to show on the primary/ok button. */ public int primaryButtonStringResource; + /** + * Optional: CharSequence to show on the primary/ok button. + * This parameter takes precedence over {@link #primaryButtonStringResource} + */ + public CharSequence primaryButtonCharSequence; + /** Optional: Resource ID of the String to show on the secondary/cancel button. */ public int secondaryButtonStringResource; } @@ -74,7 +86,7 @@ public abstract class PromoDialog extends AlwaysDismissedDialog private static final int[] CLICKABLE_BUTTON_IDS = {R.id.button_primary, R.id.button_secondary}; private final FrameLayout mScrimView; - private final PromoDialogLayout mDialogLayout; + private PromoDialogLayout mDialogLayout; protected PromoDialog(Activity activity) { super(activity, R.style.PromoDialog); @@ -85,7 +97,6 @@ public abstract class PromoDialog extends AlwaysDismissedDialog LayoutInflater.from(activity).inflate(R.layout.promo_dialog_layout, mScrimView, true); mDialogLayout = (PromoDialogLayout) mScrimView.findViewById(R.id.promo_dialog_layout); - mDialogLayout.initialize(getDialogParams()); } /** @@ -112,6 +123,8 @@ public abstract class PromoDialog extends AlwaysDismissedDialog super.onCreate(savedInstanceState); setContentView(mScrimView); + mDialogLayout.initialize(getDialogParams()); + // Force the window to allow the dialog contents be as wide as necessary. getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogLayout.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogLayout.java index d9fc7165c1c..600212068de 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogLayout.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogLayout.java @@ -81,8 +81,8 @@ public final class PromoDialogLayout extends BoundedLinearLayout { /** Initializes the dialog contents using the given params. Should only be called once. */ void initialize(DialogParams params) { assert mParams == null && params != null; - assert params.headerStringResource != 0; - assert params.primaryButtonStringResource != 0; + assert params.headerStringResource != 0 || params.headerCharSequence != null; + assert params.primaryButtonStringResource != 0 || params.primaryButtonCharSequence != null; mParams = params; if (mParams.drawableInstance != null) { @@ -99,7 +99,11 @@ public final class PromoDialogLayout extends BoundedLinearLayout { } // Create the header. - mHeaderView.setText(mParams.headerStringResource); + if (mParams.headerCharSequence != null) { + mHeaderView.setText(mParams.headerCharSequence); + } else { + mHeaderView.setText(mParams.headerStringResource); + } // Set up the subheader text. if (mParams.subheaderCharSequence != null) { @@ -124,7 +128,9 @@ public final class PromoDialogLayout extends BoundedLinearLayout { // Create the buttons. DualControlLayout buttonBar = (DualControlLayout) findViewById(R.id.button_bar); - String primaryString = getResources().getString(mParams.primaryButtonStringResource); + String primaryString = mParams.primaryButtonCharSequence != null + ? mParams.primaryButtonCharSequence.toString() + : getResources().getString(mParams.primaryButtonStringResource); buttonBar.addView( DualControlLayout.createButtonForLayout(getContext(), true, primaryString, null)); diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java index df463c2ca1e..0246900b7d4 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java @@ -7,7 +7,6 @@ package org.chromium.components.browser_ui.widget; import android.app.Activity; import android.content.DialogInterface; import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; import android.text.method.LinkMovementMethod; import android.view.View; import android.view.View.MeasureSpec; @@ -16,6 +15,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.core.view.ViewCompat; +import androidx.test.filters.SmallTest; import org.junit.Assert; import org.junit.Test; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java index 4c9eab9f127..c2ac17da022 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java @@ -7,12 +7,13 @@ package org.chromium.components.browser_ui.widget; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.SmallTest; import android.support.test.rule.UiThreadTestRule; import android.view.View; import android.view.ViewGroup.MarginLayoutParams; import android.widget.RadioButton; +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Before; import org.junit.Rule; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java index 9c379ac4673..54a2c38801e 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonRenderTest.java @@ -6,10 +6,11 @@ package org.chromium.components.browser_ui.widget; import android.app.Activity; import android.graphics.Color; -import android.support.test.filters.SmallTest; import android.view.LayoutInflater; import android.view.View; +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -41,8 +42,7 @@ public class RadioButtonRenderTest extends DummyUiActivityTestCase { new NightModeTestUtils.NightModeParams().getParameters(); @Rule - public RenderTestRule mRenderTestRule = - new RenderTestRule("chrome/test/data/android/render_tests"); + public RenderTestRule mRenderTestRule = new RenderTestRule(); private RadioButtonWithDescriptionLayout mLayout; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayoutTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayoutTest.java index abbb5974759..c831fb5f45d 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayoutTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionLayoutTest.java @@ -7,7 +7,6 @@ package org.chromium.components.browser_ui.widget; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.SmallTest; import android.support.test.rule.UiThreadTestRule; import android.text.TextUtils; import android.view.LayoutInflater; @@ -16,6 +15,8 @@ import android.view.ViewGroup.MarginLayoutParams; import android.widget.EditText; import android.widget.TextView; +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Before; import org.junit.Rule; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java index 28e78008db2..66d0d8d9bfa 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java @@ -6,7 +6,6 @@ package org.chromium.components.browser_ui.widget; import android.app.Activity; import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; import android.text.InputType; import android.text.TextUtils; import android.view.KeyEvent; @@ -17,6 +16,9 @@ import android.widget.EditText; import android.widget.RadioButton; import android.widget.TextView; +import androidx.test.filters.SmallTest; + +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -266,14 +268,11 @@ public class RadioButtonWithEditTextTest extends DummyUiActivityTestCase { } private void assertIsKeyboardShowing(boolean isShowing) { - CriteriaHelper.pollUiThread( - new Criteria("Keyboard visibility does not consist with test setting.") { - @Override - public boolean isSatisfied() { - return KeyboardVisibilityDelegate.getInstance().isKeyboardShowing( - mActivity, mEditText) - == isShowing; - } - }); + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat("Keyboard visibility does not consist with test setting.", + KeyboardVisibilityDelegate.getInstance().isKeyboardShowing( + mActivity, mEditText), + Matchers.is(isShowing)); + }); } } diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGeneratorTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGeneratorTest.java index 4a20edde91b..8a7f4f809b5 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGeneratorTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGeneratorTest.java @@ -7,7 +7,8 @@ package org.chromium.components.browser_ui.widget; import android.content.Context; import android.graphics.Color; import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; + +import androidx.test.filters.SmallTest; import org.junit.Assert; import org.junit.Before; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayoutTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayoutTest.java index 2b5a03bc0af..854b7da4ad1 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayoutTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayoutTest.java @@ -6,11 +6,11 @@ package org.chromium.components.browser_ui.widget; import android.content.Context; import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.util.LayoutDirection; import android.view.View; import android.view.View.MeasureSpec; +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -525,7 +525,7 @@ public class WrappingLayoutTest { ViewExpectation expectationB, ViewExpectation expectationC) { WrappingLayoutSubclass layout = WrappingLayoutSubclass.create( mContext, leftTopPadding, bottomRightPadding, spacing); - layout.setLayoutDirection(LayoutDirection.RTL); + layout.setLayoutDirection(View.LAYOUT_DIRECTION_RTL); layout.addTestViews(margin); layout.layoutAtSize(width, height, specWidth, specHeight, 0, 0); diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/highlight/ViewHighlighterTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/highlight/ViewHighlighterTest.java index 89022527e1a..1413dcca40a 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/highlight/ViewHighlighterTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/highlight/ViewHighlighterTest.java @@ -8,11 +8,12 @@ import android.content.Context; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.support.test.InstrumentationRegistry; -import android.support.test.filters.MediumTest; import android.support.test.rule.UiThreadTestRule; import android.view.View; import android.widget.ImageView; +import androidx.test.filters.MediumTest; + import org.junit.Assert; import org.junit.Before; import org.junit.Rule; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/image_tiles/TileCoordinatorImpl.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/image_tiles/TileCoordinatorImpl.java index bf599b35094..81e7e318a69 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/image_tiles/TileCoordinatorImpl.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/image_tiles/TileCoordinatorImpl.java @@ -40,7 +40,7 @@ class TileCoordinatorImpl implements ImageTileCoordinator { for (int i = 0; i < mModel.size(); i++) { oldTiles.add(mModel.get(i)); } - boolean shouldAnimate = !oldTiles.isEmpty() && !oldTiles.equals(tiles); + boolean shouldAnimate = !oldTiles.isEmpty() && !tiles.isEmpty() && !oldTiles.equals(tiles); mModel.set(tiles); mView.scrollToBeginning(); diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/image_tiles/TileListView.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/image_tiles/TileListView.java index 9d1b4b8f2c2..4e7166f23f8 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/image_tiles/TileListView.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/image_tiles/TileListView.java @@ -8,6 +8,8 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; import android.view.View; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; @@ -58,7 +60,7 @@ class TileListView { mView.setItemAnimator(null); mLayoutAnimationController = AnimationUtils.loadLayoutAnimation(context, R.anim.image_grid_enter); - + configureAnimationListener(); mTileSizeSupplier = new TileSizeSupplier(context); PropertyModelChangeProcessor.create( @@ -86,7 +88,25 @@ class TileListView { * Called to show enter animation for the list items. */ void showAnimation(boolean animate) { - // TODO(shaktisahu): Fix animations. + if (animate) { + mView.setLayoutAnimation(mLayoutAnimationController); + mView.scheduleLayoutAnimation(); + } + } + + private void configureAnimationListener() { + mView.setLayoutAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) {} + + @Override + public void onAnimationEnd(Animation animation) { + mView.setLayoutAnimation(null); + } + + @Override + public void onAnimationRepeat(Animation animation) {} + }); } private class ItemDecorationImpl extends ItemDecoration { diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/ListMenuRenderTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/ListMenuRenderTest.java index 4fa1d04947c..ea9367e9b52 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/ListMenuRenderTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/ListMenuRenderTest.java @@ -44,8 +44,7 @@ public class ListMenuRenderTest extends DummyUiActivityTestCase { new NightModeTestUtils.NightModeParams().getParameters(); @Rule - public RenderTestRule mRenderTestRule = - new RenderTestRule("chrome/test/data/android/render_tests"); + public RenderTestRule mRenderTestRule = new RenderTestRule(); private View mView; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardCoordinatorTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardCoordinatorTest.java index 85bd7e6ac5d..be10a6832e7 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardCoordinatorTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardCoordinatorTest.java @@ -7,10 +7,10 @@ package org.chromium.components.browser_ui.widget.promo; import android.content.Context; import android.graphics.drawable.Drawable; import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; import android.view.View; import androidx.appcompat.content.res.AppCompatResources; +import androidx.test.filters.SmallTest; import org.junit.Assert; import org.junit.Before; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardImpressionTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardImpressionTest.java index 871b0594c6c..a911afaf2bf 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardImpressionTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardImpressionTest.java @@ -5,11 +5,12 @@ package org.chromium.components.browser_ui.widget.promo; import android.app.Activity; -import android.support.test.filters.SmallTest; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.FrameLayout; +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardViewRenderTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardViewRenderTest.java index 34c2aa3763e..857f5cd03b9 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardViewRenderTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/promo/PromoCardViewRenderTest.java @@ -6,14 +6,15 @@ package org.chromium.components.browser_ui.widget.promo; import android.app.Activity; import android.graphics.drawable.Drawable; -import android.support.test.filters.SmallTest; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import androidx.appcompat.content.res.AppCompatResources; +import androidx.test.filters.SmallTest; +import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -33,7 +34,6 @@ import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.test.util.DummyUiActivityTestCase; import org.chromium.ui.test.util.NightModeTestUtils; import org.chromium.ui.test.util.RenderTestRule; -import org.chromium.ui.widget.ButtonCompat; import java.util.List; @@ -48,8 +48,7 @@ public class PromoCardViewRenderTest extends DummyUiActivityTestCase { new NightModeTestUtils.NightModeParams().getParameters(); @Rule - public RenderTestRule mRenderTestRule = - new RenderTestRule("chrome/test/data/android/render_tests"); + public RenderTestRule mRenderTestRule = new RenderTestRule(); public PromoCardViewRenderTest(boolean nightModeEnabled) { NightModeTestUtils.setUpNightModeForDummyUiActivity(nightModeEnabled); @@ -110,11 +109,12 @@ public class PromoCardViewRenderTest extends DummyUiActivityTestCase { mModel.set(PromoCardProperties.HAS_SECONDARY_BUTTON, false); setPromoCard(LayoutStyle.LARGE); - CriteriaHelper.pollUiThread(Criteria.equals(View.GONE, () -> { - ButtonCompat secondaryButton = - mPromoCardCoordinator.getView().findViewById(R.id.promo_secondary_button); - return secondaryButton.getVisibility(); - })); + CriteriaHelper.pollUiThread(() -> { + int visibility = mPromoCardCoordinator.getView() + .findViewById(R.id.promo_secondary_button) + .getVisibility(); + Criteria.checkThat(visibility, Matchers.is(View.GONE)); + }); mRenderTestRule.render(mPromoCardCoordinator.getView(), "promo_card_secondary_hidden"); } diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimTest.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimTest.java index 95dea15976a..6f01c2aa11a 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimTest.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimTest.java @@ -13,13 +13,14 @@ import static org.junit.Assert.assertTrue; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; -import android.support.test.filters.SmallTest; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.ColorInt; +import androidx.test.filters.SmallTest; +import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,6 +31,7 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.Feature; +import org.chromium.content_public.browser.test.util.Criteria; import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.test.util.DummyUiActivityTestCase; @@ -103,10 +105,10 @@ public class ScrimTest extends DummyUiActivityTestCase { ThreadUtils.runOnUiThreadBlocking(() -> mScrimCoordinator.hideScrim(false)); - CriteriaHelper.pollUiThread(() - -> mScrimCoordinator.getViewForTesting() == null, - "Scrim should be null after being hidden.", CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL, - CriteriaHelper.DEFAULT_POLLING_INTERVAL); + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat("Scrim should be null after being hidden.", + mScrimCoordinator.getViewForTesting(), Matchers.nullValue()); + }); } @Test @@ -215,10 +217,10 @@ public class ScrimTest extends DummyUiActivityTestCase { ThreadUtils.runOnUiThreadBlocking(() -> mScrimCoordinator.hideScrim(false)); - CriteriaHelper.pollUiThread(() - -> mScrimCoordinator.getViewForTesting() == null, - "Scrim should be null after being hidden.", CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL, - CriteriaHelper.DEFAULT_POLLING_INTERVAL); + CriteriaHelper.pollUiThread(() -> { + Criteria.checkThat("Scrim should be null after being hidden.", + mScrimCoordinator.getViewForTesting(), Matchers.nullValue()); + }); } @Test diff --git a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/text/AlertDialogEditText.java b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/text/AlertDialogEditText.java index 9cffe2c5911..9620c8fad8b 100644 --- a/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/text/AlertDialogEditText.java +++ b/chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/text/AlertDialogEditText.java @@ -4,70 +4,50 @@ package org.chromium.components.browser_ui.widget.text; +import android.annotation.SuppressLint; import android.content.Context; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.os.Build; +import android.text.TextUtils; import android.util.AttributeSet; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuItem; +import android.view.ViewStructure; import android.widget.EditText; import androidx.appcompat.widget.AppCompatEditText; import org.chromium.base.ApiCompatibilityUtils; -import org.chromium.components.browser_ui.widget.R; +import org.chromium.base.annotations.VerifiesOnO; /** - * EditText to use in AlertDialog needed due to b/20882793 and b/122113958. This class should be - * removed when we roll to AppCompat with a fix for both issues. + * Wrapper class needed due to b/122113958. * * Note that for password fields the hint text is expected to be set in XML so that it is available - * during inflation. If the hint text or content description is changed programatically, consider + * during inflation. If the hint text or content description is changed programmatically, consider * calling {@link ApiCompatibilityUtils#setPasswordEditTextContentDescription(EditText)} after * the change. */ +@VerifiesOnO public class AlertDialogEditText extends AppCompatEditText { + private String mUrl; + public AlertDialogEditText(Context context, AttributeSet attrs) { super(context, attrs); } + public void setUrl(String url) { + mUrl = url; + } + @Override protected void onFinishInflate() { super.onFinishInflate(); - ApiCompatibilityUtils.setPasswordEditTextContentDescription(this); + } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) return; - - setCustomSelectionActionModeCallback(new ActionMode.Callback() { - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return true; - } - - @Override - public void onDestroyActionMode(ActionMode mode) {} - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - for (int i = 0; i < menu.size(); i++) { - MenuItem item = menu.getItem(i); - Drawable icon = item.getIcon(); - if (icon == null) break; - icon.setColorFilter(ApiCompatibilityUtils.getColor( - getResources(), R.color.default_icon_color), - PorterDuff.Mode.SRC_IN); - item.setIcon(icon); - } - return true; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - return false; - } - }); + @Override + @SuppressLint("NewApi") + public void onProvideAutofillStructure(ViewStructure structure, int flags) { + if (!TextUtils.isEmpty(mUrl)) { + structure.setWebDomain(mUrl); + } + super.onProvideAutofillStructure(structure, flags); } } -- cgit v1.2.1