capture_mode_camera_controller.cc 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/capture_mode/capture_mode_camera_controller.h"
  5. #include <algorithm>
  6. #include <cstring>
  7. #include "ash/accessibility/accessibility_controller_impl.h"
  8. #include "ash/capture_mode/capture_mode_button.h"
  9. #include "ash/capture_mode/capture_mode_camera_preview_view.h"
  10. #include "ash/capture_mode/capture_mode_constants.h"
  11. #include "ash/capture_mode/capture_mode_controller.h"
  12. #include "ash/capture_mode/capture_mode_metrics.h"
  13. #include "ash/capture_mode/capture_mode_session.h"
  14. #include "ash/capture_mode/capture_mode_util.h"
  15. #include "ash/public/cpp/capture_mode/capture_mode_delegate.h"
  16. #include "ash/public/cpp/shell_window_ids.h"
  17. #include "ash/root_window_controller.h"
  18. #include "ash/shell.h"
  19. #include "ash/strings/grit/ash_strings.h"
  20. #include "ash/system/message_center/unified_message_center_bubble.h"
  21. #include "ash/system/status_area_widget.h"
  22. #include "ash/system/tray/system_tray_notifier.h"
  23. #include "ash/system/unified/unified_system_tray.h"
  24. #include "ash/system/unified/unified_system_tray_controller.h"
  25. #include "ash/wm/pip/pip_positioner.h"
  26. #include "ash/wm/window_state.h"
  27. #include "ash/wm/wm_event.h"
  28. #include "base/bind.h"
  29. #include "base/callback_helpers.h"
  30. #include "base/check.h"
  31. #include "base/containers/contains.h"
  32. #include "base/strings/stringprintf.h"
  33. #include "base/time/time.h"
  34. #include "media/capture/video/video_capture_device_descriptor.h"
  35. #include "ui/aura/window_targeter.h"
  36. #include "ui/base/l10n/l10n_util.h"
  37. #include "ui/compositor/layer.h"
  38. #include "ui/display/screen.h"
  39. #include "ui/gfx/animation/tween.h"
  40. #include "ui/gfx/geometry/point.h"
  41. #include "ui/gfx/geometry/point_conversions.h"
  42. #include "ui/gfx/geometry/rect.h"
  43. #include "ui/gfx/geometry/size.h"
  44. #include "ui/gfx/geometry/transform.h"
  45. #include "ui/views/animation/animation_builder.h"
  46. #include "ui/views/widget/widget.h"
  47. #include "ui/wm/core/coordinate_conversion.h"
  48. #include "ui/wm/core/window_properties.h"
  49. #include "ui/wm/core/window_util.h"
  50. namespace ash {
  51. namespace {
  52. // The maximum amount of time we allow a `selected_camera_` to remain
  53. // disconnected before we consider it gone forever, and we clear its ID from
  54. // `selected_camera_`.
  55. constexpr base::TimeDelta kDisconnectionGracePeriod = base::Seconds(10);
  56. // The animation duration for the bounds change operation on the camera preview.
  57. constexpr base::TimeDelta kCameraBoundsChangeAnimationDuration =
  58. base::Milliseconds(150);
  59. // The duration for the camera preview fading out process.
  60. constexpr base::TimeDelta kCameraPreviewFadeOutDuration =
  61. base::Milliseconds(50);
  62. // The duration for the camera preview fading in process.
  63. constexpr base::TimeDelta kCameraPreviewFadeInDuration =
  64. base::Milliseconds(150);
  65. // Defines a map type to map a camera model ID (or display name) to the number
  66. // of cameras of that model that are currently connected.
  67. using ModelIdToCountMap = std::map<std::string, int>;
  68. // Using the given `cam_models_map` which tracks the number of cameras connected
  69. // of each model, returns the next `CameraId::number` for the given
  70. // `model_id_or_display_name`.
  71. int GetNextCameraNumber(const std::string& model_id_or_display_name,
  72. ModelIdToCountMap* cam_models_map) {
  73. return ++(*cam_models_map)[model_id_or_display_name];
  74. }
  75. // Returns a reference to either the model ID (if available) or the display name
  76. // from the given `descriptor`.
  77. const std::string& PickModelIdOrDisplayName(
  78. const media::VideoCaptureDeviceDescriptor& descriptor) {
  79. return descriptor.model_id.empty() ? descriptor.display_name()
  80. : descriptor.model_id;
  81. }
  82. // Returns true if the `incoming_list` (supplied by the video source provider)
  83. // contains different items than the ones in `current_list` (which is the
  84. // currently `available_cameras_` maintained by `CaptureModeCameraController`).
  85. bool DidDevicesChange(
  86. const std::vector<media::VideoCaptureDeviceInfo>& incoming_list,
  87. const CameraInfoList& current_list) {
  88. if (incoming_list.size() != current_list.size())
  89. return true;
  90. ModelIdToCountMap cam_models_map;
  91. for (const auto& incoming_camera : incoming_list) {
  92. const auto& device_id = incoming_camera.descriptor.device_id;
  93. const auto iter = std::find_if(current_list.begin(), current_list.end(),
  94. [device_id](const CameraInfo& info) {
  95. return info.device_id == device_id;
  96. });
  97. if (iter == current_list.end())
  98. return true;
  99. const auto& model_id_or_display_name =
  100. PickModelIdOrDisplayName(incoming_camera.descriptor);
  101. const int cam_number =
  102. GetNextCameraNumber(model_id_or_display_name, &cam_models_map);
  103. const CameraInfo& found_info = *iter;
  104. if (found_info.display_name != incoming_camera.descriptor.display_name() ||
  105. found_info.camera_id.model_id_or_display_name() !=
  106. model_id_or_display_name ||
  107. found_info.camera_id.number() != cam_number) {
  108. // It is unexpected that the supported formats of the same camera device
  109. // change, so we ignore comparing them here.
  110. return true;
  111. }
  112. }
  113. return false;
  114. }
  115. // Picks and returns the most suitable supported camera format from the given
  116. // list of `supported_formats` for the given camera `preview_widget_size`.
  117. // Note that this assumes that `supported_formats` is sorted as described in the
  118. // documentation of `CameraInfo::supported_formats`.
  119. media::VideoCaptureFormat PickSuitableCaptureFormat(
  120. const gfx::Size& preview_widget_size,
  121. const media::VideoCaptureFormats& supported_formats) {
  122. DCHECK(!supported_formats.empty());
  123. DCHECK_EQ(preview_widget_size.height(), preview_widget_size.width())
  124. << "The preview widget is always assumed to be a square.";
  125. size_t result_index = 0;
  126. float current_frame_rate = 0.f;
  127. for (size_t i = 0; i < supported_formats.size(); ++i) {
  128. const auto& format = supported_formats[i];
  129. // Once we find a format with a larger height than the preview's, we stop
  130. // and return what we found so far.
  131. if (format.frame_size.height() > preview_widget_size.height())
  132. break;
  133. if (format.frame_rate >= current_frame_rate && format.frame_rate <= 30.f) {
  134. current_frame_rate = format.frame_rate;
  135. result_index = i;
  136. }
  137. }
  138. return supported_formats[result_index];
  139. }
  140. // Only rear device cameras marked as facing the environment should not act as
  141. // a mirror (those cameras are typically not used as selfie cams).
  142. bool ShouldCameraActLikeAMirror(const CameraInfo& camera_info) {
  143. return camera_info.camera_facing_mode !=
  144. media::MEDIA_VIDEO_FACING_ENVIRONMENT;
  145. }
  146. // Returns the CameraInfo item in `list` whose ID is equal to the given `id`, or
  147. // nullptr if no such item exists.
  148. const CameraInfo* GetCameraInfoById(const CameraId& id,
  149. const CameraInfoList& list) {
  150. const auto iter = std::find_if(
  151. list.begin(), list.end(),
  152. [&id](const CameraInfo& info) { return info.camera_id == id; });
  153. return iter == list.end() ? nullptr : &(*iter);
  154. }
  155. // Returns the widget init params needed to create the camera preview widget.
  156. views::Widget::InitParams CreateWidgetParams(const gfx::Rect& bounds) {
  157. views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  158. params.parent = CaptureModeController::Get()->GetCameraPreviewParentWindow();
  159. params.bounds = bounds;
  160. params.name = "CameraPreviewWidget";
  161. return params;
  162. }
  163. // Called by `ContinueDraggingPreview` to make sure camera preview is not
  164. // dragged outside of the capture surface.
  165. void AdjustBoundsWithinConfinedBounds(const gfx::Rect& confined_bounds,
  166. gfx::Rect& preview_bounds) {
  167. const int x = preview_bounds.x();
  168. if (int offset = x - confined_bounds.x(); offset < 0) {
  169. preview_bounds.set_x(x - offset);
  170. } else if (int offset = confined_bounds.right() - preview_bounds.right();
  171. offset < 0) {
  172. preview_bounds.set_x(x + offset);
  173. }
  174. const int y = preview_bounds.y();
  175. if (int offset = y - confined_bounds.y(); offset < 0) {
  176. preview_bounds.set_y(y - offset);
  177. } else if (int offset = confined_bounds.bottom() - preview_bounds.bottom();
  178. offset < 0) {
  179. preview_bounds.set_y(y + offset);
  180. }
  181. }
  182. // Returns the bounds that should be used in the bounds animation of the given
  183. // `camera_preview_window`. If this window is parented to a window that uses
  184. // screen coordinates, then the given `target_bounds` are in screen coordinates,
  185. // and cannot be used for bounds animation (bounds animates relative to the
  186. // window's parent). In this case, the bounds returned are in parent
  187. // coordinates.
  188. gfx::Rect GetTargetBoundsForBoundsAnimation(
  189. const gfx::Rect& target_bounds,
  190. aura::Window* camera_preview_window) {
  191. gfx::Rect result = target_bounds;
  192. auto* parent = camera_preview_window->parent();
  193. if (parent->GetProperty(wm::kUsesScreenCoordinatesKey))
  194. wm::ConvertRectFromScreen(parent, &result);
  195. return result;
  196. }
  197. gfx::Rect GetCollisionAvoidanceRect(aura::Window* root_window) {
  198. DCHECK(root_window);
  199. UnifiedSystemTray* tray = RootWindowController::ForWindow(root_window)
  200. ->GetStatusAreaWidget()
  201. ->unified_system_tray();
  202. if (!tray->IsBubbleShown())
  203. return gfx::Rect();
  204. gfx::Rect collision_avoidance_rect = tray->GetBubbleBoundsInScreen();
  205. auto* message_center_bubble = tray->message_center_bubble();
  206. if (message_center_bubble->IsMessageCenterVisible())
  207. collision_avoidance_rect.Union(message_center_bubble->GetBoundsInScreen());
  208. // TODO(conniekxu): Return a vector of collision avoidance rects including
  209. // other system UIs, like launcher.
  210. return collision_avoidance_rect;
  211. }
  212. // Called to avoid overlap between camera preview and system UIs when camera
  213. // preview's bounds are changed, camera preview is created or destroyed.
  214. void UpdateFloatingPanelBoundsIfNeeded(aura::Window* root_window) {
  215. DCHECK(root_window);
  216. Shell::Get()->accessibility_controller()->UpdateFloatingPanelBoundsIfNeeded();
  217. auto* pip_window_container =
  218. root_window->GetChildById(kShellWindowId_PipContainer);
  219. for (aura::Window* pip_window : pip_window_container->children()) {
  220. auto* pip_window_state = WindowState::Get(pip_window);
  221. if (pip_window_state->IsPip())
  222. pip_window_state->UpdatePipBounds();
  223. }
  224. }
  225. // Calculates a temporary suitable size to use for creating the preview widget.
  226. // This size will be used to pick a suitable camera capture format, hence it
  227. // will be based on the maximum size the preview can ever get to on the device
  228. // with the current configuration.
  229. gfx::Size CalculatePreviewInitialSize() {
  230. int max_shorter_side = 0;
  231. for (auto* root_window : Shell::GetAllRootWindows()) {
  232. const auto work_area = display::Screen::GetScreen()
  233. ->GetDisplayNearestWindow(root_window)
  234. .work_area();
  235. const int shorter_side = std::min(work_area.width(), work_area.height());
  236. max_shorter_side = std::max(max_shorter_side, shorter_side);
  237. }
  238. const int preview_diameter =
  239. max_shorter_side / capture_mode::kCaptureSurfaceShortSideDivider;
  240. return gfx::Size(preview_diameter, preview_diameter);
  241. }
  242. // Returns the appropriate `message_id` for ChromeVox alert on setting camera
  243. // preview snap position. `for_collision_avoidance` indicates whether the
  244. // preview snap position updating happens because of its bounds overlap with
  245. // other system surfaces.
  246. int GetMessageIdForSnapPosition(CameraPreviewSnapPosition snap_position,
  247. bool for_collision_avoidance) {
  248. switch (snap_position) {
  249. case CameraPreviewSnapPosition::kTopRight:
  250. return for_collision_avoidance
  251. ? IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_SNAPPED_TO_UPPER_RIGHT_ON_CONFLICT
  252. : IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_SNAPPED_TO_UPPER_RIGHT;
  253. case CameraPreviewSnapPosition::kTopLeft:
  254. return for_collision_avoidance
  255. ? IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_SNAPPED_TO_UPPER_LEFT_ON_CONFLICT
  256. : IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_SNAPPED_TO_UPPER_LEFT;
  257. case CameraPreviewSnapPosition::kBottomRight:
  258. return for_collision_avoidance
  259. ? IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_SNAPPED_TO_LOWER_RIGHT_ON_CONFLICT
  260. : IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_SNAPPED_TO_LOWER_RIGHT;
  261. case CameraPreviewSnapPosition::kBottomLeft:
  262. return for_collision_avoidance
  263. ? IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_SNAPPED_TO_LOWER_LEFT_ON_CONFLICT
  264. : IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_SNAPPED_TO_LOWER_LEFT;
  265. }
  266. }
  267. // Defines a window targeter that will be installed on the camera preview
  268. // widget's window so that we can allow located events outside of the camera
  269. // preview circle to go through and not be consumed by the camera preview. This
  270. // enables the user to interact with other UI components below camera preview.
  271. class CameraPreviewTargeter : public aura::WindowTargeter {
  272. public:
  273. explicit CameraPreviewTargeter(aura::Window* camera_preview_window)
  274. : camera_preview_window_(camera_preview_window) {}
  275. CameraPreviewTargeter(const CameraPreviewTargeter&) = delete;
  276. CameraPreviewTargeter& operator=(const CameraPreviewTargeter&) = delete;
  277. ~CameraPreviewTargeter() override = default;
  278. // aura::WindowTargeter:
  279. ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
  280. ui::Event* event) override {
  281. if (event->IsLocatedEvent()) {
  282. auto screen_location = event->AsLocatedEvent()->root_location();
  283. wm::ConvertPointToScreen(camera_preview_window_->GetRootWindow(),
  284. &screen_location);
  285. const gfx::Rect camera_preview_bounds =
  286. camera_preview_window_->GetBoundsInScreen();
  287. const gfx::Point camera_preview_center_point =
  288. camera_preview_bounds.CenterPoint();
  289. const int camera_preview_radius = camera_preview_bounds.width() / 2 -
  290. capture_mode::kCameraPreviewBorderSize;
  291. // Check if events are outside of the camera preview circle by comparing
  292. // if the distance between screen location and center of camera preview is
  293. // larger than camera preview circle's radius. If it's larger, allow the
  294. // events to go through so that they can be used by other UI components
  295. // below camera preview.
  296. if ((screen_location - camera_preview_center_point).LengthSquared() >
  297. camera_preview_radius * camera_preview_radius) {
  298. return nullptr;
  299. }
  300. }
  301. return aura::WindowTargeter::FindTargetForEvent(root, event);
  302. }
  303. private:
  304. aura::Window* const camera_preview_window_;
  305. };
  306. capture_mode_util::AnimationParams BuildCameraVisibilityAnimationParams(
  307. bool target_visibility,
  308. bool apply_scale_up_animation) {
  309. return {target_visibility ? kCameraPreviewFadeInDuration
  310. : kCameraPreviewFadeOutDuration,
  311. gfx::Tween::LINEAR, apply_scale_up_animation};
  312. }
  313. } // namespace
  314. // -----------------------------------------------------------------------------
  315. // CameraId:
  316. CameraId::CameraId(std::string model_id_or_display_name, int number)
  317. : model_id_or_display_name_(std::move(model_id_or_display_name)),
  318. number_(number) {
  319. DCHECK(!model_id_or_display_name_.empty());
  320. DCHECK_GE(number, 1);
  321. }
  322. bool CameraId::operator<(const CameraId& rhs) const {
  323. const int result = std::strcmp(model_id_or_display_name_.c_str(),
  324. rhs.model_id_or_display_name_.c_str());
  325. return result != 0 ? result : (number_ < rhs.number_);
  326. }
  327. std::string CameraId::ToString() const {
  328. return base::StringPrintf("%s:%0d", model_id_or_display_name_.c_str(),
  329. number_);
  330. }
  331. // -----------------------------------------------------------------------------
  332. // CameraInfo:
  333. CameraInfo::CameraInfo(CameraId camera_id,
  334. std::string device_id,
  335. std::string display_name,
  336. const media::VideoCaptureFormats& supported_formats,
  337. media::VideoFacingMode camera_facing_mode)
  338. : camera_id(std::move(camera_id)),
  339. device_id(std::move(device_id)),
  340. display_name(std::move(display_name)),
  341. supported_formats(supported_formats),
  342. camera_facing_mode(camera_facing_mode) {}
  343. CameraInfo::CameraInfo(CameraInfo&&) = default;
  344. CameraInfo& CameraInfo::operator=(CameraInfo&&) = default;
  345. CameraInfo::~CameraInfo() = default;
  346. // -----------------------------------------------------------------------------
  347. // CaptureModeCameraController:
  348. CaptureModeCameraController::CaptureModeCameraController(
  349. CaptureModeDelegate* delegate)
  350. : delegate_(delegate) {
  351. DCHECK(delegate_);
  352. DCHECK(base::SystemMonitor::Get())
  353. << "No instance of SystemMonitor exists. If this is a unit test, please "
  354. "create one.";
  355. base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
  356. ReconnectToVideoSourceProvider();
  357. Shell::Get()->system_tray_notifier()->AddSystemTrayObserver(this);
  358. }
  359. CaptureModeCameraController::~CaptureModeCameraController() {
  360. base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
  361. Shell::Get()->system_tray_notifier()->RemoveSystemTrayObserver(this);
  362. }
  363. void CaptureModeCameraController::AddObserver(Observer* observer) {
  364. observers_.AddObserver(observer);
  365. }
  366. void CaptureModeCameraController::RemoveObserver(Observer* observer) {
  367. observers_.RemoveObserver(observer);
  368. }
  369. void CaptureModeCameraController::MaybeSelectFirstCamera() {
  370. if (!selected_camera_.is_valid() && !available_cameras_.empty())
  371. SetSelectedCamera(available_cameras_[0].camera_id);
  372. }
  373. bool CaptureModeCameraController::IsCameraDisabledByPolicy() const {
  374. return delegate_->IsCameraDisabledByPolicy();
  375. }
  376. std::string CaptureModeCameraController::GetDisplayNameOfSelectedCamera()
  377. const {
  378. if (selected_camera_.is_valid()) {
  379. const CameraInfo* camera_info =
  380. GetCameraInfoById(selected_camera_, available_cameras_);
  381. // `camera_info` might not exist in the test even though the
  382. // `selected_camera_` is valid.
  383. if (camera_info)
  384. return camera_info->display_name;
  385. }
  386. return std::string();
  387. }
  388. void CaptureModeCameraController::SetSelectedCamera(CameraId camera_id) {
  389. // When cameras are disabled by policy, we don't allow any camera selection.
  390. if (IsCameraDisabledByPolicy()) {
  391. LOG(WARNING) << "Camera is disabled by policy. Selecting camera: "
  392. << camera_id.ToString() << " will be ignored.";
  393. camera_id = CameraId{};
  394. }
  395. if (selected_camera_ == camera_id)
  396. return;
  397. selected_camera_ = std::move(camera_id);
  398. camera_reconnect_timer_.Stop();
  399. for (auto& observer : observers_)
  400. observer.OnSelectedCameraChanged(selected_camera_);
  401. const std::string camera_display_name = GetDisplayNameOfSelectedCamera();
  402. if (!camera_display_name.empty()) {
  403. capture_mode_util::TriggerAccessibilityAlert(l10n_util::GetStringFUTF8(
  404. IDS_ASH_SCREEN_CAPTURE_SELECTED_CAMERA_CHANGED,
  405. base::UTF8ToUTF16(camera_display_name)));
  406. }
  407. RefreshCameraPreview();
  408. }
  409. void CaptureModeCameraController::SetShouldShowPreview(bool value) {
  410. should_show_preview_ = value;
  411. RefreshCameraPreview();
  412. }
  413. void CaptureModeCameraController::MaybeReparentPreviewWidget() {
  414. if (!camera_preview_widget_)
  415. return;
  416. // Remove the camera preview from its transient parent if it has one. And
  417. // reparenting it to the corresponding parent again. This is done to keep
  418. // the camera preview in a correct state that is not a transient child of
  419. // its parent.
  420. auto* native_window = camera_preview_widget_->GetNativeWindow();
  421. if (auto* transient_parent = wm::GetTransientParent(native_window))
  422. wm::RemoveTransientChild(transient_parent, native_window);
  423. const bool was_visible_before = camera_preview_widget_->IsVisible();
  424. auto* controller = CaptureModeController::Get();
  425. auto* parent = controller->GetCameraPreviewParentWindow();
  426. DCHECK(parent);
  427. if (parent != native_window->parent())
  428. views::Widget::ReparentNativeView(native_window, parent);
  429. DCHECK(!wm::GetTransientParent(native_window));
  430. MaybeUpdatePreviewWidget();
  431. if (was_visible_before != camera_preview_widget_->IsVisible()) {
  432. capture_mode_util::TriggerAccessibilityAlertSoon(
  433. was_visible_before ? IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_HIDDEN
  434. : IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_ON);
  435. }
  436. }
  437. void CaptureModeCameraController::SetCameraPreviewSnapPosition(
  438. CameraPreviewSnapPosition value,
  439. bool animate) {
  440. // Trigger a11y alert on setting camera preview snap position even though the
  441. // snap position may actually not change.
  442. capture_mode_util::TriggerAccessibilityAlert(
  443. GetMessageIdForSnapPosition(value, /*for_collision_avoidance=*/false));
  444. camera_preview_snap_position_ = value;
  445. MaybeUpdatePreviewWidget(animate);
  446. }
  447. void CaptureModeCameraController::MaybeUpdatePreviewWidget(bool animate) {
  448. if (!camera_preview_widget_)
  449. return;
  450. auto* controller = CaptureModeController::Get();
  451. DCHECK(controller->IsActive() || controller->is_recording_in_progress());
  452. const gfx::Rect confine_bounds = controller->GetCameraPreviewConfineBounds();
  453. const capture_mode_util::CameraPreviewSizeSpecs size_specs =
  454. capture_mode_util::CalculateCameraPreviewSizeSpecs(
  455. confine_bounds.size(), is_camera_preview_collapsed_);
  456. camera_preview_view_->SetIsCollapsible(size_specs.is_collapsible);
  457. // If the surface within which the preview is confined becomes too small, the
  458. // preview should hide immediately to avoid seeing it hide with animation
  459. // outside the bounds of the new confine bounds. https://crbug.com/1320087.
  460. const bool should_animate_visibility =
  461. !size_specs.is_surface_too_small && !confine_bounds.IsEmpty() &&
  462. (camera_preview_widget_->GetNativeWindow()->parent()->GetId() !=
  463. kShellWindowId_UnparentedContainer);
  464. // We don't need to update the bounds if the widget is hidden.
  465. // Also notice we should update the bounds before updating the visibility
  466. // since when `is_first_bounds_update_` is true, we should apply the scale up
  467. // animation which requires the bounds are set first to the camera preview.
  468. const bool did_bounds_change =
  469. size_specs.should_be_visible &&
  470. SetCameraPreviewBounds(
  471. CalculatePreviewWidgetTargetBounds(confine_bounds, size_specs.size),
  472. animate);
  473. const bool did_visibility_change = capture_mode_util::SetWidgetVisibility(
  474. camera_preview_widget_.get(), size_specs.should_be_visible,
  475. !should_animate_visibility
  476. ? absl::nullopt
  477. : absl::make_optional<capture_mode_util::AnimationParams>(
  478. BuildCameraVisibilityAnimationParams(
  479. /*target_visibility=*/size_specs.should_be_visible,
  480. /*apply_scale_up_animation=*/is_first_bounds_update_)));
  481. if (controller->IsActive() && !controller->is_recording_in_progress()) {
  482. controller->capture_mode_session()
  483. ->OnCameraPreviewBoundsOrVisibilityChanged(
  484. /*capture_surface_became_too_small=*/size_specs
  485. .is_surface_too_small,
  486. /*did_bounds_or_visibility_change=*/did_visibility_change ||
  487. did_bounds_change);
  488. }
  489. if (did_bounds_change) {
  490. UpdateFloatingPanelBoundsIfNeeded(
  491. camera_preview_widget_->GetNativeWindow()->GetRootWindow());
  492. }
  493. }
  494. void CaptureModeCameraController::StartDraggingPreview(
  495. const gfx::PointF& screen_location) {
  496. is_drag_in_progress_ = true;
  497. previous_location_in_screen_ = screen_location;
  498. camera_preview_view_->RefreshResizeButtonVisibility();
  499. auto* controller = CaptureModeController::Get();
  500. if (controller->IsActive())
  501. controller->capture_mode_session()->OnCameraPreviewDragStarted();
  502. // Use cursor compositing instead of the platform cursor when dragging to
  503. // ensure the cursor is aligned with the camera preview.
  504. Shell::Get()->UpdateCursorCompositingEnabled();
  505. }
  506. void CaptureModeCameraController::ContinueDraggingPreview(
  507. const gfx::PointF& screen_location) {
  508. gfx::Rect current_bounds = GetCurrentBoundsMatchingConfineBoundsCoordinates();
  509. current_bounds.Offset(
  510. gfx::ToRoundedVector2d(screen_location - previous_location_in_screen_));
  511. AdjustBoundsWithinConfinedBounds(
  512. CaptureModeController::Get()->GetCameraPreviewConfineBounds(),
  513. current_bounds);
  514. camera_preview_widget_->SetBounds(current_bounds);
  515. previous_location_in_screen_ = screen_location;
  516. }
  517. void CaptureModeCameraController::EndDraggingPreview(
  518. const gfx::PointF& screen_location,
  519. bool is_touch) {
  520. ContinueDraggingPreview(screen_location);
  521. SetCameraPreviewSnapPosition(CalculateSnapPositionOnDragEnded(),
  522. /*animate=*/true);
  523. is_drag_in_progress_ = false;
  524. camera_preview_view_->RefreshResizeButtonVisibility();
  525. // Disable cursor compositing at the end of the drag.
  526. Shell::Get()->UpdateCursorCompositingEnabled();
  527. auto* controller = CaptureModeController::Get();
  528. if (controller->IsActive()) {
  529. controller->capture_mode_session()->OnCameraPreviewDragEnded(
  530. gfx::ToRoundedPoint(screen_location), is_touch);
  531. }
  532. }
  533. void CaptureModeCameraController::ToggleCameraPreviewSize() {
  534. DCHECK(camera_preview_view_);
  535. is_camera_preview_collapsed_ = !is_camera_preview_collapsed_;
  536. MaybeUpdatePreviewWidget(/*animate=*/true);
  537. }
  538. void CaptureModeCameraController::OnCaptureSessionStarted() {
  539. GetCameraDevices();
  540. }
  541. void CaptureModeCameraController::OnRecordingStarted(
  542. bool is_in_projector_mode) {
  543. // Check if there's a camera disconnection that happened before recording
  544. // starts. In this case, we don't want the camera preview to show, even if the
  545. // camera reconnects within the allowed grace period.
  546. if (selected_camera_.is_valid() && !camera_preview_widget_)
  547. SetShouldShowPreview(false);
  548. in_recording_camera_disconnections_ = 0;
  549. const bool starts_with_camera = camera_preview_widget();
  550. RecordRecordingStartsWithCamera(starts_with_camera, is_in_projector_mode);
  551. RecordCameraSizeOnStart(is_camera_preview_collapsed_
  552. ? CaptureModeCameraSize::kCollapsed
  553. : CaptureModeCameraSize::kExpanded);
  554. RecordCameraPositionOnStart(camera_preview_snap_position_);
  555. }
  556. void CaptureModeCameraController::OnRecordingEnded() {
  557. DCHECK(in_recording_camera_disconnections_);
  558. SetShouldShowPreview(false);
  559. RecordCameraDisconnectionsDuringRecordings(
  560. *in_recording_camera_disconnections_);
  561. in_recording_camera_disconnections_.reset();
  562. }
  563. void CaptureModeCameraController::OnFrameHandlerFatalError() {
  564. if (!camera_preview_widget_)
  565. return;
  566. DCHECK(camera_preview_view_);
  567. DCHECK_EQ(selected_camera_, camera_preview_view_->camera_id());
  568. base::EraseIf(available_cameras_, [&](const CameraInfo& info) {
  569. return selected_camera_ == info.camera_id;
  570. });
  571. for (auto& observer : observers_)
  572. observer.OnAvailableCamerasChanged(available_cameras_);
  573. RefreshCameraPreview();
  574. GetCameraDevices();
  575. }
  576. void CaptureModeCameraController::OnShuttingDown() {
  577. // This should destroy any camera preview if present.
  578. SetShouldShowPreview(false);
  579. DCHECK(!should_show_preview_);
  580. DCHECK(!camera_preview_widget_);
  581. is_shutting_down_ = true;
  582. }
  583. void CaptureModeCameraController::PseudoFocusCameraPreview() {
  584. DCHECK(camera_preview_view_);
  585. DCHECK(camera_preview_view_->GetVisible());
  586. auto* controller = CaptureModeController::Get();
  587. DCHECK(!controller->IsActive());
  588. DCHECK(controller->is_recording_in_progress());
  589. camera_preview_view_->PseudoFocus();
  590. camera_preview_view_->UpdateA11yOverrideWindow();
  591. }
  592. void CaptureModeCameraController::OnActiveUserSessionChanged() {
  593. if (!did_first_user_login_) {
  594. did_first_user_login_ = true;
  595. GetCameraDevices();
  596. }
  597. }
  598. void CaptureModeCameraController::OnDevicesChanged(
  599. base::SystemMonitor::DeviceType device_type) {
  600. if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE)
  601. GetCameraDevices();
  602. }
  603. void CaptureModeCameraController::OnSystemTrayBubbleShown() {
  604. MaybeUpdatePreviewWidget(/*animate=*/true);
  605. }
  606. void CaptureModeCameraController::ReconnectToVideoSourceProvider() {
  607. if (is_shutting_down_)
  608. return;
  609. video_source_provider_remote_.reset();
  610. most_recent_request_id_ = 0;
  611. delegate_->ConnectToVideoSourceProvider(
  612. video_source_provider_remote_.BindNewPipeAndPassReceiver());
  613. video_source_provider_remote_.set_disconnect_handler(base::BindOnce(
  614. &CaptureModeCameraController::ReconnectToVideoSourceProvider,
  615. base::Unretained(this)));
  616. GetCameraDevices();
  617. }
  618. void CaptureModeCameraController::GetCameraDevices() {
  619. if (is_shutting_down_ || !did_first_user_login_)
  620. return;
  621. DCHECK(video_source_provider_remote_);
  622. video_source_provider_remote_->GetSourceInfos(base::BindOnce(
  623. &CaptureModeCameraController::OnCameraDevicesReceived,
  624. weak_ptr_factory_.GetWeakPtr(), ++most_recent_request_id_));
  625. }
  626. void CaptureModeCameraController::OnCameraDevicesReceived(
  627. RequestId request_id,
  628. const std::vector<media::VideoCaptureDeviceInfo>& devices) {
  629. if (request_id < most_recent_request_id_) {
  630. // Ignore any out-dated requests replies, since a reply from a more recent
  631. // request is pending.
  632. return;
  633. }
  634. DCHECK_EQ(request_id, most_recent_request_id_);
  635. // Run the optional for-test closure at the exit of this function's scope.
  636. base::ScopedClosureRunner deferred_runner;
  637. if (on_camera_list_received_for_test_) {
  638. deferred_runner.ReplaceClosure(
  639. std::move(on_camera_list_received_for_test_));
  640. }
  641. const bool should_report_cameras_number =
  642. !did_report_number_of_cameras_before_ ||
  643. (devices.size() != available_cameras_.size());
  644. if (should_report_cameras_number) {
  645. did_report_number_of_cameras_before_ = true;
  646. RecordNumberOfConnectedCameras(devices.size());
  647. }
  648. if (!DidDevicesChange(devices, available_cameras_))
  649. return;
  650. available_cameras_.clear();
  651. ModelIdToCountMap cam_models_map;
  652. for (const auto& device : devices) {
  653. const auto& descriptor = device.descriptor;
  654. const auto& model_id_or_display_name = PickModelIdOrDisplayName(descriptor);
  655. const int cam_number =
  656. GetNextCameraNumber(model_id_or_display_name, &cam_models_map);
  657. available_cameras_.emplace_back(
  658. CameraId(model_id_or_display_name, cam_number), descriptor.device_id,
  659. descriptor.display_name(), device.supported_formats, descriptor.facing);
  660. }
  661. for (auto& observer : observers_)
  662. observer.OnAvailableCamerasChanged(available_cameras_);
  663. RefreshCameraPreview();
  664. }
  665. void CaptureModeCameraController::RefreshCameraPreview() {
  666. if (is_shutting_down_)
  667. return;
  668. bool was_visible_before = false;
  669. aura::Window* old_root = nullptr;
  670. if (camera_preview_widget_) {
  671. was_visible_before = camera_preview_widget_->IsVisible();
  672. old_root = camera_preview_widget_->GetNativeWindow()->GetRootWindow();
  673. }
  674. // Trigger a11y alert and update floating windows bounds when camera preview
  675. // is created or destroyed. The reason to trigger
  676. // `RunPostRefreshCameraPreview` at the exit of this function is we should
  677. // wait for camera preview's creation or destruction to be finished.
  678. base::ScopedClosureRunner deferred_runner(
  679. base::BindOnce(&CaptureModeCameraController::RunPostRefreshCameraPreview,
  680. weak_ptr_factory_.GetWeakPtr(), was_visible_before));
  681. const CameraInfo* camera_info = nullptr;
  682. if (selected_camera_.is_valid()) {
  683. if (camera_info = GetCameraInfoById(selected_camera_, available_cameras_);
  684. camera_info) {
  685. if (camera_reconnect_timer_.IsRunning()) {
  686. const base::TimeDelta remaining_time =
  687. camera_reconnect_timer_.desired_run_time() - base::TimeTicks::Now();
  688. const int reconnect_duration_in_seconds =
  689. (kDisconnectionGracePeriod - remaining_time).InSeconds();
  690. RecordCameraReconnectDuration(reconnect_duration_in_seconds,
  691. kDisconnectionGracePeriod.InSeconds());
  692. capture_mode_util::TriggerAccessibilityAlert(
  693. IDS_ASH_SCREEN_CAPTURE_CAMERA_RECONNECTED);
  694. }
  695. // When a selected camera becomes available, we stop any grace period
  696. // timer (if any), and decide whether to show or hide the preview widget
  697. // based on the current value of `should_show_preview_`.
  698. camera_reconnect_timer_.Stop();
  699. if (!should_show_preview_)
  700. camera_info = nullptr;
  701. } else {
  702. // Here the selected camera is disconnected, we'll give it a grace period
  703. // just in case it may reconnect again (this helps in the case of flaky
  704. // camera connections).
  705. camera_reconnect_timer_.Start(
  706. FROM_HERE, kDisconnectionGracePeriod, this,
  707. &CaptureModeCameraController::OnSelectedCameraDisconnected);
  708. if (in_recording_camera_disconnections_)
  709. ++(*in_recording_camera_disconnections_);
  710. capture_mode_util::TriggerAccessibilityAlert(
  711. IDS_ASH_SCREEN_CAPTURE_CAMERA_DISCONNECTED);
  712. }
  713. }
  714. if (!camera_info) {
  715. camera_preview_widget_.reset();
  716. camera_preview_view_ = nullptr;
  717. if (old_root)
  718. UpdateFloatingPanelBoundsIfNeeded(old_root);
  719. return;
  720. }
  721. // Destroying the existing camera preview widget before recreating a new one
  722. // when a different camera was selected.
  723. if (camera_preview_view_ &&
  724. camera_preview_view_->camera_id() != selected_camera_) {
  725. camera_preview_widget_.reset();
  726. camera_preview_view_ = nullptr;
  727. }
  728. DCHECK(!IsCameraDisabledByPolicy());
  729. if (!camera_preview_widget_) {
  730. const auto initial_temp_bounds = gfx::Rect(CalculatePreviewInitialSize());
  731. camera_preview_widget_ = std::make_unique<views::Widget>();
  732. camera_preview_widget_->Init(CreateWidgetParams(initial_temp_bounds));
  733. auto* camera_preview_window = camera_preview_widget_->GetNativeWindow();
  734. camera_preview_window->SetEventTargeter(
  735. std::make_unique<CameraPreviewTargeter>(camera_preview_window));
  736. mojo::Remote<video_capture::mojom::VideoSource> camera_video_source;
  737. video_source_provider_remote_->GetVideoSource(
  738. camera_info->device_id,
  739. camera_video_source.BindNewPipeAndPassReceiver());
  740. camera_preview_view_ = camera_preview_widget_->SetContentsView(
  741. std::make_unique<CameraPreviewView>(
  742. this, selected_camera_, std::move(camera_video_source),
  743. PickSuitableCaptureFormat(initial_temp_bounds.size(),
  744. camera_info->supported_formats),
  745. ShouldCameraActLikeAMirror(*camera_info)));
  746. ui::Layer* layer = camera_preview_widget_->GetLayer();
  747. layer->SetFillsBoundsOpaquely(false);
  748. layer->SetMasksToBounds(true);
  749. // Set `is_first_bounds_update_` to true here right after it's recreated.
  750. // And set it back to false after camera preview is parented and updated to
  751. // the correct bounds and with correct visibility. The value of
  752. // `is_first_bounds_update_` will be used in `FadeInCameraPreview`, if it's
  753. // true, scale up animation will be applied to show camera preview.
  754. is_first_bounds_update_ = true;
  755. MaybeReparentPreviewWidget();
  756. is_first_bounds_update_ = false;
  757. }
  758. DCHECK(camera_preview_view_);
  759. DCHECK_EQ(selected_camera_, camera_preview_view_->camera_id());
  760. }
  761. void CaptureModeCameraController::OnSelectedCameraDisconnected() {
  762. DCHECK(selected_camera_.is_valid());
  763. LOG(WARNING)
  764. << "Selected camera: " << selected_camera_.ToString()
  765. << " remained disconnected for longer than the grace period. Clearing.";
  766. SetSelectedCamera(CameraId());
  767. }
  768. gfx::Rect CaptureModeCameraController::CalculatePreviewWidgetTargetBounds(
  769. const gfx::Rect& confine_bounds,
  770. const gfx::Size& preview_size) {
  771. auto* controller = CaptureModeController::Get();
  772. aura::Window* parent =
  773. camera_preview_widget_
  774. ? camera_preview_widget_->GetNativeWindow()->parent()
  775. : controller->GetCameraPreviewParentWindow();
  776. DCHECK(parent);
  777. const gfx::Rect collision_rect_screen =
  778. GetCollisionAvoidanceRect(parent->GetRootWindow());
  779. std::vector<CameraPreviewSnapPosition> snap_positions = {
  780. CameraPreviewSnapPosition::kBottomRight,
  781. CameraPreviewSnapPosition::kTopRight, CameraPreviewSnapPosition::kTopLeft,
  782. CameraPreviewSnapPosition::kBottomLeft};
  783. // Move `camera_preview_snap_position_` to the beginning of `snap_positions`
  784. // vector, since we should always try the current snap position first.
  785. base::EraseIf(snap_positions,
  786. [this](CameraPreviewSnapPosition snap_position) {
  787. return snap_position == camera_preview_snap_position_;
  788. });
  789. snap_positions.insert(snap_positions.begin(), camera_preview_snap_position_);
  790. // Cache the current preview bounds and return it directly when we find no
  791. // other snap position with no collisions.
  792. gfx::Rect current_preview_bounds;
  793. for (CameraPreviewSnapPosition snap_position : snap_positions) {
  794. gfx::Rect preview_bounds = GetPreviewWidgetBoundsForSnapPosition(
  795. confine_bounds, preview_size, snap_position);
  796. if (!current_preview_bounds.IsEmpty())
  797. current_preview_bounds = preview_bounds;
  798. gfx::Rect preview_bounds_in_screen = preview_bounds;
  799. // Need to convert preview bounds to screen bounds if it's not since we
  800. // need to check whether it intersects with system tray bounds in screen.
  801. // Make sure we use the same coordinate system before we make the
  802. // comparison.
  803. if (!parent->GetProperty(wm::kUsesScreenCoordinatesKey))
  804. wm::ConvertRectToScreen(parent, &preview_bounds_in_screen);
  805. if (!preview_bounds_in_screen.Intersects(collision_rect_screen)) {
  806. if (snap_position != camera_preview_snap_position_) {
  807. camera_preview_snap_position_ = snap_position;
  808. capture_mode_util::TriggerAccessibilityAlert(
  809. GetMessageIdForSnapPosition(snap_position,
  810. /*for_collision_avoidance=*/true));
  811. }
  812. // Notice return `preview_bounds` instead of `preview_bounds_in_screen`,
  813. // since it's the target bounds for camera preview in its parent's
  814. // coordinate system.
  815. return preview_bounds;
  816. }
  817. }
  818. return current_preview_bounds;
  819. }
  820. gfx::Rect CaptureModeCameraController::GetPreviewWidgetBoundsForSnapPosition(
  821. const gfx::Rect& confine_bounds,
  822. const gfx::Size& preview_size,
  823. CameraPreviewSnapPosition snap_position) const {
  824. auto* controller = CaptureModeController::Get();
  825. DCHECK(controller->IsActive() || controller->is_recording_in_progress());
  826. if (confine_bounds.IsEmpty())
  827. return gfx::Rect(preview_size);
  828. gfx::Point origin;
  829. switch (snap_position) {
  830. case CameraPreviewSnapPosition::kTopLeft:
  831. origin = confine_bounds.origin();
  832. origin.Offset(capture_mode::kSpaceBetweenCameraPreviewAndEdges,
  833. capture_mode::kSpaceBetweenCameraPreviewAndEdges);
  834. break;
  835. case CameraPreviewSnapPosition::kBottomLeft:
  836. origin = gfx::Point(
  837. confine_bounds.x() + capture_mode::kSpaceBetweenCameraPreviewAndEdges,
  838. confine_bounds.bottom() - preview_size.height() -
  839. capture_mode::kSpaceBetweenCameraPreviewAndEdges);
  840. break;
  841. case CameraPreviewSnapPosition::kBottomRight:
  842. origin = gfx::Point(confine_bounds.right() - preview_size.width() -
  843. capture_mode::kSpaceBetweenCameraPreviewAndEdges,
  844. confine_bounds.bottom() - preview_size.height() -
  845. capture_mode::kSpaceBetweenCameraPreviewAndEdges);
  846. break;
  847. case CameraPreviewSnapPosition::kTopRight:
  848. origin = gfx::Point(confine_bounds.right() - preview_size.width() -
  849. capture_mode::kSpaceBetweenCameraPreviewAndEdges,
  850. confine_bounds.y() +
  851. capture_mode::kSpaceBetweenCameraPreviewAndEdges);
  852. break;
  853. }
  854. return gfx::Rect(origin, preview_size);
  855. }
  856. CameraPreviewSnapPosition
  857. CaptureModeCameraController::CalculateSnapPositionOnDragEnded() const {
  858. const gfx::Point center_point_of_preview_widget =
  859. GetCurrentBoundsMatchingConfineBoundsCoordinates().CenterPoint();
  860. const gfx::Point center_point_of_confine_bounds =
  861. CaptureModeController::Get()
  862. ->GetCameraPreviewConfineBounds()
  863. .CenterPoint();
  864. if (center_point_of_preview_widget.x() < center_point_of_confine_bounds.x()) {
  865. return center_point_of_preview_widget.y() <
  866. center_point_of_confine_bounds.y()
  867. ? CameraPreviewSnapPosition::kTopLeft
  868. : CameraPreviewSnapPosition::kBottomLeft;
  869. }
  870. return center_point_of_preview_widget.y() < center_point_of_confine_bounds.y()
  871. ? CameraPreviewSnapPosition::kTopRight
  872. : CameraPreviewSnapPosition::kBottomRight;
  873. }
  874. gfx::Rect
  875. CaptureModeCameraController::GetCurrentBoundsMatchingConfineBoundsCoordinates()
  876. const {
  877. aura::Window* preview_window = camera_preview_widget_->GetNativeWindow();
  878. aura::Window* parent = preview_window->parent();
  879. if (parent->GetProperty(wm::kUsesScreenCoordinatesKey))
  880. return preview_window->GetBoundsInScreen();
  881. return preview_window->bounds();
  882. }
  883. void CaptureModeCameraController::RunPostRefreshCameraPreview(
  884. bool was_preview_visible_before) {
  885. const bool is_preview_visible_now =
  886. camera_preview_widget_ && camera_preview_widget_->IsVisible();
  887. if (was_preview_visible_before != is_preview_visible_now) {
  888. capture_mode_util::TriggerAccessibilityAlertSoon(
  889. was_preview_visible_before
  890. ? IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_HIDDEN
  891. : IDS_ASH_SCREEN_CAPTURE_CAMERA_PREVIEW_ON);
  892. }
  893. if (camera_preview_widget_) {
  894. UpdateFloatingPanelBoundsIfNeeded(
  895. camera_preview_widget_->GetNativeWindow()->GetRootWindow());
  896. }
  897. }
  898. bool CaptureModeCameraController::SetCameraPreviewBounds(
  899. const gfx::Rect& target_bounds,
  900. bool animate) {
  901. DCHECK(camera_preview_widget_);
  902. const auto current_bounds =
  903. GetCurrentBoundsMatchingConfineBoundsCoordinates();
  904. if (target_bounds == current_bounds)
  905. return false;
  906. auto* preview_window = camera_preview_widget_->GetNativeWindow();
  907. if (animate) {
  908. views::AnimationBuilder builder;
  909. builder.SetPreemptionStrategy(
  910. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  911. // If the size changes, that means the preview view has to re-layout to keep
  912. // the resize button at the same size, and at the same offset from the
  913. // bottom of the preview. In this case we have to use bounds animation to
  914. // achieve this. Otherwise, transform animation will cause the button to
  915. // shrink or expand during the animation.
  916. if (target_bounds.size() == current_bounds.size()) {
  917. // Use transform animation.
  918. camera_preview_widget_->SetBounds(target_bounds);
  919. gfx::Transform transform;
  920. transform.Translate(current_bounds.CenterPoint() -
  921. target_bounds.CenterPoint());
  922. ui::Layer* layer = preview_window->layer();
  923. layer->SetTransform(transform);
  924. builder.Once()
  925. .SetDuration(kCameraBoundsChangeAnimationDuration)
  926. .SetTransform(layer, gfx::Transform(),
  927. gfx::Tween::ACCEL_5_70_DECEL_90);
  928. } else {
  929. // Use bounds animation.
  930. const auto target_bounds_in_parent =
  931. GetTargetBoundsForBoundsAnimation(target_bounds, preview_window);
  932. builder.Once()
  933. .SetDuration(kCameraBoundsChangeAnimationDuration)
  934. .SetBounds(preview_window, target_bounds_in_parent,
  935. gfx::Tween::ACCEL_20_DECEL_100);
  936. }
  937. } else {
  938. camera_preview_widget_->SetBounds(target_bounds);
  939. }
  940. return true;
  941. }
  942. } // namespace ash