1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
ash / scanner / scanner_controller.cc [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/scanner/scanner_controller.h"
#include <memory>
#include <utility>
#include <vector>
#include "ash/constants/ash_switches.h"
#include "ash/public/cpp/notification_utils.h"
#include "ash/public/cpp/scanner/scanner_delegate.h"
#include "ash/public/cpp/scanner/scanner_enums.h"
#include "ash/public/cpp/scanner/scanner_profile_scoped_delegate.h"
#include "ash/public/cpp/system/toast_data.h"
#include "ash/public/cpp/system/toast_manager.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/scanner/scanner_command_delegate_impl.h"
#include "ash/scanner/scanner_session.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_refptr.h"
#include "components/account_id/account_id.h"
#include "components/manta/proto/scanner.pb.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notifier_id.h"
namespace ash {
namespace {
constexpr char kScannerActionNotificationId[] = "scanner_action_notification";
constexpr char kScannerNotifierId[] = "ash.scanner";
constexpr char kScannerActionSuccessToastId[] = "scanner_action_success";
constexpr char kScannerActionFailureToastId[] = "scanner_action_failure";
// Shows an action progress notification. Note that this will remove the
// previous action notification if there is one.
void ShowActionProgressNotification(
manta::proto::ScannerAction::ActionCase action_case) {
message_center::RichNotificationData optional_fields;
// Show an infinite loading progress bar.
optional_fields.progress = -1;
optional_fields.never_timeout = true;
auto* message_center = message_center::MessageCenter::Get();
message_center->RemoveNotification(kScannerActionNotificationId,
/*by_user=*/false);
// TODO: crbug.com/375967525 - Finalize the action notification strings and
// icon.
message_center->AddNotification(CreateSystemNotificationPtr(
message_center::NOTIFICATION_TYPE_PROGRESS, kScannerActionNotificationId,
action_case == manta::proto::ScannerAction::kCopyToClipboard
? u"Copying text..."
: u"Creating...",
/*message=*/u"",
/*display_source=*/u"", GURL(),
message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
kScannerNotifierId,
NotificationCatalogName::kScannerAction),
optional_fields, /*delegate=*/nullptr, kCaptureModeIcon,
message_center::SystemNotificationWarningLevel::NORMAL));
}
// Should be called when an action finishes execution.
void OnActionFinished(manta::proto::ScannerAction::ActionCase action_case,
bool success) {
// Remove the action progress notification.
message_center::MessageCenter::Get()->RemoveNotification(
kScannerActionNotificationId,
/*by_user=*/false);
if (success) {
// TODO: crbug.com/375967525 - Finalize the action toast string.
if (action_case == manta::proto::ScannerAction::kCopyToClipboard) {
ToastManager::Get()->Show(ToastData(
kScannerActionSuccessToastId, ToastCatalogName::kScannerActionSuccess,
u"Text copied to clipboard"));
}
// TODO: crbug.com/383925780 - We should also show a toast for other action
// cases once the feedback mechanism is ready.
} else {
// TODO: crbug.com/383926250 - The action failure text should depend on the
// type of action attempted.
constexpr char16_t kPlaceholderActionFailureText[] = u"Action Failed";
ToastManager::Get()->Show(ToastData(kScannerActionFailureToastId,
ToastCatalogName::kScannerActionFailure,
kPlaceholderActionFailureText));
}
}
} // namespace
ScannerController::ScannerController(std::unique_ptr<ScannerDelegate> delegate)
: delegate_(std::move(delegate)) {}
ScannerController::~ScannerController() = default;
void ScannerController::OnActiveUserSessionChanged(
const AccountId& account_id) {
scanner_session_ = nullptr;
command_delegate_ = nullptr;
}
bool ScannerController::CanStartSession() {
ScannerProfileScopedDelegate* profile_scoped_delegate =
delegate_->GetProfileScopedDelegate();
if (profile_scoped_delegate == nullptr) {
return false;
}
return profile_scoped_delegate->GetSystemState().status ==
ScannerStatus::kEnabled;
}
ScannerSession* ScannerController::StartNewSession() {
// Reset the current session if there is one. We do this here to ensure that
// the old session is destroyed before attempting to create the new session
// (to avoid subtle issues from having simultaneously existing sessions).
scanner_session_ = nullptr;
if (CanStartSession()) {
ScannerProfileScopedDelegate* profile_scoped_delegate =
delegate_->GetProfileScopedDelegate();
// Keep the existing `command_delegate_` if there is one, to allow commands
// from previous sessions to continue in the background if needed.
if (command_delegate_ == nullptr) {
command_delegate_ =
std::make_unique<ScannerCommandDelegateImpl>(profile_scoped_delegate);
}
scanner_session_ = std::make_unique<ScannerSession>(
profile_scoped_delegate, command_delegate_.get());
}
return scanner_session_.get();
}
void ScannerController::FetchActionsForImage(
scoped_refptr<base::RefCountedMemory> jpeg_bytes,
ScannerSession::FetchActionsCallback callback) {
if (!scanner_session_) {
std::move(callback).Run({});
return;
}
scanner_session_->FetchActionsForImage(jpeg_bytes, std::move(callback));
}
void ScannerController::OnSessionUIClosed() {
scanner_session_ = nullptr;
}
void ScannerController::ExecuteAction(
const ScannerActionViewModel& scanner_action) {
const manta::proto::ScannerAction::ActionCase action_case =
scanner_action.GetActionCase();
scanner_action.ExecuteAction(base::BindOnce(&OnActionFinished, action_case));
ShowActionProgressNotification(action_case);
}
bool ScannerController::HasActiveSessionForTesting() const {
return !!scanner_session_;
}
} // namespace ash