| // Copyright 2022 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 "ash/accessibility/accessibility_controller_impl.h" |
| #include "ash/accessibility/autoclick/autoclick_controller.h" |
| #include "ash/capture_mode/capture_mode_bar_view.h" |
| #include "ash/capture_mode/capture_mode_button.h" |
| #include "ash/capture_mode/capture_mode_camera_controller.h" |
| #include "ash/capture_mode/capture_mode_camera_preview_view.h" |
| #include "ash/capture_mode/capture_mode_constants.h" |
| #include "ash/capture_mode/capture_mode_controller.h" |
| #include "ash/capture_mode/capture_mode_menu_group.h" |
| #include "ash/capture_mode/capture_mode_metrics.h" |
| #include "ash/capture_mode/capture_mode_session.h" |
| #include "ash/capture_mode/capture_mode_session_test_api.h" |
| #include "ash/capture_mode/capture_mode_settings_test_api.h" |
| #include "ash/capture_mode/capture_mode_settings_view.h" |
| #include "ash/capture_mode/capture_mode_test_util.h" |
| #include "ash/capture_mode/capture_mode_toast_controller.h" |
| #include "ash/capture_mode/capture_mode_toggle_button.h" |
| #include "ash/capture_mode/capture_mode_types.h" |
| #include "ash/capture_mode/capture_mode_util.h" |
| #include "ash/capture_mode/fake_camera_device.h" |
| #include "ash/capture_mode/fake_folder_selection_dialog_factory.h" |
| #include "ash/capture_mode/fake_video_source_provider.h" |
| #include "ash/capture_mode/test_capture_mode_delegate.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/display/window_tree_host_manager.h" |
| #include "ash/public/cpp/capture_mode/capture_mode_test_api.h" |
| #include "ash/public/cpp/window_properties.h" |
| #include "ash/resources/vector_icons/vector_icons.h" |
| #include "ash/shell.h" |
| #include "ash/strings/grit/ash_strings.h" |
| #include "ash/style/ash_color_provider.h" |
| #include "ash/system/accessibility/autoclick_menu_bubble_controller.h" |
| #include "ash/system/unified/unified_system_tray.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/wm_event.h" |
| #include "base/bind.h" |
| #include "base/files/file_path.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/system/system_monitor.h" |
| #include "base/test/bind.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/timer/timer.h" |
| #include "cc/paint/skia_paint_canvas.h" |
| #include "media/base/video_facing.h" |
| #include "media/base/video_frame.h" |
| #include "media/renderers/paint_canvas_video_renderer.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/ui_base_types.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/geometry/vector2d.h" |
| #include "ui/gfx/image/image_unittest_util.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| #include "ui/views/view.h" |
| #include "ui/views/view_observer.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/wm/core/coordinate_conversion.h" |
| #include "ui/wm/core/window_util.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kDefaultCameraDeviceId[] = "/dev/videoX"; |
| constexpr char kDefaultCameraDisplayName[] = "Default Cam"; |
| constexpr char kDefaultCameraModelId[] = "0def:c000"; |
| |
| TestCaptureModeDelegate* GetTestDelegate() { |
| return static_cast<TestCaptureModeDelegate*>( |
| CaptureModeController::Get()->delegate_for_testing()); |
| } |
| |
| CaptureModeCameraController* GetCameraController() { |
| return CaptureModeController::Get()->camera_controller(); |
| } |
| |
| // Returns the current root window where the current capture activities are |
| // hosted in. |
| aura::Window* GetCurrentRoot() { |
| auto* controller = CaptureModeController::Get(); |
| if (controller->IsActive()) |
| return controller->capture_mode_session()->current_root(); |
| |
| if (controller->is_recording_in_progress()) { |
| return controller->video_recording_watcher_for_testing() |
| ->window_being_recorded() |
| ->GetRootWindow(); |
| } |
| |
| return Shell::GetPrimaryRootWindow(); |
| } |
| |
| bool IsWindowStackedRightBelow(aura::Window* window, aura::Window* sibling) { |
| DCHECK_EQ(window->parent(), sibling->parent()); |
| const auto& children = window->parent()->children(); |
| const int sibling_index = |
| std::find(children.begin(), children.end(), sibling) - children.begin(); |
| return sibling_index > 0 && children[sibling_index - 1] == window; |
| } |
| |
| // Defines a waiter for the camera devices change notifications. |
| class CameraDevicesChangeWaiter : public CaptureModeCameraController::Observer { |
| public: |
| CameraDevicesChangeWaiter() { GetCameraController()->AddObserver(this); } |
| CameraDevicesChangeWaiter(const CameraDevicesChangeWaiter&) = delete; |
| CameraDevicesChangeWaiter& operator=(const CameraDevicesChangeWaiter&) = |
| delete; |
| ~CameraDevicesChangeWaiter() override { |
| GetCameraController()->RemoveObserver(this); |
| } |
| |
| int camera_change_event_count() const { return camera_change_event_count_; } |
| int selected_camera_change_event_count() const { |
| return selected_camera_change_event_count_; |
| } |
| |
| void Wait() { loop_.Run(); } |
| |
| // CaptureModeCameraController::Observer: |
| void OnAvailableCamerasChanged(const CameraInfoList& cameras) override { |
| ++camera_change_event_count_; |
| loop_.Quit(); |
| } |
| |
| void OnSelectedCameraChanged(const CameraId& camera_id) override { |
| ++selected_camera_change_event_count_; |
| } |
| |
| private: |
| base::RunLoop loop_; |
| |
| // Tracks the number of times the observer call `OnAvailableCamerasChanged()` |
| // was triggered. |
| int camera_change_event_count_ = 0; |
| |
| // Tracks the number of times `OnSelectedCameraChanged()` was triggered. |
| int selected_camera_change_event_count_ = 0; |
| }; |
| |
| // Defines a waiter to observe the visibility change of the view. |
| class ViewVisibilityChangeWaiter : public views::ViewObserver { |
| public: |
| explicit ViewVisibilityChangeWaiter(views::View* view) : view_(view) { |
| view_->AddObserver(this); |
| } |
| |
| ~ViewVisibilityChangeWaiter() override { view_->RemoveObserver(this); } |
| |
| void Wait() { wait_loop_.Run(); } |
| |
| // views::ViewObserver: |
| void OnViewVisibilityChanged(views::View* observed_view, |
| views::View* starting_view) override { |
| wait_loop_.Quit(); |
| } |
| |
| private: |
| views::View* const view_; |
| base::RunLoop wait_loop_; |
| }; |
| |
| gfx::Rect GetTooSmallToFitCameraRegion() { |
| return {100, 100, |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera - 1, |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera - 1}; |
| } |
| |
| } // namespace |
| |
| class CaptureModeCameraTest : public AshTestBase { |
| public: |
| CaptureModeCameraTest() { |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kCaptureModeSelfieCamera); |
| } |
| CaptureModeCameraTest(const CaptureModeCameraTest&) = delete; |
| CaptureModeCameraTest& operator=(const CaptureModeCameraTest&) = delete; |
| ~CaptureModeCameraTest() override = default; |
| |
| // AshTestBase: |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| FakeFolderSelectionDialogFactory::Start(); |
| window_ = CreateTestWindow(gfx::Rect(30, 40, 600, 500)); |
| } |
| |
| void TearDown() override { |
| window_.reset(); |
| FakeFolderSelectionDialogFactory::Stop(); |
| AshTestBase::TearDown(); |
| } |
| |
| aura::Window* window() const { return window_.get(); } |
| |
| void StartRecordingFromSource(CaptureModeSource source) { |
| auto* controller = CaptureModeController::Get(); |
| controller->SetSource(source); |
| |
| switch (source) { |
| case CaptureModeSource::kFullscreen: |
| case CaptureModeSource::kRegion: |
| break; |
| case CaptureModeSource::kWindow: |
| GetEventGenerator()->MoveMouseTo( |
| window_->GetBoundsInScreen().CenterPoint()); |
| break; |
| } |
| CaptureModeTestApi().PerformCapture(); |
| WaitForRecordingToStart(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| } |
| |
| void AddFakeCamera(const std::string& device_id, |
| const std::string& display_name, |
| const std::string& model_id, |
| media::VideoFacingMode camera_facing_mode = |
| media::MEDIA_VIDEO_FACING_NONE) { |
| CameraDevicesChangeWaiter waiter; |
| GetTestDelegate()->video_source_provider()->AddFakeCamera( |
| device_id, display_name, model_id, camera_facing_mode); |
| waiter.Wait(); |
| } |
| |
| void RemoveFakeCamera(const std::string& device_id) { |
| CameraDevicesChangeWaiter waiter; |
| GetTestDelegate()->video_source_provider()->RemoveFakeCamera(device_id); |
| waiter.Wait(); |
| } |
| |
| void AddDefaultCamera() { |
| AddFakeCamera(kDefaultCameraDeviceId, kDefaultCameraDisplayName, |
| kDefaultCameraModelId); |
| } |
| |
| void RemoveDefaultCamera() { RemoveFakeCamera(kDefaultCameraDeviceId); } |
| |
| // Adds the default camera, sets it as the selected camera, then removes it, |
| // which triggers the camera disconnection grace period. Returns a pointer to |
| // the `CaptureModeCameraController`. |
| CaptureModeCameraController* AddAndRemoveCameraAndTriggerGracePeriod() { |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| RemoveDefaultCamera(); |
| return camera_controller; |
| } |
| |
| void OpenSettingsView() { |
| CaptureModeSession* session = |
| CaptureModeController::Get()->capture_mode_session(); |
| DCHECK(session); |
| ClickOnView(CaptureModeSessionTestApi(session) |
| .GetCaptureModeBarView() |
| ->settings_button(), |
| GetEventGenerator()); |
| } |
| |
| void DragPreviewToPoint(views::Widget* preview_widget, |
| const gfx::Point& screen_location, |
| bool by_touch_gestures = false, |
| bool drop = true) { |
| DCHECK(preview_widget); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->set_current_screen_location( |
| preview_widget->GetWindowBoundsInScreen().CenterPoint()); |
| if (by_touch_gestures) { |
| event_generator->PressTouch(); |
| // Move the touch by an enough amount in X to make sure it generates a |
| // serial of gesture scroll events instead of a fling event. |
| event_generator->MoveTouchBy(50, 0); |
| event_generator->MoveTouch(screen_location); |
| if (drop) |
| event_generator->ReleaseTouch(); |
| } else { |
| event_generator->PressLeftButton(); |
| event_generator->MoveMouseTo(screen_location); |
| if (drop) |
| event_generator->ReleaseLeftButton(); |
| } |
| } |
| |
| CaptureModeButton* GetPreviewResizeButton() const { |
| return GetCameraController()->camera_preview_view()->resize_button(); |
| } |
| |
| // Verifies that the camera preview is placed on the correct position based on |
| // current preview snap position and the given `confine_bounds_in_screen`. |
| void VerifyPreviewAlignment(const gfx::Rect& confine_bounds_in_screen) { |
| auto* camera_controller = GetCameraController(); |
| const auto* preview_widget = camera_controller->camera_preview_widget(); |
| DCHECK(preview_widget); |
| const gfx::Rect camera_preview_bounds = |
| preview_widget->GetWindowBoundsInScreen(); |
| |
| switch (camera_controller->camera_preview_snap_position()) { |
| case CameraPreviewSnapPosition::kTopLeft: { |
| gfx::Point expect_origin = confine_bounds_in_screen.origin(); |
| expect_origin.Offset(capture_mode::kSpaceBetweenCameraPreviewAndEdges, |
| capture_mode::kSpaceBetweenCameraPreviewAndEdges); |
| EXPECT_EQ(expect_origin, camera_preview_bounds.origin()); |
| break; |
| } |
| case CameraPreviewSnapPosition::kBottomLeft: { |
| const gfx::Point expect_bottom_left = |
| gfx::Point(confine_bounds_in_screen.x() + |
| capture_mode::kSpaceBetweenCameraPreviewAndEdges, |
| confine_bounds_in_screen.bottom() - |
| capture_mode::kSpaceBetweenCameraPreviewAndEdges); |
| EXPECT_EQ(expect_bottom_left, camera_preview_bounds.bottom_left()); |
| break; |
| } |
| case CameraPreviewSnapPosition::kBottomRight: { |
| const gfx::Point expect_bottom_right = |
| gfx::Point(confine_bounds_in_screen.right() - |
| capture_mode::kSpaceBetweenCameraPreviewAndEdges, |
| confine_bounds_in_screen.bottom() - |
| capture_mode::kSpaceBetweenCameraPreviewAndEdges); |
| EXPECT_EQ(expect_bottom_right, camera_preview_bounds.bottom_right()); |
| break; |
| } |
| case CameraPreviewSnapPosition::kTopRight: { |
| const gfx::Point expect_top_right = |
| gfx::Point(confine_bounds_in_screen.right() - |
| capture_mode::kSpaceBetweenCameraPreviewAndEdges, |
| confine_bounds_in_screen.y() + |
| capture_mode::kSpaceBetweenCameraPreviewAndEdges); |
| EXPECT_EQ(expect_top_right, camera_preview_bounds.top_right()); |
| break; |
| } |
| } |
| } |
| |
| // Verifies that the icon image and the tooltip of the resize button gets |
| // updated correctly when pressed. |
| void VerifyResizeButton(bool is_collapsed, CaptureModeButton* resize_button) { |
| SkColor color = AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kIconColorPrimary); |
| const gfx::ImageSkia collapse_icon_image = |
| gfx::CreateVectorIcon(kCaptureModeCameraPreviewCollapseIcon, color); |
| const gfx::ImageSkia expand_icon_image = |
| gfx::CreateVectorIcon(kCaptureModeCameraPreviewExpandIcon, color); |
| |
| const SkBitmap* expected_icon = is_collapsed ? expand_icon_image.bitmap() |
| : collapse_icon_image.bitmap(); |
| const SkBitmap* actual_icon = |
| resize_button->GetImage(views::ImageButton::ButtonState::STATE_NORMAL) |
| .bitmap(); |
| EXPECT_TRUE(gfx::test::AreBitmapsEqual(*actual_icon, *expected_icon)); |
| |
| const auto expected_tooltip_text = l10n_util::GetStringUTF16( |
| is_collapsed ? IDS_ASH_SCREEN_CAPTURE_TOOLTIP_EXPAND_SELFIE_CAMERA |
| : IDS_ASH_SCREEN_CAPTURE_TOOLTIP_COLLAPSE_SELFIE_CAMERA); |
| EXPECT_EQ(resize_button->GetTooltipText(), expected_tooltip_text); |
| } |
| |
| // Select capture region by pressing and dragging the mouse. |
| void SelectCaptureRegion(const gfx::Rect& region, bool release_mouse = true) { |
| auto* controller = CaptureModeController::Get(); |
| ASSERT_TRUE(controller->IsActive()); |
| ASSERT_EQ(CaptureModeSource::kRegion, controller->source()); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->set_current_screen_location(region.origin()); |
| event_generator->PressLeftButton(); |
| event_generator->MoveMouseTo(region.bottom_right()); |
| if (release_mouse) |
| event_generator->ReleaseLeftButton(); |
| EXPECT_EQ(region, controller->user_capture_region()); |
| } |
| |
| void ConvertToPipWindow(aura::Window* pip_window) { |
| WindowState* window_state = WindowState::Get(pip_window); |
| DCHECK(!window_state->IsPip()); |
| views::Widget::GetWidgetForNativeWindow(pip_window) |
| ->SetZOrderLevel(ui::ZOrderLevel::kFloatingWindow); |
| pip_window->SetProperty(kWindowPipTypeKey, true); |
| DCHECK(window_state->IsPip()); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| std::unique_ptr<aura::Window> window_; |
| }; |
| |
| TEST_F(CaptureModeCameraTest, SizeSpecsBigEnoughRegion) { |
| gfx::Size confine_bounds_size(800, 700); |
| { |
| auto specs = capture_mode_util::CalculateCameraPreviewSizeSpecs( |
| confine_bounds_size, |
| /*is_collapsed=*/false); |
| EXPECT_TRUE(specs.is_collapsible); |
| EXPECT_TRUE(specs.should_be_visible); |
| EXPECT_EQ(specs.size.width(), specs.size.height()); |
| EXPECT_EQ(specs.size.width(), |
| 700 / capture_mode::kCaptureSurfaceShortSideDivider); |
| } |
| |
| // Transposing the confine bounds (e.g. due to rotation) should have no effect |
| // since we always consider the shorter side. |
| confine_bounds_size.Transpose(); |
| { |
| auto specs = capture_mode_util::CalculateCameraPreviewSizeSpecs( |
| confine_bounds_size, |
| /*is_collapsed=*/false); |
| EXPECT_TRUE(specs.is_collapsible); |
| EXPECT_TRUE(specs.should_be_visible); |
| EXPECT_EQ(specs.size.width(), |
| 700 / capture_mode::kCaptureSurfaceShortSideDivider); |
| } |
| |
| { |
| auto specs = capture_mode_util::CalculateCameraPreviewSizeSpecs( |
| confine_bounds_size, |
| /*is_collapsed=*/true); |
| EXPECT_TRUE(specs.is_collapsible); |
| EXPECT_TRUE(specs.should_be_visible); |
| EXPECT_EQ(specs.size.width(), capture_mode::kMinCameraPreviewDiameter); |
| } |
| } |
| |
| TEST_F(CaptureModeCameraTest, SizeSpecsNotCollapsible) { |
| gfx::Size confine_bounds_size(800, 500); |
| auto specs = capture_mode_util::CalculateCameraPreviewSizeSpecs( |
| confine_bounds_size, |
| /*is_collapsed=*/false); |
| EXPECT_FALSE(specs.is_collapsible); |
| EXPECT_TRUE(specs.should_be_visible); |
| EXPECT_EQ(specs.size.width(), specs.size.height()); |
| EXPECT_EQ(specs.size.width(), |
| 500 / capture_mode::kCaptureSurfaceShortSideDivider); |
| EXPECT_FALSE(specs.is_surface_too_small); |
| } |
| |
| TEST_F(CaptureModeCameraTest, SizeSpecsHiddenPreview) { |
| gfx::Size confine_bounds_size(800, 170); |
| auto specs = capture_mode_util::CalculateCameraPreviewSizeSpecs( |
| confine_bounds_size, |
| /*is_collapsed=*/false); |
| EXPECT_FALSE(specs.is_collapsible); |
| EXPECT_FALSE(specs.should_be_visible); |
| EXPECT_EQ(specs.size.width(), specs.size.height()); |
| EXPECT_EQ(specs.size.width(), capture_mode::kMinCameraPreviewDiameter); |
| EXPECT_TRUE(specs.is_surface_too_small); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CameraDevicesChanges) { |
| auto* camera_controller = GetCameraController(); |
| ASSERT_TRUE(camera_controller); |
| EXPECT_TRUE(camera_controller->available_cameras().empty()); |
| EXPECT_FALSE(camera_controller->selected_camera().is_valid()); |
| EXPECT_FALSE(camera_controller->should_show_preview()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| const std::string device_id = "/dev/video0"; |
| const std::string display_name = "Integrated Webcam"; |
| const std::string model_id = "0123:4567"; |
| AddFakeCamera(device_id, display_name, model_id); |
| |
| EXPECT_EQ(1u, camera_controller->available_cameras().size()); |
| EXPECT_TRUE(camera_controller->available_cameras()[0].camera_id.is_valid()); |
| EXPECT_EQ(model_id, camera_controller->available_cameras()[0] |
| .camera_id.model_id_or_display_name()); |
| EXPECT_EQ(1, camera_controller->available_cameras()[0].camera_id.number()); |
| EXPECT_EQ(device_id, camera_controller->available_cameras()[0].device_id); |
| EXPECT_EQ(display_name, |
| camera_controller->available_cameras()[0].display_name); |
| EXPECT_FALSE(camera_controller->should_show_preview()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| RemoveFakeCamera(device_id); |
| |
| EXPECT_TRUE(camera_controller->available_cameras().empty()); |
| EXPECT_FALSE(camera_controller->should_show_preview()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CameraRemovedWhileWaitingForCameraDevices) { |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| EXPECT_EQ(1u, camera_controller->available_cameras().size()); |
| |
| // The system monitor can trigger several notifications about devices changes |
| // for the same camera addition event. We will simulate a camera getting |
| // removed right while we're still waiting for the video source provider to |
| // send us the list. https://crbug.com/1295377. |
| auto* video_source_provider = GetTestDelegate()->video_source_provider(); |
| { |
| base::RunLoop loop; |
| video_source_provider->set_on_replied_with_source_infos( |
| base::BindLambdaForTesting([&]() { loop.Quit(); })); |
| base::SystemMonitor::Get()->ProcessDevicesChanged( |
| base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE); |
| loop.Run(); |
| } |
| |
| RemoveDefaultCamera(); |
| EXPECT_TRUE(camera_controller->available_cameras().empty()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, SelectingUnavailableCamera) { |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| // Selecting a camera that doesn't exist in the list shouldn't show its |
| // preview. |
| camera_controller->SetSelectedCamera(CameraId("model", 1)); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, SelectingAvailableCamera) { |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| AddDefaultCamera(); |
| |
| EXPECT_EQ(1u, camera_controller->available_cameras().size()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| // Selecting an available camera while showing the preview is allowed should |
| // result in creating the preview widget. |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, SelectedCameraBecomesAvailable) { |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| // The selected camera becomes available while `should_show_preview_` is still |
| // true. The preview should show in this case. |
| AddDefaultCamera(); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| |
| // Clearing the selected camera should hide the preview. |
| camera_controller->SetSelectedCamera(CameraId()); |
| EXPECT_FALSE(camera_controller->selected_camera().is_valid()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, SelectingDifferentCameraCreatesNewPreviewWidget) { |
| AddDefaultCamera(); |
| const std::string device_id = "/dev/video0"; |
| const std::string display_name = "Integrated Webcam"; |
| const std::string model_id = "0123:4567"; |
| AddFakeCamera(device_id, display_name, model_id); |
| |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* current_preview_widget = camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(current_preview_widget); |
| |
| // Selecting a different camera should result in the recreation of the preview |
| // widget. |
| camera_controller->SetSelectedCamera(CameraId(model_id, 1)); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| EXPECT_NE(current_preview_widget, camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, MultipleCamerasOfTheSameModel) { |
| auto* camera_controller = GetCameraController(); |
| |
| const std::string device_id_1 = "/dev/video0"; |
| const std::string display_name = "Integrated Webcam"; |
| const std::string model_id = "0123:4567"; |
| AddFakeCamera(device_id_1, display_name, model_id); |
| |
| const auto& available_cameras = camera_controller->available_cameras(); |
| EXPECT_EQ(1u, available_cameras.size()); |
| EXPECT_EQ(1, available_cameras[0].camera_id.number()); |
| EXPECT_EQ(model_id, |
| available_cameras[0].camera_id.model_id_or_display_name()); |
| |
| // Adding a new camera of the same model should be correctly tracked with a |
| // different ID. |
| const std::string device_id_2 = "/dev/video1"; |
| AddFakeCamera(device_id_2, display_name, model_id); |
| |
| EXPECT_EQ(2u, available_cameras.size()); |
| EXPECT_EQ(2, available_cameras[1].camera_id.number()); |
| EXPECT_EQ(model_id, |
| available_cameras[1].camera_id.model_id_or_display_name()); |
| EXPECT_NE(available_cameras[0].camera_id, available_cameras[1].camera_id); |
| } |
| |
| TEST_F(CaptureModeCameraTest, MissingCameraModelId) { |
| auto* camera_controller = GetCameraController(); |
| |
| const std::string device_id = "/dev/video0"; |
| const std::string display_name = "Integrated Webcam"; |
| AddFakeCamera(device_id, display_name, /*model_id=*/""); |
| |
| // The camera's display name should be used instead of a model ID when it's |
| // missing. |
| const auto& available_cameras = camera_controller->available_cameras(); |
| EXPECT_EQ(1u, available_cameras.size()); |
| EXPECT_TRUE(available_cameras[0].camera_id.is_valid()); |
| EXPECT_EQ(1, available_cameras[0].camera_id.number()); |
| EXPECT_EQ(display_name, |
| available_cameras[0].camera_id.model_id_or_display_name()); |
| |
| // If the SystemMonitor triggered a device change alert for some reason, but |
| // the actual list of cameras didn't change, observers should never be |
| // notified again. |
| { |
| base::RunLoop loop; |
| camera_controller->SetOnCameraListReceivedForTesting(loop.QuitClosure()); |
| CameraDevicesChangeWaiter observer; |
| base::SystemMonitor::Get()->ProcessDevicesChanged( |
| base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE); |
| loop.Run(); |
| EXPECT_EQ(0, observer.camera_change_event_count()); |
| } |
| } |
| |
| TEST_F(CaptureModeCameraTest, CameraFramesFlipping) { |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| int index = 0; |
| for (const auto facing_mode : |
| {media::MEDIA_VIDEO_FACING_NONE, media::MEDIA_VIDEO_FACING_USER, |
| media::MEDIA_VIDEO_FACING_ENVIRONMENT}) { |
| const std::string device_id = base::StringPrintf("/dev/video%d", index); |
| const std::string display_name = base::StringPrintf("Camera %d", index); |
| AddFakeCamera(device_id, display_name, display_name, facing_mode); |
| camera_controller->SetSelectedCamera(CameraId(display_name, 1)); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| const bool should_be_flipped = |
| facing_mode != media::MEDIA_VIDEO_FACING_ENVIRONMENT; |
| EXPECT_EQ(should_be_flipped, camera_controller->camera_preview_view() |
| ->should_flip_frames_horizontally()) |
| << "Failed for facing mode: " << facing_mode; |
| ++index; |
| } |
| } |
| |
| TEST_F(CaptureModeCameraTest, DisconnectSelectedCamera) { |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| const CameraId camera_id(kDefaultCameraModelId, 1); |
| camera_controller->SetSelectedCamera(camera_id); |
| |
| // Disconnect a selected camera, and expect that the grace period timer is |
| // running. |
| RemoveDefaultCamera(); |
| base::OneShotTimer* timer = |
| camera_controller->camera_reconnect_timer_for_test(); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(camera_id, camera_controller->selected_camera()); |
| |
| // When the timer fires before the camera gets reconnected, the selected |
| // camera ID is cleared. |
| timer->FireNow(); |
| EXPECT_FALSE(camera_controller->selected_camera().is_valid()); |
| EXPECT_FALSE(timer->IsRunning()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, SelectUnavailableCameraDuringGracePeriod) { |
| auto* camera_controller = AddAndRemoveCameraAndTriggerGracePeriod(); |
| base::OneShotTimer* timer = |
| camera_controller->camera_reconnect_timer_for_test(); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| // Selecting an unavailable camera during the grace period should keep the |
| // timer running for another grace period. |
| const CameraId new_camera_id("Different Camera", 1); |
| camera_controller->SetSelectedCamera(new_camera_id); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(new_camera_id, camera_controller->selected_camera()); |
| |
| // Once the timer fires the new ID will also be cleared. |
| timer->FireNow(); |
| EXPECT_FALSE(camera_controller->selected_camera().is_valid()); |
| EXPECT_FALSE(timer->IsRunning()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, SelectAvailableCameraDuringGracePeriod) { |
| const std::string device_id = "/dev/video0"; |
| const std::string display_name = "Integrated Webcam"; |
| const CameraId available_camera_id(display_name, 1); |
| AddFakeCamera(device_id, display_name, /*model_id=*/""); |
| |
| // This adds the default camera as the selected one, and removes it triggering |
| // a grace period. |
| auto* camera_controller = AddAndRemoveCameraAndTriggerGracePeriod(); |
| base::OneShotTimer* timer = |
| camera_controller->camera_reconnect_timer_for_test(); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| // Selecting the available camera during the grace period should stop the |
| // timer immediately. |
| camera_controller->SetSelectedCamera(available_camera_id); |
| EXPECT_FALSE(timer->IsRunning()); |
| EXPECT_EQ(available_camera_id, camera_controller->selected_camera()); |
| } |
| |
| // This tests simulates a flaky camera connection. |
| TEST_F(CaptureModeCameraTest, ReconnectDuringGracePeriod) { |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* camera_controller = AddAndRemoveCameraAndTriggerGracePeriod(); |
| base::OneShotTimer* timer = |
| camera_controller->camera_reconnect_timer_for_test(); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| // Re-add the camera during the grace period, the timer should stop, and the |
| // preview should reshow. |
| AddDefaultCamera(); |
| EXPECT_FALSE(timer->IsRunning()); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| } |
| |
| // Tests a flaky camera that disconnects before recording begins and reconnects |
| // during recording within the grace period. |
| TEST_F(CaptureModeCameraTest, ReconnectDuringGracePeriodAfterRecordingStarts) { |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* camera_controller = AddAndRemoveCameraAndTriggerGracePeriod(); |
| base::OneShotTimer* timer = |
| camera_controller->camera_reconnect_timer_for_test(); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| // Start recording, and expect that `should_show_preview_` will change to |
| // false. |
| EXPECT_TRUE(camera_controller->should_show_preview()); |
| StartVideoRecordingImmediately(); |
| EXPECT_FALSE(camera_controller->should_show_preview()); |
| |
| // Re-add the camera during the grace period, the timer should stop, but the |
| // preview should not be recreated. |
| AddDefaultCamera(); |
| EXPECT_FALSE(timer->IsRunning()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, SelectedCameraChangedObserver) { |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| const CameraId camera_id(kDefaultCameraModelId, 1); |
| |
| CameraDevicesChangeWaiter observer; |
| camera_controller->SetSelectedCamera(camera_id); |
| EXPECT_EQ(1, observer.selected_camera_change_event_count()); |
| |
| // Selecting the same camera ID again should not trigger an observer call. |
| camera_controller->SetSelectedCamera(camera_id); |
| EXPECT_EQ(1, observer.selected_camera_change_event_count()); |
| |
| // Clearing the ID should. |
| camera_controller->SetSelectedCamera(CameraId()); |
| EXPECT_EQ(2, observer.selected_camera_change_event_count()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, ShouldShowPreviewTest) { |
| auto* controller = CaptureModeController::Get(); |
| auto* camera_controller = GetCameraController(); |
| controller->SetSource(CaptureModeSource::kFullscreen); |
| controller->SetType(CaptureModeType::kVideo); |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| // should_show_preview() should return true when CaptureModeSession is started |
| // in video recording mode. |
| EXPECT_TRUE(camera_controller->should_show_preview()); |
| // Switch to image capture mode, should_show_preview() should return false. |
| controller->SetType(CaptureModeType::kImage); |
| EXPECT_FALSE(camera_controller->should_show_preview()); |
| // Stop an existing capture session, should_show_preview() should return |
| // false. |
| controller->Stop(); |
| EXPECT_FALSE(camera_controller->should_show_preview()); |
| EXPECT_FALSE(controller->IsActive()); |
| |
| // Start another capture session and start video recording, |
| // should_show_preview() should return false when video recording ends. |
| controller->SetType(CaptureModeType::kVideo); |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| EXPECT_TRUE(camera_controller->should_show_preview()); |
| controller->StartVideoRecordingImmediatelyForTesting(); |
| EXPECT_TRUE(camera_controller->should_show_preview()); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| EXPECT_FALSE(camera_controller->should_show_preview()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, ManagedByPolicyCameraOptions) { |
| GetTestDelegate()->set_is_camera_disabled_by_policy(true); |
| |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| OpenSettingsView(); |
| |
| // At this moment, there are no camera devices connected. The camera menu |
| // group should be hidden. |
| CaptureModeSettingsTestApi test_api; |
| CaptureModeMenuGroup* camera_menu_group = test_api.GetCameraMenuGroup(); |
| ASSERT_TRUE(camera_menu_group); |
| EXPECT_FALSE(camera_menu_group->GetVisible()); |
| |
| // Camera addition/removal are still observed even when managed by policy, but |
| // once a camera is added, the group becomes visible, but shows only a dimmed |
| // "Off" option. |
| AddDefaultCamera(); |
| EXPECT_TRUE(camera_menu_group->GetVisible()); |
| EXPECT_TRUE(camera_menu_group->IsOptionChecked(kCameraOff)); |
| EXPECT_FALSE(camera_menu_group->IsOptionEnabled(kCameraOff)); |
| EXPECT_FALSE(test_api.GetCameraOption(kCameraDevicesBegin)); |
| |
| // Selecting a camera will be ignored. |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| EXPECT_FALSE(camera_controller->selected_camera().is_valid()); |
| EXPECT_TRUE(camera_menu_group->IsOptionChecked(kCameraOff)); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| // Removing the existing camera should hide the camera menu group and remove |
| // all its options. |
| RemoveDefaultCamera(); |
| EXPECT_FALSE(camera_menu_group->GetVisible()); |
| EXPECT_FALSE(test_api.GetCameraOption(kCameraOff)); |
| } |
| |
| // Tests that the options on camera menu are shown and checked correctly when |
| // adding or removing cameras. Also tests that `selected_camera_` is updated |
| // correspondently. |
| TEST_F(CaptureModeCameraTest, CheckCameraOptions) { |
| auto* camera_controller = GetCameraController(); |
| const std::string device_id_1 = "/dev/video0"; |
| const std::string display_name_1 = "Integrated Webcam"; |
| |
| const std::string device_id_2 = "/dev/video1"; |
| const std::string display_name_2 = "Integrated Webcam 1"; |
| |
| AddFakeCamera(device_id_1, display_name_1, display_name_1); |
| AddFakeCamera(device_id_2, display_name_2, display_name_2); |
| |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| OpenSettingsView(); |
| |
| // Check camera settings is shown. `Off` option is checked. Two camera options |
| // are not checked. |
| CaptureModeSettingsTestApi test_api; |
| CaptureModeMenuGroup* camera_menu_group = test_api.GetCameraMenuGroup(); |
| EXPECT_TRUE(camera_menu_group && camera_menu_group->GetVisible()); |
| EXPECT_TRUE(camera_menu_group->IsOptionChecked(kCameraOff)); |
| EXPECT_FALSE(camera_menu_group->IsOptionChecked(kCameraDevicesBegin)); |
| EXPECT_FALSE(camera_menu_group->IsOptionChecked(kCameraDevicesBegin + 1)); |
| EXPECT_FALSE(camera_controller->selected_camera().is_valid()); |
| |
| // Click the option for camera device 1, check its display name matches with |
| // the camera device 1 display name and it's checked. Also check the selected |
| // camera is valid now. |
| ClickOnView(test_api.GetCameraOption(kCameraDevicesBegin), |
| GetEventGenerator()); |
| EXPECT_FALSE(camera_menu_group->IsOptionChecked(kCameraOff)); |
| EXPECT_EQ(base::UTF16ToUTF8(camera_menu_group->GetOptionLabelForTesting( |
| kCameraDevicesBegin)), |
| display_name_1); |
| EXPECT_TRUE(camera_menu_group->IsOptionChecked(kCameraDevicesBegin)); |
| EXPECT_TRUE(camera_controller->selected_camera().is_valid()); |
| |
| // Now disconnect camera device 1. |
| RemoveFakeCamera(device_id_1); |
| |
| // Check the camera device 1 is removed from the camera menu. There's only a |
| // camera option for camera device 2. Selected camera is still valid. `Off` |
| // option and option for camera device 2 are not checked. |
| EXPECT_TRUE(test_api.GetCameraOption(kCameraDevicesBegin)); |
| EXPECT_FALSE(test_api.GetCameraOption(kCameraDevicesBegin + 1)); |
| EXPECT_EQ(base::UTF16ToUTF8(camera_menu_group->GetOptionLabelForTesting( |
| kCameraDevicesBegin)), |
| display_name_2); |
| EXPECT_FALSE(camera_menu_group->IsOptionChecked(kCameraOff)); |
| EXPECT_FALSE(camera_menu_group->IsOptionChecked(kCameraDevicesBegin)); |
| EXPECT_TRUE(camera_controller->selected_camera().is_valid()); |
| |
| // Now connect the camera device 1 again. |
| AddFakeCamera(device_id_1, display_name_1, display_name_1); |
| |
| // Check the camera device 1 is added back to the camera menu and it's checked |
| // automatically. Check the selected option's label matches with the camera |
| // device 1's display name. |
| // Note that `device_id_1` ("/dev/video0") comes before `device_id_2` |
| // ("/dev/video1") in sort order, so it will be added first in the menu. |
| EXPECT_TRUE(test_api.GetCameraOption(kCameraDevicesBegin)); |
| EXPECT_TRUE(test_api.GetCameraOption(kCameraDevicesBegin + 1)); |
| EXPECT_FALSE(camera_menu_group->IsOptionChecked(kCameraOff)); |
| EXPECT_TRUE(camera_menu_group->IsOptionChecked(kCameraDevicesBegin)); |
| EXPECT_FALSE(camera_menu_group->IsOptionChecked(kCameraDevicesBegin + 1)); |
| EXPECT_EQ(base::UTF16ToUTF8(camera_menu_group->GetOptionLabelForTesting( |
| kCameraDevicesBegin)), |
| display_name_1); |
| EXPECT_EQ(base::UTF16ToUTF8(camera_menu_group->GetOptionLabelForTesting( |
| kCameraDevicesBegin + 1)), |
| display_name_2); |
| EXPECT_TRUE(camera_controller->selected_camera().is_valid()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CameraPreviewWidgetStackingInFullscreen) { |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(camera_preview_widget); |
| |
| auto* preview_window = camera_preview_widget->GetNativeWindow(); |
| const auto* menu_container = preview_window->GetRootWindow()->GetChildById( |
| kShellWindowId_MenuContainer); |
| auto* parent = preview_window->parent(); |
| // Parent of the preview should be the MenuContainer when capture mode |
| // session is active with `kFullscreen` type. And the preview window should |
| // be the bottom-most child of it. |
| EXPECT_EQ(parent, menu_container); |
| EXPECT_EQ(menu_container->children().front(), preview_window); |
| |
| StartRecordingFromSource(CaptureModeSource::kFullscreen); |
| // Parent of the preview should be the MenuContainer when video recording |
| // in progress with `kFullscreen` type. And the preview window should be the |
| // top-most child of it. |
| preview_window = camera_preview_widget->GetNativeWindow(); |
| parent = preview_window->parent(); |
| EXPECT_EQ(parent, menu_container); |
| EXPECT_EQ(menu_container->children().back(), preview_window); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CameraPreviewWidgetStackingInRegion) { |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(camera_preview_widget); |
| |
| auto* preview_window = camera_preview_widget->GetNativeWindow(); |
| // Parent of the preview should be the UnparentedContainer when the user |
| // capture region is not set. |
| EXPECT_TRUE(controller->user_capture_region().IsEmpty()); |
| EXPECT_EQ(preview_window->parent(), |
| preview_window->GetRootWindow()->GetChildById( |
| kShellWindowId_UnparentedContainer)); |
| EXPECT_FALSE(camera_preview_widget->IsVisible()); |
| |
| controller->SetUserCaptureRegion(gfx::Rect(10, 20, 80, 60), |
| /*by_user=*/true); |
| StartRecordingFromSource(CaptureModeSource::kRegion); |
| const auto* menu_container = preview_window->GetRootWindow()->GetChildById( |
| kShellWindowId_MenuContainer); |
| // Parent of the preview should be the MenuContainer when video recording |
| // in progress with `kRegion` type. And the preview window should be the |
| // top-most child of it. |
| ASSERT_EQ(preview_window->parent(), menu_container); |
| EXPECT_EQ(menu_container->children().back(), preview_window); |
| } |
| |
| // Tests that camera preview widget is shown, hidden and parented correctly |
| // while moving, dragging and updating the user selection region. |
| TEST_F(CaptureModeCameraTest, CameraPreviewWhileUpdatingCaptureRegion) { |
| UpdateDisplay("800x700"); |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| auto* capture_session = controller->capture_mode_session(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(camera_preview_widget); |
| auto* preview_window = camera_preview_widget->GetNativeWindow(); |
| |
| const int min_region_length = |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera; |
| const gfx::Rect capture_region(10, 20, min_region_length, min_region_length); |
| controller->SetUserCaptureRegion(capture_region, /*by_user=*/true); |
| |
| // After user capture region is set, parent of the preview should be the |
| // MenuContainer. |
| const auto* menu_container = preview_window->GetRootWindow()->GetChildById( |
| kShellWindowId_MenuContainer); |
| ASSERT_EQ(preview_window->parent(), menu_container); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_TRUE(preview_window->IsVisible()); |
| |
| // Press the bottom right of selection region. Verify preview is hidden and |
| // it's still parented to `menu_container`. |
| auto* event_generator = GetEventGenerator(); |
| event_generator->set_current_screen_location(capture_region.bottom_right()); |
| event_generator->PressLeftButton(); |
| EXPECT_FALSE(camera_preview_widget->IsVisible()); |
| EXPECT_FALSE(preview_window->IsVisible()); |
| ASSERT_EQ(preview_window->parent(), menu_container); |
| |
| // Move mouse to update the selection region. Verify preview is still |
| // hidden. |
| const gfx::Vector2d delta(15, 20); |
| event_generator->MoveMouseTo(capture_region.bottom_right() + delta); |
| EXPECT_TRUE(capture_session->is_drag_in_progress()); |
| EXPECT_FALSE(camera_preview_widget->IsVisible()); |
| EXPECT_FALSE(preview_window->IsVisible()); |
| ASSERT_EQ(preview_window->parent(), menu_container); |
| |
| // Now release the drag to end selection region update. Verify preview is |
| // shown and parent of the preview should be MenuContainer. |
| event_generator->ReleaseLeftButton(); |
| EXPECT_FALSE(capture_session->is_drag_in_progress()); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_TRUE(preview_window->IsVisible()); |
| EXPECT_EQ(preview_window->parent(), menu_container); |
| |
| // Press in the selection region to move it around. Since in the |
| // use case, selection region is not updated, preview should not be hidden. |
| const gfx::Point current_position(capture_region.origin() + delta); |
| event_generator->set_current_screen_location(current_position); |
| event_generator->PressLeftButton(); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_EQ(preview_window->parent(), menu_container); |
| |
| // Move mouse to move selection region around. Verify preview is shown. |
| event_generator->MoveMouseTo(current_position + delta); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_EQ(preview_window->parent(), menu_container); |
| |
| // Now release the move to end moving selection region. Verify preview is |
| // shown. |
| event_generator->ReleaseLeftButton(); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_EQ(preview_window->parent(), menu_container); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CameraPreviewWidgetStackingInWindow) { |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(camera_preview_widget); |
| |
| auto* preview_window = camera_preview_widget->GetNativeWindow(); |
| // The parent of the preview should be the UnparentedContainer when selected |
| // window is not set. |
| ASSERT_FALSE(controller->capture_mode_session()->GetSelectedWindow()); |
| EXPECT_EQ(preview_window->parent(), |
| preview_window->GetRootWindow()->GetChildById( |
| kShellWindowId_UnparentedContainer)); |
| EXPECT_FALSE(camera_preview_widget->IsVisible()); |
| |
| StartRecordingFromSource(CaptureModeSource::kWindow); |
| // Parent of the preview widget should be the window being recorded when video |
| // recording in progress with `kWindow` type. And the preview window should be |
| // the top-most child of it. |
| const auto* parent = preview_window->parent(); |
| const auto* window_being_recorded = |
| controller->video_recording_watcher_for_testing() |
| ->window_being_recorded(); |
| ASSERT_EQ(parent, window_being_recorded); |
| EXPECT_EQ(window_being_recorded->children().back(), preview_window); |
| } |
| |
| // Tests the visibility of camera menu when there's no camera connected. |
| TEST_F(CaptureModeCameraTest, CheckCameraMenuVisibility) { |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| OpenSettingsView(); |
| |
| // Check camera menu is hidden. |
| CaptureModeSettingsTestApi test_api; |
| CaptureModeMenuGroup* camera_menu_group = test_api.GetCameraMenuGroup(); |
| EXPECT_TRUE(camera_menu_group && !camera_menu_group->GetVisible()); |
| |
| // Connect a camera. |
| AddDefaultCamera(); |
| |
| // Check the camera menu is shown. And there's an `Off` option and an option |
| // for the connected camera. |
| camera_menu_group = test_api.GetCameraMenuGroup(); |
| EXPECT_TRUE(camera_menu_group && camera_menu_group->GetVisible()); |
| EXPECT_TRUE(test_api.GetCameraOption(kCameraOff)); |
| EXPECT_TRUE(test_api.GetCameraOption(kCameraDevicesBegin)); |
| |
| // Now disconnect the camera. |
| RemoveDefaultCamera(); |
| |
| // Check the menu group is hidden again and all options has been removed from |
| // the menu. |
| camera_menu_group = test_api.GetCameraMenuGroup(); |
| EXPECT_TRUE(camera_menu_group && !camera_menu_group->GetVisible()); |
| EXPECT_FALSE(test_api.GetCameraOption(kCameraOff)); |
| EXPECT_FALSE(test_api.GetCameraOption(kCameraDevicesBegin)); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CameraPreviewWidgetBounds) { |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| ASSERT_EQ(CameraPreviewSnapPosition::kBottomRight, |
| camera_controller->camera_preview_snap_position()); |
| |
| const auto* preview_widget = camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(preview_widget); |
| |
| // Verifies the camera preview's alignment with `kBottomRight` snap position |
| // and `kFullscreen` capture source. |
| const auto* capture_mode_session = controller->capture_mode_session(); |
| const gfx::Rect work_area = |
| display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(capture_mode_session->current_root()) |
| .work_area(); |
| VerifyPreviewAlignment(work_area); |
| |
| // Switching to `kRegion` without capture region set, the preview widget |
| // should not be shown. |
| controller->SetSource(CaptureModeSource::kRegion); |
| EXPECT_TRUE(controller->user_capture_region().IsEmpty()); |
| EXPECT_FALSE(preview_widget->IsVisible()); |
| |
| // Verifies the camera preview's alignment with `kBottomRight` snap position |
| // and `kRegion` capture source. |
| const gfx::Rect capture_region(10, 20, 300, 200); |
| controller->SetUserCaptureRegion(capture_region, /*by_user=*/true); |
| VerifyPreviewAlignment(capture_region); |
| |
| // Verifies the camera preview's alignment after switching back to |
| // `kFullscreen.` |
| controller->SetSource(CaptureModeSource::kFullscreen); |
| VerifyPreviewAlignment(work_area); |
| |
| // Verifies the camera preview's alignment with `kBottomLeft` snap position |
| // and `kRegion` capture source. |
| controller->SetSource(CaptureModeSource::kRegion); |
| camera_controller->SetCameraPreviewSnapPosition( |
| CameraPreviewSnapPosition::kBottomLeft); |
| VerifyPreviewAlignment(capture_region); |
| |
| // Verifies the camera preview's alignment with `kTopLeft` snap position |
| // and `kRegion` capture source. |
| camera_controller->SetCameraPreviewSnapPosition( |
| CameraPreviewSnapPosition::kTopLeft); |
| VerifyPreviewAlignment(capture_region); |
| |
| // Verifies the camera preview's alignment with `kTopRight` snap position |
| // and `kRegion` capture source. |
| camera_controller->SetCameraPreviewSnapPosition( |
| CameraPreviewSnapPosition::kTopRight); |
| VerifyPreviewAlignment(capture_region); |
| |
| // Set capture region to empty, the preview should be hidden again. |
| controller->SetUserCaptureRegion(gfx::Rect(), /*by_user=*/true); |
| EXPECT_FALSE(preview_widget->IsVisible()); |
| |
| // Verifies the camera preview's alignment with `kTopRight` snap position and |
| // `kWindow` capture source. |
| StartRecordingFromSource(CaptureModeSource::kWindow); |
| const auto* window_being_recorded = |
| controller->video_recording_watcher_for_testing() |
| ->window_being_recorded(); |
| DCHECK(window_being_recorded); |
| VerifyPreviewAlignment(window_being_recorded->GetBoundsInScreen()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, MultiDisplayCameraPreviewWidgetBounds) { |
| UpdateDisplay("800x700,801+0-800x700"); |
| |
| const gfx::Point point_in_second_display = gfx::Point(1000, 500); |
| auto* event_generator = GetEventGenerator(); |
| MoveMouseToAndUpdateCursorDisplay(point_in_second_display, event_generator); |
| |
| // Start the capture session in the second display. |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| const gfx::Rect second_display_bounds(801, 0, 800, 700); |
| // The camera preview should reside inside the second display when we start |
| // capture session in the second display. |
| const auto* preview_widget = camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(second_display_bounds.Contains( |
| preview_widget->GetWindowBoundsInScreen())); |
| |
| // Move the capture session to the primary display should move the camera |
| // preview to the primary display as well. |
| MoveMouseToAndUpdateCursorDisplay(gfx::Point(10, 20), event_generator); |
| EXPECT_TRUE(gfx::Rect(0, 0, 800, 700) |
| .Contains(preview_widget->GetWindowBoundsInScreen())); |
| |
| // Move back to the second display, switch to `kRegion` and set the capture |
| // region. The camera preview should be moved back to the second display and |
| // inside the capture region. |
| MoveMouseToAndUpdateCursorDisplay(point_in_second_display, event_generator); |
| controller->SetSource(CaptureModeSource::kRegion); |
| // The capture region set through `controller` is in root coordinate. |
| const gfx::Rect capture_region(100, 0, 400, 550); |
| controller->SetUserCaptureRegion(capture_region, /*by_user=*/true); |
| const gfx::Rect capture_region_in_screen(901, 0, 400, 550); |
| const gfx::Rect preview_bounds = preview_widget->GetWindowBoundsInScreen(); |
| EXPECT_TRUE(second_display_bounds.Contains(preview_bounds)); |
| EXPECT_TRUE(capture_region_in_screen.Contains(preview_bounds)); |
| |
| // Start the window recording inside the second display, the camera preview |
| // should be inside the window that is being recorded inside the second |
| // display. |
| window()->SetBoundsInScreen( |
| gfx::Rect(900, 0, 600, 500), |
| display::Screen::GetScreen()->GetDisplayNearestWindow( |
| Shell::GetAllRootWindows()[1])); |
| StartRecordingFromSource(CaptureModeSource::kWindow); |
| const auto* window_being_recorded = |
| controller->video_recording_watcher_for_testing() |
| ->window_being_recorded(); |
| EXPECT_TRUE(window_being_recorded->GetBoundsInScreen().Contains( |
| preview_widget->GetWindowBoundsInScreen())); |
| } |
| |
| // Tests that switching from `kImage` to `kVideo` with capture source `kWindow`, |
| // and capture window is already selected before the switch, the camera preview |
| // widget should be positioned and parented correctly. |
| TEST_F(CaptureModeCameraTest, CameraPreviewWidgetAfterTypeSwitched) { |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kImage); |
| auto* camera_controller = GetCameraController(); |
| GetEventGenerator()->MoveMouseToCenterOf(window()); |
| |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| controller->SetType(CaptureModeType::kVideo); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(camera_preview_widget); |
| auto* camera_preview_window = camera_preview_widget->GetNativeWindow(); |
| const auto* selected_window = |
| controller->capture_mode_session()->GetSelectedWindow(); |
| ASSERT_EQ(camera_preview_window->parent(), selected_window); |
| |
| // Verify that camera preview is at the bottom right corner of the window. |
| VerifyPreviewAlignment(selected_window->GetBoundsInScreen()); |
| // `camera_preview_window` should not have a transient parent. |
| EXPECT_FALSE(wm::GetTransientParent(camera_preview_window)); |
| } |
| |
| // Tests that audio and camera menu groups should be hidden from the settings |
| // menu when there's a video recording in progress. |
| TEST_F(CaptureModeCameraTest, |
| AudioAndCameraMenuGroupsAreHiddenWhenVideoRecordingInProgress) { |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| auto* camera_controller = controller->camera_controller(); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| |
| // Verify there's no camera preview created, since we don't select any camera. |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| // Check capture session is shut down after the video recording starts. |
| EXPECT_FALSE(controller->IsActive()); |
| |
| // Start a new session, check the type should be switched automatically to |
| // kImage. |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| EXPECT_EQ(CaptureModeType::kImage, controller->type()); |
| |
| // Verify there's no camera preview created after a new session started. |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| OpenSettingsView(); |
| // Check the audio and camera menu groups are hidden from the settings. |
| CaptureModeSettingsTestApi test_api_new; |
| EXPECT_FALSE(test_api_new.GetCameraMenuGroup()); |
| EXPECT_FALSE(test_api_new.GetAudioInputMenuGroup()); |
| EXPECT_TRUE(test_api_new.GetSaveToMenuGroup()); |
| } |
| |
| // Verify that starting a new capture session and updating capture source won't |
| // affect the current camera preview when there's a video recording is progress. |
| TEST_F(CaptureModeCameraTest, |
| CameraPreviewNotChangeWhenVideoRecordingInProgress) { |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| auto* camera_controller = controller->camera_controller(); |
| AddDefaultCamera(); |
| OpenSettingsView(); |
| |
| // Select the default camera for video recording. |
| CaptureModeSettingsTestApi test_api; |
| ClickOnView(test_api.GetCameraOption(kCameraDevicesBegin), |
| GetEventGenerator()); |
| StartVideoRecordingImmediately(); |
| |
| // Check that the camera preview is created. |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(camera_preview_widget); |
| |
| auto* preview_window = camera_preview_widget->GetNativeWindow(); |
| auto* parent = preview_window->parent(); |
| |
| // Start a new capture session, and set capture source to `kFullscreen`, |
| // verify the camera preview and its parent are not changed. |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| controller->SetSource(CaptureModeSource::kFullscreen); |
| EXPECT_EQ(preview_window, |
| camera_controller->camera_preview_widget()->GetNativeWindow()); |
| EXPECT_EQ( |
| parent, |
| camera_controller->camera_preview_widget()->GetNativeWindow()->parent()); |
| |
| // Update capture source to `kRegion` and set the user capture region, verify |
| // the camera preview and its parent are not changed. |
| controller->SetSource(CaptureModeSource::kRegion); |
| controller->SetUserCaptureRegion({100, 100, 200, 300}, /*by_user=*/true); |
| EXPECT_EQ(preview_window, |
| camera_controller->camera_preview_widget()->GetNativeWindow()); |
| EXPECT_EQ( |
| parent, |
| camera_controller->camera_preview_widget()->GetNativeWindow()->parent()); |
| |
| // Update capture source to `kWindow` and move mouse on top the `window`, |
| // verify that the camera preview and its parent are not changed. |
| controller->SetSource(CaptureModeSource::kWindow); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseToCenterOf(window()); |
| EXPECT_EQ(preview_window, |
| camera_controller->camera_preview_widget()->GetNativeWindow()); |
| EXPECT_EQ( |
| parent, |
| camera_controller->camera_preview_widget()->GetNativeWindow()->parent()); |
| } |
| |
| // Tests that changing the folder while there's a video recording in progress |
| // doesn't change the folder where the video being recorded will be saved to. |
| // It will only affect the image to be captured. |
| TEST_F(CaptureModeCameraTest, ChangeFolderWhileVideoRecordingInProgress) { |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| StartVideoRecordingImmediately(); |
| |
| // While the video recording is in progress, start a new capture session and |
| // update the save-to folder to the custom folder. |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| controller->SetCustomCaptureFolder( |
| CreateCustomFolderInUserDownloadsPath("test")); |
| |
| // Perform the image capture. Verify that the image is saved to the custom |
| // folder. |
| controller->PerformCapture(); |
| const base::FilePath& saved_image_file = WaitForCaptureFileToBeSaved(); |
| EXPECT_EQ(controller->GetCustomCaptureFolder(), saved_image_file.DirName()); |
| |
| // End the video recoring and verify the video is still saved to the default |
| // downloads folder. |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| const base::FilePath& saved_video_file = WaitForCaptureFileToBeSaved(); |
| EXPECT_EQ(controller->delegate_for_testing()->GetUserDefaultDownloadsFolder(), |
| saved_video_file.DirName()); |
| } |
| |
| // Tests multiple scenarios to trigger selected window updates at located |
| // position. Camera preview's native window should be added to the ignore |
| // windows and no crash should happen in these cases. |
| TEST_F(CaptureModeCameraTest, |
| UpdateSelectedWindowAtPositionWithCameraPreviewIgnored) { |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseToCenterOf(window()); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| |
| // No camera preview when it is in `kImage`. |
| controller->SetType(CaptureModeType::kImage); |
| event_generator->MoveMouseToCenterOf(window()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| |
| // The native window of camera preview widget should be ignored from the |
| // candidates of the selected window. So moving the mouse to be on top of the |
| // camera preview should not cause any crash or selected window changes. |
| controller->SetType(CaptureModeType::kVideo); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| const auto* capture_mode_session = controller->capture_mode_session(); |
| event_generator->MoveMouseToCenterOf( |
| camera_preview_widget->GetNativeWindow()); |
| EXPECT_EQ(window(), capture_mode_session->GetSelectedWindow()); |
| EXPECT_TRUE(window()->IsVisible()); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| |
| // Hide `window_` with camera preview on should not cause any crash and |
| // selected window should be updated to nullptr. |
| window()->Hide(); |
| EXPECT_FALSE(window()->IsVisible()); |
| EXPECT_FALSE(camera_preview_widget->IsVisible()); |
| EXPECT_FALSE(capture_mode_session->GetSelectedWindow()); |
| |
| // Reshow `window_` without hovering over it should not set the selected |
| // window. Camera preview should still be hidden as its parent hasn't been set |
| // to `window_` yet. |
| const auto* preview_native_window = camera_preview_widget->GetNativeWindow(); |
| window()->Show(); |
| EXPECT_TRUE(window()->IsVisible()); |
| EXPECT_FALSE(camera_preview_widget->IsVisible()); |
| EXPECT_FALSE(capture_mode_session->GetSelectedWindow()); |
| EXPECT_EQ(preview_native_window->parent(), |
| preview_native_window->GetRootWindow()->GetChildById( |
| kShellWindowId_UnparentedContainer)); |
| |
| // Hovering over `window_` should set it to the selected window, camera |
| // preview widget should be reparented to it as well. And the camera preview |
| // widget should be visible now. |
| event_generator->MoveMouseToCenterOf(window()); |
| EXPECT_EQ(preview_native_window->parent(), |
| capture_mode_session->GetSelectedWindow()); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_EQ(window(), capture_mode_session->GetSelectedWindow()); |
| } |
| |
| // Tests that capture label's opacity changes accordingly when it's overlapped |
| // or it's not overlapped with camera preview. Also tests that when located |
| // events is or is not on capture label, its opacity is updated accordingly. |
| TEST_F(CaptureModeCameraTest, |
| CaptureLabelOpacityChangeWhenOverlappingWithCameraPreview) { |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| const auto* capture_label_widget = capture_session->capture_label_widget(); |
| const ui::Layer* capture_label_layer = capture_label_widget->GetLayer(); |
| |
| // Set capture region big enough to make capture label not overlapping with |
| // camera preview. Verify capture label is fully opaque. |
| const gfx::Rect capture_region(100, 100, 700, 700); |
| SelectCaptureRegion(capture_region); |
| EXPECT_FALSE(capture_label_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), 1.f); |
| |
| // Update capture region smaller to make capture label overlap with camera |
| // preview. Verify capture label is `kOverlapOpacity`. |
| // Make sure to resize the region to a value that won't cause the camera to be |
| // hidden according to the camera size specs. |
| const int delta_x = |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera - |
| capture_region.width(); |
| const int delta_y = |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera - |
| capture_region.height(); |
| const gfx::Vector2d delta(delta_x, delta_y); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->set_current_screen_location(capture_region.bottom_right()); |
| event_generator->PressLeftButton(); |
| event_generator->MoveMouseTo(capture_region.bottom_right() + delta); |
| event_generator->ReleaseLeftButton(); |
| EXPECT_TRUE(capture_label_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| // Move mouse on top of capture label, verify it's updated to fully opaque |
| // even it's still overlapped with camera preview. |
| const gfx::Rect capture_lable_bounds = |
| capture_label_widget->GetWindowBoundsInScreen(); |
| event_generator->MoveMouseTo(capture_lable_bounds.CenterPoint()); |
| EXPECT_TRUE(capture_lable_bounds.Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), 1.0f); |
| |
| // Move mouse to the outside of capture label, verify it's updated to |
| // `kOverlapOpacity`. |
| const gfx::Vector2d delta1(50, 50); |
| event_generator->MoveMouseTo(capture_lable_bounds.bottom_right() + delta1); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| // Click on the outside of the capture region to reset it, verify capture |
| // label is updated to fully opaque. |
| const gfx::Rect current_capture_region = controller->user_capture_region(); |
| event_generator->MoveMouseTo(current_capture_region.bottom_right() + delta1); |
| event_generator->ClickLeftButton(); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), 1.0f); |
| } |
| |
| TEST_F(CaptureModeCameraTest, |
| CaptureBarOpacityChangeWhenOverlappingWithCameraPreview) { |
| // Update display size and update window with customized size to make sure |
| // camera preview overlap with capture bar with capture source `kWindow`. |
| UpdateDisplay("1366x768"); |
| window()->SetBounds({0, 195, 903, 492}); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| const auto* capture_bar_widget = capture_session->capture_mode_bar_widget(); |
| const ui::Layer* capture_bar_layer = capture_bar_widget->GetLayer(); |
| |
| // Move mouse on top of `window` to set the selected window. Verify capture |
| // bar is `kOverlapOpacity`. |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseTo(window()->GetBoundsInScreen().CenterPoint()); |
| EXPECT_EQ(capture_session->GetSelectedWindow(), window()); |
| EXPECT_TRUE(capture_bar_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| // Move mouse on top of capture bar. Verify capture bar is updated to fully |
| // opaque. |
| event_generator->MoveMouseTo( |
| capture_bar_widget->GetWindowBoundsInScreen().CenterPoint()); |
| EXPECT_TRUE(capture_bar_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), 1.0f); |
| |
| // Mouse mouse to the outside of capture bar, verify it's updated to |
| // `kOverlapOpacity`. |
| const gfx::Point capture_bar_origin = |
| capture_bar_widget->GetWindowBoundsInScreen().origin(); |
| event_generator->MoveMouseTo(capture_bar_origin.x() - 10, |
| capture_bar_origin.y() - 10); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CaptureBarOpacityChangeOnDisplayRotation) { |
| // Update display size and update window with customized size to make sure |
| // camera preview overlap with capture bar with capture source `kWindow`. |
| UpdateDisplay("1366x768"); |
| window()->SetBounds({0, 195, 903, 492}); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| const auto* capture_bar_widget = capture_session->capture_mode_bar_widget(); |
| const ui::Layer* capture_bar_layer = capture_bar_widget->GetLayer(); |
| |
| // Move mouse on top of `window` to set the selected window. Verify capture |
| // bar is `kOverlapOpacity`. |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseTo(window()->GetBoundsInScreen().CenterPoint()); |
| EXPECT_EQ(capture_session->GetSelectedWindow(), window()); |
| EXPECT_TRUE(capture_bar_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| // Rotate the primary display by 90 degrees. Verify that capture bar no longer |
| // overlaps with camera preview and it's updated to fully opaque. |
| Shell::Get()->display_manager()->SetDisplayRotation( |
| WindowTreeHostManager::GetPrimaryDisplayId(), display::Display::ROTATE_90, |
| display::Display::RotationSource::USER); |
| EXPECT_FALSE(capture_bar_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), 1.0f); |
| |
| // Rotate the primary display by 180 degrees. Verify that capture bar is |
| // overlapped with camera preview and it's updated to `kOverlapOpacity`. |
| Shell::Get()->display_manager()->SetDisplayRotation( |
| WindowTreeHostManager::GetPrimaryDisplayId(), |
| display::Display::ROTATE_180, display::Display::RotationSource::USER); |
| EXPECT_TRUE(capture_bar_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CaptureLabelOpacityChangeOnCaptureSourceChange) { |
| UpdateDisplay("800x600"); |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* camera_preview_widget = camera_controller->camera_preview_widget(); |
| auto* capture_label_widget = capture_session->capture_label_widget(); |
| ui::Layer* capture_label_layer = capture_label_widget->GetLayer(); |
| |
| // Select capture region to make sure capture label is overlapped with |
| // camera preview. Verify capture label is `kOverlapOpacity`. |
| const int min_region_length = |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera; |
| SelectCaptureRegion({100, 100, min_region_length, min_region_length}); |
| EXPECT_TRUE(capture_label_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| // Change the capture source from `kRegion` to `kFullscreen`, verify capture |
| // label is updated to fully opaque. |
| controller->SetSource(CaptureModeSource::kFullscreen); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), 1.0f); |
| } |
| |
| TEST_F(CaptureModeCameraTest, |
| CaptureLabelOpacityChangeWhileVideoRecordingInProgress) { |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* camera_preview_widget = camera_controller->camera_preview_widget(); |
| const int min_region_length = |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera; |
| controller->SetUserCaptureRegion( |
| {100, 100, min_region_length, min_region_length}, /*by_user=*/true); |
| |
| StartVideoRecordingImmediately(); |
| EXPECT_FALSE(controller->IsActive()); |
| |
| // Start a new capture session, verify even capture label is overlapped with |
| // camera preview, it's still fully opaque since camera preview does not |
| // belong to the new capture session. |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| EXPECT_EQ(CaptureModeSource::kRegion, controller->source()); |
| auto* capture_session = controller->capture_mode_session(); |
| |
| const auto* capture_label_widget = capture_session->capture_label_widget(); |
| EXPECT_TRUE(capture_label_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_label_widget->GetLayer()->GetTargetOpacity(), 1.0f); |
| } |
| |
| TEST_F(CaptureModeCameraTest, FocusableCameraPreviewInFullscreen) { |
| UpdateDisplay("800x700"); |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| auto* event_generator = GetEventGenerator(); |
| using FocusGroup = CaptureModeSessionFocusCycler::FocusGroup; |
| CaptureModeSessionTestApi test_api(controller->capture_mode_session()); |
| |
| // Tests that the camera preview is focusable in fullscreen capture. |
| auto* camera_preview_view = camera_controller->camera_preview_view(); |
| auto* resize_button = GetPreviewResizeButton(); |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/6); |
| EXPECT_EQ(FocusGroup::kCameraPreview, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| |
| // Press tab again should advance the focus on the resize button. And the |
| // resize button should be invisible before and visible after being focused. |
| EXPECT_FALSE(resize_button->GetVisible()); |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(1u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| // Press space when the resize button is focused should collapse the camera |
| // preview. |
| EXPECT_TRUE(resize_button->has_focus()); |
| EXPECT_FALSE(camera_controller->is_camera_preview_collapsed()); |
| SendKey(ui::VKEY_SPACE, event_generator); |
| EXPECT_TRUE(camera_controller->is_camera_preview_collapsed()); |
| // Press space again should expand the camera preview. |
| SendKey(ui::VKEY_SPACE, event_generator); |
| EXPECT_FALSE(camera_controller->is_camera_preview_collapsed()); |
| |
| // When the resize button inside the camera preview is focused, press tab |
| // should advance to focus on the settings button. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kSettingsClose, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| |
| // The resize button should fade out and become invisible in |
| // `kResizeButtonShowDuration` after removing focus from it. |
| base::OneShotTimer* hide_timer = |
| camera_preview_view->resize_button_hide_timer_for_test(); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_TRUE(hide_timer->IsRunning()); |
| EXPECT_EQ(hide_timer->GetCurrentDelay(), |
| capture_mode::kResizeButtonShowDuration); |
| { |
| ViewVisibilityChangeWaiter waiter(resize_button); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| hide_timer->FireNow(); |
| waiter.Wait(); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| } |
| |
| // Shift tab should advance the focus from the settings button back to the |
| // resize button inside the camera preview. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kCameraPreview, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(1u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| |
| // The resize button should keep visible when it is focused, even trigger |
| // `resize_button_hide_timer_` to refresh its visibility. |
| hide_timer = camera_preview_view->resize_button_hide_timer_for_test(); |
| EXPECT_TRUE(hide_timer->IsRunning()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| hide_timer->FireNow(); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| // Continue shift tab should move the focus from the resize button to the |
| // camera preview. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| |
| // The resize button should fade out and become invisible again in |
| // `kResizeButtonShowDuration` after removing focus from it. |
| hide_timer = camera_preview_view->resize_button_hide_timer_for_test(); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_TRUE(hide_timer->IsRunning()); |
| EXPECT_EQ(hide_timer->GetCurrentDelay(), |
| capture_mode::kResizeButtonShowDuration); |
| { |
| ViewVisibilityChangeWaiter waiter(resize_button); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| hide_timer->FireNow(); |
| waiter.Wait(); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| } |
| |
| // Tests moving the camera preview through the keyboard when it is focused. |
| EXPECT_TRUE(camera_controller->camera_preview_view()->has_focus()); |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomRight, |
| camera_controller->camera_preview_snap_position()); |
| // Press control+right should not move the camera preview, as it is currently |
| // at the right. |
| SendKey(ui::VKEY_RIGHT, event_generator, ui::EF_CONTROL_DOWN); |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomRight, |
| camera_controller->camera_preview_snap_position()); |
| // Press control+down should not move the camera preview either, as it is |
| // currently at the bottom. |
| SendKey(ui::VKEY_DOWN, event_generator, ui::EF_CONTROL_DOWN); |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomRight, |
| camera_controller->camera_preview_snap_position()); |
| // Press control+left should move the camera preview from bottom right to |
| // bottom left. |
| SendKey(ui::VKEY_LEFT, event_generator, ui::EF_CONTROL_DOWN); |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomLeft, |
| camera_controller->camera_preview_snap_position()); |
| // Press control+right now should work to move the camera preview from bottom |
| // left to bottom right. |
| SendKey(ui::VKEY_RIGHT, event_generator, ui::EF_CONTROL_DOWN); |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomRight, |
| camera_controller->camera_preview_snap_position()); |
| // Press control+up should move the camera preview from bottom right to top |
| // right. |
| SendKey(ui::VKEY_UP, event_generator, ui::EF_CONTROL_DOWN); |
| EXPECT_EQ(CameraPreviewSnapPosition::kTopRight, |
| camera_controller->camera_preview_snap_position()); |
| |
| // Shift tab again should advance the focus from the camera preview back to |
| // the window capture source button. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kTypeSource, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(4u, test_api.GetCurrentFocusIndex()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, FocusableCameraPreviewInRegion) { |
| UpdateDisplay("1366x768"); |
| auto* controller = CaptureModeController::Get(); |
| controller->SetUserCaptureRegion(gfx::Rect(10, 10, 800, 700), |
| /*by_user=*/true); |
| |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| auto* event_generator = GetEventGenerator(); |
| using FocusGroup = CaptureModeSessionFocusCycler::FocusGroup; |
| CaptureModeSessionTestApi test_api(controller->capture_mode_session()); |
| |
| // Tests that the camera preview is focusable in region capture. |
| auto* camera_preview_view = camera_controller->camera_preview_view(); |
| auto* resize_button = GetPreviewResizeButton(); |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/15); |
| EXPECT_EQ(FocusGroup::kCameraPreview, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(1u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| |
| // When the resize button inside camera preview is focused, press tab should |
| // advance to focus on the capture button. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kCaptureButton, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| |
| // Press tab again to advance focus on the settings button. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kSettingsClose, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| |
| // Shift tab should advance the focus from the settings button back to the |
| // capture button. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kCaptureButton, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| |
| // Shift tab again should advance the focus from the capture button back to |
| // the resize button inside the camera preview. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kCameraPreview, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(1u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| // Continue shift tab should move the focus from the resize button to the |
| // camera preview. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| |
| // Shift tab to advance the focus back to the window capture source button. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN, /*count=*/10); |
| EXPECT_EQ(FocusGroup::kTypeSource, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(4u, test_api.GetCurrentFocusIndex()); |
| |
| // Update the capture region to test when the resize button is not focusable. |
| controller->SetUserCaptureRegion(gfx::Rect(10, 10, 400, 550), |
| /*by_user=*/true); |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/10); |
| EXPECT_EQ(FocusGroup::kCameraPreview, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| // Press tab should advance the focus on the capture button instead of the |
| // resize button. As the resize button is forced to be hidden, which is not |
| // focusable in this case. |
| EXPECT_FALSE(camera_preview_view->is_collapsible()); |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kCaptureButton, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, FocusableCameraPreviewInWindow) { |
| UpdateDisplay("1366x768"); |
| // Create one more window besides `window_`. |
| std::unique_ptr<aura::Window> window2( |
| CreateTestWindow(gfx::Rect(150, 50, 800, 700))); |
| window()->SetBounds(gfx::Rect(30, 40, 800, 700)); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| auto* event_generator = GetEventGenerator(); |
| using FocusGroup = CaptureModeSessionFocusCycler::FocusGroup; |
| auto* capture_mode_session = controller->capture_mode_session(); |
| CaptureModeSessionTestApi test_api(capture_mode_session); |
| |
| const auto* preview_window = |
| camera_controller->camera_preview_widget()->GetNativeWindow(); |
| |
| // Tab to focus on `window2`, which is the most recently used window. It |
| // should be set to the current selected window, and the camera preview should |
| // be shown inside it. |
| auto* camera_preview_view = camera_controller->camera_preview_view(); |
| auto* resize_button = GetPreviewResizeButton(); |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/6); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(test_api.GetHighlightableWindow(window2.get())->has_focus()); |
| EXPECT_EQ(window2.get(), capture_mode_session->GetSelectedWindow()); |
| EXPECT_EQ(window2.get(), preview_window->parent()); |
| |
| // Press tab should advance to focus on the camera preview inside. But the |
| // FocusGroup should not change, as the camera preview is treated as part of |
| // the selected window in this case. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(1u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| // Press tab should focus on the resize button inside the camera preview. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(2u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| EXPECT_FALSE(camera_controller->camera_preview_view()->has_focus()); |
| |
| // Press tab again should advance focus and set another window `window_` to be |
| // the current selected window. The camera preview should be shown inside it |
| // now. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(3u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(test_api.GetHighlightableWindow(window())->has_focus()); |
| EXPECT_EQ(window(), capture_mode_session->GetSelectedWindow()); |
| EXPECT_EQ(window(), preview_window->parent()); |
| |
| // Press tab should advance to focus on the camera preview that inside |
| // `window_` now. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(4u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| // Press tab to advance the focus on the resize button inside the camera |
| // preview. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(5u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| |
| // Press tab again should advance to focus on the settings button. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kSettingsClose, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| |
| // Shift tab when the settings button is focused should advance back to focus |
| // on the resize button inside the camera preview. And the camera preview |
| // should now be shown inside `window_`, which is the current selected window. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(5u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_EQ(window(), capture_mode_session->GetSelectedWindow()); |
| EXPECT_EQ(window(), preview_window->parent()); |
| // Shift tab again should move the focus from the resize button to the camera |
| // preview. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(4u, test_api.GetCurrentFocusIndex()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| |
| // Shift tab again should focus on the `window_` |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(3u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(test_api.GetHighlightableWindow(window())->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_EQ(window(), capture_mode_session->GetSelectedWindow()); |
| |
| // Continue shift tab should advance back to focus on the resize button inside |
| // the camera preview. And the camera preview should now inside `window2`, |
| // which is the current selected window. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(2u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_EQ(window2.get(), capture_mode_session->GetSelectedWindow()); |
| EXPECT_EQ(window2.get(), preview_window->parent()); |
| // Continue shift tab should move the focus from the resize button to the |
| // camera preview. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(1u, test_api.GetCurrentFocusIndex()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| |
| // Continue shift tab should focus on the `window2`. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(test_api.GetHighlightableWindow(window2.get())->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_EQ(window2.get(), capture_mode_session->GetSelectedWindow()); |
| |
| // Continue shift tab should advance back to the window capture source button. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(FocusGroup::kTypeSource, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(4u, test_api.GetCurrentFocusIndex()); |
| |
| // Destroy a window and test that the destroyed window will not be included in |
| // the keyboard focus navigation. |
| window2.reset(); |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(test_api.GetHighlightableWindow(window())->has_focus()); |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(1u, test_api.GetCurrentFocusIndex()); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(2u, test_api.GetCurrentFocusIndex()); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| // Continue tab after focusing on the resize button should advance the focus |
| // to settings close button. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kSettingsClose, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, |
| FocusableCameraPreviewInVideoRecordingWithFullscreenCapture) { |
| UpdateDisplay("800x700"); |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| auto* event_generator = GetEventGenerator(); |
| auto* camera_preview_view = camera_controller->camera_preview_view(); |
| auto* resize_button = GetPreviewResizeButton(); |
| StartVideoRecordingImmediately(); |
| |
| EXPECT_TRUE(camera_preview_view->is_collapsible()); |
| EXPECT_FALSE(camera_controller->is_camera_preview_collapsed()); |
| |
| // Press shortcut "Search+Alt+S" should focus the camera preview. |
| SendKey(ui::VKEY_S, event_generator, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Press tab should fade in the resize button and advance the focus on it. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| // Shift tab should advance the focus back to the camera preview view. But the |
| // resize button will be kept as visible as it will be fade out in a few |
| // seconds. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| // Press tab again to focus on the resize button. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| // Press space key when the resize button is focused should be able to expand |
| // or collapse the camera preview. |
| SendKey(ui::VKEY_SPACE, event_generator); |
| EXPECT_TRUE(camera_controller->is_camera_preview_collapsed()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| SendKey(ui::VKEY_SPACE, event_generator); |
| EXPECT_FALSE(camera_controller->is_camera_preview_collapsed()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| |
| // Press escape key should remove the focus from the camera preview, either |
| // the camera preview view or the resize button. |
| SendKey(ui::VKEY_ESCAPE, event_generator); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| |
| // Press shortcut "Search+Alt+S" should not focus the camera preview when |
| // capture session is active even though video recording is in progress. |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kImage); |
| EXPECT_TRUE(controller->IsActive()); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| SendKey(ui::VKEY_S, event_generator, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| |
| // Press tab should still able to focus the camera preview view and the resize |
| // button. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/5); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| // Continue tab should be able to advance the focus on the settings button. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_TRUE(CaptureModeSessionTestApi(controller->capture_mode_session()) |
| .GetCaptureModeBarView() |
| ->settings_button() |
| ->has_focus()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, |
| FocusableCameraPreviewInVideoRecordingWithRegionCapture) { |
| auto* controller = CaptureModeController::Get(); |
| controller->SetUserCaptureRegion(gfx::Rect(10, 10, 400, 550), |
| /*by_user=*/true); |
| |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| auto* event_generator = GetEventGenerator(); |
| auto* camera_preview_view = camera_controller->camera_preview_view(); |
| auto* resize_button = GetPreviewResizeButton(); |
| StartVideoRecordingImmediately(); |
| |
| EXPECT_FALSE(camera_controller->is_camera_preview_collapsed()); |
| EXPECT_FALSE(camera_preview_view->is_collapsible()); |
| |
| // Press shortcut "Search+Alt+S" should focus the camera preview. |
| SendKey(ui::VKEY_S, event_generator, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Press tab nothing will happen. As the camera preview is not collapsible, |
| // which means the camera preview is the only focusable item. Focus will not |
| // be moved to the resize button and it will continue to be hidden. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Mouse pressing outside of the camera preview should remove the focus from |
| // the camera preview. |
| const gfx::Point origin = camera_preview_view->GetBoundsInScreen().origin(); |
| const gfx::Vector2d delta(-50, -50); |
| event_generator->MoveMouseTo(origin + delta); |
| event_generator->ClickLeftButton(); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, |
| FocusableCameraPreviewInVideoRecordingWithWindowCapture) { |
| UpdateDisplay("1366x768"); |
| window()->SetBounds(gfx::Rect(30, 40, 800, 700)); |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* event_generator = GetEventGenerator(); |
| auto* camera_preview_view = camera_controller->camera_preview_view(); |
| auto* resize_button = GetPreviewResizeButton(); |
| |
| event_generator->MoveMouseTo(window()->GetBoundsInScreen().origin()); |
| EXPECT_EQ(controller->capture_mode_session()->GetSelectedWindow(), window()); |
| StartVideoRecordingImmediately(); |
| |
| EXPECT_FALSE(camera_controller->is_camera_preview_collapsed()); |
| EXPECT_TRUE(camera_preview_view->is_collapsible()); |
| |
| // Press shortcut "Search+Alt+S" should focus the camera preview. |
| SendKey(ui::VKEY_S, event_generator, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| |
| // Press tab should fade in the resize button and advance the focus on it. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_FALSE(camera_preview_view->has_focus()); |
| EXPECT_TRUE(resize_button->has_focus()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| // Shift tab should advance the focus back to the camera preview view. But the |
| // resize button will be kept as visible as it will be fade out in a few |
| // seconds. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_SHIFT_DOWN); |
| EXPECT_TRUE(camera_preview_view->has_focus()); |
| EXPECT_FALSE(resize_button->has_focus()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CaptureBarOpacityChangeOnKeyboardNavigation) { |
| using FocusGroup = CaptureModeSessionFocusCycler::FocusGroup; |
| |
| // Update display size and update window with customized size to make sure |
| // camera preview overlap with capture bar with capture source `kWindow`. |
| UpdateDisplay("1366x768"); |
| window()->SetBounds({0, 0, 903, 700}); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); |
| CaptureModeSessionTestApi test_api(controller->capture_mode_session()); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| const auto* camera_preview_widget = |
| camera_controller->camera_preview_widget(); |
| const auto* capture_bar_widget = capture_session->capture_mode_bar_widget(); |
| const ui::Layer* capture_bar_layer = capture_bar_widget->GetLayer(); |
| |
| // Move mouse on top of `window` to set the selected window. Verify capture |
| // bar is `kOverlapOpacity`. |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseTo(window()->GetBoundsInScreen().CenterPoint()); |
| EXPECT_EQ(capture_session->GetSelectedWindow(), window()); |
| EXPECT_TRUE(capture_bar_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| // Now tab through the capture bar, verify that as long as the focus is on |
| // capture bar or capture settings menu, capture bar is updated to full |
| // opaque. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), 1.0f); |
| |
| // Tab four times to focus the last source button (window mode button). Verify |
| // capture bar is still fully opaque. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/4); |
| EXPECT_EQ(FocusGroup::kTypeSource, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(4u, test_api.GetCurrentFocusIndex()); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), 1.0f); |
| |
| // Tab once to focus on the window to be captured, verify that capture bar is |
| // `kOverlapOpacity` since the focus is removed from capture bar. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kCaptureWindow, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| // Tab three times to focus on the settings button, verify capture bar is |
| // updated to fully opaque again. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/3); |
| EXPECT_EQ(FocusGroup::kSettingsClose, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(0u, test_api.GetCurrentFocusIndex()); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), 1.0f); |
| |
| // Now enter space to open the settings menu. Verify capture bar is still full |
| // opaque. |
| SendKey(ui::VKEY_SPACE, event_generator); |
| EXPECT_EQ(FocusGroup::kPendingSettings, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), 1.0f); |
| |
| // Tab once to focus on the settings menu. Verify capture bar is still full |
| // opaque. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kSettingsMenu, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(capture_bar_layer->GetTargetOpacity(), 1.0f); |
| } |
| |
| TEST_F(CaptureModeCameraTest, CaptureLabelOpacityChangeOnKeyboardNavigation) { |
| UpdateDisplay("800x600"); |
| using FocusGroup = CaptureModeSessionFocusCycler::FocusGroup; |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| CaptureModeSessionTestApi test_api(controller->capture_mode_session()); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* camera_preview_widget = camera_controller->camera_preview_widget(); |
| auto* capture_label_widget = capture_session->capture_label_widget(); |
| ui::Layer* capture_label_layer = capture_label_widget->GetLayer(); |
| |
| // Select capture region to make sure capture label is overlapped with |
| // camera preview. Verify capture label is `kOverlapOpacity`. |
| const int min_region_length = |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera; |
| SelectCaptureRegion({100, 100, min_region_length, min_region_length}); |
| EXPECT_TRUE(capture_label_widget->GetWindowBoundsInScreen().Intersects( |
| camera_preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| auto* event_generator = GetEventGenerator(); |
| // Tab four times to focus on the region capture source, verify capture label |
| // is still `kOverlapOpacity`. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/4); |
| EXPECT_EQ(FocusGroup::kTypeSource, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(3u, test_api.GetCurrentFocusIndex()); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| // Tab twice to focus on `kSelection`, verify capture label is still |
| // `kOverlapOpacity`. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/2); |
| EXPECT_EQ(FocusGroup::kSelection, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| |
| // Tab eleven times to focus on cpature label, verify capture label is updated |
| // to fully opaque. |
| EXPECT_FALSE(camera_controller->camera_preview_view()->is_collapsible()); |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/10); |
| EXPECT_EQ(FocusGroup::kCaptureButton, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), 1.0f); |
| |
| // Tab once to focus on the setting button on capture bar, verify capture |
| // lable's opacity is updated to `kOverlapOpacity`. |
| SendKey(ui::VKEY_TAB, event_generator); |
| EXPECT_EQ(FocusGroup::kSettingsClose, test_api.GetCurrentFocusGroup()); |
| EXPECT_EQ(capture_label_layer->GetTargetOpacity(), |
| capture_mode::kCaptureUiOverlapOpacity); |
| } |
| |
| // Tests that when switching capture source from `kRegion` to `kFullscreen`, |
| // camera preview should be shown. |
| // Regression test for https://crbug.com/1316911. |
| TEST_F(CaptureModeCameraTest, CameraPreviewVisibilityOnCaptureSourceChanged) { |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| AddDefaultCamera(); |
| CaptureModeTestApi().SelectCameraAtIndex(0); |
| auto* camera_preview_widget = GetCameraController()->camera_preview_widget(); |
| auto* preview_window = camera_preview_widget->GetNativeWindow(); |
| |
| // Verify that camera preview is visible. |
| EXPECT_EQ(preview_window->parent(), |
| preview_window->GetRootWindow()->GetChildById( |
| kShellWindowId_MenuContainer)); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_TRUE(preview_window->TargetVisibility()); |
| |
| // Click on the region source button, verify that camera preview is parented |
| // to UnparentedContainer and becomes invisible. |
| auto* event_generator = GetEventGenerator(); |
| ClickOnView(GetRegionToggleButton(), event_generator); |
| EXPECT_EQ(preview_window->parent(), |
| preview_window->GetRootWindow()->GetChildById( |
| kShellWindowId_UnparentedContainer)); |
| EXPECT_FALSE(camera_preview_widget->IsVisible()); |
| |
| // Now switch capture source to `kFullscreen`, verify that camera preview is |
| // parented to MenuContainer and becomes visible again. |
| ClickOnView(GetFullscreenToggleButton(), event_generator); |
| EXPECT_EQ(preview_window->parent(), |
| preview_window->GetRootWindow()->GetChildById( |
| kShellWindowId_MenuContainer)); |
| EXPECT_TRUE(preview_window->TargetVisibility()); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| } |
| |
| // Tests that the recording starts with camera metrics are recorded correctly |
| // both in clamshell and tablet mode. |
| TEST_F(CaptureModeCameraTest, RecordingStartsWithCameraHistogramTest) { |
| base::HistogramTester histogram_tester; |
| constexpr char kHistogramNameBase[] = |
| "Ash.CaptureModeController.RecordingStartsWithCamera"; |
| |
| AddDefaultCamera(); |
| |
| struct { |
| bool tablet_enabled; |
| bool camera_on; |
| } kTestCases[] = { |
| {false, false}, |
| {false, true}, |
| {true, false}, |
| {true, true}, |
| }; |
| |
| for (const auto test_case : kTestCases) { |
| if (test_case.tablet_enabled) { |
| SwitchToTabletMode(); |
| EXPECT_TRUE(Shell::Get()->IsInTabletMode()); |
| } else { |
| EXPECT_FALSE(Shell::Get()->IsInTabletMode()); |
| } |
| |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramNameBase), test_case.camera_on, |
| 0); |
| |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| camera_controller->SetSelectedCamera( |
| test_case.camera_on ? CameraId(kDefaultCameraModelId, 1) : CameraId()); |
| |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramNameBase), test_case.camera_on, |
| 1); |
| } |
| } |
| |
| // Tests that the number of camera disconnections happens during recording is |
| // recorded correctly both in clamshell and tablet mode. |
| TEST_F(CaptureModeCameraTest, |
| RecordCameraDisconnectionsDuringRecordingsHistogramTest) { |
| constexpr char kHistogramNameBase[] = |
| "Ash.CaptureModeController.CameraDisconnectionsDuringRecordings"; |
| base::HistogramTester histogram_tester; |
| |
| auto* camera_controller = GetCameraController(); |
| |
| auto disconnect_and_reconnect_camera_n_times = [&](int n) { |
| for (int i = 0; i < n; i++) { |
| AddAndRemoveCameraAndTriggerGracePeriod(); |
| camera_controller->camera_reconnect_timer_for_test()->FireNow(); |
| } |
| }; |
| |
| for (const bool tablet_enabled : {false, true}) { |
| if (tablet_enabled) { |
| SwitchToTabletMode(); |
| EXPECT_TRUE(Shell::Get()->IsInTabletMode()); |
| } else { |
| EXPECT_FALSE(Shell::Get()->IsInTabletMode()); |
| } |
| |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| controller->StartVideoRecordingImmediatelyForTesting(); |
| |
| // Disconnect the camera, exhaust the timer and reconnect three times. The |
| // metric should record accordingly. |
| disconnect_and_reconnect_camera_n_times(3); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramNameBase), 3, 1); |
| } |
| } |
| |
| // Tests that the number of connected cameras to the device is recorded whenever |
| // the number changes. |
| TEST_F(CaptureModeCameraTest, RecordNumberOfConnectedCamerasHistogramTest) { |
| constexpr char kHistogramNameBase[] = |
| "Ash.CaptureModeController.NumberOfConnectedCameras"; |
| |
| base::HistogramTester histogram_tester; |
| // Make sure the device change alert triggered by the SystemMonitor is handled |
| // before we connect a camera device. |
| { |
| base::RunLoop loop; |
| GetCameraController()->SetOnCameraListReceivedForTesting( |
| loop.QuitClosure()); |
| base::SystemMonitor::Get()->ProcessDevicesChanged( |
| base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE); |
| loop.Run(); |
| } |
| |
| // Verify that before we connect any camera device, there's 0 cameras and it |
| // has been recorded. |
| histogram_tester.ExpectBucketCount(kHistogramNameBase, 0, 1); |
| |
| // Connect one camera, verify that the number of one camera device has been |
| // recorded once. |
| AddFakeCamera("/dev/video", "fake cam ", "model 1"); |
| histogram_tester.ExpectBucketCount(kHistogramNameBase, 1, 1); |
| |
| // Connect the second camera, verify that the number of two camera devices has |
| // been recorded once. |
| AddFakeCamera("/dev/video1", "fake cam 2", "model 2"); |
| histogram_tester.ExpectBucketCount(kHistogramNameBase, 2, 1); |
| |
| // Disconnect the second camera, now the number of connected cameres drops |
| // back to one, verify that the number of one camera device has been recorded |
| // twice. |
| RemoveFakeCamera("/dev/video1"); |
| histogram_tester.ExpectBucketCount(kHistogramNameBase, 1, 2); |
| |
| // Connect the third camera, now the number of connected cameras is two again, |
| // verify that the number of two camera devices has been recorded twice. |
| AddFakeCamera("/dev/video2", "fake cam 3", "model 3"); |
| histogram_tester.ExpectBucketCount(kHistogramNameBase, 2, 2); |
| } |
| |
| // Tests that the duration for disconnected camera to become available again is |
| // recorded correctly both in clamshell and tablet mode. |
| TEST_F(CaptureModeCameraTest, RecordCameraReconnectDurationHistogramTest) { |
| constexpr char kHistogramNameBase[] = |
| "Ash.CaptureModeController.CameraReconnectDuration"; |
| base::HistogramTester histogram_tester; |
| |
| for (const bool tablet_enabled : {false, true}) { |
| if (tablet_enabled) { |
| SwitchToTabletMode(); |
| EXPECT_TRUE(Shell::Get()->IsInTabletMode()); |
| } else { |
| EXPECT_FALSE(Shell::Get()->IsInTabletMode()); |
| } |
| |
| AddAndRemoveCameraAndTriggerGracePeriod(); |
| WaitForSeconds(1); |
| AddDefaultCamera(); |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramNameBase), 1, 1); |
| RemoveDefaultCamera(); |
| } |
| } |
| |
| // Tests that the camera size on start is recorded correctly in the metrics both |
| // in clamshell and tablet mode. |
| TEST_F(CaptureModeCameraTest, RecordingCameraSizeOnStartHistogramTest) { |
| UpdateDisplay("1366x768"); |
| |
| constexpr char kHistogramNameBase[] = |
| "Ash.CaptureModeController.RecordingCameraSizeOnStart"; |
| base::HistogramTester histogram_tester; |
| |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| for (const bool tablet_enabled : {false, true}) { |
| if (tablet_enabled) { |
| SwitchToTabletMode(); |
| EXPECT_TRUE(Shell::Get()->IsInTabletMode()); |
| } else { |
| EXPECT_FALSE(Shell::Get()->IsInTabletMode()); |
| } |
| |
| for (const bool collapsed : {false, true}) { |
| const auto sample = collapsed ? CaptureModeCameraSize::kCollapsed |
| : CaptureModeCameraSize::kExpanded; |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramNameBase), sample, 0); |
| |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| |
| auto* event_generator = GetEventGenerator(); |
| |
| // Resize button is hidden by default, click/tap on the preview to make |
| // it visible. |
| ClickOrTapView(camera_controller->camera_preview_view(), tablet_enabled, |
| event_generator); |
| |
| auto* resize_button = GetPreviewResizeButton(); |
| DCHECK(resize_button); |
| |
| if (collapsed) { |
| if (!camera_controller->is_camera_preview_collapsed()) |
| ClickOrTapView(resize_button, tablet_enabled, event_generator); |
| |
| EXPECT_TRUE(camera_controller->is_camera_preview_collapsed()); |
| } else { |
| if (camera_controller->is_camera_preview_collapsed()) |
| ClickOrTapView(resize_button, tablet_enabled, event_generator); |
| |
| EXPECT_FALSE(camera_controller->is_camera_preview_collapsed()); |
| } |
| |
| StartVideoRecordingImmediately(); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramNameBase), sample, 1); |
| } |
| } |
| } |
| |
| // Tests that the camera position on start is recorded correctly in the metrics |
| // both in clamshell and tablet mode. |
| TEST_F(CaptureModeCameraTest, RecordingCameraPositionOnStartHistogramTest) { |
| constexpr char kHistogramName[] = |
| "Ash.CaptureModeController.RecordingCameraPositionOnStart"; |
| base::HistogramTester histogram_tester; |
| |
| StartCaptureSession(CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| AddDefaultCamera(); |
| auto* camera_controller = GetCameraController(); |
| const CameraId camera_id(kDefaultCameraModelId, 1); |
| camera_controller->SetSelectedCamera(camera_id); |
| |
| const CameraPreviewSnapPosition kCameraPositionTestCases[]{ |
| CameraPreviewSnapPosition::kTopLeft, |
| CameraPreviewSnapPosition::kBottomLeft, |
| CameraPreviewSnapPosition::kTopRight, |
| CameraPreviewSnapPosition::kBottomRight}; |
| |
| for (const bool tablet_enabled : {false, true}) { |
| if (tablet_enabled) { |
| SwitchToTabletMode(); |
| EXPECT_TRUE(Shell::Get()->IsInTabletMode()); |
| } else { |
| EXPECT_FALSE(Shell::Get()->IsInTabletMode()); |
| } |
| |
| for (const auto camera_position : kCameraPositionTestCases) { |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramName), camera_position, 0); |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| DCHECK(camera_controller->camera_preview_widget()); |
| camera_controller->SetCameraPreviewSnapPosition(camera_position); |
| StartVideoRecordingImmediately(); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramName), camera_position, 1); |
| } |
| } |
| } |
| |
| TEST_F(CaptureModeCameraTest, ToastVisibilityChangeOnCaptureRegionUpdated) { |
| UpdateDisplay("800x600"); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| auto* capture_toast_controller = capture_session->capture_toast_controller(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Set capture region big enough to fit the camera preview. Verify the |
| // current capture toast is `kUserNudge`. |
| const gfx::Rect capture_region(100, 100, 300, 300); |
| SelectCaptureRegion(capture_region); |
| auto* capture_toast_widget = capture_toast_controller->capture_toast_widget(); |
| EXPECT_TRUE(capture_toast_widget); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kUserNudge); |
| |
| // Update capture region small enough to not fit the camera preview. Verify |
| // that the capture toast is updated to `kCameraPreview` and the user nudge is |
| // dismissed forever. |
| const int delta_x = |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera - 30 - |
| capture_region.width(); |
| const int delta_y = |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera - 30 - |
| capture_region.height(); |
| const gfx::Vector2d delta(delta_x, delta_y); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->set_current_screen_location(capture_region.bottom_right()); |
| event_generator->PressLeftButton(); |
| // Verify that when drag starts, the capture toast is hidden. |
| EXPECT_FALSE(capture_toast_widget->IsVisible()); |
| event_generator->MoveMouseTo(capture_region.bottom_right() + delta); |
| event_generator->ReleaseLeftButton(); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| EXPECT_FALSE(GetUserNudgeController()); |
| |
| // Start dragging the capture region again to update it, but make it still |
| // small enough to not fit the camera preview. Verify at the beginning of the |
| // drag, preview toast is hidden. After the drag is released, preview toast is |
| // shown again. |
| const gfx::Vector2d delta1(delta_x + 10, delta_y + 10); |
| event_generator->set_current_screen_location(capture_region.bottom_right()); |
| event_generator->PressLeftButton(); |
| // Verify that when drag starts, the capture toast is hidden. |
| EXPECT_FALSE(capture_toast_widget->IsVisible()); |
| event_generator->MoveMouseTo(capture_region.bottom_right() + delta1); |
| event_generator->ReleaseLeftButton(); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| |
| // Update capture region big enough to show the camera preview. Verify the |
| // preview toast is hidden. |
| event_generator->set_current_screen_location(capture_region.origin()); |
| event_generator->PressLeftButton(); |
| // Verify that when drag starts, the capture toast is hidden. |
| EXPECT_FALSE(capture_toast_widget->IsVisible()); |
| event_generator->MoveMouseTo(capture_region.bottom_right()); |
| event_generator->ReleaseLeftButton(); |
| // Verify that since the capture toast is dismissed, current toast type is |
| // reset. |
| EXPECT_FALSE(capture_toast_controller->current_toast_type()); |
| EXPECT_FALSE(capture_toast_widget->IsVisible()); |
| } |
| |
| // Tests that the capture toast will be faded out on time out when there are no |
| // actions taken. |
| TEST_F(CaptureModeCameraTest, ToastVisibilityChangeOnTimeOut) { |
| UpdateDisplay("800x600"); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| auto* capture_toast_controller = capture_session->capture_toast_controller(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Set capture region small enough to not fit the camera preview. Verify the |
| // current capture toast is `kCameraPreview`. |
| const gfx::Rect capture_region = GetTooSmallToFitCameraRegion(); |
| SelectCaptureRegion(capture_region); |
| auto* capture_toast_widget = capture_toast_controller->capture_toast_widget(); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| |
| // Verify the timer is running after the toast is shown and when the timer is |
| // fired, the capture toast is hidden. |
| base::OneShotTimer* timer = |
| capture_toast_controller->capture_toast_dismiss_timer_for_test(); |
| EXPECT_TRUE(timer->IsRunning()); |
| timer->FireNow(); |
| EXPECT_FALSE(capture_toast_widget->IsVisible()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, ToastVisibilityChangeOnSettingsMenuOpen) { |
| UpdateDisplay("800x600"); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| auto* capture_toast_controller = capture_session->capture_toast_controller(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Set capture region small enough to not fit the camera preview. Verify the |
| // current capture toast is `kCameraPreview`. |
| const gfx::Rect capture_region = GetTooSmallToFitCameraRegion(); |
| SelectCaptureRegion(capture_region); |
| auto* capture_toast_widget = capture_toast_controller->capture_toast_widget(); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| |
| // Now open settings menu, verify that preview toast is dismissed immediately |
| // on settings menu open. |
| OpenSettingsView(); |
| EXPECT_FALSE(capture_toast_widget->IsVisible()); |
| } |
| |
| TEST_F(CaptureModeCameraTest, ToastVisibilityChangeOnCaptureRegionMoved) { |
| UpdateDisplay("800x600"); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| auto* capture_toast_controller = capture_session->capture_toast_controller(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Set capture region small enough to not fit the camera preview. Verify the |
| // current capture toast is `kCameraPreview`. |
| const gfx::Rect capture_region = GetTooSmallToFitCameraRegion(); |
| SelectCaptureRegion(capture_region); |
| auto* capture_toast_widget = capture_toast_controller->capture_toast_widget(); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| |
| // Start moving the capture region, verify the preview toast is hidden at the |
| // beginning of the move and is shown once the move is done. |
| const gfx::Vector2d delta(20, 20); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseTo(capture_region.origin() + delta); |
| event_generator->PressLeftButton(); |
| EXPECT_FALSE(capture_toast_widget->IsVisible()); |
| event_generator->MoveMouseTo(capture_region.CenterPoint()); |
| event_generator->ReleaseLeftButton(); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| } |
| |
| // Tests that the preview toast shows correctly when capture mode is turned on |
| // through quick settings which keeps the configurations) from the previous |
| // session. |
| TEST_F(CaptureModeCameraTest, ToastVisibilityChangeOnCaptureModeTurnedOn) { |
| UpdateDisplay("800x600"); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| auto* capture_toast_controller = capture_session->capture_toast_controller(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Set capture region small enough to not fit the camera preview. Verify the |
| // current capture toast is `kCameraPreview`. |
| const gfx::Rect capture_region = GetTooSmallToFitCameraRegion(); |
| SelectCaptureRegion(capture_region); |
| auto* capture_toast_widget = capture_toast_controller->capture_toast_widget(); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| |
| // Close capture mode. |
| controller->Stop(); |
| |
| // Turn on capture mode again through the quick settings, verify that toast |
| // preview is visible. |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| capture_session = controller->capture_mode_session(); |
| capture_toast_controller = capture_session->capture_toast_controller(); |
| EXPECT_TRUE(capture_toast_controller->capture_toast_widget()->IsVisible()); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| } |
| |
| TEST_F(CaptureModeCameraTest, ToastStackingOrderChangeOnCaptureModeTurnedOn) { |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Set capture region small enough to make capture toast shown. |
| const gfx::Rect capture_region = GetTooSmallToFitCameraRegion(); |
| SelectCaptureRegion(capture_region); |
| |
| // Close capture mode. |
| controller->Stop(); |
| |
| // Turn on capture mode again through the quick settings, verify that the |
| // stacking order for capture toast relative to other capture UIs is correct. |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* capture_toast_controller = capture_session->capture_toast_controller(); |
| auto* capture_toast_widget = capture_toast_controller->capture_toast_widget(); |
| auto* capture_toast_window = capture_toast_widget->GetNativeWindow(); |
| auto* capture_label_window = |
| capture_session->capture_label_widget()->GetNativeWindow(); |
| auto* capture_bar_window = |
| capture_session->capture_mode_bar_widget()->GetNativeWindow(); |
| auto* camera_preview_window = |
| camera_controller->camera_preview_widget()->GetNativeWindow(); |
| |
| EXPECT_TRUE( |
| IsWindowStackedRightBelow(capture_label_window, capture_bar_window)); |
| EXPECT_TRUE( |
| IsWindowStackedRightBelow(capture_toast_window, capture_label_window)); |
| EXPECT_TRUE( |
| IsWindowStackedRightBelow(camera_preview_window, capture_toast_window)); |
| EXPECT_TRUE(IsLayerStackedRightBelow(capture_session->layer(), |
| camera_preview_window->layer())); |
| } |
| |
| TEST_F(CaptureModeCameraTest, ToastVisibilityChangeOnPerformingCapture) { |
| UpdateDisplay("800x600"); |
| |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* camera_controller = GetCameraController(); |
| auto* capture_toast_controller = capture_session->capture_toast_controller(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Set capture region small enough to not fit the camera preview. Verify the |
| // current capture toast is `kCameraPreview`. |
| const gfx::Rect capture_region = GetTooSmallToFitCameraRegion(); |
| SelectCaptureRegion(capture_region); |
| auto* capture_toast_widget = capture_toast_controller->capture_toast_widget(); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| |
| // Perform the screen recording, verify that the capture toast is going to be |
| // faded out. |
| controller->PerformCapture(); |
| EXPECT_EQ(capture_toast_widget->GetLayer()->GetTargetOpacity(), 0.f); |
| } |
| |
| TEST_F(CaptureModeCameraTest, ToastVisibilityChangeOnMultiDisplays) { |
| UpdateDisplay("800x700,801+0-800x700"); |
| const gfx::Rect first_display_bounds(0, 0, 800, 700); |
| const gfx::Rect second_display_bounds(801, 0, 800, 700); |
| |
| // Set the window's bounds small enough to not fit the camera preview. |
| window()->SetBoundsInScreen( |
| gfx::Rect(600, 500, 100, 100), |
| display::Screen::GetScreen()->GetDisplayNearestWindow( |
| Shell::GetAllRootWindows()[0])); |
| |
| // Create a window in the second display and set its bounds small enough to |
| // not fit the camera preview. |
| std::unique_ptr<aura::Window> window1(CreateTestWindow()); |
| window1->SetBoundsInScreen( |
| gfx::Rect(1400, 500, 100, 100), |
| display::Screen::GetScreen()->GetDisplayNearestWindow( |
| Shell::GetAllRootWindows()[1])); |
| |
| // Start the capture session. |
| auto* controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); |
| auto* camera_controller = GetCameraController(); |
| auto* capture_session = controller->capture_mode_session(); |
| auto* capture_toast_controller = capture_session->capture_toast_controller(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Move the mouse on top of `window` to select it, since it's too small to fit |
| // the camera preview, verify the preview toast shows and it's on the first |
| // display. |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseTo(window()->GetBoundsInScreen().CenterPoint()); |
| EXPECT_EQ(capture_session->GetSelectedWindow(), window()); |
| auto* capture_toast_widget = capture_toast_controller->capture_toast_widget(); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| first_display_bounds.Contains( |
| capture_toast_widget->GetWindowBoundsInScreen()); |
| |
| // Now move the mouse to the top of `window1`, since it's also too small to |
| // fit the camera preview, verify the preview toast still shows. Since |
| // `window1` is on the second display, verify the preview toast also shows up |
| // on the second display. |
| event_generator->MoveMouseTo(window1->GetBoundsInScreen().CenterPoint()); |
| EXPECT_EQ(capture_session->GetSelectedWindow(), window1.get()); |
| EXPECT_TRUE(capture_toast_widget->IsVisible()); |
| ASSERT_TRUE(capture_toast_controller->current_toast_type()); |
| EXPECT_EQ(*(capture_toast_controller->current_toast_type()), |
| CaptureToastType::kCameraPreview); |
| second_display_bounds.Contains( |
| capture_toast_widget->GetWindowBoundsInScreen()); |
| |
| // Move mouse to the outside of `window1`, verify that preview toast is |
| // dismissed since there's no window selected for now. |
| event_generator->MoveMouseTo({1300, 500}); |
| EXPECT_FALSE(capture_toast_widget->IsVisible()); |
| |
| // Update the bounds of `window` big enough to fit the camera preview. |
| window()->SetBoundsInScreen( |
| gfx::Rect(100, 200, 300, 300), |
| display::Screen::GetScreen()->GetDisplayNearestWindow( |
| Shell::GetAllRootWindows()[0])); |
| |
| // Now move the mouse to the top of `window` again, verify that preview toast |
| // is not shown, since the window is big enough to show the camera preview. |
| event_generator->MoveMouseTo(window()->GetBoundsInScreen().CenterPoint()); |
| EXPECT_EQ(capture_session->GetSelectedWindow(), window()); |
| EXPECT_FALSE(capture_toast_widget->IsVisible()); |
| } |
| |
| class CaptureModeCameraPreviewTest |
| : public CaptureModeCameraTest, |
| public testing::WithParamInterface<CaptureModeSource> { |
| public: |
| enum class CameraPreviewState { |
| // The camera preview is shown inside an area that makes its expanded size |
| // big enough so it can collapse to a smaller size. |
| kCollapsible, |
| |
| // The camera preview is shown inside an area that is small enough to |
| // disable its collapsability without causing it to hide. |
| kNotCollapsible, |
| |
| // The camera preview is shown inside an area that is too small for it to |
| // show at all. |
| kHidden, |
| }; |
| |
| CaptureModeCameraPreviewTest() = default; |
| CaptureModeCameraPreviewTest(const CaptureModeCameraPreviewTest&) = delete; |
| CaptureModeCameraPreviewTest& operator=(const CaptureModeCameraPreviewTest&) = |
| delete; |
| ~CaptureModeCameraPreviewTest() override = default; |
| |
| void StartCaptureSessionWithParam() { |
| auto* controller = CaptureModeController::Get(); |
| const gfx::Rect capture_region(10, 20, 1300, 750); |
| controller->SetUserCaptureRegion(capture_region, /*by_user=*/true); |
| // Set the window's bounds big enough here to make sure after display |
| // rotation, the event is located on top of `window_`. |
| // TODO(conniekxu): investigate why the position of the event received is |
| // different than the position we pass. |
| window()->SetBounds({30, 40, 1300, 750}); |
| |
| StartCaptureSession(GetParam(), CaptureModeType::kVideo); |
| if (GetParam() == CaptureModeSource::kWindow) |
| GetEventGenerator()->MoveMouseToCenterOf(window()); |
| } |
| |
| gfx::Size GetMinSurfaceSizeForCollapsibleCamera() const { |
| const int min_length = capture_mode::kMinCollapsibleCameraPreviewDiameter * |
| capture_mode::kCaptureSurfaceShortSideDivider; |
| return gfx::Size(min_length, min_length); |
| } |
| |
| gfx::Size GetMinSurfaceSizeSoCameraBecomes( |
| CameraPreviewState preview_state) const { |
| gfx::Size min_size = GetMinSurfaceSizeForCollapsibleCamera(); |
| switch (preview_state) { |
| case CameraPreviewState::kCollapsible: |
| min_size.Enlarge(10, 20); |
| break; |
| case CameraPreviewState::kNotCollapsible: |
| min_size.Enlarge(-10, -20); |
| break; |
| case CameraPreviewState::kHidden: |
| const int length_for_hidden = |
| capture_mode::kMinCaptureSurfaceShortSideLengthForVisibleCamera - 5; |
| min_size.SetSize(length_for_hidden, length_for_hidden - 5); |
| break; |
| } |
| return min_size; |
| } |
| |
| void ResizeDisplaySoCameraPreviewBecomes(CameraPreviewState preview_state) { |
| gfx::Size min_size = GetMinSurfaceSizeSoCameraBecomes(preview_state); |
| const int shelf_size = ShelfConfig::Get()->shelf_size(); |
| min_size.Enlarge(shelf_size, shelf_size); |
| UpdateDisplay(min_size.ToString()); |
| } |
| |
| void ResizeRegionSoCameraPreviewBecomes(CameraPreviewState preview_state) { |
| CaptureModeController::Get()->SetUserCaptureRegion( |
| gfx::Rect(GetMinSurfaceSizeSoCameraBecomes(preview_state)), |
| /*by_user=*/true); |
| } |
| |
| void ResizeWindowSoCameraPreviewBecomes(CameraPreviewState preview_state) { |
| window()->SetBounds( |
| gfx::Rect(GetMinSurfaceSizeSoCameraBecomes(preview_state))); |
| } |
| |
| void ResizeSurfaceSoCameraPreviewBecomes(CameraPreviewState preview_state) { |
| switch (GetParam()) { |
| case CaptureModeSource::kFullscreen: |
| ResizeDisplaySoCameraPreviewBecomes(preview_state); |
| break; |
| case CaptureModeSource::kRegion: |
| ResizeRegionSoCameraPreviewBecomes(preview_state); |
| break; |
| case CaptureModeSource::kWindow: |
| ResizeWindowSoCameraPreviewBecomes(preview_state); |
| break; |
| } |
| } |
| |
| // Based on the `CaptureModeSource`, it returns the current capture region's |
| // bounds in screen. |
| gfx::Rect GetCaptureBoundsInScreen() const { |
| auto* controller = CaptureModeController::Get(); |
| auto* root = GetCurrentRoot(); |
| |
| switch (GetParam()) { |
| case CaptureModeSource::kFullscreen: |
| return display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(root) |
| .work_area(); |
| |
| case CaptureModeSource::kRegion: { |
| auto* recording_watcher = |
| controller->video_recording_watcher_for_testing(); |
| gfx::Rect capture_region = |
| controller->is_recording_in_progress() |
| ? recording_watcher->GetEffectivePartialRegionBounds() |
| : controller->user_capture_region(); |
| wm::ConvertRectToScreen(root, &capture_region); |
| return capture_region; |
| } |
| |
| case CaptureModeSource::kWindow: |
| return window()->GetBoundsInScreen(); |
| } |
| } |
| |
| gfx::Size GetExpectedPreviewSize(bool collapsed) const { |
| return capture_mode_util::CalculateCameraPreviewSizeSpecs( |
| GetCaptureBoundsInScreen().size(), collapsed) |
| .size; |
| } |
| |
| // Returns the cursor type when cursor is on top of the current capture |
| // surface. |
| ui::mojom::CursorType GetCursorTypeOnCaptureSurface() const { |
| DCHECK(CaptureModeController::Get()->IsActive()); |
| |
| switch (GetParam()) { |
| case CaptureModeSource::kFullscreen: |
| case CaptureModeSource::kWindow: |
| return ui::mojom::CursorType::kCustom; |
| case CaptureModeSource::kRegion: |
| return ui::mojom::CursorType::kMove; |
| } |
| } |
| }; |
| |
| TEST_P(CaptureModeCameraPreviewTest, PreviewVisibilityWhileFolderSelection) { |
| AddDefaultCamera(); |
| StartCaptureSessionWithParam(); |
| CaptureModeTestApi().SelectCameraAtIndex(0); |
| |
| // The camera preview should be initially visible. |
| auto* controller = CaptureModeController::Get(); |
| ASSERT_TRUE(controller->IsActive()); |
| auto* preview_widget = GetCameraController()->camera_preview_widget(); |
| ASSERT_TRUE(preview_widget); |
| EXPECT_TRUE(preview_widget->IsVisible()); |
| |
| // Click on the settings button, the settings menu should open, and the camera |
| // preview should remain visible. |
| CaptureModeSessionTestApi session_test_api( |
| controller->capture_mode_session()); |
| auto* settings_button = |
| session_test_api.GetCaptureModeBarView()->settings_button(); |
| auto* event_generator = GetEventGenerator(); |
| ClickOnView(settings_button, event_generator); |
| ASSERT_TRUE(session_test_api.GetCaptureModeSettingsWidget()); |
| EXPECT_TRUE(preview_widget->IsVisible()); |
| |
| // Click on the "Select folder ..." option, the folder selection dialog should |
| // open, all capture UIs should hide, including the camera preview. |
| CaptureModeSettingsTestApi settings_test_api; |
| ClickOnView(settings_test_api.GetSelectFolderMenuItem(), event_generator); |
| EXPECT_TRUE(session_test_api.IsFolderSelectionDialogShown()); |
| EXPECT_FALSE(session_test_api.IsAllUisVisible()); |
| EXPECT_FALSE(preview_widget->IsVisible()); |
| |
| // Dismiss the folder selection dialog, all capture UIs should show again, |
| // including the camera preview. |
| FakeFolderSelectionDialogFactory::Get()->CancelDialog(); |
| EXPECT_FALSE(session_test_api.IsFolderSelectionDialogShown()); |
| EXPECT_TRUE(session_test_api.IsAllUisVisible()); |
| EXPECT_TRUE(preview_widget->IsVisible()); |
| } |
| |
| // Tests that camera preview's bounds is updated after display rotations with |
| // two use cases, when capture session is active and when there's a video |
| // recording in progress. |
| TEST_P(CaptureModeCameraPreviewTest, DisplayRotation) { |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Verify that the camera preview should be at the bottom right corner of |
| // capture bounds. |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| |
| // Rotate the primary display by 90 degrees. Verify that the camera preview |
| // is still at the bottom right corner of capture bounds. |
| Shell::Get()->display_manager()->SetDisplayRotation( |
| WindowTreeHostManager::GetPrimaryDisplayId(), display::Display::ROTATE_90, |
| display::Display::RotationSource::USER); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| |
| // Start video recording, verify camera preview's bounds is updated after |
| // display rotations when there's a video recording in progress. |
| StartVideoRecordingImmediately(); |
| EXPECT_FALSE(CaptureModeController::Get()->IsActive()); |
| |
| // Rotate the primary display by 180 degrees. Verify that the camera preview |
| // is still at the bottom right corner of capture bounds. |
| Shell::Get()->display_manager()->SetDisplayRotation( |
| WindowTreeHostManager::GetPrimaryDisplayId(), |
| display::Display::ROTATE_180, display::Display::RotationSource::USER); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| |
| // Rotate the primary display by 270 degrees. Verify that the camera preview |
| // is still at the bottom right corner of capture bounds. |
| Shell::Get()->display_manager()->SetDisplayRotation( |
| WindowTreeHostManager::GetPrimaryDisplayId(), |
| display::Display::ROTATE_270, display::Display::RotationSource::USER); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| } |
| |
| // Tests that when camera preview is being dragged, at the end of the drag, it |
| // should be snapped to the correct snap position. It tests two use cases, |
| // when capture session is active and when there's a video recording in |
| // progress including drag to snap by mouse and by touch. |
| TEST_P(CaptureModeCameraPreviewTest, CameraPreviewDragToSnap) { |
| UpdateDisplay("1600x800"); |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Point capture_bounds_center_point = |
| GetCaptureBoundsInScreen().CenterPoint(); |
| |
| // Verify that by default the snap position should be `kBottomRight` and |
| // camera preview is placed at the correct position. |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomRight, |
| camera_controller->camera_preview_snap_position()); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| |
| // Drag the camera preview for a small distance. Tests that even though the |
| // snap position does not change, the preview should be snapped back to its |
| // previous position. |
| DragPreviewToPoint(preview_widget, {capture_bounds_center_point.x() + 20, |
| capture_bounds_center_point.y() + 20}); |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomRight, |
| camera_controller->camera_preview_snap_position()); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| |
| // Drag and drop camera preview by mouse to the top right of the |
| // `capture_bounds_center_point`, verify that camera preview is snapped to |
| // the top right with correct position. |
| DragPreviewToPoint(preview_widget, {capture_bounds_center_point.x() + 20, |
| capture_bounds_center_point.y() - 20}); |
| EXPECT_EQ(CameraPreviewSnapPosition::kTopRight, |
| camera_controller->camera_preview_snap_position()); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| |
| // Now drag and drop camera preview by touch to the top left of the center |
| // point, verify that camera preview is snapped to the top left with correct |
| // position. |
| DragPreviewToPoint(preview_widget, |
| {capture_bounds_center_point.x() - 20, |
| capture_bounds_center_point.y() - 20}, |
| /*by_touch_gestures=*/true); |
| EXPECT_EQ(CameraPreviewSnapPosition::kTopLeft, |
| camera_controller->camera_preview_snap_position()); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| |
| // Start video recording, verify camera preview is snapped to the correct |
| // snap position at the end of drag when there's a video recording in |
| // progress. |
| StartVideoRecordingImmediately(); |
| EXPECT_FALSE(CaptureModeController::Get()->IsActive()); |
| |
| // Drag and drop camera preview by mouse to the bottom left of the center |
| // point, verify that camera preview is snapped to the bottom left with |
| // correct position. |
| DragPreviewToPoint(preview_widget, {capture_bounds_center_point.x() - 20, |
| capture_bounds_center_point.y() + 20}); |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomLeft, |
| camera_controller->camera_preview_snap_position()); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| |
| // Now drag and drop camera preview by touch to the bottom right of the |
| // center point, verify that camera preview is snapped to the bottom right |
| // with correct position. |
| DragPreviewToPoint(preview_widget, |
| {capture_bounds_center_point.x() + 20, |
| capture_bounds_center_point.y() + 20}, |
| /*by_touch_gestures=*/true); |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomRight, |
| camera_controller->camera_preview_snap_position()); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| } |
| |
| // Tests the use case after pressing on the resize button on camera preview and |
| // releasing the press outside of camera preview, camera preview is still |
| // draggable. Regression test for https://crbug.com/1308885. |
| TEST_P(CaptureModeCameraPreviewTest, |
| CameraPreviewDragToSnapAfterPressOnResizeButton) { |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| auto* resize_button = GetPreviewResizeButton(); |
| const int camera_previw_width = |
| preview_widget->GetWindowBoundsInScreen().width(); |
| const gfx::Point capture_bounds_center_point = |
| GetCaptureBoundsInScreen().CenterPoint(); |
| const gfx::Point center_point_of_resize_button = |
| resize_button->GetBoundsInScreen().CenterPoint(); |
| |
| // By default the snap position of preview widget should be `kBottomRight`. |
| EXPECT_EQ(CameraPreviewSnapPosition::kBottomRight, |
| camera_controller->camera_preview_snap_position()); |
| |
| auto* event_generator = GetEventGenerator(); |
| event_generator->set_current_screen_location(center_point_of_resize_button); |
| event_generator->PressLeftButton(); |
| |
| const gfx::Vector2d delta(-camera_previw_width, -camera_previw_width); |
| // Now move mouse to the outside of the camera preview and then release. |
| event_generator->MoveMouseTo(center_point_of_resize_button + delta); |
| event_generator->ReleaseLeftButton(); |
| |
| // Now try to drag the camera preview to the top left, after camera preview is |
| // snapped, the current snap position should be `kTopLeft`. |
| DragPreviewToPoint(preview_widget, capture_bounds_center_point + delta); |
| EXPECT_EQ(CameraPreviewSnapPosition::kTopLeft, |
| camera_controller->camera_preview_snap_position()); |
| } |
| |
| TEST_P(CaptureModeCameraPreviewTest, CaptureUisVisibilityChangeOnDragAndDrop) { |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| auto* capture_session = CaptureModeController::Get()->capture_mode_session(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Point center_point_of_preview_widget = |
| preview_widget->GetWindowBoundsInScreen().CenterPoint(); |
| |
| const auto* capture_bar_widget = capture_session->capture_mode_bar_widget(); |
| const auto* capture_label_widget = capture_session->capture_label_widget(); |
| |
| // Press on top of the preview widget. Verify capture bar and capture label |
| // are hidden. |
| auto* event_generator = GetEventGenerator(); |
| event_generator->set_current_screen_location(center_point_of_preview_widget); |
| event_generator->PressLeftButton(); |
| EXPECT_FALSE(capture_bar_widget->IsVisible()); |
| EXPECT_FALSE(capture_label_widget->IsVisible()); |
| |
| // Now drag and move the preview widget. Verify capture bar and capture |
| // label are still hidden. |
| const gfx::Vector2d delta(-50, -60); |
| event_generator->MoveMouseTo(center_point_of_preview_widget + delta); |
| EXPECT_FALSE(capture_bar_widget->IsVisible()); |
| EXPECT_FALSE(capture_label_widget->IsVisible()); |
| |
| // Release the drag. Verify capture bar and capture label are shown again. |
| event_generator->ReleaseLeftButton(); |
| EXPECT_TRUE(capture_bar_widget->IsVisible()); |
| EXPECT_TRUE(capture_label_widget->IsVisible()); |
| } |
| |
| TEST_P(CaptureModeCameraPreviewTest, CameraPreviewDragToSnapOnMultipleDisplay) { |
| UpdateDisplay("800x700,801+0-800x700"); |
| |
| const gfx::Point point_in_second_display = gfx::Point(1000, 500); |
| auto* event_generator = GetEventGenerator(); |
| MoveMouseToAndUpdateCursorDisplay(point_in_second_display, event_generator); |
| |
| // Start capture mode on the second display. |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Point capture_bounds_center_point = |
| GetCaptureBoundsInScreen().CenterPoint(); |
| |
| // Drag and drop camera preview by mouse to the top right of the |
| // `capture_bounds_center_point`, verify that camera preview is snapped to |
| // the top right with correct position. |
| DragPreviewToPoint(preview_widget, {capture_bounds_center_point.x() + 20, |
| capture_bounds_center_point.y() - 20}); |
| EXPECT_EQ(CameraPreviewSnapPosition::kTopRight, |
| camera_controller->camera_preview_snap_position()); |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| } |
| |
| // Tests that when there's a video recording is in progress, start a new |
| // capture session will make camera preview not draggable. |
| TEST_P(CaptureModeCameraPreviewTest, |
| DragPreviewInNewCaptureSessionWhileVideoRecordingInProgress) { |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Point capture_bounds_center_point = |
| GetCaptureBoundsInScreen().CenterPoint(); |
| |
| StartVideoRecordingImmediately(); |
| EXPECT_FALSE(CaptureModeController::Get()->IsActive()); |
| // Start a new capture session while a video recording is in progress. |
| auto* controller = CaptureModeController::Get(); |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| |
| const gfx::Rect preview_bounds_in_screen_before_drag = |
| preview_widget->GetWindowBoundsInScreen(); |
| const auto snap_position_before_drag = |
| camera_controller->camera_preview_snap_position(); |
| // Verify by default snap position is `kBottomRight`. |
| EXPECT_EQ(snap_position_before_drag, CameraPreviewSnapPosition::kBottomRight); |
| |
| // Try to drag camera preview by mouse without dropping it, verify camera |
| // preview is not draggable and its position is not changed. |
| DragPreviewToPoint(preview_widget, |
| {preview_bounds_in_screen_before_drag.x() + 20, |
| preview_bounds_in_screen_before_drag.y() + 20}, |
| /*by_touch_gestures=*/false, |
| /*drop=*/false); |
| EXPECT_EQ(preview_widget->GetWindowBoundsInScreen(), |
| preview_bounds_in_screen_before_drag); |
| |
| // Try to drag and drop camera preview by touch to the top left of the |
| // current capture bounds' center point, verity it's not moved. Also verify |
| // the snap position is not updated. |
| DragPreviewToPoint(preview_widget, |
| {capture_bounds_center_point.x() - 20, |
| capture_bounds_center_point.y() - 20}, |
| /*by_touch_gestures=*/true); |
| EXPECT_EQ(preview_widget->GetWindowBoundsInScreen(), |
| preview_bounds_in_screen_before_drag); |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| snap_position_before_drag); |
| } |
| |
| // Tests that dragging camera preview outside of the preview circle shouldn't |
| // work even if the drag events are contained in the preview bounds. |
| TEST_P(CaptureModeCameraPreviewTest, DragPreviewOutsidePreviewCircle) { |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Point capture_bounds_center_point = |
| GetCaptureBoundsInScreen().CenterPoint(); |
| const gfx::Rect preview_bounds_in_screen_before_drag = |
| preview_widget->GetWindowBoundsInScreen(); |
| |
| // Try to drag camera preview at its origin point, verify camera |
| // preview is not draggable and its position is not changed. |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseTo(preview_bounds_in_screen_before_drag.origin()); |
| event_generator->PressLeftButton(); |
| event_generator->MoveMouseTo(capture_bounds_center_point); |
| EXPECT_EQ(preview_widget->GetWindowBoundsInScreen(), |
| preview_bounds_in_screen_before_drag); |
| } |
| |
| // Tests that dragging camera preview outside of the preview circle doesn't |
| // work when video recording is in progress. |
| TEST_P(CaptureModeCameraPreviewTest, |
| DragPreviewOutsidePreviewCircleWhileVideoRecordingInProgress) { |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Point capture_bounds_center_point = |
| GetCaptureBoundsInScreen().CenterPoint(); |
| |
| const gfx::Rect preview_bounds_in_screen_before_drag = |
| preview_widget->GetWindowBoundsInScreen(); |
| const auto snap_position_before_drag = |
| camera_controller->camera_preview_snap_position(); |
| // Verify by default snap position is `kBottomRight`. |
| EXPECT_EQ(snap_position_before_drag, CameraPreviewSnapPosition::kBottomRight); |
| |
| // Try to drag camera preview at its origin point to the top left of current |
| // capture bounds' center point, verity it's not moved. |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseTo(preview_bounds_in_screen_before_drag.origin()); |
| event_generator->PressLeftButton(); |
| event_generator->MoveMouseTo(capture_bounds_center_point); |
| EXPECT_EQ(preview_widget->GetWindowBoundsInScreen(), |
| preview_bounds_in_screen_before_drag); |
| |
| // Release drag, verify snap position is not changed. |
| event_generator->ReleaseLeftButton(); |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| snap_position_before_drag); |
| } |
| |
| // Tests that when mouse event is on top of camera preview circle, cursor type |
| // should be updated accordingly. |
| TEST_P(CaptureModeCameraPreviewTest, CursorTypeUpdates) { |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Rect preview_bounds_in_screen = |
| preview_widget->GetWindowBoundsInScreen(); |
| const gfx::Point camera_preview_center_point = |
| preview_bounds_in_screen.CenterPoint(); |
| const gfx::Point camera_preview_origin_point = |
| preview_bounds_in_screen.origin(); |
| auto* event_generator = GetEventGenerator(); |
| |
| auto* cursor_manager = Shell::Get()->cursor_manager(); |
| // Verify that moving mouse to the origin point on camera preview won't |
| // update the cursor type to `kPointer`. |
| event_generator->MoveMouseTo(preview_bounds_in_screen.origin()); |
| EXPECT_NE(cursor_manager->GetCursor(), ui::mojom::CursorType::kPointer); |
| |
| // Verify that moving mouse on camera preview will update the cursor type |
| // to `kPointer`. |
| event_generator->MoveMouseTo(camera_preview_center_point); |
| EXPECT_EQ(cursor_manager->GetCursor(), ui::mojom::CursorType::kPointer); |
| |
| // Move mouse from camera preview to capture surface, verify cursor type is |
| // updated to the correct type of the current capture source. |
| event_generator->MoveMouseTo({camera_preview_origin_point.x() - 10, |
| camera_preview_origin_point.y() - 10}); |
| EXPECT_EQ(cursor_manager->GetCursor(), GetCursorTypeOnCaptureSurface()); |
| |
| // Drag camera preview, verify that cursor type is updated to `kPointer`. |
| DragPreviewToPoint(preview_widget, |
| {camera_preview_center_point.x() - 10, |
| camera_preview_center_point.y() - 10}, |
| /*by_touch_gestures=*/false, |
| /*drop=*/false); |
| EXPECT_EQ(cursor_manager->GetCursor(), ui::mojom::CursorType::kPointer); |
| |
| // Continue dragging and then drop camera preview, make sure cursor's |
| // position is outside of camera preview after it's snapped. Verify cursor |
| // type is updated to the correct type of the current capture source. |
| DragPreviewToPoint(preview_widget, {camera_preview_origin_point.x() - 20, |
| camera_preview_origin_point.y() - 20}); |
| EXPECT_EQ(cursor_manager->GetCursor(), GetCursorTypeOnCaptureSurface()); |
| } |
| |
| // Tests the functionality of resize button on changing the size of the camera |
| // preview widget, updating the icon image and tooltip text after clicking on |
| // it. It also tests the ability to restore to previous resize button settings |
| // if any when initiating a new capture mode session. |
| TEST_P(CaptureModeCameraPreviewTest, ResizePreviewWidget) { |
| UpdateDisplay("800x700"); |
| StartCaptureSessionWithParam(); |
| auto* controller = CaptureModeController::Get(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| views::Widget* preview_widget = camera_controller->camera_preview_widget(); |
| DCHECK(preview_widget); |
| const auto default_preview_bounds = preview_widget->GetWindowBoundsInScreen(); |
| EXPECT_EQ(default_preview_bounds.size(), |
| GetExpectedPreviewSize(/*collapsed=*/false)); |
| |
| auto* resize_button = GetPreviewResizeButton(); |
| auto* event_generator = GetEventGenerator(); |
| |
| // Tests the default settings of the resize button. |
| VerifyResizeButton(camera_controller->is_camera_preview_collapsed(), |
| resize_button); |
| |
| // First time click on resize button will make the preview widget collapse |
| // to half of the default size with tooltip text and resize button icon |
| // changed to expanded related contents accordingly. |
| ClickOnView(resize_button, event_generator); |
| EXPECT_EQ(preview_widget->GetWindowBoundsInScreen().size(), |
| GetExpectedPreviewSize(/*collapsed=*/true)); |
| VerifyResizeButton(camera_controller->is_camera_preview_collapsed(), |
| resize_button); |
| |
| // Second time click on resize button will make the preview widget expand |
| // back to the default size with tooltip text and resize button icon changed |
| // to the collapsed related contents accordingly. |
| ClickOnView(resize_button, event_generator); |
| EXPECT_EQ(preview_widget->GetWindowBoundsInScreen(), default_preview_bounds); |
| VerifyResizeButton(camera_controller->is_camera_preview_collapsed(), |
| resize_button); |
| |
| // Click on the resize button again will collapse the preview widget. Exit the |
| // session and start a new session, the settings for preview widget bounds and |
| // resize button will be restored. |
| ClickOnView(resize_button, event_generator); |
| EXPECT_EQ(preview_widget->GetWindowBoundsInScreen().size(), |
| GetExpectedPreviewSize(/*collapsed=*/true)); |
| VerifyResizeButton(camera_controller->is_camera_preview_collapsed(), |
| resize_button); |
| const auto collapsed_preview_bounds = |
| preview_widget->GetWindowBoundsInScreen(); |
| controller->Stop(); |
| |
| StartCaptureSessionWithParam(); |
| preview_widget = camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(preview_widget); |
| EXPECT_EQ(preview_widget->GetWindowBoundsInScreen(), |
| collapsed_preview_bounds); |
| |
| resize_button = GetPreviewResizeButton(); |
| EXPECT_TRUE(resize_button); |
| VerifyResizeButton(camera_controller->is_camera_preview_collapsed(), |
| resize_button); |
| } |
| |
| // Tests that resizing the camera preview using the resize button, which uses |
| // the bounds animation, works correctly on a secondary display. Regression test |
| // for https://crbug.com/1313247. |
| TEST_P(CaptureModeCameraPreviewTest, MultiDisplayResize) { |
| UpdateDisplay("800x700,801+0-800x700"); |
| ASSERT_EQ(2u, Shell::GetAllRootWindows().size()); |
| |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| // Put the cursor in the secondary display, and expect the session root to be |
| // there. |
| auto* event_generator = GetEventGenerator(); |
| MoveMouseToAndUpdateCursorDisplay(gfx::Point(900, 500), event_generator); |
| StartCaptureSessionWithParam(); |
| auto* controller = CaptureModeController::Get(); |
| auto* session = controller->capture_mode_session(); |
| auto* display_2_root = Shell::GetAllRootWindows()[1]; |
| |
| // When capturing a window, set its bounds such that it is placed on the |
| // secondary display. |
| if (GetParam() == CaptureModeSource::kWindow) { |
| views::Widget::GetWidgetForNativeWindow(window())->SetBounds( |
| {900, 10, 700, 650}); |
| EXPECT_EQ(display_2_root, window()->GetRootWindow()); |
| event_generator->MoveMouseToCenterOf(window()); |
| } |
| |
| EXPECT_EQ(display_2_root, session->current_root()); |
| |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| |
| auto* resize_button = GetPreviewResizeButton(); |
| ClickOnView(resize_button, event_generator); |
| |
| VerifyPreviewAlignment(GetCaptureBoundsInScreen()); |
| } |
| |
| // Tests the visibility of the resize button on mouse events. |
| TEST_P(CaptureModeCameraPreviewTest, ResizeButtonVisibilityOnMouseEvents) { |
| UpdateDisplay("1366x768"); |
| |
| StartCaptureSessionWithParam(); |
| CaptureModeCameraController* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| views::Widget* preview_widget = camera_controller->camera_preview_widget(); |
| DCHECK(preview_widget); |
| const gfx::Rect default_preview_bounds = |
| preview_widget->GetWindowBoundsInScreen(); |
| |
| CaptureModeButton* resize_button = GetPreviewResizeButton(); |
| auto* event_generator = GetEventGenerator(); |
| |
| // Tests that the resize button is hidden by default. |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Tests that the resize button will show up when the mouse is entering the |
| // bounds of the preview widget. |
| event_generator->MoveMouseTo(default_preview_bounds.CenterPoint()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| // Tests that the resize button will stay visible while moving the mouse |
| // within the bounds of the preview widget. |
| event_generator->MoveMouseTo(default_preview_bounds.top_center()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| // Tests that when the mouse is exiting the bounds of the preview widget, the |
| // resize button will disappear after the predefined duration. |
| auto outside_point = default_preview_bounds.origin(); |
| outside_point.Offset(-1, -1); |
| event_generator->MoveMouseTo(outside_point); |
| |
| base::OneShotTimer* timer = camera_controller->camera_preview_view() |
| ->resize_button_hide_timer_for_test(); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(timer->GetCurrentDelay(), capture_mode::kResizeButtonShowDuration); |
| |
| { |
| ViewVisibilityChangeWaiter waiter(resize_button); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| timer->FireNow(); |
| waiter.Wait(); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| } |
| } |
| |
| // Tests the visibility of the resize button on tap events. |
| TEST_P(CaptureModeCameraPreviewTest, ResizeButtonVisibilityOnTapEvents) { |
| UpdateDisplay("800x700"); |
| StartCaptureSessionWithParam(); |
| CaptureModeCameraController* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| views::Widget* preview_widget = camera_controller->camera_preview_widget(); |
| DCHECK(preview_widget); |
| const gfx::Rect default_preview_bounds = |
| preview_widget->GetWindowBoundsInScreen(); |
| |
| CaptureModeButton* resize_button = GetPreviewResizeButton(); |
| auto* event_generator = GetEventGenerator(); |
| |
| // Tests that the resize button is hidden by default. |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Tests that resize button shows up when tapping within the bounds of the |
| // preview widget and will fade out after the predefined duration. |
| event_generator->GestureTapAt(default_preview_bounds.CenterPoint()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| base::OneShotTimer* timer = camera_controller->camera_preview_view() |
| ->resize_button_hide_timer_for_test(); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(timer->GetCurrentDelay(), capture_mode::kResizeButtonShowDuration); |
| |
| { |
| ViewVisibilityChangeWaiter waiter(resize_button); |
| timer->FireNow(); |
| waiter.Wait(); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| } |
| } |
| |
| // Tests the visibility of the resize button on camera preview drag to snap. |
| TEST_P(CaptureModeCameraPreviewTest, |
| ResizeButtonVisibilityOnCameraPreviewDragToSnap) { |
| UpdateDisplay("1366x768"); |
| StartCaptureSessionWithParam(); |
| CaptureModeCameraController* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| views::Widget* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Rect preview_bounds = preview_widget->GetWindowBoundsInScreen(); |
| |
| CaptureModeButton* resize_button = GetPreviewResizeButton(); |
| auto* event_generator = GetEventGenerator(); |
| |
| // Tests that the resize button is hidden by default. |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Tests that the resize button will show up when the mouse is entering the |
| // bounds of the preview widget. |
| event_generator->MoveMouseTo(preview_bounds.CenterPoint()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| // Tests that when start dragging camera preview, resize button will be |
| // hidden. |
| event_generator->PressLeftButton(); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Drag camera preview, test that resize button is still hidden. |
| event_generator->MoveMouseBy(-300, -300); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Now release drag, verify that resize button is still hidden since cursor is |
| // not on top of camera preview after it's snapped. |
| event_generator->ReleaseLeftButton(); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Now drag camera preview with a small distance, make sure when it's snapped |
| // cursor is still on top of it. Verify that resize button is shown after |
| // camera preview is snapped. |
| const gfx::Vector2d delta(-30, -30); |
| DragPreviewToPoint(preview_widget, preview_bounds.CenterPoint() + delta); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| } |
| |
| TEST_P(CaptureModeCameraPreviewTest, CameraPreviewDeintersectsWithSystemTray) { |
| UpdateDisplay("1366x768"); |
| |
| // Open system tray. |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| |
| auto* system_tray = GetPrimaryUnifiedSystemTray(); |
| event_generator->MoveMouseTo(system_tray->GetBoundsInScreen().CenterPoint()); |
| event_generator->ClickLeftButton(); |
| EXPECT_TRUE(system_tray->IsBubbleShown()); |
| |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| // Verify current default snap position is the `kBottomRight` before we select |
| // a camera device. |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| EXPECT_TRUE(system_tray->IsBubbleShown()); |
| |
| // Verify that camera preview doesn't overlap with system tray when it's |
| // shown. Also verify current snap position is updated and not `kBottomRight` |
| // anymore. |
| EXPECT_FALSE(system_tray->GetBubbleBoundsInScreen().Intersects( |
| preview_widget->GetWindowBoundsInScreen())); |
| EXPECT_NE(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| } |
| |
| TEST_P(CaptureModeCameraPreviewTest, |
| CameraPreviewDeintersectsWithSystemTrayWhileVideoRecordingInProgress) { |
| // Update display size big enough to make sure when capture source is |
| // `kWindow`, the selected window is not system tray. |
| UpdateDisplay("1366x768"); |
| |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Point capture_bounds_center_point = |
| GetCaptureBoundsInScreen().CenterPoint(); |
| |
| StartVideoRecordingImmediately(); |
| // Verify current snap position is `kBottomRight`; |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| // Now open system tray. |
| ui::test::EventGenerator* event_generator = GetEventGenerator(); |
| auto* system_tray = GetPrimaryUnifiedSystemTray(); |
| event_generator->MoveMouseTo(system_tray->GetBoundsInScreen().CenterPoint()); |
| event_generator->ClickLeftButton(); |
| EXPECT_TRUE(system_tray->IsBubbleShown()); |
| |
| // Verify that after system tray is open, camera preview is snapped and |
| // doesn't overlap with system tray. |
| EXPECT_NE(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| EXPECT_FALSE(system_tray->GetBubbleBoundsInScreen().Intersects( |
| preview_widget->GetWindowBoundsInScreen())); |
| |
| // Now try to drag camera preview to the bottom right corner, verify that |
| // since system tray is still open, when drag is released, camera preview is |
| // not snapped to the bottom right corner even it's the nearest corner to the |
| // release position if system tray is still shown. |
| const gfx::Vector2d delta(20, 20); |
| DragPreviewToPoint(preview_widget, capture_bounds_center_point + delta); |
| // Please notice, when capture source is `kWindow`, once the drag starts, |
| // system tray will be closed, in this use case we just need to verify camera |
| // preview is snapped to the bottom right corner. |
| if (system_tray->IsBubbleShown()) { |
| EXPECT_NE(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| EXPECT_FALSE(system_tray->GetBubbleBoundsInScreen().Intersects( |
| preview_widget->GetWindowBoundsInScreen())); |
| } else { |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| } |
| } |
| |
| TEST_P(CaptureModeCameraPreviewTest, CameraPreviewDeintersectsWithPipWindow) { |
| // Create a window at the bottom right of the display, then convert it to a |
| // PIP window. |
| std::unique_ptr<aura::Window> pip_window( |
| CreateTestWindow(gfx::Rect(700, 450, 104, 100))); |
| ConvertToPipWindow(pip_window.get()); |
| const gfx::Rect origin_pip_window_bounds = pip_window->GetBoundsInScreen(); |
| |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| |
| // Verify that after preview widget is enabled, pip window is repositioned to |
| // avoid the overlap with camera preview. |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| const gfx::Rect current_pip_window_bounds = pip_window->GetBoundsInScreen(); |
| EXPECT_NE(origin_pip_window_bounds, current_pip_window_bounds); |
| EXPECT_FALSE(current_pip_window_bounds.Intersects( |
| preview_widget->GetWindowBoundsInScreen())); |
| } |
| |
| TEST_P(CaptureModeCameraPreviewTest, |
| CameraPreviewDeintersectsWithPipWindowDuringRecording) { |
| // Create a window at the top left of the display, then convert it to a PIP |
| // window. |
| std::unique_ptr<aura::Window> pip_window( |
| CreateTestWindow(gfx::Rect(0, 0, 104, 100))); |
| ConvertToPipWindow(pip_window.get()); |
| const gfx::Rect origin_pip_window_bounds = pip_window->GetBoundsInScreen(); |
| |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| const gfx::Point capture_bounds_center_point = |
| GetCaptureBoundsInScreen().CenterPoint(); |
| |
| // Verify camera preview is enabled, current snap position is `kBottomRight` |
| // and pip window is not repositioned since there's no overlap. |
| EXPECT_TRUE(preview_widget); |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| EXPECT_EQ(origin_pip_window_bounds, pip_window->GetBoundsInScreen()); |
| |
| StartVideoRecordingImmediately(); |
| // Now drag camera preview to the top left corner, verify pip window is |
| // repositioned to avoid overlap with camera preview. |
| const gfx::Vector2d delta(-20, -20); |
| DragPreviewToPoint(preview_widget, capture_bounds_center_point + delta); |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kTopLeft); |
| EXPECT_NE(origin_pip_window_bounds, pip_window->GetBoundsInScreen()); |
| EXPECT_FALSE(preview_widget->GetWindowBoundsInScreen().Intersects( |
| pip_window->GetBoundsInScreen())); |
| } |
| |
| TEST_P(CaptureModeCameraPreviewTest, |
| CameraPreviewDeintersectsWithAutoclickBar) { |
| // Update display size big enough to make sure when capture source is |
| // `kWindow`, the selected window is not system tray. |
| UpdateDisplay("1366x768"); |
| // Enable autoclick bar. |
| auto* autoclick_controller = Shell::Get()->autoclick_controller(); |
| autoclick_controller->SetEnabled(true, /*show_confirmation_dialog=*/false); |
| Shell::Get() |
| ->accessibility_controller() |
| ->GetFeature(AccessibilityControllerImpl::FeatureType::kAutoclick) |
| .SetEnabled(true); |
| |
| views::Widget* autoclick_bubble_widget = |
| autoclick_controller->GetMenuBubbleControllerForTesting() |
| ->GetBubbleWidgetForTesting(); |
| EXPECT_TRUE(autoclick_bubble_widget->IsVisible()); |
| const gfx::Rect origin_autoclick_bar_bounds = |
| autoclick_bubble_widget->GetWindowBoundsInScreen(); |
| |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| |
| // Verify that after preview widget is enabled, autoclick bar is repositioned |
| // to avoid the overlap with camera preview. |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| const gfx::Rect current_autoclick_bar_bounds = |
| autoclick_bubble_widget->GetWindowBoundsInScreen(); |
| EXPECT_NE(origin_autoclick_bar_bounds, current_autoclick_bar_bounds); |
| EXPECT_FALSE(current_autoclick_bar_bounds.Intersects( |
| preview_widget->GetWindowBoundsInScreen())); |
| } |
| |
| TEST_P(CaptureModeCameraPreviewTest, |
| CameraPreviewDeintersectsWithSystemTrayOnSizeChanged) { |
| // Update display size to make sure when system tray is open, camera preview |
| // can stay in the same side with it when camera preview is collapsed, |
| // otherwise, camera preview should be snapped to the other side of the |
| // display. |
| UpdateDisplay("1366x700"); |
| |
| StartCaptureSessionWithParam(); |
| auto* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* preview_widget = camera_controller->camera_preview_widget(); |
| |
| // Verify camera preview is enabled, and by default, the current snap position |
| // should be `kBottomRight`. |
| EXPECT_TRUE(preview_widget); |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kBottomRight); |
| |
| // Click on resize button to collapse camera preview. |
| auto* resize_button = GetPreviewResizeButton(); |
| auto* event_generator = GetEventGenerator(); |
| ClickOnView(resize_button, event_generator); |
| EXPECT_TRUE(camera_controller->is_camera_preview_collapsed()); |
| |
| StartVideoRecordingImmediately(); |
| // Open system tray. |
| auto* system_tray = GetPrimaryUnifiedSystemTray(); |
| event_generator->MoveMouseTo(system_tray->GetBoundsInScreen().CenterPoint()); |
| event_generator->ClickLeftButton(); |
| EXPECT_TRUE(system_tray->IsBubbleShown()); |
| |
| // After system tray is shown, verify that camera preview is snapped to the |
| // top right corner, and there's no overlap between camera preview and system |
| // tray. |
| EXPECT_TRUE(system_tray->IsBubbleShown()); |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kTopRight); |
| EXPECT_FALSE(preview_widget->GetWindowBoundsInScreen().Intersects( |
| system_tray->GetBoundsInScreen())); |
| |
| // Click on the resize button to enlarge camera preview. Verify that camera |
| // preview remains snapped to the top right corner, since there's no overlap. |
| ClickOnView(resize_button, event_generator); |
| EXPECT_FALSE(preview_widget->GetWindowBoundsInScreen().Intersects( |
| system_tray->GetBoundsInScreen())); |
| EXPECT_EQ(camera_controller->camera_preview_snap_position(), |
| CameraPreviewSnapPosition::kTopRight); |
| } |
| |
| TEST_P(CaptureModeCameraPreviewTest, CameraPreviewSpecs) { |
| AddDefaultCamera(); |
| CaptureModeTestApi().SelectCameraAtIndex(0); |
| auto* camera_controller = GetCameraController(); |
| |
| struct { |
| CameraPreviewState preview_state; |
| std::string scope_trace; |
| } kTestCases[] = { |
| {CameraPreviewState::kCollapsible, "Collapsible Preview"}, |
| {CameraPreviewState::kNotCollapsible, "Not Collapsible Preview"}, |
| {CameraPreviewState::kHidden, "Hidden Preview"}, |
| }; |
| |
| for (const auto& test_case : kTestCases) { |
| SCOPED_TRACE(test_case.scope_trace); |
| |
| UpdateDisplay("1366x700"); |
| StartCaptureSessionWithParam(); |
| auto* camera_preview_widget = camera_controller->camera_preview_widget(); |
| auto* camera_preview_view = camera_controller->camera_preview_view(); |
| EXPECT_TRUE(camera_preview_widget); |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_TRUE(camera_preview_view->is_collapsible()); |
| |
| ResizeSurfaceSoCameraPreviewBecomes(test_case.preview_state); |
| const auto preview_screen_bounds = |
| camera_preview_widget->GetWindowBoundsInScreen(); |
| switch (test_case.preview_state) { |
| case CameraPreviewState::kCollapsible: |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_TRUE(camera_preview_view->is_collapsible()); |
| EXPECT_EQ(preview_screen_bounds.size(), |
| GetExpectedPreviewSize(/*collapsed=*/false)); |
| break; |
| |
| case CameraPreviewState::kNotCollapsible: |
| EXPECT_TRUE(camera_preview_widget->IsVisible()); |
| EXPECT_FALSE(camera_preview_view->is_collapsible()); |
| EXPECT_EQ(preview_screen_bounds.size(), |
| GetExpectedPreviewSize(/*collapsed=*/false)); |
| break; |
| |
| case CameraPreviewState::kHidden: |
| EXPECT_FALSE(camera_preview_widget->IsVisible()); |
| EXPECT_FALSE(camera_preview_view->is_collapsible()); |
| break; |
| } |
| } |
| } |
| |
| // Tests that the resize button will stay visible after mouse exiting the |
| // preview and time exceeding the predefined duration on mouse event when switch |
| // access is enabled. And the resize button will behave in a default way if |
| // switch access is not enabled. |
| TEST_P(CaptureModeCameraPreviewTest, |
| ResizeButtonSwitchAccessVisibilityTestOnMouseEvent) { |
| UpdateDisplay("1366x768"); |
| |
| CaptureModeCameraController* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* event_generator = GetEventGenerator(); |
| |
| for (const bool switch_access_enabled : {false, true}) { |
| AccessibilityControllerImpl* a11y_controller = |
| Shell::Get()->accessibility_controller(); |
| a11y_controller->switch_access().SetEnabled(switch_access_enabled); |
| EXPECT_EQ(switch_access_enabled, a11y_controller->IsSwitchAccessRunning()); |
| |
| StartCaptureSessionWithParam(); |
| views::Widget* preview_widget = camera_controller->camera_preview_widget(); |
| DCHECK(preview_widget); |
| gfx::Rect preview_bounds = preview_widget->GetWindowBoundsInScreen(); |
| CaptureModeButton* resize_button = GetPreviewResizeButton(); |
| |
| // Tests the default visibility of the resize button based on whether switch |
| // access is enabled or not. |
| EXPECT_EQ(resize_button->GetVisible(), |
| switch_access_enabled ? true : false); |
| |
| event_generator->MoveMouseTo(preview_bounds.CenterPoint()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| auto outside_point = preview_bounds.origin(); |
| outside_point.Offset(-1, -1); |
| event_generator->MoveMouseTo(outside_point); |
| base::OneShotTimer* timer = camera_controller->camera_preview_view() |
| ->resize_button_hide_timer_for_test(); |
| timer->FireNow(); |
| EXPECT_EQ(resize_button->GetVisible(), |
| switch_access_enabled ? true : false); |
| |
| // Tests that the resize button will be hidden when start dragging the |
| // camera preview regardless of whether the switch access is enabled or not. |
| event_generator->MoveMouseTo(preview_bounds.CenterPoint()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| event_generator->PressLeftButton(); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| event_generator->MoveMouseBy(-100, -100); |
| EXPECT_FALSE(resize_button->GetVisible()); |
| |
| // Tests that the resize button will be visible if the switch access is |
| // enabled after releasing the drag and not visible otherwise. |
| event_generator->ReleaseLeftButton(); |
| EXPECT_EQ(resize_button->GetVisible(), |
| switch_access_enabled ? true : false); |
| |
| CaptureModeController::Get()->Stop(); |
| } |
| } |
| |
| // Tests that the resize button will stay visible after tapping on the preview |
| // and time exceeding the predefined duration on tap event when switch access is |
| // enabled. And the resize button will behave in a default way if switch |
| // access is not enabled. |
| TEST_P(CaptureModeCameraPreviewTest, |
| ResizeButtonSwitchAccessVisibilityTestOnTapEvent) { |
| UpdateDisplay("1366x768"); |
| |
| SwitchToTabletMode(); |
| EXPECT_TRUE(Shell::Get()->IsInTabletMode()); |
| |
| CaptureModeCameraController* camera_controller = GetCameraController(); |
| AddDefaultCamera(); |
| camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1)); |
| auto* event_generator = GetEventGenerator(); |
| |
| for (const bool switch_access_enabled : {false, true}) { |
| AccessibilityControllerImpl* a11y_controller = |
| Shell::Get()->accessibility_controller(); |
| a11y_controller->switch_access().SetEnabled(switch_access_enabled); |
| EXPECT_EQ(switch_access_enabled, a11y_controller->IsSwitchAccessRunning()); |
| |
| StartCaptureSessionWithParam(); |
| views::Widget* preview_widget = camera_controller->camera_preview_widget(); |
| DCHECK(preview_widget); |
| gfx::Rect preview_bounds = preview_widget->GetWindowBoundsInScreen(); |
| CaptureModeButton* resize_button = GetPreviewResizeButton(); |
| |
| // Tests the default visibility of the resize button based on whether switch |
| // access is enabled or not. |
| EXPECT_EQ(resize_button->GetVisible(), |
| switch_access_enabled ? true : false); |
| |
| event_generator->GestureTapAt(preview_bounds.CenterPoint()); |
| EXPECT_TRUE(resize_button->GetVisible()); |
| |
| base::OneShotTimer* timer = camera_controller->camera_preview_view() |
| ->resize_button_hide_timer_for_test(); |
| if (timer->IsRunning()) |
| timer->FireNow(); |
| |
| EXPECT_EQ(resize_button->GetVisible(), |
| switch_access_enabled ? true : false); |
| CaptureModeController::Get()->Stop(); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| CaptureModeCameraPreviewTest, |
| testing::Values(CaptureModeSource::kFullscreen, |
| CaptureModeSource::kRegion, |
| CaptureModeSource::kWindow)); |
| |
| class ProjectorCaptureModeCameraTest : public CaptureModeCameraTest { |
| public: |
| ProjectorCaptureModeCameraTest() = default; |
| ~ProjectorCaptureModeCameraTest() override = default; |
| |
| // CaptureModeCameraTest: |
| void SetUp() override { |
| CaptureModeCameraTest::SetUp(); |
| projector_helper_.SetUp(); |
| } |
| |
| void StartProjectorModeSession() { |
| projector_helper_.StartProjectorModeSession(); |
| } |
| |
| private: |
| ProjectorCaptureModeIntegrationHelper projector_helper_; |
| }; |
| |
| TEST_F(ProjectorCaptureModeCameraTest, NoAvailableCameras) { |
| // Initially no camera should be selected. |
| auto* camera_controller = GetCameraController(); |
| EXPECT_FALSE(camera_controller->selected_camera().is_valid()); |
| |
| // Starting a projector session should not result in showing any cameras, or |
| // any crashes. |
| StartProjectorModeSession(); |
| EXPECT_FALSE(camera_controller->selected_camera().is_valid()); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_F(ProjectorCaptureModeCameraTest, FirstCamSelectedByDefault) { |
| AddDefaultCamera(); |
| |
| // Initially no camera should be selected. |
| auto* camera_controller = GetCameraController(); |
| EXPECT_FALSE(camera_controller->selected_camera().is_valid()); |
| |
| // Starting a projector session should result in selecting the first available |
| // camera by default, and its preview should be visible. |
| StartProjectorModeSession(); |
| EXPECT_TRUE(camera_controller->selected_camera().is_valid()); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_F(ProjectorCaptureModeCameraTest, |
| SessionStartsWithAnAlreadySelectedCamera) { |
| const std::string model_id_1 = "model1"; |
| const std::string model_id_2 = "model2"; |
| AddFakeCamera("/dev/video0", "fake cam 1", model_id_1); |
| AddFakeCamera("/dev/video1", "fake cam 2", model_id_2); |
| |
| // Initially there's a camera already selected before we start the session, |
| // and it's the second camera in the list. |
| auto* camera_controller = GetCameraController(); |
| CameraId cam_id_1(model_id_1, 1); |
| CameraId cam_id_2(model_id_2, 1); |
| EXPECT_EQ(cam_id_1, camera_controller->available_cameras()[0].camera_id); |
| EXPECT_EQ(cam_id_2, camera_controller->available_cameras()[1].camera_id); |
| camera_controller->SetSelectedCamera(cam_id_2); |
| EXPECT_TRUE(camera_controller->selected_camera().is_valid()); |
| |
| // Starting a projector session should not result in selecting the first |
| // camera. The already selected camera should remain as is. |
| StartProjectorModeSession(); |
| EXPECT_TRUE(camera_controller->selected_camera().is_valid()); |
| EXPECT_EQ(cam_id_2, camera_controller->selected_camera()); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| } |
| |
| // Tests that the recording starts with camera metrics are recorded correctly in |
| // a projector-initiated recording. |
| TEST_F(ProjectorCaptureModeCameraTest, |
| ProjectorRecordingStartsWithCameraHistogramTest) { |
| base::HistogramTester histogram_tester; |
| constexpr char kHistogramNameBase[] = |
| "Ash.CaptureModeController.Projector.RecordingStartsWithCamera"; |
| |
| AddDefaultCamera(); |
| |
| struct { |
| bool tablet_enabled; |
| bool camera_on; |
| } kTestCases[] = { |
| {false, false}, |
| {false, true}, |
| {true, false}, |
| {true, true}, |
| }; |
| |
| for (const auto test_case : kTestCases) { |
| if (test_case.tablet_enabled) { |
| SwitchToTabletMode(); |
| EXPECT_TRUE(Shell::Get()->IsInTabletMode()); |
| } else { |
| EXPECT_FALSE(Shell::Get()->IsInTabletMode()); |
| } |
| |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramNameBase), test_case.camera_on, |
| 0); |
| |
| auto* controller = CaptureModeController::Get(); |
| controller->SetType(CaptureModeType::kVideo); |
| controller->SetSource(CaptureModeSource::kFullscreen); |
| |
| StartProjectorModeSession(); |
| EXPECT_TRUE(controller->IsActive()); |
| auto* session = controller->capture_mode_session(); |
| EXPECT_TRUE(session->is_in_projector_mode()); |
| |
| GetCameraController()->SetSelectedCamera( |
| test_case.camera_on ? CameraId(kDefaultCameraModelId, 1) : CameraId()); |
| |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| |
| WaitForSeconds(1); |
| |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| |
| histogram_tester.ExpectBucketCount( |
| GetCaptureModeHistogramName(kHistogramNameBase), test_case.camera_on, |
| 1); |
| } |
| } |
| |
| // A test fixture for testing the rendered video frames. The boolean parameter |
| // determines the type of the buffer that backs the video frames. `true` means |
| // the `kGpuMemoryBuffer` is used, `false` means the `kSharedMemory` buffer type |
| // is used. |
| class CaptureModeCameraFramesTest : public CaptureModeCameraTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| CaptureModeCameraFramesTest() = default; |
| CaptureModeCameraFramesTest(const CaptureModeCameraFramesTest&) = delete; |
| CaptureModeCameraFramesTest& operator=(const CaptureModeCameraFramesTest&) = |
| delete; |
| ~CaptureModeCameraFramesTest() override = default; |
| |
| bool ShouldUseGpuMemoryBuffers() const { return GetParam(); } |
| |
| // CaptureModeCameraFramesTest: |
| void SetUp() override { |
| CaptureModeCameraTest::SetUp(); |
| CaptureModeTestApi test_api; |
| test_api.SetForceUseGpuMemoryBufferForCameraFrames( |
| ShouldUseGpuMemoryBuffers()); |
| AddDefaultCamera(); |
| ASSERT_EQ(1u, test_api.GetNumberOfAvailableCameras()); |
| test_api.SelectCameraAtIndex(0); |
| const CameraId camera_id(kDefaultCameraModelId, 1); |
| EXPECT_EQ(camera_id, GetCameraController()->selected_camera()); |
| } |
| |
| void TearDown() override { |
| CaptureModeTestApi().SetForceUseGpuMemoryBufferForCameraFrames(false); |
| CaptureModeCameraTest::TearDown(); |
| } |
| }; |
| |
| namespace { |
| |
| // Waits for several rendered frames and verifies that the content of the |
| // received video frames are the same as that of the produced video frames. |
| void WaitForAndVerifyRenderedVideoFrame() { |
| // Render a number of frames that are 3 times the size of the buffer pool. |
| // This allows us to exercise calls to `OnNewBuffer()` and potentially |
| // `OnFrameDropped()`. |
| for (size_t i = 0; i < 3 * FakeCameraDevice::kMaxBufferCount; ++i) { |
| base::RunLoop loop; |
| CaptureModeTestApi().SetOnCameraVideoFrameRendered( |
| base::BindLambdaForTesting([&loop]( |
| scoped_refptr<media::VideoFrame> frame) { |
| ASSERT_TRUE(frame); |
| const gfx::Size frame_size = frame->visible_rect().size(); |
| const auto produced_frame_bitmap = |
| FakeCameraDevice::GetProducedFrameAsBitmap(frame_size); |
| |
| media::PaintCanvasVideoRenderer renderer; |
| SkBitmap received_frame_bitmap; |
| |
| scoped_refptr<viz::RasterContextProvider> raster_context_provider = |
| aura::Env::GetInstance() |
| ->context_factory() |
| ->SharedMainThreadRasterContextProvider(); |
| received_frame_bitmap.allocN32Pixels(frame_size.width(), |
| frame_size.height()); |
| cc::SkiaPaintCanvas canvas(received_frame_bitmap); |
| renderer.Copy(frame, &canvas, raster_context_provider.get()); |
| |
| EXPECT_TRUE(gfx::test::AreBitmapsEqual(produced_frame_bitmap, |
| received_frame_bitmap)); |
| |
| loop.Quit(); |
| })); |
| loop.Run(); |
| } |
| } |
| |
| } // namespace |
| |
| TEST_P(CaptureModeCameraFramesTest, VerifyFrames) { |
| CaptureModeTestApi().StartForFullscreen(/*for_video=*/true); |
| EXPECT_TRUE(GetCameraController()->camera_preview_widget()); |
| WaitForAndVerifyRenderedVideoFrame(); |
| } |
| |
| TEST_P(CaptureModeCameraFramesTest, TurnOffCameraWhileRendering) { |
| CaptureModeTestApi test_api; |
| test_api.StartForFullscreen(/*for_video=*/true); |
| auto* camera_controller = GetCameraController(); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| WaitForAndVerifyRenderedVideoFrame(); |
| test_api.TurnCameraOff(); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_P(CaptureModeCameraFramesTest, DisconnectCameraWhileRendering) { |
| CaptureModeTestApi test_api; |
| test_api.StartForFullscreen(/*for_video=*/true); |
| auto* camera_controller = GetCameraController(); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| WaitForAndVerifyRenderedVideoFrame(); |
| RemoveDefaultCamera(); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| } |
| |
| TEST_P(CaptureModeCameraFramesTest, SelectAnotherCameraWhileRendering) { |
| CaptureModeTestApi test_api; |
| test_api.StartForFullscreen(/*for_video=*/true); |
| auto* camera_controller = GetCameraController(); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| auto* preview_view = camera_controller->camera_preview_view(); |
| ASSERT_TRUE(preview_view); |
| EXPECT_EQ(preview_view->camera_id(), camera_controller->selected_camera()); |
| WaitForAndVerifyRenderedVideoFrame(); |
| |
| // Adding a new camera while rendering an existing one should not affect |
| // anything since the new one is not selected yet. |
| const std::string device_id = "/dev/video0"; |
| const std::string display_name = "Integrated Webcam"; |
| const std::string model_id = "0123:4567"; |
| AddFakeCamera(device_id, display_name, model_id); |
| EXPECT_EQ(preview_view, camera_controller->camera_preview_view()); |
| |
| // Now select the new camera, a new widget should be created immediately for |
| // the new camera. |
| const CameraId second_camera_id(model_id, 1); |
| camera_controller->SetSelectedCamera(second_camera_id); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| EXPECT_NE(preview_view, camera_controller->camera_preview_view()); |
| preview_view = camera_controller->camera_preview_view(); |
| EXPECT_EQ(preview_view->camera_id(), second_camera_id); |
| WaitForAndVerifyRenderedVideoFrame(); |
| } |
| |
| // Regression test for https://crbug.com/1316230. |
| TEST_P(CaptureModeCameraFramesTest, CameraFatalErrors) { |
| CaptureModeTestApi().StartForFullscreen(/*for_video=*/true); |
| auto* camera_controller = GetCameraController(); |
| EXPECT_TRUE(camera_controller->selected_camera().is_valid()); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| WaitForAndVerifyRenderedVideoFrame(); |
| |
| // When a camera fatal error happens during rendering, we detect that an |
| // consider it as a camera disconnection, which will result in the temporary |
| // removal of the preview, before it gets re-added again when we refresh the |
| // list of cameras. |
| auto* video_source_provider = GetTestDelegate()->video_source_provider(); |
| video_source_provider->TriggerFatalErrorOnCamera(kDefaultCameraDeviceId); |
| CameraDevicesChangeWaiter().Wait(); |
| EXPECT_FALSE(camera_controller->camera_preview_widget()); |
| EXPECT_TRUE(camera_controller->selected_camera().is_valid()); |
| |
| // Now wait for the camera to be re-added again. |
| CameraDevicesChangeWaiter().Wait(); |
| EXPECT_TRUE(camera_controller->camera_preview_widget()); |
| WaitForAndVerifyRenderedVideoFrame(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, CaptureModeCameraFramesTest, testing::Bool()); |
| |
| // The test fixture for starting test without active session. |
| using NoSessionCaptureModeCameraTest = NoSessionAshTestBase; |
| |
| // Tests that camera info is requested after the user logs in instead of on |
| // Chrome startup. |
| TEST_F(NoSessionCaptureModeCameraTest, RequestCameraInfoAfterUserLogsIn) { |
| auto* camera_controller = GetCameraController(); |
| GetTestDelegate()->video_source_provider()->AddFakeCameraWithoutNotifying( |
| "/dev/video0", "Integrated Webcam", "0123:4567", |
| media::MEDIA_VIDEO_FACING_NONE); |
| |
| // Verify that the camera devices info is not updated yet. |
| EXPECT_TRUE(camera_controller->available_cameras().empty()); |
| |
| // Simulate the user login process and wait for the camera info to be updated. |
| { |
| base::RunLoop loop; |
| GetCameraController()->SetOnCameraListReceivedForTesting( |
| loop.QuitClosure()); |
| SimulateUserLogin("example@gmail.com", user_manager::USER_TYPE_REGULAR); |
| loop.Run(); |
| } |
| |
| // Verify that after the user logs in, the camera info is up-to-date. |
| EXPECT_EQ(camera_controller->available_cameras().size(), 1u); |
| } |
| |
| } // namespace ash |