#include "ash/assistant/model/assistant_response.h"
#include <utility>
#include "ash/assistant/model/assistant_response_observer.h"
#include "ash/assistant/model/ui/assistant_error_element.h"
#include "ash/assistant/model/ui/assistant_ui_element.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/unguessable_token.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
#include "chromeos/ash/services/assistant/public/cpp/features.h"
namespace ash {
struct AssistantResponse::PendingUiElement {
public:
PendingUiElement() = default;
~PendingUiElement() = default;
PendingUiElement(const PendingUiElement&) = delete;
PendingUiElement& operator=(const PendingUiElement&) = delete;
std::unique_ptr<AssistantUiElement> ui_element;
bool is_processing = false;
};
class AssistantResponse::Processor {
public:
Processor(AssistantResponse* response, ProcessingCallback callback)
: response_(response), callback_(std::move(callback)) {}
Processor(const Processor& copy) = delete;
Processor& operator=(const Processor& assign) = delete;
~Processor() {
if (callback_)
std::move(callback_).Run(false);
}
void Process() {
DCHECK_EQ(ProcessingState::kUnprocessed, response_->processing_state());
response_->set_processing_state(ProcessingState::kProcessing);
processing_count_ = response_->GetUiElements().size();
if (processing_count_ == 0) {
TryFinishing();
return;
}
for (const auto& ui_element : response_->GetUiElements()) {
ui_element->Process(
base::BindOnce(&AssistantResponse::Processor::OnFinishedProcessing,
weak_ptr_factory_.GetWeakPtr()));
}
}
private:
void OnFinishedProcessing() {
--processing_count_;
TryFinishing();
}
void TryFinishing() {
if (!callback_ || processing_count_ > 0)
return;
response_->set_processing_state(ProcessingState::kProcessed);
std::move(callback_).Run(true);
}
const raw_ptr<AssistantResponse, ExperimentalAsh> response_;
ProcessingCallback callback_;
int processing_count_ = 0;
base::WeakPtrFactory<AssistantResponse::Processor> weak_ptr_factory_{this};
};
AssistantResponse::AssistantResponse() = default;
AssistantResponse::~AssistantResponse() {
processor_.reset();
}
void AssistantResponse::AddObserver(AssistantResponseObserver* observer) const {
observers_.AddObserver(observer);
}
void AssistantResponse::RemoveObserver(
AssistantResponseObserver* observer) const {
observers_.RemoveObserver(observer);
}
void AssistantResponse::AddUiElement(
std::unique_ptr<AssistantUiElement> ui_element) {
auto pending_ui_element = std::make_unique<PendingUiElement>();
pending_ui_element->ui_element = std::move(ui_element);
pending_ui_element->is_processing = true;
pending_ui_elements_.push_back(std::move(pending_ui_element));
pending_ui_elements_.back()->ui_element->Process(base::BindOnce(
[](const base::WeakPtr<AssistantResponse>& self,
PendingUiElement* pending_ui_element) {
if (!self)
return;
pending_ui_element->is_processing = false;
while (!self->pending_ui_elements_.empty() &&
!self->pending_ui_elements_.front()->is_processing) {
self->ui_elements_.push_back(
std::move(self->pending_ui_elements_.front()->ui_element));
self->pending_ui_elements_.pop_front();
self->NotifyUiElementAdded(self->ui_elements_.back().get());
}
},
weak_factory_.GetWeakPtr(),
base::Unretained(pending_ui_elements_.back().get())));
}
const std::vector<std::unique_ptr<AssistantUiElement>>&
AssistantResponse::GetUiElements() const {
return ui_elements_;
}
void AssistantResponse::AddSuggestions(
const std::vector<AssistantSuggestion>& suggestions) {
for (const auto& suggestion : suggestions)
suggestions_.push_back(suggestion);
NotifySuggestionsAdded(suggestions);
}
const assistant::AssistantSuggestion* AssistantResponse::GetSuggestionById(
const base::UnguessableToken& id) const {
for (auto& suggestion : suggestions_) {
if (suggestion.id == id)
return &suggestion;
}
return nullptr;
}
const std::vector<assistant::AssistantSuggestion>&
AssistantResponse::GetSuggestions() const {
return suggestions_;
}
void AssistantResponse::Process(ProcessingCallback callback) {
processor_ = std::make_unique<Processor>(this, std::move(callback));
processor_->Process();
}
void AssistantResponse::NotifyUiElementAdded(
const AssistantUiElement* ui_element) {
for (auto& observer : observers_)
observer.OnUiElementAdded(ui_element);
}
void AssistantResponse::NotifySuggestionsAdded(
const std::vector<AssistantSuggestion>& suggestions) {
for (auto& observer : observers_)
observer.OnSuggestionsAdded(suggestions);
}
bool AssistantResponse::ContainsUiElement(
const AssistantUiElement* element) const {
DCHECK(element);
bool contains_element = base::Contains(
ui_elements_, *element, &std::unique_ptr<AssistantUiElement>::operator*);
return contains_element || ContainsPendingUiElement(element);
}
bool AssistantResponse::ContainsPendingUiElement(
const AssistantUiElement* element) const {
DCHECK(element);
return base::ranges::any_of(
pending_ui_elements_,
[element](const std::unique_ptr<PendingUiElement>& other) {
return *other->ui_element == *element;
});
}
}