Commit 7fa67e3f authored by Ahmed Fakhry's avatar Ahmed Fakhry Committed by Commit Bot

capture_mode: Add logic to capture images

This CL implements the image capture part of this feature,
from all the possible sources, and handles saving the image
files and showing preview notifications as well as error
notifications.

Later CLs will implement logic to disable capture mode
operations such as policy or Data Leak Prevention (DLP).

BUG=1121699

Change-Id: I6dfba6f57adb0c48270b4d5ebb71e7d28fe489df
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2376476
Commit-Queue: Ahmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804036}
parent 11d2a7ec
...@@ -2691,6 +2691,18 @@ Here are some things you can try to get started. ...@@ -2691,6 +2691,18 @@ Here are some things you can try to get started.
</message> </message>
<!-- Capture Mode --> <!-- Capture Mode -->
<message name="IDS_ASH_SCREEN_CAPTURE_DISABLED_TITLE" desc="The title of the notification when capture mode is disabled.">
Screen capture disabled
</message>
<message name="IDS_ASH_SCREEN_CAPTURE_FAILURE_TITLE" desc="The title of the notification when capture mode fails.">
An error occurred
</message>
<message name="IDS_ASH_SCREEN_CAPTURE_DISABLED_MESSAGE" desc="The text of the notification when capture mode is disabled.">
The ability to screen capture has been disabled by your administrator.
</message>
<message name="IDS_ASH_SCREEN_CAPTURE_FAILURE_MESSAGE" desc="The text of the notification when capture mode fails.">
Failed to capture screen
</message>
<message name="IDS_ASH_SCREEN_CAPTURE_DISPLAY_SOURCE" desc="The name that is the display source of the screen capture notification."> <message name="IDS_ASH_SCREEN_CAPTURE_DISPLAY_SOURCE" desc="The name that is the display source of the screen capture notification.">
Screen capture Screen capture
</message> </message>
......
c4ac976289a1db5f5e982fdd4148d219848331ac
\ No newline at end of file
c4ac976289a1db5f5e982fdd4148d219848331ac
\ No newline at end of file
79ed560659475d7632f6a9266546b4a7f7bc5de7
\ No newline at end of file
79ed560659475d7632f6a9266546b4a7f7bc5de7
\ No newline at end of file
This diff is collapsed.
...@@ -10,8 +10,15 @@ ...@@ -10,8 +10,15 @@
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/capture_mode/capture_mode_types.h" #include "ash/capture_mode/capture_mode_types.h"
#include "ash/public/cpp/capture_mode_delegate.h" #include "ash/public/cpp/capture_mode_delegate.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect.h"
#include "ui/gfx/image/image.h"
namespace base {
class FilePath;
class Time;
} // namespace base
namespace ash { namespace ash {
...@@ -62,11 +69,43 @@ class ASH_EXPORT CaptureModeController { ...@@ -62,11 +69,43 @@ class ASH_EXPORT CaptureModeController {
void EndVideoRecording(); void EndVideoRecording();
private: private:
// Show notification for the newly taken screenshot or screen recording. // Returns true if doing a screen capture is currently allowed, false
void ShowNotification(const base::FilePath& screen_capture_path); // otherwise.
bool IsCaptureAllowed() const;
// The below functions start the actual image/video capture. They expect that
// the capture session is still active when called, so they can retrieve the
// capture parameters they need. They will end the sessions themselves.
// They should never be called if IsCaptureAllowed() returns false.
void CaptureImage();
void CaptureVideo();
// Called back when an image has been captured to trigger an attempt to save
// the image as a file. |timestamp| is the time at which the capture was
// triggered, and |png_bytes| is the buffer containing the captured image in a
// PNG format.
void OnImageCaptured(base::Time timestamp,
scoped_refptr<base::RefCountedMemory> png_bytes);
// Called back when an attempt to save the image file has been completed, with
// |success| indicating whether the attempt succeeded for failed. |png_bytes|
// is the buffer containing the captured image in a PNG format, which will be
// used to show a preview of the image in a notification. If saving was
// successful, then the image was saved in |path|.
void OnImageFileSaved(scoped_refptr<base::RefCountedMemory> png_bytes,
const base::FilePath& path,
bool success);
// Shows a preview notification of the newly taken screenshot or screen
// recording.
void ShowPreviewNotification(const base::FilePath& screen_capture_path,
const gfx::Image& preview_image);
void HandleNotificationClicked(const base::FilePath& screen_capture_path, void HandleNotificationClicked(const base::FilePath& screen_capture_path,
base::Optional<int> button_index); base::Optional<int> button_index);
// Builds a path for an image screenshot file that was taken at |timestamp|.
base::FilePath BuildImagePath(base::Time timestamp) const;
std::unique_ptr<CaptureModeDelegate> delegate_; std::unique_ptr<CaptureModeDelegate> delegate_;
CaptureModeType type_ = CaptureModeType::kImage; CaptureModeType type_ = CaptureModeType::kImage;
......
...@@ -88,6 +88,15 @@ CaptureModeSession::~CaptureModeSession() { ...@@ -88,6 +88,15 @@ CaptureModeSession::~CaptureModeSession() {
SetMouseWarpEnabled(old_mouse_warp_status_); SetMouseWarpEnabled(old_mouse_warp_status_);
} }
aura::Window* CaptureModeSession::GetSelectedWindow() const {
// Note that the capture bar widget is activatable, so we can't use
// window_util::GetActiveWindow(). Instead, we use the MRU window tracker and
// get the top-most window if any.
auto mru_windows =
Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
return mru_windows.empty() ? nullptr : mru_windows[0];
}
void CaptureModeSession::OnCaptureSourceChanged(CaptureModeSource new_source) { void CaptureModeSession::OnCaptureSourceChanged(CaptureModeSource new_source) {
capture_mode_bar_view_->OnCaptureSourceChanged(new_source); capture_mode_bar_view_->OnCaptureSourceChanged(new_source);
SetMouseWarpEnabled(new_source != CaptureModeSource::kRegion); SetMouseWarpEnabled(new_source != CaptureModeSource::kRegion);
...@@ -136,14 +145,8 @@ void CaptureModeSession::OnTouchEvent(ui::TouchEvent* event) { ...@@ -136,14 +145,8 @@ void CaptureModeSession::OnTouchEvent(ui::TouchEvent* event) {
} }
gfx::Rect CaptureModeSession::GetSelectedWindowBounds() const { gfx::Rect CaptureModeSession::GetSelectedWindowBounds() const {
// Note that the capture bar widget is activatable, so we can't use auto* window = GetSelectedWindow();
// window_util::GetActiveWindow(). Instead, we use the MRU window tracker and return window ? window->bounds() : gfx::Rect();
// get the top-most window if any.
auto mru_windows =
Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
if (!mru_windows.empty())
return mru_windows[0]->bounds();
return gfx::Rect();
} }
void CaptureModeSession::RefreshStackingOrder(aura::Window* parent_container) { void CaptureModeSession::RefreshStackingOrder(aura::Window* parent_container) {
......
...@@ -38,10 +38,15 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -38,10 +38,15 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
CaptureModeSession& operator=(const CaptureModeSession&) = delete; CaptureModeSession& operator=(const CaptureModeSession&) = delete;
~CaptureModeSession() override; ~CaptureModeSession() override;
aura::Window* current_root() const { return current_root_; }
CaptureModeBarView* capture_mode_bar_view() const { CaptureModeBarView* capture_mode_bar_view() const {
return capture_mode_bar_view_; return capture_mode_bar_view_;
} }
// Gets the current window selected for |kWindow| capture source. Returns
// nullptr if no window is available for selection.
aura::Window* GetSelectedWindow() const;
// Called when either the capture source or type changes. // Called when either the capture source or type changes.
void OnCaptureSourceChanged(CaptureModeSource new_source); void OnCaptureSourceChanged(CaptureModeSource new_source);
void OnCaptureTypeChanged(CaptureModeType new_type); void OnCaptureTypeChanged(CaptureModeType new_type);
...@@ -57,7 +62,7 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner, ...@@ -57,7 +62,7 @@ class ASH_EXPORT CaptureModeSession : public ui::LayerOwner,
void OnTouchEvent(ui::TouchEvent* event) override; void OnTouchEvent(ui::TouchEvent* event) override;
private: private:
// Gets the current window selected for |kWindow| capture source. // Gets the bounds of current window selected for |kWindow| capture source.
gfx::Rect GetSelectedWindowBounds() const; gfx::Rect GetSelectedWindowBounds() const;
// Ensures that the bar widget is on top of everything, and the overlay (which // Ensures that the bar widget is on top of everything, and the overlay (which
......
...@@ -16,4 +16,8 @@ base::FilePath TestCaptureModeDelegate::GetActiveUserDownloadsDir() const { ...@@ -16,4 +16,8 @@ base::FilePath TestCaptureModeDelegate::GetActiveUserDownloadsDir() const {
void TestCaptureModeDelegate::ShowScreenCaptureItemInFolder( void TestCaptureModeDelegate::ShowScreenCaptureItemInFolder(
const base::FilePath& file_path) {} const base::FilePath& file_path) {}
bool TestCaptureModeDelegate::Uses24HourFormat() const {
return false;
}
} // namespace ash } // namespace ash
...@@ -19,6 +19,7 @@ class TestCaptureModeDelegate : public CaptureModeDelegate { ...@@ -19,6 +19,7 @@ class TestCaptureModeDelegate : public CaptureModeDelegate {
// CaptureModeDelegate: // CaptureModeDelegate:
base::FilePath GetActiveUserDownloadsDir() const override; base::FilePath GetActiveUserDownloadsDir() const override;
void ShowScreenCaptureItemInFolder(const base::FilePath& file_path) override; void ShowScreenCaptureItemInFolder(const base::FilePath& file_path) override;
bool Uses24HourFormat() const override;
}; };
} // namespace ash } // namespace ash
......
...@@ -27,6 +27,11 @@ class ASH_PUBLIC_EXPORT CaptureModeDelegate { ...@@ -27,6 +27,11 @@ class ASH_PUBLIC_EXPORT CaptureModeDelegate {
// Shows the screenshot or screen recording item in the screen capture folder. // Shows the screenshot or screen recording item in the screen capture folder.
virtual void ShowScreenCaptureItemInFolder( virtual void ShowScreenCaptureItemInFolder(
const base::FilePath& file_path) = 0; const base::FilePath& file_path) = 0;
// Returns true if the current user is using the 24-hour format (i.e. 14:00
// vs. 2:00 PM). This is used to build the file name of the captured image or
// video.
virtual bool Uses24HourFormat() const = 0;
}; };
} // namespace ash } // namespace ash
......
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
#include "chrome/browser/ui/ash/chrome_capture_mode_delegate.h" #include "chrome/browser/ui/ash/chrome_capture_mode_delegate.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/i18n/time_formatting.h"
#include "chrome/browser/download/download_prefs.h" #include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/platform_util.h" #include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/pref_names.h"
#include "chromeos/login/login_state/login_state.h" #include "chromeos/login/login_state/login_state.h"
#include "components/prefs/pref_service.h"
ChromeCaptureModeDelegate::ChromeCaptureModeDelegate() = default; ChromeCaptureModeDelegate::ChromeCaptureModeDelegate() = default;
...@@ -26,3 +29,12 @@ void ChromeCaptureModeDelegate::ShowScreenCaptureItemInFolder( ...@@ -26,3 +29,12 @@ void ChromeCaptureModeDelegate::ShowScreenCaptureItemInFolder(
platform_util::ShowItemInFolder(ProfileManager::GetActiveUserProfile(), platform_util::ShowItemInFolder(ProfileManager::GetActiveUserProfile(),
file_path); file_path);
} }
bool ChromeCaptureModeDelegate::Uses24HourFormat() const {
Profile* profile = ProfileManager::GetActiveUserProfile();
// TODO(afakhry): Consider moving |prefs::kUse24HourClock| to ash/public so
// we can do this entirely in ash.
if (profile)
return profile->GetPrefs()->GetBoolean(prefs::kUse24HourClock);
return base::GetHourClockType() == base::k24HourClock;
}
...@@ -20,6 +20,7 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate { ...@@ -20,6 +20,7 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate {
// ash::CaptureModeDelegate: // ash::CaptureModeDelegate:
base::FilePath GetActiveUserDownloadsDir() const override; base::FilePath GetActiveUserDownloadsDir() const override;
void ShowScreenCaptureItemInFolder(const base::FilePath& file_path) override; void ShowScreenCaptureItemInFolder(const base::FilePath& file_path) override;
bool Uses24HourFormat() const override;
}; };
#endif // CHROME_BROWSER_UI_ASH_CHROME_CAPTURE_MODE_DELEGATE_H_ #endif // CHROME_BROWSER_UI_ASH_CHROME_CAPTURE_MODE_DELEGATE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment